Merge "Add ability to toggle themed icons (1/3)" into main
diff --git a/res/layout/customization_option_entry_themed_icons.xml b/res/layout/customization_option_entry_themed_icons.xml
index 06461dc..8710f8f 100644
--- a/res/layout/customization_option_entry_themed_icons.xml
+++ b/res/layout/customization_option_entry_themed_icons.xml
@@ -54,7 +54,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
- android:clickable="false"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/src/com/android/customization/picker/themedicon/data/repository/ThemedIconRepository.kt b/src/com/android/customization/picker/themedicon/data/repository/ThemedIconRepository.kt
new file mode 100644
index 0000000..6e86ac8
--- /dev/null
+++ b/src/com/android/customization/picker/themedicon/data/repository/ThemedIconRepository.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.customization.picker.themedicon.data.repository
+
+import kotlinx.coroutines.flow.Flow
+
+interface ThemedIconRepository {
+
+ val isAvailable: Flow<Boolean>
+
+ val isActivated: Flow<Boolean>
+
+ suspend fun setThemedIconEnabled(enabled: Boolean)
+}
diff --git a/src/com/android/customization/picker/themedicon/data/repository/ThemedIconRepositoryImpl.kt b/src/com/android/customization/picker/themedicon/data/repository/ThemedIconRepositoryImpl.kt
new file mode 100644
index 0000000..ab1cdb3
--- /dev/null
+++ b/src/com/android/customization/picker/themedicon/data/repository/ThemedIconRepositoryImpl.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.customization.picker.themedicon.data.repository
+
+import android.content.ContentResolver
+import android.content.ContentValues
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.database.ContentObserver
+import android.net.Uri
+import com.android.customization.module.CustomizationPreferences
+import com.android.themepicker.R
+import com.android.wallpaper.module.InjectorProvider
+import com.android.wallpaper.picker.di.modules.BackgroundDispatcher
+import dagger.hilt.android.qualifiers.ApplicationContext
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+@Singleton
+class ThemedIconRepositoryImpl
+@Inject
+constructor(
+ @ApplicationContext private val appContext: Context,
+ private val contentResolver: ContentResolver,
+ packageManager: PackageManager,
+ @BackgroundDispatcher private val backgroundScope: CoroutineScope,
+) : ThemedIconRepository {
+ private val uri: MutableStateFlow<Uri?> = MutableStateFlow(null)
+ private var getUriJob: Job =
+ backgroundScope.launch {
+ val homeIntent = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+ val resolveInfo =
+ packageManager.resolveActivity(
+ homeIntent,
+ PackageManager.MATCH_DEFAULT_ONLY or PackageManager.GET_META_DATA,
+ )
+ val providerAuthority =
+ resolveInfo
+ ?.activityInfo
+ ?.metaData
+ ?.getString(appContext.getString(R.string.themed_icon_metadata_key))
+ val providerInfo =
+ providerAuthority?.let { authority ->
+ val info = packageManager.resolveContentProvider(authority, 0)
+ val hasPermission =
+ info?.readPermission?.let {
+ if (it.isNotEmpty()) {
+ appContext.checkSelfPermission(it) ==
+ PackageManager.PERMISSION_GRANTED
+ } else true
+ } ?: true
+ if (!hasPermission) {
+ null
+ } else {
+ info
+ }
+ }
+ uri.value =
+ providerInfo?.let {
+ Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(providerInfo.authority)
+ .appendPath(ICON_THEMED)
+ .build()
+ }
+ }
+
+ override val isAvailable: Flow<Boolean> =
+ uri.map { it != null }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ override val isActivated: Flow<Boolean> =
+ callbackFlow {
+ var disposableHandle: DisposableHandle? = null
+ launch {
+ uri.collect {
+ disposableHandle?.dispose()
+ if (it != null) {
+ val contentObserver =
+ object : ContentObserver(null) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(getThemedIconEnabled(it))
+ }
+ }
+ contentResolver.registerContentObserver(
+ it,
+ /* notifyForDescendants= */ true,
+ contentObserver,
+ )
+
+ trySend(getThemedIconEnabled(it))
+
+ disposableHandle = DisposableHandle {
+ contentResolver.unregisterContentObserver(contentObserver)
+ }
+ }
+ }
+ }
+ awaitClose { disposableHandle?.dispose() }
+ }
+ .stateIn(
+ scope = backgroundScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ private fun getThemedIconEnabled(uri: Uri): Boolean {
+ val cursor =
+ contentResolver.query(
+ uri,
+ /* projection= */ null,
+ /* selection= */ null,
+ /* selectionArgs= */ null,
+ /* sortOrder= */ null,
+ )
+ var isEnabled = false
+ if (cursor != null && cursor.moveToNext()) {
+ isEnabled = (cursor.getInt(cursor.getColumnIndex(COL_ICON_THEMED_VALUE)) == ENABLED)
+ val preferences =
+ InjectorProvider.getInjector().getPreferences(appContext)
+ as CustomizationPreferences
+ if (preferences.getThemedIconEnabled() != isEnabled) {
+ preferences.setThemedIconEnabled(isEnabled)
+ }
+ }
+ cursor?.close()
+ return isEnabled
+ }
+
+ override suspend fun setThemedIconEnabled(enabled: Boolean) {
+ getUriJob.join()
+ uri.value?.let {
+ val values = ContentValues()
+ values.put(COL_ICON_THEMED_VALUE, enabled)
+ contentResolver.update(it, values, /* where= */ null, /* selectionArgs= */ null)
+ }
+ }
+
+ companion object {
+ private const val ICON_THEMED = "icon_themed"
+ private const val COL_ICON_THEMED_VALUE = "boolean_value"
+ private const val ENABLED = 1
+ }
+}
diff --git a/src/com/android/customization/picker/themedicon/domain/interactor/ThemedIconInteractor.kt b/src/com/android/customization/picker/themedicon/domain/interactor/ThemedIconInteractor.kt
new file mode 100644
index 0000000..8a33f96
--- /dev/null
+++ b/src/com/android/customization/picker/themedicon/domain/interactor/ThemedIconInteractor.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.customization.picker.themedicon.domain.interactor
+
+import com.android.customization.picker.themedicon.data.repository.ThemedIconRepository
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.flow.Flow
+
+@Singleton
+class ThemedIconInteractor @Inject constructor(private val repository: ThemedIconRepository) {
+ val isAvailable: Flow<Boolean> = repository.isAvailable
+ val isActivated: Flow<Boolean> = repository.isActivated
+
+ suspend fun setThemedIconEnabled(enabled: Boolean) = repository.setThemedIconEnabled(enabled)
+}
diff --git a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
index 60925bf..c71f860 100644
--- a/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
+++ b/src/com/android/wallpaper/customization/ui/binder/ThemePickerCustomizationOptionBinder.kt
@@ -20,6 +20,7 @@
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
+import android.widget.Switch
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
@@ -141,6 +142,13 @@
val optionColorContrastIcon: ImageView =
optionColorContrast.requireViewById(R.id.option_entry_color_contrast_icon)
+ val optionThemedIcons =
+ homeScreenCustomizationOptionEntries
+ .find { it.first == ThemePickerHomeCustomizationOption.THEMED_ICONS }
+ ?.second
+ val optionThemedIconsSwitch =
+ optionThemedIcons?.findViewById<Switch>(R.id.option_entry_themed_icons_switch)
+
val optionsViewModel =
viewModel.customizationOptionsViewModel as ThemePickerCustomizationOptionsViewModel
lifecycleOwner.lifecycleScope.launch {
@@ -243,6 +251,28 @@
}
}
}
+
+ if (optionThemedIconsSwitch != null) {
+ launch {
+ optionsViewModel.themedIconViewModel.isAvailable.collect { isAvailable ->
+ optionThemedIconsSwitch.isEnabled = isAvailable
+ }
+ }
+
+ launch {
+ optionsViewModel.themedIconViewModel.isActivated.collect {
+ optionThemedIconsSwitch.isChecked = it
+ }
+ }
+
+ launch {
+ optionsViewModel.themedIconViewModel.toggleThemedIcon.collect {
+ optionThemedIconsSwitch.setOnCheckedChangeListener { _, _ ->
+ launch { it.invoke() }
+ }
+ }
+ }
+ }
}
}
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
index 4832e1d..29b8b6a 100644
--- a/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ThemePickerCustomizationOptionsViewModel.kt
@@ -48,6 +48,7 @@
shapeGridPickerViewModelFactory: ShapeGridPickerViewModel.Factory,
val colorContrastSectionViewModel: ColorContrastSectionViewModel2,
val darkModeViewModel: DarkModeViewModel,
+ val themedIconViewModel: ThemedIconViewModel,
@Assisted private val viewModelScope: CoroutineScope,
) : CustomizationOptionsViewModel {
diff --git a/src/com/android/wallpaper/customization/ui/viewmodel/ThemedIconViewModel.kt b/src/com/android/wallpaper/customization/ui/viewmodel/ThemedIconViewModel.kt
new file mode 100644
index 0000000..ac1ebdc
--- /dev/null
+++ b/src/com/android/wallpaper/customization/ui/viewmodel/ThemedIconViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wallpaper.customization.ui.viewmodel
+
+import com.android.customization.module.logging.ThemesUserEventLogger
+import com.android.customization.picker.themedicon.domain.interactor.ThemedIconInteractor
+import dagger.hilt.android.scopes.ViewModelScoped
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+@ViewModelScoped
+class ThemedIconViewModel
+@Inject
+constructor(
+ private val interactor: ThemedIconInteractor,
+ private val logger: ThemesUserEventLogger,
+) {
+ val isAvailable: Flow<Boolean> = interactor.isAvailable
+ val isActivated: Flow<Boolean> = interactor.isActivated
+
+ val toggleThemedIcon: Flow<suspend () -> Unit> =
+ isActivated.map {
+ {
+ val newValue = !it
+ interactor.setThemedIconEnabled(newValue)
+ logger.logThemedIconApplied(newValue)
+ }
+ }
+}
diff --git a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
index 1f6938f..9fc59c7 100644
--- a/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
+++ b/src/com/android/wallpaper/picker/common/preview/ui/binder/ThemePickerWorkspaceCallbackBinder.kt
@@ -222,6 +222,12 @@
workspaceCallback.sendMessage(MESSAGE_ID_UPDATE_COLOR, bundle)
}
}
+
+ launch {
+ viewModel.themedIconViewModel.isActivated.collect {
+ workspaceCallback.sendMessage(MESSAGE_ID_UPDATE_COLOR, Bundle.EMPTY)
+ }
+ }
}
}
}
diff --git a/src_override/com/android/wallpaper/modules/ThemePickerAppModule.kt b/src_override/com/android/wallpaper/modules/ThemePickerAppModule.kt
index a2989bb..5f4da33 100644
--- a/src_override/com/android/wallpaper/modules/ThemePickerAppModule.kt
+++ b/src_override/com/android/wallpaper/modules/ThemePickerAppModule.kt
@@ -30,6 +30,8 @@
import com.android.customization.picker.color.data.repository.ColorPickerRepository2
import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl
import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl2
+import com.android.customization.picker.themedicon.data.repository.ThemedIconRepository
+import com.android.customization.picker.themedicon.data.repository.ThemedIconRepositoryImpl
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
@@ -99,6 +101,10 @@
@Binds
@Singleton
+ abstract fun bindThemedIconRepository(impl: ThemedIconRepositoryImpl): ThemedIconRepository
+
+ @Binds
+ @Singleton
abstract fun bindCreativeCategoryInteractor(
impl: CreativeCategoryInteractorImpl
): CreativeCategoryInteractor
diff --git a/tests/common/src/com/android/customization/module/logging/TestThemesUserEventLogger.kt b/tests/common/src/com/android/customization/module/logging/TestThemesUserEventLogger.kt
index 05c95b0..2bb88d8 100644
--- a/tests/common/src/com/android/customization/module/logging/TestThemesUserEventLogger.kt
+++ b/tests/common/src/com/android/customization/module/logging/TestThemesUserEventLogger.kt
@@ -43,6 +43,9 @@
var useDarkTheme: Boolean = false
private set
+ var useThemedIcon: Boolean = false
+ private set
+
override fun logThemeColorApplied(@ColorSource source: Int, style: Int, seedColor: Int) {
this.themeColorSource = source
this.themeColorStyle = style
@@ -59,7 +62,9 @@
this.clockSize = clockSize
}
- override fun logThemedIconApplied(useThemeIcon: Boolean) {}
+ override fun logThemedIconApplied(useThemeIcon: Boolean) {
+ this.useThemedIcon = useThemeIcon
+ }
override fun logLockScreenNotificationApplied(showLockScreenNotifications: Boolean) {}
diff --git a/tests/common/src/com/android/customization/picker/themedicon/data/repository/FakeThemedIconRepository.kt b/tests/common/src/com/android/customization/picker/themedicon/data/repository/FakeThemedIconRepository.kt
new file mode 100644
index 0000000..0cae929
--- /dev/null
+++ b/tests/common/src/com/android/customization/picker/themedicon/data/repository/FakeThemedIconRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.customization.picker.themedicon.data.repository
+
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+@Singleton
+class FakeThemedIconRepository @Inject constructor() : ThemedIconRepository {
+ override val isAvailable = MutableStateFlow(true).asStateFlow()
+
+ private val _isActivated = MutableStateFlow(false)
+ override val isActivated = _isActivated.asStateFlow()
+
+ override suspend fun setThemedIconEnabled(enabled: Boolean) {
+ _isActivated.value = enabled
+ }
+}
diff --git a/tests/module/src/com/android/wallpaper/ThemePickerTestModule.kt b/tests/module/src/com/android/wallpaper/ThemePickerTestModule.kt
index 2eb5c10..25a6d65 100644
--- a/tests/module/src/com/android/wallpaper/ThemePickerTestModule.kt
+++ b/tests/module/src/com/android/wallpaper/ThemePickerTestModule.kt
@@ -30,6 +30,8 @@
import com.android.customization.picker.color.data.repository.ColorPickerRepository2
import com.android.customization.picker.color.data.repository.ColorPickerRepositoryImpl
import com.android.customization.picker.color.data.repository.FakeColorPickerRepository2
+import com.android.customization.picker.themedicon.data.repository.FakeThemedIconRepository
+import com.android.customization.picker.themedicon.data.repository.ThemedIconRepository
import com.android.customization.testing.TestCustomizationInjector
import com.android.customization.testing.TestDefaultCustomizationPreferences
import com.android.systemui.shared.clocks.ClockRegistry
@@ -100,6 +102,10 @@
@Binds
@Singleton
+ abstract fun bindThemedIconRepository(impl: FakeThemedIconRepository): ThemedIconRepository
+
+ @Binds
+ @Singleton
abstract fun bindCustomizationInjector(impl: TestCustomizationInjector): CustomizationInjector
@Binds
diff --git a/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ThemedIconViewModelTest.kt b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ThemedIconViewModelTest.kt
new file mode 100644
index 0000000..1fb4bed
--- /dev/null
+++ b/tests/robotests/src/com/android/wallpaper/customization/ui/viewmodel/ThemedIconViewModelTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wallpaper.customization.ui.viewmodel
+
+import com.android.customization.module.logging.TestThemesUserEventLogger
+import com.android.customization.picker.themedicon.domain.interactor.ThemedIconInteractor
+import com.android.wallpaper.testing.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import javax.inject.Inject
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@HiltAndroidTest
+@OptIn(ExperimentalCoroutinesApi::class)
+@RunWith(RobolectricTestRunner::class)
+class ThemedIconViewModelTest {
+ @get:Rule var hiltRule = HiltAndroidRule(this)
+
+ @Inject lateinit var logger: TestThemesUserEventLogger
+ @Inject lateinit var themedIconInteractor: ThemedIconInteractor
+ private lateinit var themedIconViewModel: ThemedIconViewModel
+
+ @Inject lateinit var testDispatcher: TestDispatcher
+ @Inject lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ hiltRule.inject()
+ Dispatchers.setMain(testDispatcher)
+
+ themedIconViewModel = ThemedIconViewModel(themedIconInteractor, logger)
+ }
+
+ @Test
+ fun toggleThemedIcon() {
+ testScope.runTest {
+ val isActivated = collectLastValue(themedIconViewModel.isActivated)
+ val toggleThemedIcon = collectLastValue(themedIconViewModel.toggleThemedIcon)
+ assertThat(isActivated()).isFalse()
+
+ toggleThemedIcon()?.invoke()
+
+ assertThat(isActivated()).isTrue()
+
+ toggleThemedIcon()?.invoke()
+
+ assertThat(isActivated()).isFalse()
+ }
+ }
+
+ @Test
+ fun toggleThemedIcon_shouldLogThemedIcon() {
+ testScope.runTest {
+ val toggleThemedIcon = collectLastValue(themedIconViewModel.toggleThemedIcon)
+ assertThat(logger.useThemedIcon).isFalse()
+
+ toggleThemedIcon()?.invoke()
+
+ assertThat(logger.useThemedIcon).isTrue()
+ }
+ }
+}