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

Commit a7692277 authored by Lucas Silva's avatar Lucas Silva
Browse files

Add lock icon to glanceable hub

This change also wraps the hub in a constraint layout, so that we can
reuse the existing keyguard section for lock icon.

Bug: 326060686
Test: atest CommunalViewModelTest
Test: atest GlanceableHubContainerControllerTest
Flag: ACONFIG com.android.systemui.communal_hub TEAMFOOD
Change-Id: I973ded25a754d3e042f901acd620d8d45d8e1d93
parent 119272db
Loading
Loading
Loading
Loading
+21 −19
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
import com.android.compose.animation.scene.FixedSizeEdgeDetector
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
@@ -33,33 +34,38 @@ import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
import com.android.systemui.statusbar.phone.SystemUIDialogFactory

object Communal {
    object Elements {
        val Scrim = ElementKey("Scrim", scenePicker = LowestZIndexScenePicker)
        val Content = ElementKey("CommunalContent")
        val Grid = ElementKey("CommunalContent")
        val LockIcon = ElementKey("CommunalLockIcon")
        val IndicationArea = ElementKey("CommunalIndicationArea")
    }
}

object AllElements : ElementMatcher {
    override fun matches(key: ElementKey, scene: SceneKey) = true
}

val sceneTransitions = transitions {
    to(CommunalScenes.Communal, key = CommunalTransitionKeys.SimpleFade) {
        spec = tween(durationMillis = 250)
        fade(Communal.Elements.Scrim)
        fade(Communal.Elements.Content)
        fade(AllElements)
    }
    to(CommunalScenes.Communal) {
        spec = tween(durationMillis = 1000)
        translate(Communal.Elements.Content, Edge.Right)
        timestampRange(startMillis = 167, endMillis = 334) {
            fade(Communal.Elements.Scrim)
            fade(Communal.Elements.Content)
        }
        translate(Communal.Elements.Grid, Edge.Right)
        timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
    }
    to(CommunalScenes.Blank) {
        spec = tween(durationMillis = 1000)
        translate(Communal.Elements.Content, Edge.Right)
        timestampRange(endMillis = 167) { fade(Communal.Elements.Content) }
        translate(Communal.Elements.Grid, Edge.Right)
        timestampRange(endMillis = 167) {
            fade(Communal.Elements.Grid)
            fade(Communal.Elements.IndicationArea)
            fade(Communal.Elements.LockIcon)
        }
        timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
    }
}
@@ -75,8 +81,8 @@ fun CommunalContainer(
    modifier: Modifier = Modifier,
    viewModel: CommunalViewModel,
    dataSourceDelegator: SceneDataSourceDelegator,
    dialogFactory: SystemUIDialogFactory,
    colors: CommunalColors,
    content: CommunalContent,
) {
    val coroutineScope = rememberCoroutineScope()
    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
@@ -127,7 +133,7 @@ fun CommunalContainer(
            userActions =
                mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank)
        ) {
            CommunalScene(viewModel, colors, dialogFactory, modifier = modifier)
            CommunalScene(colors, content)
        }
    }

