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

Commit 9dc9ce2e authored by Beverly Tai's avatar Beverly Tai Committed by Automerger Merge Worker
Browse files

Merge changes I7b329ef9,I0d297749 into udc-qpr-dev am: f7e849a1 am: e9f1ef5b

parents 4d9527e9 e9f1ef5b
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -25,19 +25,24 @@ 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.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
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.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.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.ExperimentalCoroutinesApi

/** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */
@ExperimentalCoroutinesApi
@SysUISingleton
class KeyguardViewConfigurator
@Inject
@@ -51,11 +56,14 @@ constructor(
    private val indicationController: KeyguardIndicationController,
    private val keyguardLayoutManager: KeyguardLayoutManager,
    private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
    private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
    private val chipbarCoordinator: ChipbarCoordinator,
) : CoreStartable {

    private var indicationAreaHandle: DisposableHandle? = null

    override fun start() {
        bindKeyguardRootView()
        val notificationPanel =
            notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
        bindIndicationArea(notificationPanel)
@@ -116,4 +124,13 @@ constructor(
            }
        }
    }

    private fun bindKeyguardRootView() {
        KeyguardRootViewBinder.bind(
            keyguardRootView,
            featureFlags,
            occludingAppDeviceEntryMessageViewModel,
            chipbarCoordinator,
        )
    }
}
+138 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.domain.interactor

import android.content.Context
import android.content.Intent
import android.hardware.fingerprint.FingerprintManager
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch

/** Business logic for handling authentication events when an app is occluding the lockscreen. */
@ExperimentalCoroutinesApi
@SysUISingleton
class OccludingAppDeviceEntryInteractor
@Inject
constructor(
    biometricMessageInteractor: BiometricMessageInteractor,
    fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
    keyguardInteractor: KeyguardInteractor,
    primaryBouncerInteractor: PrimaryBouncerInteractor,
    alternateBouncerInteractor: AlternateBouncerInteractor,
    @Application scope: CoroutineScope,
    private val context: Context,
    activityStarter: ActivityStarter,
) {
    private val keyguardOccludedByApp: Flow<Boolean> =
        combine(
                keyguardInteractor.isKeyguardOccluded,
                keyguardInteractor.isKeyguardShowing,
                primaryBouncerInteractor.isShowing,
                alternateBouncerInteractor.isVisible,
            ) { occluded, showing, primaryBouncerShowing, alternateBouncerVisible ->
                occluded && showing && !primaryBouncerShowing && !alternateBouncerVisible
            }
            .distinctUntilChanged()
    private val fingerprintUnlockSuccessEvents: Flow<Unit> =
        fingerprintAuthRepository.authenticationStatus
            .ifKeyguardOccludedByApp()
            .filter { it is SuccessFingerprintAuthenticationStatus }
            .map {} // maps FingerprintAuthenticationStatus => Unit
    private val fingerprintLockoutEvents: Flow<Unit> =
        fingerprintAuthRepository.authenticationStatus
            .ifKeyguardOccludedByApp()
            .filter {
                it is ErrorFingerprintAuthenticationStatus &&
                    (it.msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT ||
                        it.msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
            }
            .map {} // maps FingerprintAuthenticationStatus => Unit
    val message: Flow<BiometricMessage?> =
        merge(
                biometricMessageInteractor.fingerprintErrorMessage,
                biometricMessageInteractor.fingerprintFailMessage,
                biometricMessageInteractor.fingerprintHelpMessage,
            )
            .ifKeyguardOccludedByApp(/* elseFlow */ flowOf(null))

    init {
        scope.launch {
            // On fingerprint success, go to the home screen
            fingerprintUnlockSuccessEvents.collect { goToHomeScreen() }
        }

        scope.launch {
            // On device fingerprint lockout, request the bouncer with a runnable to
            // go to the home screen. Without this, the bouncer won't proceed to the home screen.
            fingerprintLockoutEvents.collect {
                activityStarter.dismissKeyguardThenExecute(
                    object : ActivityStarter.OnDismissAction {
                        override fun onDismiss(): Boolean {
                            goToHomeScreen()
                            return false
                        }

                        override fun willRunAnimationOnKeyguard(): Boolean {
                            return false
                        }
                    },
                    /* cancel= */ null,
                    /* afterKeyguardGone */ false
                )
            }
        }
    }

    /** Launches an Activity which forces the current app to background by going home. */
    private fun goToHomeScreen() {
        context.startActivity(
            Intent(Intent.ACTION_MAIN).apply {
                addCategory(Intent.CATEGORY_HOME)
                flags = Intent.FLAG_ACTIVITY_NEW_TASK
            }
        )
    }

    private fun <T> Flow<T>.ifKeyguardOccludedByApp(elseFlow: Flow<T> = emptyFlow()): Flow<T> {
        return keyguardOccludedByApp.flatMapLatest { keyguardOccludedByApp ->
            if (keyguardOccludedByApp) {
                this
            } else {
                elseFlow
            }
        }
    }
}
+93 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.ui.binder

import android.annotation.DrawableRes
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.TintedIcon
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.temporarydisplay.ViewPriority
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.launch

/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@ExperimentalCoroutinesApi
object KeyguardRootViewBinder {
    @JvmStatic
    fun bind(
        view: ViewGroup,
        featureFlags: FeatureFlags,
        occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
        chipbarCoordinator: ChipbarCoordinator,
    ) {
        if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
            view.repeatWhenAttached {
                repeatOnLifecycle(Lifecycle.State.CREATED) {
                    launch {
                        occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage
                            ->
                            if (biometricMessage?.message != null) {
                                chipbarCoordinator.displayView(
                                    createChipbarInfo(
                                        biometricMessage.message,
                                        R.drawable.ic_lock,
                                    )
                                )
                            } else {
                                chipbarCoordinator.removeView(ID, "occludingAppMsgNull")
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Creates an instance of [ChipbarInfo] that can be sent to [ChipbarCoordinator] for display.
     */
    private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo {
        return ChipbarInfo(
            startIcon =
                TintedIcon(
                    Icon.Resource(icon, null),
                    ChipbarInfo.DEFAULT_ICON_TINT,
                ),
            text = Text.Loaded(message),
            endItem = null,
            vibrationEffect = null,
            windowTitle = "OccludingAppUnlockMsgChip",
            wakeReason = "OCCLUDING_APP_UNLOCK_MSG_CHIP",
            timeoutMs = 3500,
            id = ID,
            priority = ViewPriority.CRITICAL,
            instanceId = null,
        )
    }

    private const val ID = "occluding_app_device_entry_unlock_msg"
}
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.ui.viewmodel

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.BiometricMessage
import com.android.systemui.keyguard.domain.interactor.OccludingAppDeviceEntryInteractor
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow

/** Shows authentication messages over occcluding apps over the lockscreen. */
@ExperimentalCoroutinesApi
@SysUISingleton
class OccludingAppDeviceEntryMessageViewModel
@Inject
constructor(
    interactor: OccludingAppDeviceEntryInteractor,
) {
    val message: Flow<BiometricMessage?> = interactor.message
}
+299 −0

File added.

Preview size limit exceeded, changes collapsed.