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

Commit 5f5abbfe authored by Chandru S's avatar Chandru S Committed by Android (Google) Code Review
Browse files

Merge "Change the compose bouncer wire-up to follow recommended architecture guidelines" into main

parents 9f11bb82 29e6bfef
Loading
Loading
Loading
Loading
+3 −11
Original line number Diff line number Diff line
@@ -2,13 +2,11 @@ package com.android.systemui.bouncer.ui.binder

import android.view.ViewGroup
import com.android.keyguard.KeyguardMessageAreaController
import com.android.keyguard.ViewMediatorCallback
import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.dagger.SysUISingleton
@@ -39,12 +37,9 @@ constructor(
data class ComposeBouncerDependencies
@Inject
constructor(
    val legacyInteractor: PrimaryBouncerInteractor,
    val viewModelFactory: BouncerSceneContentViewModel.Factory,
    val dialogFactory: BouncerDialogFactory,
    val authenticationInteractor: AuthenticationInteractor,
    val viewMediatorCallback: ViewMediatorCallback?,
    val selectedUserInteractor: SelectedUserInteractor,
    val bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
)

/**
@@ -63,12 +58,9 @@ constructor(
            val deps = composeBouncerDependencies.get()
            ComposeBouncerViewBinder.bind(
                view,
                deps.legacyInteractor,
                deps.viewModelFactory,
                deps.dialogFactory,
                deps.authenticationInteractor,
                deps.selectedUserInteractor,
                deps.viewMediatorCallback,
                deps.bouncerContainerViewModelFactory,
            )
        } else {
            val deps = legacyBouncerDependencies.get()
+32 −66
Original line number Diff line number Diff line
@@ -5,36 +5,34 @@ import androidx.activity.OnBackPressedDispatcher
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.compose.ui.platform.ComposeView
import androidx.core.view.isVisible
import androidx.core.view.isGone
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.composable.BouncerContainer
import com.android.systemui.bouncer.ui.viewmodel.BouncerContainerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
import kotlinx.coroutines.awaitCancellation

/** View binder responsible for binding the compose version of the bouncer. */
object ComposeBouncerViewBinder {
    fun bind(
        view: ViewGroup,
        legacyInteractor: PrimaryBouncerInteractor,
        viewModelFactory: BouncerSceneContentViewModel.Factory,
        dialogFactory: BouncerDialogFactory,
        authenticationInteractor: AuthenticationInteractor,
        selectedUserInteractor: SelectedUserInteractor,
        viewMediatorCallback: ViewMediatorCallback?,
        bouncerContainerViewModelFactory: BouncerContainerViewModel.Factory,
    ) {
        view.addView(
            ComposeView(view.context).apply {
                repeatWhenAttached {
                    repeatOnLifecycle(Lifecycle.State.CREATED) {
                        setViewTreeOnBackPressedDispatcherOwner(
        view.repeatWhenAttached {
            view.viewModel(
                minWindowLifecycleState = WindowLifecycleState.ATTACHED,
                factory = { bouncerContainerViewModelFactory.create() },
                traceName = "ComposeBouncerViewBinder",
            ) { viewModel ->
                try {
                    view.setViewTreeOnBackPressedDispatcherOwner(
                        object : OnBackPressedDispatcherOwner {
                            override val onBackPressedDispatcher =
                                OnBackPressedDispatcher().apply {
@@ -43,51 +41,19 @@ object ComposeBouncerViewBinder {
                                    )
                                }

                                override val lifecycle: Lifecycle =
                                    this@repeatWhenAttached.lifecycle
                            override val lifecycle: Lifecycle = this@repeatWhenAttached.lifecycle
                        }
                    )

                    view.addView(
                        ComposeView(view.context).apply {
                            setContent { BouncerContainer(viewModelFactory, dialogFactory) }
                        }
                }
            }
                    )

        view.repeatWhenAttached {
            repeatOnLifecycle(Lifecycle.State.CREATED) {
                launch {
                    legacyInteractor.isShowing.collectLatest { bouncerShowing ->
                        view.isVisible = bouncerShowing
                    }
                }

                launch {
                    authenticationInteractor.onAuthenticationResult.collectLatest {
                        authenticationSucceeded ->
                        if (authenticationSucceeded) {
                            // Some dismiss actions require that keyguard be dismissed right away or
                            // deferred until something else later on dismisses keyguard (eg. end of
                            // a hide animation).
                            val deferKeyguardDone =
                                legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
                            legacyInteractor.setDismissAction(null, null)

                            viewMediatorCallback?.let {
                                val selectedUserId = selectedUserInteractor.getSelectedUserId()
                                if (deferKeyguardDone == true) {
                                    it.keyguardDonePending(selectedUserId)
                                } else {
                                    it.keyguardDone(selectedUserId)
                                }
                            }
                        }
                    }
                }
                launch {
                    legacyInteractor.startingDisappearAnimation.collectLatest {
                        it.run()
                        legacyInteractor.hide()
                    }
                    view.setSnapshotBinding { view.isGone = !viewModel.isVisible }
                    awaitCancellation()
                } finally {
                    view.removeAllViews()
                }
            }
        }
+84 −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.bouncer.ui.viewmodel

import androidx.compose.runtime.getValue
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch

class BouncerContainerViewModel
@AssistedInject
constructor(
    private val legacyInteractor: PrimaryBouncerInteractor,
    private val authenticationInteractor: AuthenticationInteractor,
    private val selectedUserInteractor: SelectedUserInteractor,
    private val viewMediatorCallback: ViewMediatorCallback?,
) : ExclusiveActivatable() {

    private val hydrator = Hydrator("BouncerContainerViewModel")

    val isVisible: Boolean by
        hydrator.hydratedStateOf(traceName = "isVisible", source = legacyInteractor.isShowing)

    override suspend fun onActivated(): Nothing {
        coroutineScope {
            launch {
                authenticationInteractor.onAuthenticationResult.collect { authenticationSucceeded ->
                    if (authenticationSucceeded) {
                        // Some dismiss actions require that keyguard be dismissed right away or
                        // deferred until something else later on dismisses keyguard (eg. end of
                        // a hide animation).
                        val deferKeyguardDone =
                            legacyInteractor.bouncerDismissAction?.onDismissAction?.onDismiss()
                        legacyInteractor.setDismissAction(null, null)

                        viewMediatorCallback?.let {
                            val selectedUserId = selectedUserInteractor.getSelectedUserId()
                            if (deferKeyguardDone == true) {
                                it.keyguardDonePending(selectedUserId)
                            } else {
                                it.keyguardDone(selectedUserId)
                            }
                        }
                    }
                }
            }

            launch {
                legacyInteractor.startingDisappearAnimation.collect {
                    it.run()
                    legacyInteractor.hide()
                }
            }

            hydrator.activate()
        }
    }

    @AssistedFactory
    interface Factory {
        fun create(): BouncerContainerViewModel
    }
}