Remove bouncer root view whenever it is not needed.
1. Finishing a transition from * -> Gone removes the bouncer parent view
2. Starting a transition from Gone -> * adds the bouncer parent view
Removing the bouncer parent view cancels all the coroutines launched by the compose bouncer
Fixes: 364401028
Test: verified that compose bouncer works as expected, added logs to verify that bouncer is removed and re-added
Flag: com.android.systemui.compose_bouncer
Change-Id: I167a55284b13940d331c96f4a72defe2cc069aba
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 b5e54d5..fdbc18d 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,7 +5,6 @@
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.compose.ui.platform.ComposeView
-import androidx.core.view.isGone
import androidx.lifecycle.Lifecycle
import com.android.systemui.bouncer.ui.BouncerDialogFactory
import com.android.systemui.bouncer.ui.composable.BouncerContainer
@@ -13,7 +12,6 @@
import com.android.systemui.bouncer.ui.viewmodel.BouncerSceneContentViewModel
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
import kotlinx.coroutines.awaitCancellation
@@ -50,7 +48,6 @@
setContent { BouncerContainer(viewModelFactory, dialogFactory) }
}
)
- 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
index c60f932..5a4f8eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerContainerViewModel.kt
@@ -16,17 +16,15 @@
package com.android.systemui.bouncer.ui.viewmodel
-import androidx.compose.runtime.getValue
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
@@ -39,11 +37,6 @@
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
) : ExclusiveActivatable() {
- private val hydrator = Hydrator("BouncerContainerViewModel")
-
- val isVisible: Boolean by
- hydrator.hydratedStateOf(traceName = "isVisible", source = legacyInteractor.isShowing)
-
override suspend fun onActivated(): Nothing {
coroutineScope {
launch {
@@ -74,8 +67,7 @@
legacyInteractor.hide()
}
}
-
- hydrator.activate()
+ awaitCancellation()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 1c223db..bf88807 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -30,6 +30,8 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.core.view.ViewKt;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardUnfoldTransition;
@@ -38,6 +40,7 @@
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags;
import com.android.systemui.bouncer.ui.binder.BouncerViewBinder;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.SysUISingleton;
@@ -51,10 +54,12 @@
import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.Edge;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
@@ -111,6 +116,7 @@
private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final QuickSettingsController mQuickSettingsController;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final GlanceableHubContainerController
mGlanceableHubContainerController;
private GestureDetector mPulsingWakeupGestureHandler;
@@ -140,6 +146,7 @@
private final PanelExpansionInteractor mPanelExpansionInteractor;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
+ private ViewGroup mBouncerParentView;
/**
* If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been
* intercepted and all future touch events for the gesture should be processed by this view.
@@ -217,6 +224,7 @@
mPulsingGestureListener = pulsingGestureListener;
mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener;
mNotificationInsetsController = notificationInsetsController;
+ mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mGlanceableHubContainerController = glanceableHubContainerController;
mFeatureFlagsClassic = featureFlagsClassic;
mSysUIKeyEventHandler = sysUIKeyEventHandler;
@@ -227,7 +235,7 @@
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
- bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
+ bindBouncer(bouncerViewBinder);
collectFlow(mView, keyguardTransitionInteractor.transition(
Edge.create(LOCKSCREEN, DREAMING)),
@@ -256,6 +264,35 @@
dumpManager.registerDumpable(this);
}
+ private void bindBouncer(BouncerViewBinder bouncerViewBinder) {
+ if (ComposeBouncerFlags.INSTANCE.isOnlyComposeBouncerEnabled()) {
+ collectFlow(mView, mKeyguardTransitionInteractor.isFinishedIn(Scenes.Gone,
+ KeyguardState.GONE), this::removeBouncerParentView);
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ new Edge.StateToState(KeyguardState.GONE, null)),
+ this::handleGoneToAnyOtherStateTransition);
+ collectFlow(mView, mPrimaryBouncerInteractor.isShowing(),
+ (showing) -> ViewKt.setVisible(mBouncerParentView, showing));
+ }
+ mBouncerParentView = mView.findViewById(R.id.keyguard_bouncer_container);
+ bouncerViewBinder.bind(mBouncerParentView);
+ }
+
+ private void handleGoneToAnyOtherStateTransition(TransitionStep transitionStep) {
+ if (transitionStep.getTransitionState() == TransitionState.STARTED) {
+ if (mView.indexOfChild(mBouncerParentView) != -1) {
+ mView.removeView(mBouncerParentView);
+ }
+ mView.addView(mBouncerParentView);
+ }
+ }
+
+ private void removeBouncerParentView(boolean isFinishedInGoneState) {
+ if (isFinishedInGoneState) {
+ mView.removeView(mBouncerParentView);
+ }
+ }
+
/**
* @return Location where to place the KeyguardMessageArea
*/