diff options
13 files changed, 769 insertions, 4 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt new file mode 100644 index 000000000000..3f05fef3c141 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/ColorInversionRepositoryImplTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023 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.systemui.accessibility.data.repository + +import android.os.UserHandle +import android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.settings.FakeSettings +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionRepositoryImplTest : SysuiTestCase() { + + private val testDispatcher = StandardTestDispatcher() + private val scope = TestScope(testDispatcher) + private val settings: FakeSettings = FakeSettings() + + private lateinit var underTest: ColorInversionRepository + + @Before + fun setUp() { + underTest = + ColorInversionRepositoryImpl( + testDispatcher, + settings, + ) + } + + @Test + fun isEnabled_initiallyGetsSettingsValue() = + scope.runTest { + settings.putIntForUser(SETTING_NAME, 1, TEST_USER_1.identifier) + + underTest = + ColorInversionRepositoryImpl( + testDispatcher, + settings, + ) + + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + runCurrent() + + val actualValue: Boolean = underTest.isEnabled(TEST_USER_1).first() + assertThat(actualValue).isTrue() + } + + @Test + fun isEnabled_settingUpdated_valueUpdated() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + + settings.putIntForUser(SETTING_NAME, ENABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + } + + @Test + fun isEnabled_settingForUserOneOnly_valueUpdatedForUserOneOnly() = + scope.runTest { + underTest.isEnabled(TEST_USER_1).launchIn(backgroundScope) + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_1.identifier) + underTest.isEnabled(TEST_USER_2).launchIn(backgroundScope) + settings.putIntForUser(SETTING_NAME, DISABLED, TEST_USER_2.identifier) + + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isFalse() + assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + + settings.putIntForUser(SETTING_NAME, ENABLED, TEST_USER_1.identifier) + runCurrent() + assertThat(underTest.isEnabled(TEST_USER_1).first()).isTrue() + assertThat(underTest.isEnabled(TEST_USER_2).first()).isFalse() + } + + @Test + fun setEnabled() = + scope.runTest { + val success = underTest.setIsEnabled(true, TEST_USER_1) + runCurrent() + assertThat(success).isTrue() + + val actualValue = settings.getIntForUser(SETTING_NAME, TEST_USER_1.identifier) + assertThat(actualValue).isEqualTo(ENABLED) + } + + @Test + fun setDisabled() = + scope.runTest { + val success = underTest.setIsEnabled(false, TEST_USER_1) + runCurrent() + assertThat(success).isTrue() + + val actualValue = settings.getIntForUser(SETTING_NAME, TEST_USER_1.identifier) + assertThat(actualValue).isEqualTo(DISABLED) + } + + companion object { + private const val SETTING_NAME = ACCESSIBILITY_DISPLAY_INVERSION_ENABLED + private const val DISABLED = 0 + private const val ENABLED = 1 + private val TEST_USER_1 = UserHandle.of(1)!! + private val TEST_USER_2 = UserHandle.of(2)!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt new file mode 100644 index 000000000000..f3c3579966fd --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion.domain + +import android.graphics.drawable.TestStubDrawable +import android.widget.Switch +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.tileimpl.SubtitleArrayMapping +import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.android.systemui.qs.tiles.impl.inversion.qsColorInversionTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionTileMapperTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val colorInversionTileConfig = kosmos.qsColorInversionTileConfig + private val subtitleArrayId = + SubtitleArrayMapping.getSubtitleId(colorInversionTileConfig.tileSpec.spec) + private val subtitleArray = context.resources.getStringArray(subtitleArrayId) + // Using lazy (versus =) to make sure we override the right context -- see b/311612168 + private val mapper by lazy { + ColorInversionTileMapper( + context.orCreateTestableResources + .apply { + addOverride(R.drawable.qs_invert_colors_icon_off, TestStubDrawable()) + addOverride(R.drawable.qs_invert_colors_icon_on, TestStubDrawable()) + } + .resources, + context.theme + ) + } + + @Test + fun disabledModel() { + val inputModel = ColorInversionTileModel(false) + + val outputState = mapper.map(colorInversionTileConfig, inputModel) + + val expectedState = + createColorInversionTileState( + QSTileState.ActivationState.INACTIVE, + subtitleArray[1], + R.drawable.qs_invert_colors_icon_off + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + @Test + fun enabledModel() { + val inputModel = ColorInversionTileModel(true) + + val outputState = mapper.map(colorInversionTileConfig, inputModel) + + val expectedState = + createColorInversionTileState( + QSTileState.ActivationState.ACTIVE, + subtitleArray[2], + R.drawable.qs_invert_colors_icon_on + ) + QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) + } + + private fun createColorInversionTileState( + activationState: QSTileState.ActivationState, + secondaryLabel: String, + iconRes: Int, + ): QSTileState { + val label = context.getString(R.string.quick_settings_inversion_label) + return QSTileState( + { Icon.Loaded(context.getDrawable(iconRes)!!, null) }, + label, + activationState, + secondaryLabel, + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), + label, + null, + QSTileState.SideViewIcon.None, + QSTileState.EnabledState.ENABLED, + Switch::class.qualifiedName + ) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt new file mode 100644 index 000000000000..24c7bfb2d324 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.os.UserHandle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository +import com.android.systemui.coroutines.collectValues +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.toCollection +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionTileDataInteractorTest : SysuiTestCase() { + + private val colorInversionRepository = FakeColorInversionRepository() + private val underTest: ColorInversionTileDataInteractor = + ColorInversionTileDataInteractor(colorInversionRepository) + + @Test + fun alwaysAvailable() = runTest { + val availability = underTest.availability(TEST_USER).toCollection(mutableListOf()) + + assertThat(availability).hasSize(1) + assertThat(availability.last()).isTrue() + } + + @Test + fun dataMatchesTheRepository() = runTest { + val dataList: List<ColorInversionTileModel> by + collectValues(underTest.tileData(TEST_USER, flowOf(DataUpdateTrigger.InitialRequest))) + runCurrent() + + colorInversionRepository.setIsEnabled(true, TEST_USER) + runCurrent() + + colorInversionRepository.setIsEnabled(false, TEST_USER) + runCurrent() + + assertThat(dataList).hasSize(3) + assertThat(dataList.map { it.isEnabled }).isEqualTo(listOf(false, true, false)) + } + + private companion object { + val TEST_USER = UserHandle.of(1)!! + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt new file mode 100644 index 000000000000..99bae18d43d4 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.os.UserHandle +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository +import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject +import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ColorInversionUserActionInteractorTest : SysuiTestCase() { + + private val testUser = UserHandle.CURRENT + private val repository = FakeColorInversionRepository() + private val inputHandler = FakeQSTileIntentUserInputHandler() + + private val underTest = + ColorInversionUserActionInteractor( + repository, + inputHandler, + ) + + @Test + fun handleClickWhenEnabled() = runTest { + val wasEnabled = true + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorInversionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleClickWhenDisabled() = runTest { + val wasEnabled = false + repository.setIsEnabled(wasEnabled, testUser) + + underTest.handleInput(QSTileInputTestKtx.click(ColorInversionTileModel(wasEnabled))) + + assertThat(repository.isEnabled(testUser).value).isEqualTo(!wasEnabled) + } + + @Test + fun handleLongClickWhenDisabled() = runTest { + val enabled = false + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorInversionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_INVERSION_SETTINGS) + } + } + + @Test + fun handleLongClickWhenEnabled() = runTest { + val enabled = true + + underTest.handleInput(QSTileInputTestKtx.longClick(ColorInversionTileModel(enabled))) + + QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput { + assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_INVERSION_SETTINGS) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt index 24aa11e10f30..8c2d221e3f97 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/AccessibilityModule.kt @@ -18,6 +18,8 @@ package com.android.systemui.accessibility import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository import com.android.systemui.accessibility.data.repository.ColorCorrectionRepositoryImpl +import com.android.systemui.accessibility.data.repository.ColorInversionRepository +import com.android.systemui.accessibility.data.repository.ColorInversionRepositoryImpl import com.android.systemui.accessibility.qs.QSAccessibilityModule import dagger.Binds import dagger.Module @@ -25,7 +27,8 @@ import dagger.Module @Module(includes = [QSAccessibilityModule::class]) interface AccessibilityModule { @Binds - abstract fun colorCorrectionRepository( - impl: ColorCorrectionRepositoryImpl - ): ColorCorrectionRepository + fun colorCorrectionRepository(impl: ColorCorrectionRepositoryImpl): ColorCorrectionRepository + + @Binds + fun colorInversionRepository(impl: ColorInversionRepositoryImpl): ColorInversionRepository } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt new file mode 100644 index 000000000000..bbf10c509e50 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/ColorInversionRepository.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 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.systemui.accessibility.data.repository + +import android.os.UserHandle +import android.provider.Settings.Secure +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.settings.SecureSettings +import com.android.systemui.util.settings.SettingsProxyExt.observerFlow +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext + +/** Provides data related to color inversion. */ +interface ColorInversionRepository { + /** Observable for whether color inversion is enabled */ + fun isEnabled(userHandle: UserHandle): Flow<Boolean> + + /** Sets color inversion enabled state. */ + suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean +} + +@SysUISingleton +class ColorInversionRepositoryImpl +@Inject +constructor( + @Background private val bgCoroutineContext: CoroutineContext, + private val secureSettings: SecureSettings, +) : ColorInversionRepository { + + override fun isEnabled(userHandle: UserHandle): Flow<Boolean> = + secureSettings + .observerFlow(userHandle.identifier, SETTING_NAME) + .onStart { emit(Unit) } + .map { secureSettings.getIntForUser(SETTING_NAME, userHandle.identifier) == ENABLED } + .distinctUntilChanged() + .flowOn(bgCoroutineContext) + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean = + withContext(bgCoroutineContext) { + secureSettings.putIntForUser( + SETTING_NAME, + if (isEnabled) ENABLED else DISABLED, + userHandle.identifier + ) + } + + companion object { + private const val SETTING_NAME = Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED + private const val DISABLED = 0 + private const val ENABLED = 1 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt index df7fdb8e6058..135ab35a4681 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt @@ -31,6 +31,10 @@ import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrection import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel +import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper +import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel @@ -87,8 +91,8 @@ interface QSAccessibilityModule { fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*> companion object { - const val COLOR_CORRECTION_TILE_SPEC = "color_correction" + const val COLOR_INVERSION_TILE_SPEC = "inversion" @Provides @IntoMap @@ -120,5 +124,36 @@ interface QSAccessibilityModule { stateInteractor, mapper, ) + + @Provides + @IntoMap + @StringKey(COLOR_INVERSION_TILE_SPEC) + fun provideColorInversionTileConfig(uiEventLogger: QsEventLogger): QSTileConfig = + QSTileConfig( + tileSpec = TileSpec.create(COLOR_INVERSION_TILE_SPEC), + uiConfig = + QSTileUIConfig.Resource( + iconRes = R.drawable.qs_invert_colors_icon_off, + labelRes = R.string.quick_settings_inversion_label, + ), + instanceId = uiEventLogger.getNewInstanceId(), + ) + + /** Inject ColorInversionTile into tileViewModelMap in QSModule */ + @Provides + @IntoMap + @StringKey(COLOR_INVERSION_TILE_SPEC) + fun provideColorInversionTileViewModel( + factory: QSTileViewModelFactory.Static<ColorInversionTileModel>, + mapper: ColorInversionTileMapper, + stateInteractor: ColorInversionTileDataInteractor, + userActionInteractor: ColorInversionUserActionInteractor + ): QSTileViewModel = + factory.create( + TileSpec.create(COLOR_INVERSION_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt new file mode 100644 index 000000000000..4af985424a39 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion.domain + +import android.content.res.Resources +import android.content.res.Resources.Theme +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileConfig +import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.res.R +import javax.inject.Inject + +/** Maps [ColorInversionTileModel] to [QSTileState]. */ +class ColorInversionTileMapper +@Inject +constructor( + @Main private val resources: Resources, + private val theme: Theme, +) : QSTileDataToStateMapper<ColorInversionTileModel> { + override fun map(config: QSTileConfig, data: ColorInversionTileModel): QSTileState = + QSTileState.build(resources, theme, config.uiConfig) { + val subtitleArray = resources.getStringArray(R.array.tile_states_inversion) + + if (data.isEnabled) { + activationState = QSTileState.ActivationState.ACTIVE + secondaryLabel = subtitleArray[2] + icon = { + Icon.Loaded( + resources.getDrawable(R.drawable.qs_invert_colors_icon_on, theme), + null + ) + } + } else { + activationState = QSTileState.ActivationState.INACTIVE + secondaryLabel = subtitleArray[1] + icon = { + Icon.Loaded( + resources.getDrawable(R.drawable.qs_invert_colors_icon_off, theme), + null + ) + } + } + contentDescription = label + supportedActions = + setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt new file mode 100644 index 000000000000..7f3dd3e17b79 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.os.UserHandle +import com.android.systemui.accessibility.data.repository.ColorInversionRepository +import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger +import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map + +/** Observes color inversion state changes providing the [ColorInversionTileModel]. */ +class ColorInversionTileDataInteractor +@Inject +constructor( + private val colorInversionRepository: ColorInversionRepository, +) : QSTileDataInteractor<ColorInversionTileModel> { + + override fun tileData( + user: UserHandle, + triggers: Flow<DataUpdateTrigger> + ): Flow<ColorInversionTileModel> { + return colorInversionRepository.isEnabled(user).map { ColorInversionTileModel(it) } + } + override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt new file mode 100644 index 000000000000..43b58c83f7ef --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion.domain.interactor + +import android.content.Intent +import android.provider.Settings +import com.android.systemui.accessibility.data.repository.ColorInversionRepository +import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.interactor.QSTileInput +import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor +import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel +import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction +import javax.inject.Inject + +/** Handles color inversion tile clicks. */ +class ColorInversionUserActionInteractor +@Inject +constructor( + private val colorInversionRepository: ColorInversionRepository, + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, +) : QSTileUserActionInteractor<ColorInversionTileModel> { + + override suspend fun handleInput(input: QSTileInput<ColorInversionTileModel>): Unit = + with(input) { + when (action) { + is QSTileUserAction.Click -> { + colorInversionRepository.setIsEnabled( + !data.isEnabled, + user, + ) + } + is QSTileUserAction.LongClick -> { + qsTileIntentUserActionHandler.handle( + action.view, + Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS) + ) + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/model/ColorInversionTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/model/ColorInversionTileModel.kt new file mode 100644 index 000000000000..affaa6d533bf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/model/ColorInversionTileModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion.domain.model + +/** + * Color inversion tile model. + * + * @param isEnabled is true when the color inversion is enabled; + */ +@JvmInline value class ColorInversionTileModel(val isEnabled: Boolean) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorInversionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorInversionRepository.kt new file mode 100644 index 000000000000..9a3b9e2cc701 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeColorInversionRepository.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 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.systemui.accessibility.data.repository + +import android.os.UserHandle +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FakeColorInversionRepository : ColorInversionRepository { + private val userMap = mutableMapOf<Int, MutableStateFlow<Boolean>>() + + override fun isEnabled(userHandle: UserHandle): StateFlow<Boolean> { + return getFlow(userHandle.identifier) + } + + override suspend fun setIsEnabled(isEnabled: Boolean, userHandle: UserHandle): Boolean { + getFlow(userHandle.identifier).value = isEnabled + return true + } + + /** initializes the flow if already not */ + private fun getFlow(userId: Int): MutableStateFlow<Boolean> { + return userMap.getOrPut(userId) { MutableStateFlow(false) } + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/inversion/ColorInversionTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/inversion/ColorInversionTileKosmos.kt new file mode 100644 index 000000000000..fad34aab05e0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/inversion/ColorInversionTileKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.systemui.qs.tiles.impl.inversion + +import com.android.systemui.accessibility.qs.QSAccessibilityModule +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.qs.qsEventLogger + +val Kosmos.qsColorInversionTileConfig by + Kosmos.Fixture { QSAccessibilityModule.provideColorInversionTileConfig(qsEventLogger) } |