diff options
| author | 2024-09-04 16:10:22 +0000 | |
|---|---|---|
| committer | 2024-09-04 16:10:22 +0000 | |
| commit | 5f5abbfe0a0caa52fa21faf9e9ca521246b49cd0 (patch) | |
| tree | 09ccfd946f0260fe524e192edd27d9d33685242a | |
| parent | 9f11bb825b3e7e628a24fe15e344431baba4a943 (diff) | |
| parent | 29e6bfef478935993e6e12db6cd161e61afd34c8 (diff) | |
Merge "Change the compose bouncer wire-up to follow recommended architecture guidelines" into main
3 files changed, 119 insertions, 77 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt index cc8dce7938aa..49dadcefb276 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerViewBinder.kt @@ -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() diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt index c4bbd9cf0d9f..b5e54d5f079b 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/ComposeBouncerViewBinder.kt @@ -5,89 +5,55 @@ 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( - object : OnBackPressedDispatcherOwner { - override val onBackPressedDispatcher = - OnBackPressedDispatcher().apply { - setOnBackInvokedDispatcher( - view.viewRootImpl.onBackInvokedDispatcher - ) - } - - override val lifecycle: Lifecycle = - this@repeatWhenAttached.lifecycle - } - ) - setContent { BouncerContainer(viewModelFactory, dialogFactory) } - } - } - } - ) - view.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.CREATED) { - launch { - legacyInteractor.isShowing.collectLatest { bouncerShowing -> - view.isVisible = bouncerShowing - } - } + view.viewModel( + minWindowLifecycleState = WindowLifecycleState.ATTACHED, + factory = { bouncerContainerViewModelFactory.create() }, + traceName = "ComposeBouncerViewBinder", + ) { viewModel -> + try { + view.setViewTreeOnBackPressedDispatcherOwner( + object : OnBackPressedDispatcherOwner { + override val onBackPressedDispatcher = + OnBackPressedDispatcher().apply { + setOnBackInvokedDispatcher( + view.viewRootImpl.onBackInvokedDispatcher + ) + } - 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) + override val lifecycle: Lifecycle = this@repeatWhenAttached.lifecycle + } + ) - viewMediatorCallback?.let { - val selectedUserId = selectedUserInteractor.getSelectedUserId() - if (deferKeyguardDone == true) { - it.keyguardDonePending(selectedUserId) - } else { - it.keyguardDone(selectedUserId) - } - } + view.addView( + ComposeView(view.context).apply { + setContent { BouncerContainer(viewModelFactory, dialogFactory) } } - } - } - launch { - legacyInteractor.startingDisappearAnimation.collectLatest { - it.run() - legacyInteractor.hide() - } + ) + view.setSnapshotBinding { view.isGone = !viewModel.isVisible } + awaitCancellation() + } finally { + view.removeAllViews() } } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt new file mode 100644 index 000000000000..d223657aa74d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt @@ -0,0 +1,84 @@ +/* + * 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 + } +} |