Merge "Transitions - Fix scrim flash as bouncer goes away" into tm-qpr-dev

This commit is contained in:
Matt Pietal
2023-03-08 20:43:35 +00:00
committed by Android (Google) Code Review
11 changed files with 212 additions and 38 deletions

View File

@@ -39,6 +39,7 @@ import static java.lang.Integer.max;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
@@ -1067,10 +1068,14 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
AnimatorSet anims = new AnimatorSet();
ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE);
yAnim.setDuration(500);
yAnim.start();
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
0f);
anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
anims.playTogether(alphaAnim, yAnim);
anims.start();
}
private void setupUserSwitcher() {

View File

@@ -127,6 +127,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
private View.OnKeyListener mOnKeyListener = (v, keyCode, event) -> interceptMediaKey(event);
private ActivityStarter.OnDismissAction mDismissAction;
private Runnable mCancelAction;
private boolean mWillRunDismissFromKeyguard;
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -262,8 +263,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
// If there's a pending runnable because the user interacted with a widget
// and we're leaving keyguard, then run it.
boolean deferKeyguardDone = false;
mWillRunDismissFromKeyguard = false;
if (mDismissAction != null) {
deferKeyguardDone = mDismissAction.onDismiss();
mWillRunDismissFromKeyguard = mDismissAction.willRunAnimationOnKeyguard();
mDismissAction = null;
mCancelAction = null;
}
@@ -525,6 +528,13 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
return mDismissAction != null || mCancelAction != null;
}
/**
* @return will the dismissal run from the keyguard layout (instead of from bouncer)
*/
public boolean willRunDismissFromKeyguard() {
return mWillRunDismissFromKeyguard;
}
/**
* Remove any dismiss action or cancel action that was set.
*/

View File

@@ -52,6 +52,7 @@ interface BouncerViewDelegate {
cancelAction: Runnable?,
)
fun willDismissWithActions(): Boolean
fun willRunDismissFromKeyguard(): Boolean
/** @return the {@link OnBackAnimationCallback} to animate Bouncer during a back gesture. */
fun getBackCallback(): OnBackAnimationCallback
}

View File

