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

Commit 8e18d4b6 authored by Jiaming Cheng's avatar Jiaming Cheng Committed by Android (Google) Code Review
Browse files

Merge "[QSDetailedView] Add the generic view and view models" into main

parents ca649f05 836e5a96
Loading
Loading
Loading
Loading
+41 −12
Original line number Diff line number Diff line
@@ -44,8 +44,11 @@ import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.panels.ui.compose.EditMode
import com.android.systemui.qs.panels.ui.compose.TileDetails
import com.android.systemui.qs.panels.ui.compose.TileGrid
import com.android.systemui.qs.ui.viewmodel.QuickSettingsContainerViewModel
import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeOverlayActionsViewModel
@@ -116,20 +119,45 @@ constructor(
    }
}

// A sealed interface to represent the possible states of the `ShadeBody`
sealed interface ShadeBodyState {
    data object Editing : ShadeBodyState
    data object TileDetails : ShadeBodyState
    data object Default : ShadeBodyState
}

// Function to map the current state of the `ShadeBody`
fun checkQsState(isEditing: Boolean, tileDetails: TileDetailsViewModel?): ShadeBodyState {
    if (isEditing) {
        return ShadeBodyState.Editing
    } else if (tileDetails != null && QsDetailedView.isEnabled) {
        return ShadeBodyState.TileDetails
    }
    return ShadeBodyState.Default
}

@Composable
fun SceneScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) {
    val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
    val tileDetails = viewModel.detailsViewModel.activeTileDetails

    AnimatedContent(
        targetState = isEditing,
        targetState = checkQsState(isEditing, tileDetails),
        transitionSpec = { fadeIn(tween(500)) togetherWith fadeOut(tween(500)) },
    ) { editing ->
        if (editing) {
    ) { state ->
        when (state) {
            ShadeBodyState.Editing -> {
                EditMode(
                    viewModel = viewModel.editModeViewModel,
                modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(QuickSettingsShade.Dimensions.Padding),
                )
        } else {
            }
            ShadeBodyState.TileDetails -> {
                TileDetails(viewModel.detailsViewModel)
            }
            else -> {
                QuickSettingsLayout(
                    viewModel = viewModel,
                    modifier = Modifier.sysuiResTag("quick_settings_panel"),
@@ -137,6 +165,7 @@ fun SceneScope.ShadeBody(viewModel: QuickSettingsContainerViewModel) {
            }
        }
    }
}

/** Column containing Brightness and QS tiles. */
@Composable
+94 −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.panels.ui.viewmodel


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.qs.FakeQSTile
import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@SmallTest
class DetailsViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private lateinit var underTest: DetailsViewModel
    private val spec = TileSpec.create("internet")
    private val specNoDetails = TileSpec.create("NoDetailsTile")

    @Before
    fun setUp() {
        underTest = kosmos.detailsViewModel
    }

    @OptIn(ExperimentalCoroutinesApi::class)
    @Test
    fun changeTileDetailsViewModel() = with(kosmos) {
        testScope.runTest {
            val specs = listOf(
                spec,
                specNoDetails,
            )
            tileSpecRepository.setTiles(0, specs)
            runCurrent()

            val tiles = currentTilesInteractor.currentTiles.value

            assertThat(currentTilesInteractor.currentTilesSpecs.size).isEqualTo(2)
            assertThat(tiles!![1].spec).isEqualTo(specNoDetails)
            (tiles!![1].tile as FakeQSTile).hasDetailsViewModel = false

            assertThat(underTest.activeTileDetails).isNull()

            // Click on the tile who has the `spec`.
            assertThat(underTest.onTileClicked(spec)).isTrue()
            assertThat(underTest.activeTileDetails).isNotNull()
            assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet")

            // Click on a tile who dose not have a valid spec.
            assertThat(underTest.onTileClicked(null)).isFalse()
            assertThat(underTest.activeTileDetails).isNull()

            // Click again on the tile who has the `spec`.
            assertThat(underTest.onTileClicked(spec)).isTrue()
            assertThat(underTest.activeTileDetails).isNotNull()
            assertThat(underTest.activeTileDetails?.getTitle()).isEqualTo("internet")

            // Click on a tile who dose not have a detailed view.
            assertThat(underTest.onTileClicked(specNoDetails)).isFalse()
            assertThat(underTest.activeTileDetails).isNull()

            underTest.closeDetailedView()
            assertThat(underTest.activeTileDetails).isNull()

            assertThat(underTest.onTileClicked(null)).isFalse()
        }
    }
}
+27 −1
Original line number Diff line number Diff line
@@ -691,6 +691,32 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
            verify(logger, never()).logTileUserChanged(TileSpec.create("a"), 0)
        }


    @Test
    fun getTileDetails() =
        testScope.runTest(USER_INFO_0) {
            val tiles by collectLastValue(underTest.currentTiles)
            val tileA = TileSpec.create("a")
            val tileB = TileSpec.create("b")
            val tileNoDetails = TileSpec.create("NoDetails")

            val specs = listOf(tileA, tileB, tileNoDetails)

            assertThat(tiles!!.isEmpty()).isTrue()

            tileSpecRepository.setTiles(USER_INFO_0.id, specs)
            assertThat(tiles!!.size).isEqualTo(3)

            // The third tile doesn't have a details view.
            assertThat(tiles!![2].spec).isEqualTo(tileNoDetails)
            (tiles!![2].tile as FakeQSTile).hasDetailsViewModel = false

            assertThat(tiles!![0].tile.detailsViewModel.getTitle()).isEqualTo("a")
            assertThat(tiles!![1].tile.detailsViewModel.getTitle()).isEqualTo("b")
            assertThat(tiles!![2].tile.detailsViewModel).isNull()
        }


    private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) {
        this.state = state
        this.label = label
@@ -770,7 +796,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() {
        private val USER_INFO_0 = UserInfo().apply { id = 0 }
        private val USER_INFO_1 = UserInfo().apply { id = 1 }

        private val VALID_TILES = setOf("a", "b", "c", "d", "e")
        private val VALID_TILES = setOf("a", "b", "c", "d", "e", "NoDetails")
        private val TEST_COMPONENT = ComponentName("pkg", "cls")
        private val CUSTOM_TILE_SPEC = TileSpec.Companion.create(TEST_COMPONENT)
    }
+2 −0
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ java_library {
        "SystemUICommon",
        "SystemUILogLib",
        "androidx.annotation_annotation",
        "androidx.compose.ui_ui",
        "androidx.compose.runtime_runtime",
    ],
}

+5 −0
Original line number Diff line number Diff line
@@ -120,6 +120,11 @@ public interface QSTile {
     */
    boolean isListening();

    /**
     * Return this tile's {@link TileDetailsViewModel} to be used to render the TileDetailsView.
     */
    default TileDetailsViewModel getDetailsViewModel() { return null; }

    @ProvidesInterface(version = Callback.VERSION)
    interface Callback {
        static final int VERSION = 2;
Loading