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

Commit a3a7fabf authored by Brad Hinegardner's avatar Brad Hinegardner
Browse files

Split up KeyguardBottomArea

This splits the KeyguardBottomArea into many pieces:
- Left shortcut
- Right shortcut
- Ambient indication container
- Customize Lockscreen button

Each of the elements above are now able to be independently laid out.

The alpha of the entire KeyguardRootView can now be easily modified, rather than having to adjust the alpha of each sub-element individually.

Fixes: 278057014
Test: atest KeyguardQuickAffordancesCombinedViewModelTest.kt
Test: atest KeyguardRootViewModelTest.kt
Change-Id: I098174809144cdf09dd71665ab724f8c508ae9e9
parent 9ae55983
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -65,17 +65,17 @@
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <include layout="@layout/status_bar_expanded"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />

    <!-- Root for all keyguard content. It was previously located within the shade. -->
    <com.android.systemui.keyguard.ui.view.KeyguardRootView
        android:id="@id/keyguard_root_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <include layout="@layout/status_bar_expanded"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />

    <!-- Shared container for the notification stack. Can be positioned by either
         the keyguard_root_view or notification_panel -->
    <com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+6 −0
Original line number Diff line number Diff line
@@ -821,6 +821,12 @@
    <dimen name="keyguard_affordance_fixed_radius">24dp</dimen>
    <dimen name="keyguard_affordance_fixed_padding">12dp</dimen>

    <!--  The width/height/padding of the keyguard settings popup menu  -->
    <dimen name="keyguard_settings_popup_menu_icon_height">24dp</dimen>
    <dimen name="keyguard_settings_popup_menu_icon_width">24dp</dimen>
    <dimen name="keyguard_settings_popup_menu_icon_end_margin">8dp</dimen>
    <dimen name="keyguard_settings_popup_menu_padding">12dp</dimen>

    <!-- Amount the button should shake when it's not long-pressed for long enough. -->
    <dimen name="keyguard_affordance_shake_amplitude">8dp</dimen>

+136 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

package com.android.systemui.keyguard

