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

Commit a7e33d62 authored by Liam, Lee Pong Lam's avatar Liam, Lee Pong Lam
Browse files

[smartspace] Refactor WakefulnessLifecycle to PowerInteractor

Refactor WakefulnessLifecycle to PowerInteractor by view model

Flag: NONE
Test: Unit tests / Manual
Bug: 331451011
Change-Id: I6ac13c5130f128e6c4f9f8182c68535cb0fa97a4
parent 8a14cfba
Loading
Loading
Loading
Loading
+103 −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.smartspace

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.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.smartspace.ui.viewmodel.SmartspaceViewModel
import com.android.systemui.smartspace.viewmodel.smartspaceViewModelFactory
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withTimeoutOrNull
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class SmartspaceViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope

    private val powerInteractor = kosmos.powerInteractor
    private val smartspaceViewModelFactory = kosmos.smartspaceViewModelFactory

    private lateinit var underTest: SmartspaceViewModel

    @Test
    fun dateVew_isAwakeTrue_true() =
        testScope.runTest {
            underTest = smartspaceViewModelFactory.create(SmartspaceViewModel.SURFACE_DATE_VIEW)

            powerInteractor.setAwakeForTest()
            val isAwake by collectLastValue(underTest.isAwake)

            assertThat(isAwake).isTrue()
        }

    @Test
    fun dateVew_isAwakeFalse_false() =
        testScope.runTest {
            underTest = smartspaceViewModelFactory.create(SmartspaceViewModel.SURFACE_DATE_VIEW)

            powerInteractor.setAsleepForTest()
            val isAwake by collectLastValue(underTest.isAwake)

            assertThat(isAwake).isFalse()
        }

    @Test
    fun dateVew_isAwakeMultipleTimes_correctResults() =
        testScope.runTest {
            underTest = smartspaceViewModelFactory.create(SmartspaceViewModel.SURFACE_DATE_VIEW)
            val isAwake by collectLastValue(underTest.isAwake)

            powerInteractor.setAsleepForTest()

            assertThat(isAwake).isFalse()

            powerInteractor.setAwakeForTest()

            assertThat(isAwake).isTrue()

            powerInteractor.setAsleepForTest()

            assertThat(isAwake).isFalse()

            powerInteractor.setAwakeForTest()

            assertThat(isAwake).isTrue()
        }

    @Test
    fun weatherView_isAwakeTrue_doesNotEmit() =
        testScope.runTest {
            underTest = smartspaceViewModelFactory.create(SmartspaceViewModel.SURFACE_WEATHER_VIEW)

            powerInteractor.setAwakeForTest()
            val isAwake = withTimeoutOrNull(100) { underTest.isAwake.firstOrNull() }

            assertThat(isAwake).isNull()
        }
}
+45 −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.smartspace.ui.binder

import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView
import com.android.systemui.smartspace.ui.viewmodel.SmartspaceViewModel
import kotlinx.coroutines.launch

/** Binds the view and view-model for the smartspace. */
object SmartspaceViewBinder {

    /** Binds the view and view-model for the smartspace. */
    fun bind(
        smartspaceView: SmartspaceView,
        viewModel: SmartspaceViewModel,
    ) {
        val view = smartspaceView as View
        view.repeatWhenAttached {
            repeatOnLifecycle(Lifecycle.State.CREATED) {
                launch {
                    // Observe screen on/off changes
                    viewModel.isAwake.collect { isAwake -> smartspaceView.setScreenOn(isAwake) }
                }
            }
        }
    }
}
+47 −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.smartspace.ui.viewmodel

import com.android.systemui.power.domain.interactor.PowerInteractor
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter

