Loading src/com/android/customization/picker/icon/data/repository/IconStyleRepository.kt +7 −1 Original line number Diff line number Diff line Loading @@ -35,5 +35,11 @@ interface IconStyleRepository { val selectedIconStyle: Flow<IconStyle> suspend fun setIconStyle(iconStyle: IconStyle) /** * Sets the selected icon style. * * @param iconStyle The icon style to set. * @return True if the icon style was set successfully and data was updated, false otherwise. */ suspend fun setIconStyle(iconStyle: IconStyle): Boolean } src/com/android/customization/picker/icon/data/repository/ThemePickerIconStyleRepository.kt +5 −2 Original line number Diff line number Diff line Loading @@ -184,12 +184,15 @@ constructor( } } override suspend fun setIconStyle(iconStyle: IconStyle) { override suspend fun setIconStyle(iconStyle: IconStyle): Boolean { themedIconUri.first()?.let { val values = ContentValues() values.put(COL_ICON_THEMED_VALUE, iconStyle == ThemePickerIconStyle.MONOCHROME) val rowsUpdated = contentResolver.update(it, values, /* where= */ null, /* selectionArgs= */ null) return rowsUpdated > 0 } return false } companion object { Loading src/com/android/customization/picker/icon/domain/interactor/AppIconInteractor.kt +2 −1 Original line number Diff line number Diff line Loading @@ -56,5 +56,6 @@ constructor( suspend fun applyShape(shapeKey: String) = shapeRepository.applyShape(shapeKey) suspend fun applyIconStyle(iconStyle: IconStyle) = iconStyleRepository.setIconStyle(iconStyle) suspend fun applyIconStyle(iconStyle: IconStyle): Boolean = iconStyleRepository.setIconStyle(iconStyle) } src/com/android/wallpaper/customization/ui/viewmodel/AppIconPickerViewModel.kt +40 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wallpaper.customization.ui.viewmodel import android.content.Context import android.util.Log import com.android.customization.model.grid.ShapeOptionModel import com.android.customization.module.logging.ThemesUserEventLogger import com.android.customization.picker.grid.ui.viewmodel.ShapeIconViewModel Loading @@ -34,7 +35,10 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ViewModelScoped import java.util.Locale import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow Loading @@ -48,6 +52,7 @@ import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout class AppIconPickerViewModel @AssistedInject Loading Loading @@ -115,7 +120,7 @@ constructor( } //// Style private val selectedIconStyle = interactor.selectedIconStyle val selectedIconStyle = interactor.selectedIconStyle private val overridingIconStyle: MutableStateFlow<IconStyle?> = MutableStateFlow(null) val previewingIconStyle = combine(selectedIconStyle, overridingIconStyle) { selected, overriding -> Loading @@ -127,9 +132,9 @@ constructor( started = SharingStarted.WhileSubscribed(), replay = 1, ) val overridingIconStyleModel = combine(overridingIconStyle, iconStylesModels) { overridingIconStyle, iconStylesModels -> iconStylesModels.find { it.iconStyle == overridingIconStyle } val previewingIconStyleModel = combine(previewingIconStyle, iconStylesModels) { previewingIconStyle, iconStylesModels -> iconStylesModels.find { it.iconStyle == previewingIconStyle } } val styleOptions: Flow<List<OptionItemViewModel2<IconStyleModel>>> = iconStylesModels.map { Loading Loading @@ -353,13 +358,34 @@ constructor( } if (styleNeedsUpdate) { coroutineScope { launch { overridingIconStyle?.let { interactor.applyIconStyle(it) } } val waitForUpdate = launch { try { withTimeout(ICON_UPDATE_TIMEOUT) { // Drop the first emitted icon style since it is the current // selection. The next emitted icon style signals an update. selectedIconStyle.drop(1).take(1).collect { return@collect } overridingIconStyle?.let { logger.logThemedIconApplied(it.getIsThemedIcon()) } } catch (e: TimeoutCancellationException) { Log.w(TAG, "Timed out waiting for icon update", e) } } launch { val success = overridingIconStyle?.let { interactor.applyIconStyle(it) } if (success == true) { logger.logThemedIconApplied( overridingIconStyle.getIsThemedIcon() ) } else { Log.w(TAG, "Apply unsuccessful, no data was updated") waitForUpdate.cancel() } } try { waitForUpdate.join() } catch (_: CancellationException) {} } } } Loading Loading @@ -447,4 +473,9 @@ constructor( interface Factory { fun create(viewModelScope: CoroutineScope): AppIconPickerViewModel } companion object { const val TAG = "AppIconPickerViewModel" val ICON_UPDATE_TIMEOUT = 1000.milliseconds } } tests/common/src/com/android/customization/picker/icon/data/repository/FakeIconStyleRepository.kt +8 −2 Original line number Diff line number Diff line Loading @@ -42,6 +42,9 @@ class FakeIconStyleRepository @Inject constructor() : IconStyleRepository { private val _selectedIconStyle = MutableStateFlow<IconStyle>(ThemePickerIconStyle.DEFAULT) override val selectedIconStyle = _selectedIconStyle.asStateFlow() var shouldApplySuccessfully = true var shouldUpdateSuccessfully = true override val iconStyleModels: Flow<List<IconStyleModel>> = isCustomizationAvailable.map { isThemedIconAvailable -> ThemePickerIconStyle.entries Loading @@ -61,9 +64,12 @@ class FakeIconStyleRepository @Inject constructor() : IconStyleRepository { ) } override suspend fun setIconStyle(iconStyle: IconStyle) { override suspend fun setIconStyle(iconStyle: IconStyle): Boolean { if (shouldApplySuccessfully && shouldUpdateSuccessfully) { _selectedIconStyle.value = iconStyle } return shouldApplySuccessfully } override suspend fun setThemedIconEnabled(enabled: Boolean) { _isThemedIconActivated.value = enabled Loading Loading
src/com/android/customization/picker/icon/data/repository/IconStyleRepository.kt +7 −1 Original line number Diff line number Diff line Loading @@ -35,5 +35,11 @@ interface IconStyleRepository { val selectedIconStyle: Flow<IconStyle> suspend fun setIconStyle(iconStyle: IconStyle) /** * Sets the selected icon style. * * @param iconStyle The icon style to set. * @return True if the icon style was set successfully and data was updated, false otherwise. */ suspend fun setIconStyle(iconStyle: IconStyle): Boolean }
src/com/android/customization/picker/icon/data/repository/ThemePickerIconStyleRepository.kt +5 −2 Original line number Diff line number Diff line Loading @@ -184,12 +184,15 @@ constructor( } } override suspend fun setIconStyle(iconStyle: IconStyle) { override suspend fun setIconStyle(iconStyle: IconStyle): Boolean { themedIconUri.first()?.let { val values = ContentValues() values.put(COL_ICON_THEMED_VALUE, iconStyle == ThemePickerIconStyle.MONOCHROME) val rowsUpdated = contentResolver.update(it, values, /* where= */ null, /* selectionArgs= */ null) return rowsUpdated > 0 } return false } companion object { Loading
src/com/android/customization/picker/icon/domain/interactor/AppIconInteractor.kt +2 −1 Original line number Diff line number Diff line Loading @@ -56,5 +56,6 @@ constructor( suspend fun applyShape(shapeKey: String) = shapeRepository.applyShape(shapeKey) suspend fun applyIconStyle(iconStyle: IconStyle) = iconStyleRepository.setIconStyle(iconStyle) suspend fun applyIconStyle(iconStyle: IconStyle): Boolean = iconStyleRepository.setIconStyle(iconStyle) }
src/com/android/wallpaper/customization/ui/viewmodel/AppIconPickerViewModel.kt +40 −9 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wallpaper.customization.ui.viewmodel import android.content.Context import android.util.Log import com.android.customization.model.grid.ShapeOptionModel import com.android.customization.module.logging.ThemesUserEventLogger import com.android.customization.picker.grid.ui.viewmodel.ShapeIconViewModel Loading @@ -34,7 +35,10 @@ import dagger.assisted.AssistedInject import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ViewModelScoped import java.util.Locale import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow Loading @@ -48,6 +52,7 @@ import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout class AppIconPickerViewModel @AssistedInject Loading Loading @@ -115,7 +120,7 @@ constructor( } //// Style private val selectedIconStyle = interactor.selectedIconStyle val selectedIconStyle = interactor.selectedIconStyle private val overridingIconStyle: MutableStateFlow<IconStyle?> = MutableStateFlow(null) val previewingIconStyle = combine(selectedIconStyle, overridingIconStyle) { selected, overriding -> Loading @@ -127,9 +132,9 @@ constructor( started = SharingStarted.WhileSubscribed(), replay = 1, ) val overridingIconStyleModel = combine(overridingIconStyle, iconStylesModels) { overridingIconStyle, iconStylesModels -> iconStylesModels.find { it.iconStyle == overridingIconStyle } val previewingIconStyleModel = combine(previewingIconStyle, iconStylesModels) { previewingIconStyle, iconStylesModels -> iconStylesModels.find { it.iconStyle == previewingIconStyle } } val styleOptions: Flow<List<OptionItemViewModel2<IconStyleModel>>> = iconStylesModels.map { Loading Loading @@ -353,13 +358,34 @@ constructor( } if (styleNeedsUpdate) { coroutineScope { launch { overridingIconStyle?.let { interactor.applyIconStyle(it) } } val waitForUpdate = launch { try { withTimeout(ICON_UPDATE_TIMEOUT) { // Drop the first emitted icon style since it is the current // selection. The next emitted icon style signals an update. selectedIconStyle.drop(1).take(1).collect { return@collect } overridingIconStyle?.let { logger.logThemedIconApplied(it.getIsThemedIcon()) } } catch (e: TimeoutCancellationException) { Log.w(TAG, "Timed out waiting for icon update", e) } } launch { val success = overridingIconStyle?.let { interactor.applyIconStyle(it) } if (success == true) { logger.logThemedIconApplied( overridingIconStyle.getIsThemedIcon() ) } else { Log.w(TAG, "Apply unsuccessful, no data was updated") waitForUpdate.cancel() } } try { waitForUpdate.join() } catch (_: CancellationException) {} } } } Loading Loading @@ -447,4 +473,9 @@ constructor( interface Factory { fun create(viewModelScope: CoroutineScope): AppIconPickerViewModel } companion object { const val TAG = "AppIconPickerViewModel" val ICON_UPDATE_TIMEOUT = 1000.milliseconds } }
tests/common/src/com/android/customization/picker/icon/data/repository/FakeIconStyleRepository.kt +8 −2 Original line number Diff line number Diff line Loading @@ -42,6 +42,9 @@ class FakeIconStyleRepository @Inject constructor() : IconStyleRepository { private val _selectedIconStyle = MutableStateFlow<IconStyle>(ThemePickerIconStyle.DEFAULT) override val selectedIconStyle = _selectedIconStyle.asStateFlow() var shouldApplySuccessfully = true var shouldUpdateSuccessfully = true override val iconStyleModels: Flow<List<IconStyleModel>> = isCustomizationAvailable.map { isThemedIconAvailable -> ThemePickerIconStyle.entries Loading @@ -61,9 +64,12 @@ class FakeIconStyleRepository @Inject constructor() : IconStyleRepository { ) } override suspend fun setIconStyle(iconStyle: IconStyle) { override suspend fun setIconStyle(iconStyle: IconStyle): Boolean { if (shouldApplySuccessfully && shouldUpdateSuccessfully) { _selectedIconStyle.value = iconStyle } return shouldApplySuccessfully } override suspend fun setThemedIconEnabled(enabled: Boolean) { _isThemedIconActivated.value = enabled Loading