import android.content.res.Configuration
import android.view.View
import android.view.ViewGroup
import com.android.systemui.CoreStartable
@@ -24,18 +25,29 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
@@ -49,26 +61,44 @@ class KeyguardViewConfigurator
constructor(
    private val keyguardRootView: KeyguardRootView,
    private val sharedNotificationContainer: SharedNotificationContainer,
    private val keyguardRootViewModel: KeyguardRootViewModel,
    private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
    private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
    private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
    private val notificationShadeWindowView: NotificationShadeWindowView,
    private val featureFlags: FeatureFlags,
    private val indicationController: KeyguardIndicationController,
    private val keyguardLayoutManager: KeyguardLayoutManager,
    private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
    private val keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
    private val falsingManager: FalsingManager,
    private val vibratorHelper: VibratorHelper,
    private val keyguardStateController: KeyguardStateController,
    private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
    private val activityStarter: ActivityStarter,
    private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
    private val chipbarCoordinator: ChipbarCoordinator,
) : CoreStartable {

    private var rootViewHandle: DisposableHandle? = null
    private var indicationAreaHandle: DisposableHandle? = null
    private var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
    private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
    private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
    private var settingsPopupMenuHandle: DisposableHandle? = null

    override fun start() {
        bindKeyguardRootView()
        val notificationPanel =
            notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
        bindIndicationArea(notificationPanel)
        unbindKeyguardBottomArea(notificationPanel)
        bindIndicationArea()
        bindLockIconView(notificationPanel)
        setupNotificationStackScrollLayout(notificationPanel)
        bindLeftShortcut()
        bindRightShortcut()
        bindAmbientIndicationArea()
        bindSettingsPopupMenu()

        keyguardLayoutManager.layoutViews()
        keyguardLayoutManagerCommandListener.start()
@@ -90,16 +120,21 @@ constructor(
        }
    }

    fun bindIndicationArea(legacyParent: ViewGroup) {
    override fun onConfigurationChanged(newConfig: Configuration?) {
        super.onConfigurationChanged(newConfig)
        leftShortcutHandle?.onConfigurationChanged()
        rightShortcutHandle?.onConfigurationChanged()
        ambientIndicationAreaHandle?.onConfigurationChanged()

        keyguardLayoutManager.layoutViews()
    }

    fun bindIndicationArea() {
        indicationAreaHandle?.dispose()

        // At startup, 2 views with the ID `R.id.keyguard_indication_area` will be available.
        // Disable one of them
        if (featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) {
            legacyParent.findViewById<View>(R.id.keyguard_indication_area)?.let {
                legacyParent.removeView(it)
            }
        } else {
        if (!featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) {
            keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let {
                keyguardRootView.removeView(it)
            }
@@ -109,7 +144,21 @@ constructor(
            KeyguardIndicationAreaBinder.bind(
                notificationShadeWindowView,
                keyguardIndicationAreaViewModel,
                indicationController
                keyguardRootViewModel,
                indicationController,
                featureFlags,
            )
    }

    private fun bindKeyguardRootView() {
        rootViewHandle?.dispose()
        rootViewHandle = KeyguardRootViewBinder.bind(
            keyguardRootView,
            keyguardRootViewModel,
            featureFlags,
            occludingAppDeviceEntryMessageViewModel,
            chipbarCoordinator,
            keyguardStateController,
        )
    }

@@ -125,12 +174,84 @@ constructor(
        }
    }

    private fun bindKeyguardRootView() {
        KeyguardRootViewBinder.bind(
    private fun bindAmbientIndicationArea() {
        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
            ambientIndicationAreaHandle?.destroy()
            ambientIndicationAreaHandle =
                KeyguardAmbientIndicationAreaViewBinder.bind(
                    notificationShadeWindowView,
                    keyguardAmbientIndicationViewModel,
                    keyguardRootViewModel,
                )
        } else {
            keyguardRootView.findViewById<View?>(R.id.ambient_indication_container)?.let {
                keyguardRootView.removeView(it)
            }
        }
    }

    private fun bindSettingsPopupMenu() {
        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
            settingsPopupMenuHandle?.dispose()
            settingsPopupMenuHandle =
                KeyguardSettingsViewBinder.bind(
                    keyguardRootView,
            featureFlags,
            occludingAppDeviceEntryMessageViewModel,
            chipbarCoordinator,
                    keyguardSettingsMenuViewModel,
                    vibratorHelper,
                    activityStarter,
                )
        } else {
            keyguardRootView.findViewById<View?>(R.id.keyguard_settings_button)?.let {
                keyguardRootView.removeView(it)
            }
        }
    }

    private fun unbindKeyguardBottomArea(legacyParent: ViewGroup) {
        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
            legacyParent.requireViewById<View>(R.id.keyguard_bottom_area).let {
                legacyParent.removeView(it)
            }
        }
    }

    private fun bindLeftShortcut() {
        leftShortcutHandle?.destroy()
        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
            leftShortcutHandle =
                KeyguardQuickAffordanceViewBinder.bind(
                    keyguardRootView.requireViewById(R.id.start_button),
                    keyguardQuickAffordancesCombinedViewModel.startButton,
                    keyguardRootViewModel.alpha,
                    falsingManager,
                    vibratorHelper,
                ) {
                    indicationController.showTransientIndication(it)
                }
        } else {
            keyguardRootView.findViewById<View?>(R.id.start_button)?.let {
                keyguardRootView.removeView(it)
            }
        }
    }

    private fun bindRightShortcut() {
        rightShortcutHandle?.destroy()
        if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
            rightShortcutHandle =
                KeyguardQuickAffordanceViewBinder.bind(
                    keyguardRootView.requireViewById(R.id.end_button),
                    keyguardQuickAffordancesCombinedViewModel.endButton,
                    keyguardRootViewModel.alpha,
                    falsingManager,
                    vibratorHelper,
                ) {
                    indicationController.showTransientIndication(it)
                }
        } else {
            keyguardRootView.findViewById<View?>(R.id.end_button)?.let {
                keyguardRootView.removeView(it)
            }
        }
    }
}
+43 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import com.android.systemui.keyguard.shared.model.ScreenModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
@@ -76,6 +77,8 @@ interface KeyguardRepository {
     */
    val bottomAreaAlpha: StateFlow<Float>

    val keyguardAlpha: StateFlow<Float>

    /**
     * Observable of the relative offset of the lock-screen clock from its natural position on the
     * screen.
@@ -170,6 +173,9 @@ interface KeyguardRepository {
    /** Whether quick settings or quick-quick settings is visible. */
    val isQuickSettingsVisible: Flow<Boolean>

    /** Represents the current state of the KeyguardRootView visibility */
    val keyguardRootViewVisibility: Flow<KeyguardRootViewVisibilityState>

    /** Receive an event for doze time tick */
    val dozeTimeTick: Flow<Unit>

@@ -194,8 +200,14 @@ interface KeyguardRepository {
    fun setAnimateDozingTransitions(animate: Boolean)

    /** Sets the current amount of alpha that should be used for rendering the bottom area. */
    @Deprecated("Deprecated as part of b/278057014")
    fun setBottomAreaAlpha(alpha: Float)

    /** Sets the current amount of alpha that should be used for rendering the keyguard. */
    fun setKeyguardAlpha(alpha: Float)

    fun setKeyguardVisibility(statusBarState: Int, goingToFullShade: Boolean, occlusionTransitionRunning: Boolean)

    /**
     * Sets the relative offset of the lock-screen clock from its natural position on the screen.
     */
@@ -244,6 +256,9 @@ constructor(
    private val _bottomAreaAlpha = MutableStateFlow(1f)
    override val bottomAreaAlpha = _bottomAreaAlpha.asStateFlow()

    private val _keyguardAlpha = MutableStateFlow(1f)
    override val keyguardAlpha = _keyguardAlpha.asStateFlow()

    private val _clockPosition = MutableStateFlow(Position(0, 0))
    override val clockPosition = _clockPosition.asStateFlow()

@@ -686,6 +701,17 @@ constructor(
    private val _isActiveDreamLockscreenHosted = MutableStateFlow(false)
    override val isActiveDreamLockscreenHosted = _isActiveDreamLockscreenHosted.asStateFlow()

    private val _keyguardRootViewVisibility =
        MutableStateFlow(
            KeyguardRootViewVisibilityState(
                com.android.systemui.statusbar.StatusBarState.SHADE,
                goingToFullShade = false,
                occlusionTransitionRunning = false,
            )
        )
    override val keyguardRootViewVisibility: Flow<KeyguardRootViewVisibilityState> =
        _keyguardRootViewVisibility.asStateFlow()

    override fun setAnimateDozingTransitions(animate: Boolean) {
        _animateBottomAreaDozingTransitions.value = animate
    }
@@ -694,6 +720,23 @@ constructor(
        _bottomAreaAlpha.value = alpha
    }

    override fun setKeyguardAlpha(alpha: Float) {
        _keyguardAlpha.value = alpha
    }

    override fun setKeyguardVisibility(
        statusBarState: Int,
        goingToFullShade: Boolean,
        occlusionTransitionRunning: Boolean
    ) {
        _keyguardRootViewVisibility.value =
            KeyguardRootViewVisibilityState(
                statusBarState,
                goingToFullShade,
                occlusionTransitionRunning
            )
    }

    override fun setClockPosition(x: Int, y: Int) {
        _clockPosition.value = Position(x, y)
    }
+44 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.graphics.Point
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
@@ -32,14 +33,17 @@ import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -63,6 +67,18 @@ constructor(
    bouncerRepository: KeyguardBouncerRepository,
    configurationRepository: ConfigurationRepository,
) {

    data class PreviewMode(
        val isInPreviewMode: Boolean = false,
        val shouldHighlightSelectedAffordance: Boolean = false,
    )

    /**
     * Whether this view-model instance is powering the preview experience that renders exclusively
     * in the wallpaper picker application. This should _always_ be `false` for the real lock screen
     * experience.
     */
    val previewMode = MutableStateFlow(PreviewMode())
    /**
     * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
     * all.
@@ -142,8 +158,6 @@ constructor(
    val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
    /** Observable for the [StatusBarState] */
    val statusBarState: Flow<StatusBarState> = repository.statusBarState
    /** Whether or not quick settings or quick quick settings are showing. */
    val isQuickSettingsVisible: Flow<Boolean> = repository.isQuickSettingsVisible
    /**
     * Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear,
     * side, under display) is used to unlock the device.
@@ -182,6 +196,18 @@ constructor(
    /** Notifies when a new configuration is set */
    val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange

    /** Represents the current state of the KeyguardRootView visibility */
    val keyguardRootViewVisibilityState: Flow<KeyguardRootViewVisibilityState> =
        repository.keyguardRootViewVisibility

    /** The position of the keyguard clock. */
    val clockPosition: Flow<Position> = repository.clockPosition

    val keyguardAlpha: Flow<Float> = repository.keyguardAlpha

    /** Whether to animate the next doze mode transition. */
    val animateDozingTransitions: Flow<Boolean> = repository.animateBottomAreaDozingTransitions

    fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> {
        return dozeTransitionModel.filter { states.contains(it.to) }
    }
@@ -211,6 +237,22 @@ constructor(
        repository.setQuickSettingsVisible(isVisible)
    }

    fun setKeyguardRootVisibility(statusBarState: Int, goingToFullShade: Boolean, isOcclusionTransitionRunning: Boolean) {
        repository.setKeyguardVisibility(statusBarState, goingToFullShade, isOcclusionTransitionRunning)
    }

    fun setClockPosition(x: Int, y: Int) {
        repository.setClockPosition(x, y)
    }

    fun setAlpha(alpha: Float) {
        repository.setKeyguardAlpha(alpha)
    }

    fun setAnimateDozingTransitions(animate: Boolean) {
        repository.setAnimateDozingTransitions(animate)
    }

    companion object {
        private const val TAG = "KeyguardInteractor"
    }
Loading