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

Commit 5d153093 authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge changes Icd71bcf3,I9ee1636c into main

* changes:
  Implement expansion to QS in keyguard
  Add squishiness to tiles.
parents 189a6633 3109ea2f
Loading
Loading
Loading
Loading
+69 −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.composefragment.viewmodel

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.testing.TestLifecycleOwner
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.testKosmos
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before

@OptIn(ExperimentalCoroutinesApi::class)
abstract class AbstractQSFragmentComposeViewModelTest : SysuiTestCase() {
    protected val kosmos = testKosmos()

    protected val lifecycleOwner =
        TestLifecycleOwner(
            initialState = Lifecycle.State.CREATED,
            coroutineDispatcher = kosmos.testDispatcher,
        )

    protected val underTest by lazy {
        kosmos.qsFragmentComposeViewModelFactory.create(lifecycleOwner.lifecycleScope)
    }

    @Before
    fun setUp() {
        Dispatchers.setMain(kosmos.testDispatcher)
    }

    @After
    fun teardown() {
        Dispatchers.resetMain()
    }

    protected inline fun TestScope.testWithinLifecycle(
        crossinline block: suspend TestScope.() -> TestResult
    ): TestResult {
        return runTest {
            lifecycleOwner.setCurrentState(Lifecycle.State.RESUMED)
            lifecycleOwner.lifecycleScope.launch { underTest.activate() }
            block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) }
        }
    }
}
+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.composefragment.viewmodel

import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized

@SmallTest
@RunWith(Parameterized::class)
@RunWithLooper
class QSFragmentComposeViewModelForceQSTest(private val testData: TestData) :
    AbstractQSFragmentComposeViewModelTest() {

    @Test
    fun forceQs_orRealExpansion() =
        with(kosmos) {
            testScope.testWithinLifecycle {
                val expansionState by collectLastValue(underTest.expansionState)

                with(testData) {
                    sysuiStatusBarStateController.setState(statusBarState)
                    underTest.isQSExpanded = expanded
                    underTest.isStackScrollerOverscrolling = stackScrollerOverScrolling
                    fakeDeviceEntryRepository.setBypassEnabled(bypassEnabled)
                    underTest.isTransitioningToFullShade = transitioningToFullShade
                    underTest.isInSplitShade = inSplitShade

                    underTest.qsExpansionValue = EXPANSION
                    assertThat(expansionState!!.progress)
                        .isEqualTo(if (expectedForceQS) 1f else EXPANSION)
                }
            }
        }

    data class TestData(
        val statusBarState: Int,
        val expanded: Boolean,
        val stackScrollerOverScrolling: Boolean,
        val bypassEnabled: Boolean,
        val transitioningToFullShade: Boolean,
        val inSplitShade: Boolean,
    ) {
        private val inKeyguard = statusBarState == StatusBarState.KEYGUARD

        private val showCollapsedOnKeyguard =
            bypassEnabled || (transitioningToFullShade && !inSplitShade)

        val expectedForceQS =
            (expanded || stackScrollerOverScrolling) && (inKeyguard && !showCollapsedOnKeyguard)
    }

    companion object {
        private const val EXPANSION = 0.3f

        @Parameterized.Parameters(name = "{0}")
        @JvmStatic
        fun createTestData(): List<TestData> {
            return statusBarStates.flatMap { statusBarState ->
                (0u..31u).map { bitfield ->
                    TestData(
                        statusBarState,
                        expanded = (bitfield or 1u) == 1u,
                        stackScrollerOverScrolling = (bitfield or 2u) == 1u,
                        bypassEnabled = (bitfield or 4u) == 1u,
                        transitioningToFullShade = (bitfield or 8u) == 1u,
                        inSplitShade = (bitfield or 16u) == 1u,
                    )
                }
            }
        }

        private val statusBarStates =
            setOf(StatusBarState.SHADE, StatusBarState.KEYGUARD, StatusBarState.SHADE_LOCKED)
    }
}
+23 −45
Original line number Diff line number Diff line
@@ -19,64 +19,28 @@ package com.android.systemui.qs.composefragment.viewmodel
import android.app.StatusBarManager
import android.content.testableContext
import android.testing.TestableLooper.RunWithLooper
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.fgsManagerController
import com.android.systemui.qs.panels.domain.interactor.tileSquishinessInteractor
import com.android.systemui.res.R
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
class QSFragmentComposeViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()

    private val lifecycleOwner =
        TestLifecycleOwner(
            initialState = Lifecycle.State.CREATED,
            coroutineDispatcher = kosmos.testDispatcher,
        )

    private val underTest by lazy {
        kosmos.qsFragmentComposeViewModelFactory.create(lifecycleOwner.lifecycleScope)
    }

    @Before
    fun setUp() {
        Dispatchers.setMain(kosmos.testDispatcher)
    }

    @After
    fun teardown() {
        Dispatchers.resetMain()
    }
