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

Commit c73f5616 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin Committed by Ale Nijamkin
Browse files

[flexiglass] Lockscreen scene

Adds the standalone KeyguardRootView to LockscreenScene.

Note how we remove it from its parent ViewGroup, if there is one,
whenever our view factory gets called. This is needed so there is only
one KeyguardRootView at all times (or it crashes because the NSSL gets
initialized twice).

Bug: 280879610
Test: manually verified that the lockscreen scene renders the real
lockscreen
Test: manually verified that pulling down brings down the shade and
pulling up brings up the bouncer
Test: manually verified the ability to unlock via PIN bouncer and face
unlock and then re-lock the device via power button + timeout - no crash
Test: manually verified correct rendering in AOD

Change-Id: I54c11bccc7a10e43afea5dbaa09013e72b0a806f
parent 8a5c6453
Loading
Loading
Loading
Loading
+25 −31
Original line number Diff line number Diff line
@@ -14,26 +14,20 @@
 * limitations under the License.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.keyguard.ui.composable

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.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import android.view.View
import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -42,6 +36,7 @@ import com.android.systemui.scene.shared.model.UserAction
import com.android.systemui.scene.ui.composable.ComposableScene
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@@ -54,6 +49,7 @@ class LockscreenScene
constructor(
    @Application private val applicationScope: CoroutineScope,
    private val viewModel: LockscreenSceneViewModel,
    @KeyguardRootView private val viewProvider: () -> @JvmSuppressWildcards View,
) : ComposableScene {
    override val key = SceneKey.Lockscreen

@@ -72,6 +68,7 @@ constructor(
    ) {
        LockscreenScene(
            viewModel = viewModel,
            viewProvider = viewProvider,
            modifier = modifier,
        )
    }
@@ -89,25 +86,22 @@ constructor(
@Composable
private fun LockscreenScene(
    viewModel: LockscreenSceneViewModel,
    viewProvider: () -> View,
    modifier: Modifier = Modifier,
) {
    // TODO(b/280879610): implement the real UI.

    val lockButtonIcon: Icon by viewModel.lockButtonIcon.collectAsState()

    Box(modifier = modifier) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier.align(Alignment.Center)
        ) {
            Text("Lockscreen", style = MaterialTheme.typography.headlineMedium)
            Row(
                horizontalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                Button(onClick = { viewModel.onLockButtonClicked() }) { Icon(lockButtonIcon) }

                Button(onClick = { viewModel.onContentClicked() }) { Text("Open some content") }
            }
        }
    AndroidView(
        factory = { _ ->
            val keyguardRootView = viewProvider()
            // Remove the KeyguardRootView from any parent it might already have in legacy code just
            // in case (a view can't have two parents).
            (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
            keyguardRootView
        },
        update = { keyguardRootView ->
            keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener {
                viewModel.onLockButtonClicked()
            }
        },
        modifier = modifier,
    )
}
+24 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 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.keyguard.qualifiers

import javax.inject.Qualifier

@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class KeyguardRootView
+41 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.keyguard.ui.view

import android.view.View
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardViewConfigurator
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
import dagger.Module
import dagger.Provides
import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi

@Module
object LockscreenSceneModule {

    @Provides
    @SysUISingleton
    @KeyguardRootView
    fun viewProvider(
        configurator: Provider<KeyguardViewConfigurator>,
    ): () -> View {
        return { configurator.get().getKeyguardRootView() }
    }
}
+0 −46
Original line number Diff line number Diff line
@@ -16,41 +16,22 @@

package com.android.systemui.keyguard.ui.viewmodel

import com.android.systemui.R
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/** Models UI state and handles user input for the lockscreen scene. */
@SysUISingleton
class LockscreenSceneViewModel
@Inject
constructor(
    @Application applicationScope: CoroutineScope,
    authenticationInteractor: AuthenticationInteractor,
    private val bouncerInteractor: BouncerInteractor,
) {
    /** The icon for the "lock" button on the lockscreen. */
    val lockButtonIcon: StateFlow<Icon> =
        authenticationInteractor.isUnlocked
            .map { isUnlocked -> lockIcon(isUnlocked = isUnlocked) }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = lockIcon(isUnlocked = authenticationInteractor.isUnlocked.value),
            )

    /** The key of the scene we should switch to when swiping up. */
    val upDestinationSceneKey: Flow<SceneKey> =
        authenticationInteractor.isUnlocked.map { isUnlocked ->
@@ -65,31 +46,4 @@ constructor(
    fun onLockButtonClicked() {
        bouncerInteractor.showOrUnlockDevice()
    }

    /** Notifies that some content on the lock screen was clicked. */
    fun onContentClicked() {
        bouncerInteractor.showOrUnlockDevice()
    }

    private fun upDestinationSceneKey(
        canSwipeToDismiss: Boolean,
    ): SceneKey {
        return if (canSwipeToDismiss) SceneKey.Gone else SceneKey.Bouncer
    }

    private fun lockIcon(
        isUnlocked: Boolean,
    ): Icon {
        return if (isUnlocked) {
            Icon.Resource(
                R.drawable.ic_device_lock_off,
                ContentDescription.Resource(R.string.accessibility_unlock_button)
            )
        } else {
            Icon.Resource(
                R.drawable.ic_device_lock_on,
                ContentDescription.Resource(R.string.accessibility_lock_icon)
            )
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.scene

import com.android.systemui.keyguard.ui.view.LockscreenSceneModule
import com.android.systemui.scene.domain.startable.SceneContainerStartableModule
import com.android.systemui.scene.shared.model.SceneContainerConfigModule
import com.android.systemui.scene.ui.composable.SceneModule
@@ -24,6 +25,7 @@ import dagger.Module
@Module(
    includes =
        [
            LockscreenSceneModule::class,
            SceneContainerConfigModule::class,
            SceneContainerStartableModule::class,
            SceneModule::class,
Loading