Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b6eda71 authored by Behnam Heydarshahi's avatar Behnam Heydarshahi
Browse files

Migrate NightDisplayTile

Fixes: 301056354
Flag: aconfig com.android.systemui.qs_new_tiles DEVELOPMENT
Flag: aconfig com.android.systemui.qs_new_tiles_future DEVELOPMENT
Test: atest NightDisplayTileDataInteractorTest
NightDisplayTileUserActionInteractorTest NightDisplayTileMapperTest
LocationTileDataInteractorTest NightDisplayRepositoryTest

Change-Id: I03b4640e382fceda3266bc98b5f67b3d3689106e
parent c14c34a0
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -66,7 +65,7 @@ class ColorInversionRepositoryImplTest : SysuiTestCase() {

            runCurrent()

            Truth.assertThat(actualValue).isFalse()
            assertThat(actualValue).isFalse()
        }

    @Test
+203 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.accessibility.data.repository

import android.hardware.display.ColorDisplayManager
import android.hardware.display.NightDisplayListener
import android.os.UserHandle
import android.provider.Settings
import android.testing.LeakCheck
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeGlobalSettings
import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.utils.leaks.FakeLocationController
import com.google.common.truth.Truth.assertThat
import java.time.LocalTime
import kotlinx.coroutines.ExperimentalCoroutinesApi
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
import org.mockito.ArgumentMatchers
import org.mockito.Mockito.verify

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayRepositoryTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val testUser = UserHandle.of(1)!!
    private val testStartTime = LocalTime.MIDNIGHT
    private val testEndTime = LocalTime.NOON
    private val colorDisplayManager =
        mock<ColorDisplayManager> {
            whenever(nightDisplayAutoMode).thenReturn(ColorDisplayManager.AUTO_MODE_DISABLED)
            whenever(isNightDisplayActivated).thenReturn(false)
            whenever(nightDisplayCustomStartTime).thenReturn(testStartTime)
            whenever(nightDisplayCustomEndTime).thenReturn(testEndTime)
        }
    private val locationController = FakeLocationController(LeakCheck())
    private val nightDisplayListener = mock<NightDisplayListener>()
    private val listenerBuilder =
        mock<NightDisplayListenerModule.Builder> {
            whenever(setUser(ArgumentMatchers.anyInt())).thenReturn(this)
            whenever(build()).thenReturn(nightDisplayListener)
        }
    private val globalSettings = kosmos.fakeGlobalSettings
    private val secureSettings = kosmos.fakeSettings
    private val testDispatcher = StandardTestDispatcher()
    private val scope = TestScope(testDispatcher)
    private val userScopedColorDisplayManager =
        mock<UserScopedService<ColorDisplayManager>> {
            whenever(forUser(eq(testUser))).thenReturn(colorDisplayManager)
        }

    private val underTest =
        NightDisplayRepository(
            testDispatcher,
            scope.backgroundScope,
            globalSettings,
            secureSettings,
            listenerBuilder,
            userScopedColorDisplayManager,
            locationController,
        )

    @Before
    fun setup() {
        enrollInForcedNightDisplayAutoMode(INITIALLY_FORCE_AUTO_MODE, testUser)
    }

    @Test
    fun nightDisplayState_matchesAutoMode() =
        scope.runTest {
            enrollInForcedNightDisplayAutoMode(INITIALLY_FORCE_AUTO_MODE, testUser)
            val callbackCaptor = argumentCaptor<NightDisplayListener.Callback>()
            val lastState by collectLastValue(underTest.nightDisplayState(testUser))

            runCurrent()

            verify(nightDisplayListener).setCallback(callbackCaptor.capture())
            val callback = callbackCaptor.value

            assertThat(lastState!!.autoMode).isEqualTo(ColorDisplayManager.AUTO_MODE_DISABLED)

            callback.onAutoModeChanged(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME)
            assertThat(lastState!!.autoMode).isEqualTo(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME)

            callback.onCustomStartTimeChanged(testStartTime)
            assertThat(lastState!!.startTime).isEqualTo(testStartTime)

            callback.onCustomEndTimeChanged(testEndTime)
            assertThat(lastState!!.endTime).isEqualTo(testEndTime)

            callback.onAutoModeChanged(ColorDisplayManager.AUTO_MODE_TWILIGHT)

            assertThat(lastState!!.autoMode).isEqualTo(ColorDisplayManager.AUTO_MODE_TWILIGHT)
        }

    @Test
    fun nightDisplayState_matchesIsNightDisplayActivated() =
        scope.runTest {
            val callbackCaptor = argumentCaptor<NightDisplayListener.Callback>()

            val lastState by collectLastValue(underTest.nightDisplayState(testUser))
            runCurrent()

            verify(nightDisplayListener).setCallback(callbackCaptor.capture())
            val callback = callbackCaptor.value
            assertThat(lastState!!.isActivated)
                .isEqualTo(colorDisplayManager.isNightDisplayActivated)

            callback.onActivated(true)
            assertThat(lastState!!.isActivated).isTrue()

            callback.onActivated(false)
            assertThat(lastState!!.isActivated).isFalse()
        }

    @Test
    fun nightDisplayState_matchesController_initiallyCustomAutoMode() =
        scope.runTest {
            whenever(colorDisplayManager.nightDisplayAutoMode)
                .thenReturn(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME)

            val lastState by collectLastValue(underTest.nightDisplayState(testUser))
            runCurrent()

            assertThat(lastState!!.autoMode).isEqualTo(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME)
        }

    @Test
    fun nightDisplayState_matchesController_initiallyTwilightAutoMode() =
        scope.runTest {
            whenever(colorDisplayManager.nightDisplayAutoMode)
                .thenReturn(ColorDisplayManager.AUTO_MODE_TWILIGHT)

            val lastState by collectLastValue(underTest.nightDisplayState(testUser))
            runCurrent()

            assertThat(lastState!!.autoMode).isEqualTo(ColorDisplayManager.AUTO_MODE_TWILIGHT)
        }

    @Test
    fun nightDisplayState_matchesForceAutoMode() =
        scope.runTest {
            enrollInForcedNightDisplayAutoMode(false, testUser)
            val lastState by collectLastValue(underTest.nightDisplayState(testUser))
            runCurrent()

            assertThat(lastState!!.shouldForceAutoMode).isEqualTo(false)

            enrollInForcedNightDisplayAutoMode(true, testUser)
            assertThat(lastState!!.shouldForceAutoMode).isEqualTo(true)
        }

    private fun enrollInForcedNightDisplayAutoMode(enroll: Boolean, userHandle: UserHandle) {
        globalSettings.putString(
            Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE,
            if (enroll) NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE
            else NIGHT_DISPLAY_FORCED_AUTO_MODE_UNAVAILABLE
        )
        secureSettings.putIntForUser(
            Settings.Secure.NIGHT_DISPLAY_AUTO_MODE,
            if (enroll) NIGHT_DISPLAY_AUTO_MODE_RAW_NOT_SET else NIGHT_DISPLAY_AUTO_MODE_RAW_SET,
            userHandle.identifier
        )
    }

    private companion object {
        const val INITIALLY_FORCE_AUTO_MODE = false
        const val NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE = "1"
        const val NIGHT_DISPLAY_FORCED_AUTO_MODE_UNAVAILABLE = "0"
        const val NIGHT_DISPLAY_AUTO_MODE_RAW_NOT_SET = -1
        const val NIGHT_DISPLAY_AUTO_MODE_RAW_SET = 0
    }
}
+98 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.qs.tiles.impl.night.domain.interactor