class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() {

    @Test
    fun qsExpansionValueChanges_correctExpansionState() =
@@ -205,16 +169,30 @@ class QSFragmentComposeViewModelTest : SysuiTestCase() {
            }
        }

    private inline fun TestScope.testWithinLifecycle(
        crossinline block: suspend TestScope.() -> TestResult
    ): TestResult {
        return runTest {
            lifecycleOwner.setCurrentState(Lifecycle.State.RESUMED)
            block().also { lifecycleOwner.setCurrentState(Lifecycle.State.DESTROYED) }
    @Test
    fun squishinessInExpansion_setInInteractor() =
        with(kosmos) {
            testScope.testWithinLifecycle {
                val squishiness by collectLastValue(tileSquishinessInteractor.squishiness)

                underTest.squishinessFractionValue = 0.3f
                assertThat(squishiness).isWithin(epsilon).of(0.3f.constrainSquishiness())

                underTest.squishinessFractionValue = 0f
                assertThat(squishiness).isWithin(epsilon).of(0f.constrainSquishiness())

                underTest.squishinessFractionValue = 1f
                assertThat(squishiness).isWithin(epsilon).of(1f.constrainSquishiness())
            }
        }

    companion object {
        private const val QS_DISABLE_FLAG = StatusBarManager.DISABLE2_QUICK_SETTINGS

        private fun Float.constrainSquishiness(): Float {
            return (0.1f + this * 0.9f).coerceIn(0f, 1f)
        }

        private const val epsilon = 0.001f
    }
}
+2 −5
Original line number Diff line number Diff line
@@ -22,10 +22,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository
import com.android.systemui.qs.panels.ui.compose.infinitegrid.InfiniteGridLayout
import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
import com.android.systemui.qs.panels.ui.viewmodel.MockTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.fixedColumnsSizeViewModel
import com.android.systemui.qs.panels.ui.viewmodel.iconTilesViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -44,8 +42,7 @@ class InfiniteGridLayoutTest : SysuiTestCase() {
                }
        }

    private val underTest =
        with(kosmos) { InfiniteGridLayout(iconTilesViewModel, fixedColumnsSizeViewModel) }
    private val underTest = kosmos.infiniteGridLayout

    @Test
    fun correctPagination_underOnePage_sameOrder() =
+7 −6
Original line number Diff line number Diff line
@@ -180,6 +180,7 @@ constructor(
        qqsMediaHost.init(MediaHierarchyManager.LOCATION_QQS)
        qsMediaHost.init(MediaHierarchyManager.LOCATION_QS)
        setListenerCollections()
        lifecycleScope.launch { viewModel.activate() }
    }

    override fun onCreateView(
@@ -331,7 +332,7 @@ constructor(
    }

    override fun setOverscrolling(overscrolling: Boolean) {
        viewModel.stackScrollerOverscrollingValue = overscrolling
        viewModel.isStackScrollerOverscrolling = overscrolling
    }

    override fun setExpanded(qsExpanded: Boolean) {
@@ -410,11 +411,11 @@ constructor(
        qsTransitionFraction: Float,
        qsSquishinessFraction: Float,
    ) {
        super.setTransitionToFullShadeProgress(
            isTransitioningToFullShade,
            qsTransitionFraction,
            qsSquishinessFraction,
        )
        viewModel.isTransitioningToFullShade = isTransitioningToFullShade
        viewModel.lockscreenToShadeProgressValue = qsTransitionFraction
        if (isTransitioningToFullShade) {
            viewModel.squishinessFractionValue = qsSquishinessFraction
        }
    }

    override fun setFancyClipping(
Loading