@@ -380,6 +380,11 @@ constructor(
return primaryBouncerView.delegate?.willDismissWithActions() == true
}
/** Will the dismissal run from the keyguard layout (instead of from bouncer) */
fun willRunDismissFromKeyguard(): Boolean {
return primaryBouncerView.delegate?.willRunDismissFromKeyguard() == true
}
/** Returns whether the bouncer should be full screen. */
private fun needsFullscreenBouncer(): Boolean {
val mode: KeyguardSecurityModel.SecurityMode =

View File

@@ -0,0 +1,23 @@
/*
* 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.shared.model
/** Alpha values for scrim updates */
data class ScrimAlpha(
val frontAlpha: Float = 0f,
val behindAlpha: Float = 0f,
val notificationsAlpha: Float = 0f,
)

View File

@@ -97,6 +97,10 @@ object KeyguardBouncerViewBinder {
override fun willDismissWithActions(): Boolean {
return securityContainerController.hasDismissActions()
}
override fun willRunDismissFromKeyguard(): Boolean {
return securityContainerController.willRunDismissFromKeyguard()
}
}
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {

View File

@@ -20,11 +20,14 @@ import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.statusbar.SysuiStatusBarStateController
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/**
* Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
@@ -36,6 +39,7 @@ class PrimaryBouncerToGoneTransitionViewModel
constructor(
private val interactor: KeyguardTransitionInteractor,
private val statusBarStateController: SysuiStatusBarStateController,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
) {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
@@ -44,26 +48,49 @@ constructor(
)
private var leaveShadeOpen: Boolean = false
private var willRunDismissFromKeyguard: Boolean = false
/** Bouncer container alpha */
val bouncerAlpha: Flow<Float> =
transitionAnimation.createFlow(
duration = 200.milliseconds,
onStep = { 1f - it },
)
/** Scrim behind alpha */
val scrimBehindAlpha: Flow<Float> =
transitionAnimation.createFlow(
duration = TO_GONE_DURATION,
interpolator = EMPHASIZED_ACCELERATE,
onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() },
onStart = {
willRunDismissFromKeyguard = primaryBouncerInteractor.willRunDismissFromKeyguard()
},
onStep = {
if (leaveShadeOpen) {
1f
if (willRunDismissFromKeyguard) {
0f
} else {
1f - it
}
},
)
/** Scrim alpha values */
val scrimAlpha: Flow<ScrimAlpha> =
transitionAnimation
.createFlow(
duration = TO_GONE_DURATION,
interpolator = EMPHASIZED_ACCELERATE,
onStart = {
leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
willRunDismissFromKeyguard =
primaryBouncerInteractor.willRunDismissFromKeyguard()
},
onStep = { 1f - it },
)
.map {
if (willRunDismissFromKeyguard) {
ScrimAlpha(
notificationsAlpha = 1f,
)
} else if (leaveShadeOpen) {
ScrimAlpha(
behindAlpha = 1f,
notificationsAlpha = 1f,
)
} else {
ScrimAlpha(behindAlpha = it)
}
}
}

View File

@@ -59,13 +59,13 @@ import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.keyguard.shared.model.ScrimAlpha;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.ViewState;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -209,7 +209,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private final ScreenOffAnimationController mScreenOffAnimationController;
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final SysuiStatusBarStateController mStatusBarStateController;
private GradientColors mColors;
private boolean mNeedsDrawableColorUpdate;
@@ -270,12 +269,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private CoroutineDispatcher mMainDispatcher;
private boolean mIsBouncerToGoneTransitionRunning = false;
private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
private final Consumer<Float> mScrimAlphaConsumer =
(Float alpha) -> {
private final Consumer<ScrimAlpha> mScrimAlphaConsumer =
(ScrimAlpha alphas) -> {
mInFrontAlpha = alphas.getFrontAlpha();
mScrimInFront.setViewAlpha(mInFrontAlpha);
mNotificationsAlpha = alphas.getNotificationsAlpha();
mNotificationsScrim.setViewAlpha(mNotificationsAlpha);
mBehindAlpha = alpha;
mScrimBehind.setViewAlpha(alpha);
mBehindAlpha = alphas.getBehindAlpha();
mScrimBehind.setViewAlpha(mBehindAlpha);
};
Consumer<TransitionStep> mPrimaryBouncerToGoneTransition;
@@ -297,7 +300,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
KeyguardTransitionInteractor keyguardTransitionInteractor,
SysuiStatusBarStateController sysuiStatusBarStateController,
@Main CoroutineDispatcher mainDispatcher,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
FeatureFlags featureFlags) {
@@ -309,7 +311,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mKeyguardStateController = keyguardStateController;
mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mStatusBarStateController = sysuiStatusBarStateController;
mKeyguardVisibilityCallback = new KeyguardVisibilityCallback();
mHandler = handler;
mMainExecutor = mainExecutor;
@@ -409,7 +410,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
mPrimaryBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha(),
collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
}
@@ -1114,8 +1115,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mBehindAlpha = 1;
}
// Prevent notification scrim flicker when transitioning away from keyguard.
if (mKeyguardStateController.isKeyguardGoingAway()
&& !mStatusBarStateController.leaveOpenOnKeyguardHide()) {
if (mKeyguardStateController.isKeyguardGoingAway()) {
mNotificationsAlpha = 0;
}

View File

@@ -518,6 +518,38 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
assertTrue("Action should exist", mKeyguardSecurityContainerController.hasDismissActions());
}
@Test
public void testWillRunDismissFromKeyguardIsTrue() {
ActivityStarter.OnDismissAction action = mock(ActivityStarter.OnDismissAction.class);
when(action.willRunAnimationOnKeyguard()).thenReturn(true);
mKeyguardSecurityContainerController.setOnDismissAction(action, null /* cancelAction */);
mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */);
assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isTrue();
}
@Test
public void testWillRunDismissFromKeyguardIsFalse() {
ActivityStarter.OnDismissAction action = mock(ActivityStarter.OnDismissAction.class);
when(action.willRunAnimationOnKeyguard()).thenReturn(false);
mKeyguardSecurityContainerController.setOnDismissAction(action, null /* cancelAction */);
mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */);
assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isFalse();
}
@Test
public void testWillRunDismissFromKeyguardIsFalseWhenNoDismissActionSet() {
mKeyguardSecurityContainerController.setOnDismissAction(null /* action */,
null /* cancelAction */);
mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */);
assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isFalse();
}
@Test
public void testOnStartingToHide() {
mKeyguardSecurityContainerController.onStartingToHide();

View File

@@ -21,7 +21,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.statusbar.SysuiStatusBarStateController
@@ -44,21 +46,86 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
private lateinit var underTest: PrimaryBouncerToGoneTransitionViewModel
private lateinit var repository: FakeKeyguardTransitionRepository
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(repository)
underTest = PrimaryBouncerToGoneTransitionViewModel(interactor, statusBarStateController)
underTest =
PrimaryBouncerToGoneTransitionViewModel(
interactor,
statusBarStateController,
primaryBouncerInteractor
)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
}
@Test
fun bouncerAlpha() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val job = underTest.bouncerAlpha.onEach { values.add(it) }.launchIn(this)
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.6f))
assertThat(values.size).isEqualTo(3)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
job.cancel()
}
@Test
fun bouncerAlpha_runDimissFromKeyguard() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val job = underTest.bouncerAlpha.onEach { values.add(it) }.launchIn(this)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.6f))
assertThat(values.size).isEqualTo(3)
values.forEach { assertThat(it).isEqualTo(0f) }
job.cancel()
}
@Test
fun scrimAlpha_runDimissFromKeyguard() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<ScrimAlpha>()
val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.6f))
repository.sendTransitionStep(step(1f))
assertThat(values.size).isEqualTo(4)
values.forEach { assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f)) }
job.cancel()
}
@Test
fun scrimBehindAlpha_leaveShadeOpen() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val values = mutableListOf<ScrimAlpha>()
val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(true)
@@ -68,7 +135,9 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
repository.sendTransitionStep(step(1f))
assertThat(values.size).isEqualTo(4)
values.forEach { assertThat(it).isEqualTo(1f) }
values.forEach {
assertThat(it).isEqualTo(ScrimAlpha(notificationsAlpha = 1f, behindAlpha = 1f))
}
job.cancel()
}
@@ -76,9 +145,9 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
@Test
fun scrimBehindAlpha_doNotLeaveShadeOpen() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val values = mutableListOf<ScrimAlpha>()
val job = underTest.scrimBehindAlpha.onEach { values.add(it) }.launchIn(this)
val job = underTest.scrimAlpha.onEach { values.add(it) }.launchIn(this)
whenever(statusBarStateController.leaveOpenOnKeyguardHide()).thenReturn(false)
@@ -88,8 +157,10 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() {
repository.sendTransitionStep(step(1f))
assertThat(values.size).isEqualTo(4)
values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }
assertThat(values[3]).isEqualTo(0f)
values.forEach { assertThat(it.notificationsAlpha).isEqualTo(0f) }
values.forEach { assertThat(it.frontAlpha).isEqualTo(0f) }
values.forEach { assertThat(it.behindAlpha).isIn(Range.closed(0f, 1f)) }
assertThat(values[3].behindAlpha).isEqualTo(0f)
job.cancel()
}

View File

@@ -71,7 +71,6 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -134,7 +133,6 @@ public class ScrimControllerTest extends SysuiTestCase {
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock private CoroutineDispatcher mMainDispatcher;
@Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@@ -249,7 +247,7 @@ public class ScrimControllerTest extends SysuiTestCase {
when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
.thenReturn(emptyFlow());
when(mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha())
when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha())
.thenReturn(emptyFlow());
mScrimController = new ScrimController(
@@ -268,7 +266,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mSysuiStatusBarStateController,
mMainDispatcher,
mLinearLargeScreenShadeInterpolator,
mFeatureFlags);
@@ -984,7 +981,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mSysuiStatusBarStateController,
mMainDispatcher,
mLinearLargeScreenShadeInterpolator,
mFeatureFlags);