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

Commit 47f8752a authored by Shawn Lee's avatar Shawn Lee Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Integrate Privacy Chip into Flexiglass Shade Header" into main

parents da215de7 9e031094
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -13,7 +13,9 @@ fun TransitionBuilder.goneToShadeTransition(
) {
    spec = tween(durationMillis = DefaultDuration.times(durationScale).inWholeMilliseconds.toInt())

    fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContent) }
    fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
    fractionRange(start = .58f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }
    fractionRange(start = .58f) { fade(ShadeHeader.Elements.PrivacyChip) }
    translate(QuickSettings.Elements.Content, y = -ShadeHeader.Dimensions.CollapsedHeight * .66f)
    translate(Notifications.Elements.NotificationScrim, Edge.Top, false)
}
+7 −2
Original line number Diff line number Diff line
@@ -13,10 +13,15 @@ fun TransitionBuilder.shadeToQuickSettingsTransition() {
    translate(Notifications.Elements.NotificationScrim, Edge.Bottom)
    timestampRange(endMillis = 83) { fade(QuickSettings.Elements.FooterActions) }

    translate(ShadeHeader.Elements.CollapsedContent, y = ShadeHeader.Dimensions.CollapsedHeight)
    translate(
        ShadeHeader.Elements.CollapsedContentStart,
        y = ShadeHeader.Dimensions.CollapsedHeight
    )
    translate(ShadeHeader.Elements.CollapsedContentEnd, y = ShadeHeader.Dimensions.CollapsedHeight)
    translate(ShadeHeader.Elements.ExpandedContent, y = (-ShadeHeader.Dimensions.ExpandedHeight))

    fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContent) }
    fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentStart) }
    fractionRange(end = .14f) { fade(ShadeHeader.Elements.CollapsedContentEnd) }

    fractionRange(start = .58f) { fade(ShadeHeader.Elements.ExpandedContent) }
}
+157 −80
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ package com.android.systemui.shade.ui.composable
import android.view.ContextThemeWrapper
import android.view.ViewGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
@@ -48,6 +49,7 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateSceneFloatAsState
@@ -57,9 +59,11 @@ import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.ui.composable.QuickSettings
import com.android.systemui.scene.ui.composable.Shade as ShadeKey
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.phone.StatusBarIconController
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager
@@ -72,7 +76,9 @@ import com.android.systemui.statusbar.policy.Clock
object ShadeHeader {
    object Elements {
        val ExpandedContent = ElementKey("ShadeHeaderExpandedContent")
        val CollapsedContent = ElementKey("ShadeHeaderCollapsedContent")
        val CollapsedContentStart = ElementKey("ShadeHeaderCollapsedContentStart")
        val CollapsedContentEnd = ElementKey("ShadeHeaderCollapsedContentEnd")
        val PrivacyChip = ElementKey("PrivacyChip", scenePicker = LowestZIndexScenePicker)
    }

    object Keys {
@@ -106,15 +112,16 @@ fun SceneScope.CollapsedShadeHeader(
                cutoutLocation != CutoutLocation.CENTER || formatProgress.value > 0.5f
            }
        }
    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()