import android.hardware.display.ColorDisplayManager
import android.hardware.display.NightDisplayListener
import android.os.UserHandle
import android.testing.LeakCheck
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.NightDisplayListenerModule
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeGlobalSettings
import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.util.time.DateFormatUtil
import com.android.systemui.utils.leaks.FakeLocationController
import com.google.common.truth.Truth.assertThat
import java.time.LocalTime
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt

@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileDataInteractorTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val testUser = UserHandle.of(1)!!
    private val testStartTime = LocalTime.MIDNIGHT
    private val testEndTime = LocalTime.NOON
    private val colorDisplayManager =
        mock<ColorDisplayManager> {
            whenever(nightDisplayAutoMode).thenReturn(ColorDisplayManager.AUTO_MODE_DISABLED)
            whenever(isNightDisplayActivated).thenReturn(false)
            whenever(nightDisplayCustomStartTime).thenReturn(testStartTime)
            whenever(nightDisplayCustomEndTime).thenReturn(testEndTime)
        }
    private val locationController = FakeLocationController(LeakCheck())
    private val nightDisplayListener = mock<NightDisplayListener>()
    private val listenerBuilder =
        mock<NightDisplayListenerModule.Builder> {
            whenever(setUser(anyInt())).thenReturn(this)
            whenever(build()).thenReturn(nightDisplayListener)
        }
    private val globalSettings = kosmos.fakeGlobalSettings
    private val secureSettings = kosmos.fakeSettings
    private val dateFormatUtil = mock<DateFormatUtil> { whenever(is24HourFormat).thenReturn(false) }
    private val testDispatcher = StandardTestDispatcher()
    private val scope = TestScope(testDispatcher)
    private val userScopedColorDisplayManager =
        mock<UserScopedService<ColorDisplayManager>> {
            whenever(forUser(eq(testUser))).thenReturn(colorDisplayManager)
        }
    private val nightDisplayRepository =
        NightDisplayRepository(
            testDispatcher,
            scope.backgroundScope,
            globalSettings,
            secureSettings,
            listenerBuilder,
            userScopedColorDisplayManager,
            locationController,
        )

    private val underTest: NightDisplayTileDataInteractor =
        NightDisplayTileDataInteractor(context, dateFormatUtil, nightDisplayRepository)

    @Test
    fun availability_matchesColorDisplayManager() = runTest {
        val availability by collectLastValue(underTest.availability(testUser))

        val expectedAvailability = ColorDisplayManager.isNightDisplayAvailable(context)
        assertThat(availability).isEqualTo(expectedAvailability)
    }
}
+177 −0
Original line number Diff line number Diff line
/*
 * 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.systemui.qs.tiles.impl.night.domain.interactor

import android.hardware.display.ColorDisplayManager
import android.hardware.display.NightDisplayListener
import android.os.UserHandle
import android.provider.Settings
import android.testing.LeakCheck
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.dagger.NightDisplayListenerModule
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.intentInputs
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
import com.android.systemui.user.utils.UserScopedService
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeGlobalSettings
import com.android.systemui.util.settings.fakeSettings
import com.android.systemui.utils.leaks.FakeLocationController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
import org.mockito.Mockito.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
class NightDisplayTileUserActionInteractorTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
    private val testUser = UserHandle.of(1)
    private val colorDisplayManager =
        mock<ColorDisplayManager> {
            whenever(nightDisplayAutoMode).thenReturn(ColorDisplayManager.AUTO_MODE_DISABLED)
            whenever(isNightDisplayActivated).thenReturn(false)
        }
    private val locationController = FakeLocationController(LeakCheck())
    private val nightDisplayListener = mock<NightDisplayListener>()
    private val listenerBuilder =
        mock<NightDisplayListenerModule.Builder> {
            whenever(setUser(ArgumentMatchers.anyInt())).thenReturn(this)
            whenever(build()).thenReturn(nightDisplayListener)
        }
    private val globalSettings = kosmos.fakeGlobalSettings
    private val secureSettings = kosmos.fakeSettings
    private val testDispatcher = StandardTestDispatcher()
    private val scope = TestScope(testDispatcher)
    private val userScopedColorDisplayManager =
        mock<UserScopedService<ColorDisplayManager>> {
            whenever(forUser(eq(testUser))).thenReturn(colorDisplayManager)
        }
    private val nightDisplayRepository =
        NightDisplayRepository(
            testDispatcher,
            scope.backgroundScope,
            globalSettings,
            secureSettings,
            listenerBuilder,
            userScopedColorDisplayManager,
            locationController,
        )

    private val underTest =
        NightDisplayTileUserActionInteractor(
            nightDisplayRepository,
            qsTileIntentUserActionHandler,
            kosmos.qsTileLogger
        )

    @Test
    fun handleClick_inactive_activates() =
        scope.runTest {
            val startingModel = NightDisplayTileModel.AutoModeOff(false, false)

            underTest.handleInput(QSTileInputTestKtx.click(startingModel, testUser))

            verify(colorDisplayManager).setNightDisplayActivated(true)
        }

    @Test
    fun handleClick_active_disables() =
        scope.runTest {
            val startingModel = NightDisplayTileModel.AutoModeOff(true, false)

            underTest.handleInput(QSTileInputTestKtx.click(startingModel, testUser))

            verify(colorDisplayManager).setNightDisplayActivated(false)
        }

    @Test
    fun handleClick_whenAutoModeTwilight_flipsState() =
        scope.runTest {
            val originalState = true
            val startingModel = NightDisplayTileModel.AutoModeTwilight(originalState, false, false)

            underTest.handleInput(QSTileInputTestKtx.click(startingModel, testUser))

            verify(colorDisplayManager).setNightDisplayActivated(!originalState)
        }

    @Test
    fun handleClick_whenAutoModeCustom_flipsState() =
        scope.runTest {
            val originalState = true
            val startingModel =
                NightDisplayTileModel.AutoModeCustom(originalState, false, null, null, false)

            underTest.handleInput(QSTileInputTestKtx.click(startingModel, testUser))

            verify(colorDisplayManager).setNightDisplayActivated(!originalState)
        }

    @Test
    fun handleLongClickWhenEnabled() =
        scope.runTest {
            val enabledState = true

            underTest.handleInput(
                QSTileInputTestKtx.longClick(
                    NightDisplayTileModel.AutoModeOff(enabledState, false),
                    testUser
                )
            )

            assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)

            val intentInput = qsTileIntentUserActionHandler.intentInputs.last()
            val actualIntentAction = intentInput.intent.action
            val expectedIntentAction = Settings.ACTION_NIGHT_DISPLAY_SETTINGS
            assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
        }

    @Test
    fun handleLongClickWhenDisabled() =
        scope.runTest {
            val enabledState = false

            underTest.handleInput(
                QSTileInputTestKtx.longClick(
                    NightDisplayTileModel.AutoModeOff(enabledState, false),
                    testUser
                )
            )

            assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)

            val intentInput = qsTileIntentUserActionHandler.intentInputs.last()
            val actualIntentAction = intentInput.intent.action
            val expectedIntentAction = Settings.ACTION_NIGHT_DISPLAY_SETTINGS
            assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
        }
}
+315 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading