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

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

Merge "Create ViewModel for QS compose and a basic fragment" into main

parents bc1ab097 c7280ebf
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -748,6 +748,7 @@ android_library {
        "//frameworks/libs/systemui:motion_tool_lib",
        "//frameworks/libs/systemui:contextualeducationlib",
        "androidx.core_core-animation-testing",
        "androidx.lifecycle_lifecycle-runtime-testing",
        "androidx.compose.ui_ui",
        "flag-junit",
        "ravenwood-junit",
@@ -789,6 +790,7 @@ android_library {
        "SystemUI-tests-base",
        "androidx.test.uiautomator_uiautomator",
        "androidx.core_core-animation-testing",
        "androidx.lifecycle_lifecycle-runtime-testing",
        "mockito-target-extended-minus-junit4",
        "mockito-kotlin-nodeps",
        "androidx.test.ext.junit",
+3 −8
Original line number Diff line number Diff line
@@ -29,8 +29,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@@ -42,6 +40,7 @@ import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.LockscreenContent
import com.android.systemui.lifecycle.rememberViewModel
@@ -114,7 +113,7 @@ constructor(
}

@Composable
private fun ShadeBody(
fun ShadeBody(
    viewModel: QuickSettingsContainerViewModel,
) {
    val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
@@ -131,6 +130,7 @@ private fun ShadeBody(
        } else {
            QuickSettingsLayout(
                viewModel = viewModel,
                modifier = Modifier.sysuiResTag("quick_settings_panel")
            )
        }
    }
@@ -158,11 +158,6 @@ private fun QuickSettingsLayout(
                Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
            viewModel.editModeViewModel::startEditing,
        )
        Button(
            onClick = { viewModel.editModeViewModel.startEditing() },
        ) {
            Text("Edit mode")
        }
    }
}

+151 −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.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.res.R
import com.android.systemui.shade.largeScreenHeaderHelper
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()
    }

    // For now the state changes at 0.5f expansion. This will change once we implement animation
    // (and this test will fail)
    @Test
    fun qsExpansionValueChanges_correctExpansionState() =
        with(kosmos) {
            testScope.testWithinLifecycle {
                val expansionState by collectLastValue(underTest.expansionState)

                underTest.qsExpansionValue = 0f
                assertThat(expansionState)
                    .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)

                underTest.qsExpansionValue = 0.3f
                assertThat(expansionState)
                    .isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QQS)

                underTest.qsExpansionValue = 0.7f
                assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)

                underTest.qsExpansionValue = 1f
                assertThat(expansionState).isEqualTo(QSFragmentComposeViewModel.QSExpansionState.QS)
            }
        }

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

                testableContext.orCreateTestableResources.addOverride(
                    R.bool.config_use_large_screen_shade_header,
                    true
                )
                fakeConfigurationRepository.onConfigurationChange()

                assertThat(qqsHeaderHeight).isEqualTo(0)
            }
        }

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

                testableContext.orCreateTestableResources.addOverride(
                    R.bool.config_use_large_screen_shade_header,
                    false
                )
                fakeConfigurationRepository.onConfigurationChange()

                assertThat(qqsHeaderHeight)
                    .isEqualTo(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
            }
        }

    @Test
    fun footerActionsControllerInit() =
        with(kosmos) {
            testScope.testWithinLifecycle {
                underTest
                runCurrent()
                assertThat(fgsManagerController.initialized).isTrue()
            }
        }

    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) }
        }
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.qs.ui.viewmodel

import android.testing.TestableLooper.RunWithLooper
import androidx.lifecycle.LifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -59,7 +60,7 @@ class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {
    private val footerActionsViewModel = mock<FooterActionsViewModel>()
    private val footerActionsViewModelFactory =
        mock<FooterActionsViewModel.Factory> {
            whenever(create(any())).thenReturn(footerActionsViewModel)
            whenever(create(any<LifecycleOwner>())).thenReturn(footerActionsViewModel)
        }
    private val footerActionsController = mock<FooterActionsController>()

+7 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.qs
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.fragments.FragmentService
import com.android.systemui.qs.composefragment.QSFragmentCompose
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -31,13 +32,18 @@ class QSFragmentStartable
@Inject
constructor(
    private val fragmentService: FragmentService,
    private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>
    private val qsFragmentLegacyProvider: Provider<QSFragmentLegacy>,
    private val qsFragmentComposeProvider: Provider<QSFragmentCompose>,
) : CoreStartable {
    override fun start() {
        fragmentService.addFragmentInstantiationProvider(
            QSFragmentLegacy::class.java,
            qsFragmentLegacyProvider
        )
        fragmentService.addFragmentInstantiationProvider(
            QSFragmentCompose::class.java,
            qsFragmentComposeProvider
        )
    }
}

Loading