class SmartspaceViewModel
@AssistedInject
constructor(
    powerInteractor: PowerInteractor,
    @Assisted val surfaceName: String,
) {

    /** Screen on/off state */
    val isAwake: Flow<Boolean> =
        powerInteractor.isAwake.filter { surfaceName != SURFACE_WEATHER_VIEW }

    @AssistedFactory
    interface Factory {
        fun create(surfaceName: String): SmartspaceViewModel
    }

    companion object {
        const val SURFACE_DATE_VIEW = "date_view"
        const val SURFACE_WEATHER_VIEW = "weather_view"
        const val SURFACE_GENERAL_VIEW = "general_view"
    }
}
+34 −6
Original line number Diff line number Diff line
@@ -61,6 +61,8 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.regionsampling.RegionSampler
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.DATE_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.smartspace.dagger.SmartspaceModule.Companion.WEATHER_SMARTSPACE_DATA_PLUGIN
import com.android.systemui.smartspace.ui.binder.SmartspaceViewBinder
import com.android.systemui.smartspace.ui.viewmodel.SmartspaceViewModel
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -99,6 +101,7 @@ constructor(
        private val bypassController: KeyguardBypassController,
        private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
        private val wakefulnessLifecycle: WakefulnessLifecycle,
        private val smartspaceViewModelFactory: SmartspaceViewModel.Factory,
        private val dumpManager: DumpManager,
        private val execution: Execution,
        @Main private val uiExecutor: Executor,
@@ -334,7 +337,12 @@ constructor(
            throw RuntimeException("Cannot build date view when not decoupled")
        }

        val view = buildView(parent, datePlugin)
        val view =
            buildView(
                surfaceName = SmartspaceViewModel.SURFACE_DATE_VIEW,
                parent = parent,
                plugin = datePlugin
            )
        connectSession()

        return view
@@ -353,7 +361,12 @@ constructor(
            throw RuntimeException("Cannot build weather view when not decoupled")
        }

        val view = buildView(parent, weatherPlugin)
        val view =
            buildView(
                surfaceName = SmartspaceViewModel.SURFACE_WEATHER_VIEW,
                parent = parent,
                plugin = weatherPlugin
            )
        connectSession()

        return view
@@ -369,13 +382,20 @@ constructor(
            throw RuntimeException("Cannot build view when not enabled")
        }

        val view = buildView(parent, plugin, configPlugin)
        val view =
            buildView(
                surfaceName = SmartspaceViewModel.SURFACE_GENERAL_VIEW,
                parent = parent,
                plugin = plugin,
                configPlugin = configPlugin
            )
        connectSession()

        return view
    }

    private fun buildView(
        surfaceName: String,
        parent: ViewGroup,
        plugin: BcSmartspaceDataPlugin?,
        configPlugin: BcSmartspaceConfigPlugin? = null
@@ -425,6 +445,14 @@ constructor(
        return (ssView as View).apply {
            setTag(R.id.tag_smartspace_view, Any())
            addOnAttachStateChangeListener(stateChangeListener)

            if (smartspaceLockscreenViewmodel()) {
                val viewModel = smartspaceViewModelFactory.create(surfaceName)
                SmartspaceViewBinder.bind(
                    smartspaceView = ssView,
                    viewModel = viewModel,
                )
            }
        }
    }

+6 −0
Original line number Diff line number Diff line
@@ -50,11 +50,14 @@ import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener
import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.ui.viewmodel.SmartspaceViewModel
import com.android.systemui.smartspace.viewmodel.smartspaceViewModelFactory
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecution
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -183,6 +186,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
    private lateinit var weatherSmartspaceView: SmartspaceView
    private lateinit var smartspaceView: SmartspaceView
    private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
    private lateinit var smartspaceViewModelFactory: SmartspaceViewModel.Factory

    private val clock = FakeSystemClock()
    private val executor = FakeExecutor(clock)
@@ -235,6 +239,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
            clock,
            dumpManager
        )
        smartspaceViewModelFactory = testKosmos().smartspaceViewModelFactory

        controller = LockscreenSmartspaceController(
                context,
@@ -252,6 +257,7 @@ class LockscreenSmartspaceControllerTest : SysuiTestCase() {
                keyguardBypassController,
                keyguardUpdateMonitor,
                wakefulnessLifecycle,
                smartspaceViewModelFactory,
                dumpManager,
                execution,
                executor,
Loading