    // This layout assumes it is globally positioned at (0, 0) and is the
    // same size as the screen.
    Layout(
        modifier = modifier.element(ShadeHeader.Elements.CollapsedContent),
        modifier = modifier,
        contents =
            listOf(
                {
                    Row {
                    Row(modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentStart)) {
                        AndroidView(
                            factory = { context ->
                                Clock(
@@ -132,7 +139,18 @@ fun SceneScope.CollapsedShadeHeader(
                    }
                },
                {
                    Row(horizontalArrangement = Arrangement.End) {
                    if (isPrivacyChipVisible) {
                        Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
                            PrivacyChip(
                                viewModel = viewModel,
                                modifier = Modifier.align(Alignment.CenterEnd),
                            )
                        }
                    } else {
                        Row(
                            horizontalArrangement = Arrangement.End,
                            modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentEnd)
                        ) {
                            SystemIconContainer {
                                when (LocalWindowSizeClass.current.widthSizeClass) {
                                    WindowWidthSizeClass.Medium,
@@ -153,12 +171,14 @@ fun SceneScope.CollapsedShadeHeader(
                                            .weight(1f, fill = false)
                                )
                                BatteryIcon(
                                createBatteryMeterViewController = createBatteryMeterViewController,
                                    createBatteryMeterViewController =
                                        createBatteryMeterViewController,
                                    useExpandedFormat = useExpandedFormat,
                                    modifier = Modifier.align(Alignment.CenterVertically),
                                )
                            }
                        }
                    }
                },
            ),
    ) { measurables, constraints ->
@@ -223,12 +243,21 @@ fun SceneScope.ExpandedShadeHeader(
            .unsafeCompositionState(initialValue = 1f)
    val useExpandedFormat by
        remember(formatProgress) { derivedStateOf { formatProgress.value > 0.5f } }
    val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()

    Box(modifier = modifier) {
        if (isPrivacyChipVisible) {
            Box(modifier = Modifier.height(CollapsedHeight).fillMaxWidth()) {
                PrivacyChip(
                    viewModel = viewModel,
                    modifier = Modifier.align(Alignment.CenterEnd),
                )
            }
        }
        Column(
            verticalArrangement = Arrangement.Bottom,
            modifier =
            modifier
                .element(ShadeHeader.Elements.ExpandedContent)
                Modifier.element(ShadeHeader.Elements.ExpandedContent)
                    .fillMaxWidth()
                    .defaultMinSize(minHeight = ShadeHeader.Dimensions.ExpandedHeight)
        ) {
@@ -287,6 +316,7 @@ fun SceneScope.ExpandedShadeHeader(
            }
        }
    }
}

@Composable
private fun BatteryIcon(
@@ -359,7 +389,14 @@ private fun SceneScope.StatusIcons(
) {
    val carrierIconSlots =
        listOf(stringResource(id = com.android.internal.R.string.status_bar_mobile))
    val cameraSlot = stringResource(id = com.android.internal.R.string.status_bar_camera)
    val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
    val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)

    val isSingleCarrier by viewModel.isSingleCarrier.collectAsState()
    val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsState()
    val isMicCameraIndicationEnabled by viewModel.isMicCameraIndicationEnabled.collectAsState()
    val isLocationIndicationEnabled by viewModel.isLocationIndicationEnabled.collectAsState()

    AndroidView(
        factory = { context ->
@@ -382,6 +419,25 @@ private fun SceneScope.StatusIcons(
            } else {
                iconContainer.addIgnoredSlots(carrierIconSlots)
            }

            if (isPrivacyChipEnabled) {
                if (isMicCameraIndicationEnabled) {
                    iconContainer.addIgnoredSlot(cameraSlot)
                    iconContainer.addIgnoredSlot(micSlot)
                } else {
                    iconContainer.removeIgnoredSlot(cameraSlot)
                    iconContainer.removeIgnoredSlot(micSlot)
                }
                if (isLocationIndicationEnabled) {
                    iconContainer.addIgnoredSlot(locationSlot)
                } else {
                    iconContainer.removeIgnoredSlot(locationSlot)
                }
            } else {
                iconContainer.removeIgnoredSlot(cameraSlot)
                iconContainer.removeIgnoredSlot(micSlot)
                iconContainer.removeIgnoredSlot(locationSlot)
            }
        },
        modifier = modifier,
    )
@@ -394,7 +450,28 @@ private fun SystemIconContainer(
) {
    // TODO(b/298524053): add hover state for this container
    Row(
        modifier = modifier.height(ShadeHeader.Dimensions.CollapsedHeight),
        modifier = modifier.height(CollapsedHeight),
        content = content,
    )
}

@Composable
private fun SceneScope.PrivacyChip(
    viewModel: ShadeHeaderViewModel,
    modifier: Modifier = Modifier,
) {
    val privacyList by viewModel.privacyItems.collectAsState()

    AndroidView(
        factory = { context ->
            val view =
                OngoingPrivacyChip(context, null).also { privacyChip ->
                    privacyChip.privacyList = privacyList
                    privacyChip.setOnClickListener { viewModel.onPrivacyChipClicked(privacyChip) }
                }
            view
        },
        update = { it.privacyList = privacyList },
        modifier = modifier.element(ShadeHeader.Elements.PrivacyChip),
    )
}
+2 −3
Original line number Diff line number Diff line
@@ -26,11 +26,11 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FooterActionsController
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.shared.model.UserActionResult
import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
@@ -58,7 +58,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val sceneInteractor by lazy { kosmos.sceneInteractor }
    private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
    private val flags = FakeFeatureFlagsClassic().also { it.set(Flags.NEW_NETWORK_SLICE_UI, false) }
    private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
@@ -95,9 +94,9 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
            ShadeHeaderViewModel(
                applicationScope = testScope.backgroundScope,
                context = context,
                sceneInteractor = sceneInteractor,
                mobileIconsInteractor = mobileIconsInteractor,
                mobileIconsViewModel = mobileIconsViewModel,
                privacyChipInteractor = kosmos.privacyChipInteractor,
                broadcastDispatcher = fakeBroadcastDispatcher,
            )

+2 −1
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.shade.domain.interactor.privacyChipInteractor
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
@@ -229,9 +230,9 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() {
            ShadeHeaderViewModel(
                applicationScope = testScope.backgroundScope,
                context = context,
                sceneInteractor = sceneInteractor,
                mobileIconsInteractor = mobileIconsInteractor,
                mobileIconsViewModel = mobileIconsViewModel,
                privacyChipInteractor = kosmos.privacyChipInteractor,
                broadcastDispatcher = fakeBroadcastDispatcher,
            )

Loading