@@ -139,20 +145,16 @@ fun CommunalContainer(
/** Scene containing the glanceable hub UI. */
@Composable
private fun SceneScope.CommunalScene(
    viewModel: CommunalViewModel,
    colors: CommunalColors,
    dialogFactory: SystemUIDialogFactory,
    content: CommunalContent,
    modifier: Modifier = Modifier,
) {
    val backgroundColor by colors.backgroundColor.collectAsState()

    Box(
        modifier =
            Modifier.element(Communal.Elements.Scrim)
                .fillMaxSize()
                .background(Color(backgroundColor.toArgb())),
    )
    Box(modifier.element(Communal.Elements.Content)) {
        CommunalHub(viewModel = viewModel, dialogFactory = dialogFactory)
    }
    with(content) { Content(modifier = modifier) }
}
+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.communal.ui.compose

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject

/** Renders the content of the glanceable hub. */
class CommunalContent
@Inject
constructor(
    private val viewModel: CommunalViewModel,
    private val dialogFactory: SystemUIDialogFactory,
    private val lockSection: LockSection,
) {

    @Composable
    fun SceneScope.Content(modifier: Modifier = Modifier) {
        Layout(
            modifier = modifier.fillMaxSize(),
            content = {
                CommunalHub(
                    viewModel = viewModel,
                    dialogFactory = dialogFactory,
                    modifier = Modifier.element(Communal.Elements.Grid)
                )
                with(lockSection) {
                    LockIcon(
                        overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer,
                        modifier = Modifier.element(Communal.Elements.LockIcon)
                    )
                }
            }
        ) { measurables, constraints ->
            val communalGridMeasurable = measurables[0]
            val lockIconMeasurable = measurables[1]

            val noMinConstraints =
                constraints.copy(
                    minWidth = 0,
                    minHeight = 0,
                )

            val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
            val lockIconBounds =
                IntRect(
                    left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
                    top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
                    right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
                    bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
                )

            val communalGridPlaceable =
                communalGridMeasurable.measure(
                    noMinConstraints.copy(maxHeight = lockIconBounds.top)
                )

            layout(constraints.maxWidth, constraints.maxHeight) {
                communalGridPlaceable.place(
                    x = 0,
                    y = 0,
                )
                lockIconPlaceable.place(
                    x = lockIconBounds.left,
                    y = lockIconBounds.top,
                )
            }
        }
    }
}
+26 −58
Original line number Diff line number Diff line
@@ -107,7 +107,6 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
import androidx.compose.ui.semantics.contentDescription
@@ -264,17 +263,6 @@ fun CommunalHub(
            }
        }

        // TODO(b/326060686): Remove this once keyguard indication area can persist over hub
        if (viewModel is CommunalViewModel) {
            val isUnlocked by viewModel.deviceUnlocked.collectAsState(initial = false)
            LockStateIcon(
                modifier =
                    Modifier.align(Alignment.BottomCenter)
                        .padding(bottom = Dimensions.LockIconBottomPadding),
                isUnlocked = isUnlocked,
            )
        }

        if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) {
            Toolbar(
                isDraggingToRemove = isDraggingToRemove,
@@ -549,26 +537,6 @@ private fun EmptyStateCta(
    }
}

@Composable
private fun LockStateIcon(
    isUnlocked: Boolean,
    modifier: Modifier = Modifier,
) {
    val colors = LocalAndroidColorScheme.current
    val resource =
        if (isUnlocked) {
            R.drawable.ic_unlocked
        } else {
            R.drawable.ic_lock
        }
    Icon(
        painter = painterResource(id = resource),
        contentDescription = null,
        tint = colors.onPrimaryContainer,
        modifier = modifier.size(Dimensions.LockIconSize),
    )
}

/**
 * Toolbar that contains action buttons to
 * 1) open the widget picker
+3 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.view.View
import android.view.WindowManager
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Constraints
@@ -68,7 +69,7 @@ constructor(
    private val notificationPanelView: NotificationPanelView,
) {
    @Composable
    fun SceneScope.LockIcon(modifier: Modifier = Modifier) {
    fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
        if (!KeyguardBottomAreaRefactor.isEnabled && !DeviceEntryUdfpsRefactor.isEnabled) {
            return
        }
@@ -93,6 +94,7 @@ constructor(
                                deviceEntryBackgroundViewModel.get(),
                                falsingManager.get(),
                                vibratorHelper.get(),
                                overrideColor,
                            )
                        }
                    } else {
+0 −2
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
import com.android.systemui.communal.ui.viewmodel.PopupType
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -145,7 +144,6 @@ class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
                kosmos.communalInteractor,
                kosmos.communalTutorialInteractor,
                kosmos.shadeInteractor,
                kosmos.deviceEntryInteractor,
                mediaHost,
                logcatLogBuffer("CommunalViewModelTest"),
            )
Loading