diff options
author | Santiago Etchebehere <santie@google.com> | 2023-03-03 14:33:30 -0800 |
---|---|---|
committer | Santiago Etchebehere <santie@google.com> | 2023-03-03 15:06:55 -0800 |
commit | 433e13b452bd151ee06179cd512f0d83447b7940 (patch) | |
tree | bc3b4322d1834f3a4ed0d029864a8a2d802dc186 | |
parent | 5577bacd945abdad388d1e2ee27f7ea90b41b49c (diff) | |
download | ThemePicker-433e13b452bd151ee06179cd512f0d83447b7940.tar.gz |
Handle the case of no grid options available
Fixes: 271343879
Test: manually checked with a fake empty URI and added
unit test in GridInteractorTest to test new behavior
Change-Id: I95918b5a73711efb137cbcc35cea1a36324f25a6
Change-Id: I25b83eb5e31cbd0d27e1f599fc5aaa6018fdb20a
7 files changed, 60 insertions, 29 deletions
diff --git a/src/com/android/customization/model/grid/GridOptionsManager.java b/src/com/android/customization/model/grid/GridOptionsManager.java index bff7933d..b7ee37fd 100644 --- a/src/com/android/customization/model/grid/GridOptionsManager.java +++ b/src/com/android/customization/model/grid/GridOptionsManager.java @@ -49,6 +49,7 @@ public class GridOptionsManager implements CustomizationManager<GridOption> { private final LauncherGridOptionsProvider mProvider; private final ThemesUserEventLogger mEventLogger; + private int mGridOptionSize = -1; /** Returns the {@link GridOptionsManager} instance. */ public static GridOptionsManager getInstance(Context context) { @@ -73,16 +74,17 @@ public class GridOptionsManager implements CustomizationManager<GridOption> { @Override public boolean isAvailable() { - int gridOptionSize = 0; - try { - gridOptionSize = sExecutorService.submit(() -> { - List<GridOption> gridOptions = mProvider.fetch(/* reload= */true); - return gridOptions == null ? 0 : gridOptions.size(); - }).get(); - } catch (InterruptedException | ExecutionException e) { - Log.w(TAG, "could not get gridOptionSize", e); + if (mGridOptionSize < 0) { + try { + mGridOptionSize = sExecutorService.submit(() -> { + List<GridOption> gridOptions = mProvider.fetch(/* reload= */true); + return gridOptions == null ? 0 : gridOptions.size(); + }).get(); + } catch (InterruptedException | ExecutionException e) { + Log.w(TAG, "could not get gridOptionSize", e); + } } - return gridOptionSize > 1 && mProvider.areGridsAvailable(); + return mGridOptionSize > 1 && mProvider.areGridsAvailable(); } @Override diff --git a/src/com/android/customization/model/grid/GridSectionController.java b/src/com/android/customization/model/grid/GridSectionController.java index 3e5dba0f..c50bfcc2 100644 --- a/src/com/android/customization/model/grid/GridSectionController.java +++ b/src/com/android/customization/model/grid/GridSectionController.java @@ -96,7 +96,7 @@ public class GridSectionController implements CustomizationSectionController<Gri @Override public void release() { - if (mIsRevampedUiEnabled) { + if (mIsRevampedUiEnabled && mGridOptionsManager.isAvailable()) { mGridOptionsManager.getOptionChangeObservable(/* handler= */ null).removeObserver( mOptionChangeObserver ); diff --git a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java index 5ae283a7..4e775c62 100644 --- a/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java +++ b/src/com/android/customization/model/grid/LauncherGridOptionsProvider.java @@ -126,6 +126,7 @@ public class LauncherGridOptionsProvider { /** * Returns an observable that receives a new value each time that the grid options are changed. + * Do not call if {@link #areGridsAvailable()} returns false */ public LiveData<Object> getOptionChangeObservable( @Nullable Handler handler) { diff --git a/src/com/android/customization/model/grid/data/repository/GridRepository.kt b/src/com/android/customization/model/grid/data/repository/GridRepository.kt index 7c84aec7..9a3be0cc 100644 --- a/src/com/android/customization/model/grid/data/repository/GridRepository.kt +++ b/src/com/android/customization/model/grid/data/repository/GridRepository.kt @@ -35,7 +35,8 @@ import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withContext interface GridRepository { - val optionChanges: Flow<Unit> + suspend fun isAvailable(): Boolean + fun getOptionChanges(): Flow<Unit> suspend fun getOptions(): GridOptionItemsModel } @@ -45,7 +46,11 @@ class GridRepositoryImpl( private val backgroundDispatcher: CoroutineDispatcher, ) : GridRepository { - override val optionChanges: Flow<Unit> = + override suspend fun isAvailable(): Boolean { + return withContext(backgroundDispatcher) { manager.isAvailable } + } + + override fun getOptionChanges(): Flow<Unit> = manager.getOptionChangeObservable(/* handler= */ null).asFlow().map {} private val selectedOption = MutableStateFlow<GridOption?>(null) diff --git a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt b/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt index 5ab9e1fa..cdb679dd 100644 --- a/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt +++ b/src/com/android/customization/model/grid/domain/interactor/GridInteractor.kt @@ -24,6 +24,9 @@ import javax.inject.Provider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn @@ -34,22 +37,30 @@ class GridInteractor( private val snapshotRestorer: Provider<GridSnapshotRestorer>, ) { val options: Flow<GridOptionItemsModel> = - // this upstream flow tells us each time the options are changed. - repository.optionChanges - // when we start, we pretend the options _just_ changed. This way, we load something as - // soon as possible into the flow so it's ready by the time the first observer starts to - // observe. - .onStart { emit(Unit) } - // each time the options changed, we load them. - .map { reload() } - // we place the loaded options in a SharedFlow so downstream observers all - // share the same flow and don't trigger a new one each time they want to start - // observing. - .shareIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - replay = 1, - ) + flow { emit(repository.isAvailable()) } + .flatMapLatest { isAvailable -> + if (isAvailable) { + // this upstream flow tells us each time the options are changed. + repository + .getOptionChanges() + // when we start, we pretend the options _just_ changed. This way, we load + // something as soon as possible into the flow so it's ready by the time the + // first observer starts to observe. + .onStart { emit(Unit) } + // each time the options changed, we load them. + .map { reload() } + // we place the loaded options in a SharedFlow so downstream observers all + // share the same flow and don't trigger a new one each time they want to + // start observing. + .shareIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + replay = 1, + ) + } else { + emptyFlow() + } + } suspend fun setSelectedOption(model: GridOptionItemModel) { model.onSelected.invoke() diff --git a/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt b/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt index 6291c219..59539379 100644 --- a/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt +++ b/tests/src/com/android/customization/model/grid/data/repository/FakeGridRepository.kt @@ -32,13 +32,17 @@ import kotlinx.coroutines.flow.stateIn class FakeGridRepository( private val scope: CoroutineScope, initialOptionCount: Int, + var available: Boolean = true ) : GridRepository { private val _optionChanges = MutableSharedFlow<Unit>( replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST, ) - override val optionChanges: Flow<Unit> = _optionChanges.asSharedFlow() + + override suspend fun isAvailable(): Boolean = available + + override fun getOptionChanges(): Flow<Unit> = _optionChanges.asSharedFlow() private val selectedOptionIndex = MutableStateFlow(0) private var options: GridOptionItemsModel = createOptions(count = initialOptionCount) diff --git a/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt b/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt index 20dd300b..f73d5a38 100644 --- a/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt +++ b/tests/src/com/android/customization/model/grid/domain/interactor/GridInteractorTest.kt @@ -135,4 +135,12 @@ class GridInteractorTest { // External updates do not record a new snapshot with the undo system. assertThat(store.retrieve()).isEqualTo(storedSnapshot) } + + @Test + fun unavailableRepository_emptyOptions() = + testScope.runTest { + repository.available = false + val options = collectLastValue(underTest.options) + assertThat(options()).isNull() + } } |