Merge "Split shade expansion: adjust alpha interpolation to match motion specs" into tm-qpr-dev am: 186e7cbd8f am: 83c4acd066

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21463824

Change-Id: I6200772f0bdfe9dba55982277af2c4d53c7551a2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Christian Göllner
2023-03-08 11:02:18 +00:00
committed by Automerger Merge Worker
26 changed files with 828 additions and 70 deletions

View File

@@ -70,6 +70,8 @@ import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.security.data.repository.SecurityRepositoryModule;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl;
import com.android.systemui.smartspace.dagger.SmartspaceModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -309,4 +311,8 @@ public abstract class SystemUIModule {
@Binds
abstract FgsManagerController bindFgsManagerController(FgsManagerControllerImpl impl);
@Binds
abstract LargeScreenShadeInterpolator largeScreensShadeInterpolator(
LargeScreenShadeInterpolatorImpl impl);
}

View File

@@ -656,4 +656,9 @@ object Flags {
// TODO(b/259428678): Tracking Bug
@JvmField
val KEYBOARD_BACKLIGHT_INDICATOR = unreleasedFlag(2601, "keyboard_backlight_indicator")
// TODO(b/272036292): Tracking Bug
@JvmField
val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION =
unreleasedFlag(2602, "large_shade_granular_alpha_interpolation")
}

View File

@@ -50,6 +50,8 @@ import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.compose.ComposeFacade;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.ui.MediaHost;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
@@ -59,6 +61,7 @@ import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -111,6 +114,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private final MediaHost mQqsMediaHost;
private final QSFragmentComponent.Factory mQsComponentFactory;
private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
private final FeatureFlags mFeatureFlags;
private final QSLogger mLogger;
private final FooterActionsController mFooterActionsController;
private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
@@ -158,12 +163,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
// visible;
private boolean mQsVisible;
/**
* Whether the notification panel uses the full width of the screen.
*
* Usually {@code true} on small screens, and {@code false} on large screens.
*/
private boolean mIsNotificationPanelFullWidth;
private boolean mIsSmallScreen;
@Inject
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
@@ -175,13 +175,17 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
DumpManager dumpManager, QSLogger qsLogger,
FooterActionsController footerActionsController,
FooterActionsViewModel.Factory footerActionsViewModelFactory) {
FooterActionsViewModel.Factory footerActionsViewModelFactory,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
FeatureFlags featureFlags) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mQsMediaHost = qsMediaHost;
mQqsMediaHost = qqsMediaHost;
mQsComponentFactory = qsComponentFactory;
mQsFragmentDisableFlagsLogger = qsFragmentDisableFlagsLogger;
mLogger = qsLogger;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mFeatureFlags = featureFlags;
commandQueue.observe(getLifecycle(), this);
mBypassController = keyguardBypassController;
mStatusBarStateController = statusBarStateController;
@@ -606,7 +610,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
@Override
public void setIsNotificationPanelFullWidth(boolean isFullWidth) {
mIsNotificationPanelFullWidth = isFullWidth;
mIsSmallScreen = isFullWidth;
}
@Override
@@ -709,7 +713,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
private float calculateAlphaProgress(float panelExpansionFraction) {
if (mIsNotificationPanelFullWidth) {
if (mIsSmallScreen) {
// Small screens. QS alpha is not animated.
return 1;
}
@@ -744,7 +748,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
// Alpha progress should be linear on lockscreen shade expansion.
return progress;
}
return ShadeInterpolation.getContentAlpha(progress);
if (mIsSmallScreen || !mFeatureFlags.isEnabled(
Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) {
return ShadeInterpolation.getContentAlpha(progress);
} else {
return mLargeScreenShadeInterpolator.getQsAlpha(progress);
}
}
@VisibleForTesting

View File

@@ -2956,7 +2956,10 @@ public final class NotificationPanelViewController implements Dumpable {
mHeadsUpStartHeight = startHeight;
float scrimMinFraction;
if (mSplitShadeEnabled) {
boolean highHun = mHeadsUpStartHeight * 2.5 > mSplitShadeScrimTransitionDistance;
boolean highHun = mHeadsUpStartHeight * 2.5
>
(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)
? mSplitShadeFullTransitionDistance : mSplitShadeScrimTransitionDistance);
// if HUN height is higher than 40% of predefined transition distance, it means HUN
// is too high for regular transition. In that case we need to calculate transition
// distance - here we take scrim transition distance as equal to shade transition

View File

@@ -0,0 +1,46 @@
/*
* 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.shade.transition
import android.util.MathUtils
import com.android.systemui.animation.ShadeInterpolation
import javax.inject.Inject
/** Interpolator responsible for the shade when in portrait on a large screen. */
internal class LargeScreenPortraitShadeInterpolator @Inject internal constructor() :
LargeScreenShadeInterpolator {
override fun getBehindScrimAlpha(fraction: Float): Float {
return MathUtils.constrainedMap(0f, 1f, 0f, 0.3f, fraction)
}
override fun getNotificationScrimAlpha(fraction: Float): Float {
return MathUtils.constrainedMap(0f, 1f, 0.3f, 0.75f, fraction)
}
override fun getNotificationContentAlpha(fraction: Float): Float {
return ShadeInterpolation.getContentAlpha(fraction)
}
override fun getNotificationFooterAlpha(fraction: Float): Float {
return ShadeInterpolation.getContentAlpha(fraction)
}
override fun getQsAlpha(fraction: Float): Float {
return ShadeInterpolation.getContentAlpha(fraction)
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.shade.transition
/** An interpolator interface for the shade expansion. */
interface LargeScreenShadeInterpolator {
/** Returns the alpha for the behind/back scrim. */
fun getBehindScrimAlpha(fraction: Float): Float
/** Returns the alpha for the notification scrim. */
fun getNotificationScrimAlpha(fraction: Float): Float
/** Returns the alpha for the notifications. */
fun getNotificationContentAlpha(fraction: Float): Float
/** Returns the alpha for the notifications footer (Manager, Clear All). */
fun getNotificationFooterAlpha(fraction: Float): Float
/** Returns the alpha for the QS panel. */
fun getQsAlpha(fraction: Float): Float
}

View File

@@ -0,0 +1,74 @@
/*
* 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.shade.transition
import android.content.Context
import android.content.res.Configuration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.LargeScreenUtils
import javax.inject.Inject
/** Interpolator responsible for the shade when on large screens. */
@SysUISingleton
internal class LargeScreenShadeInterpolatorImpl
@Inject
internal constructor(
configurationController: ConfigurationController,
private val context: Context,
private val splitShadeInterpolator: SplitShadeInterpolator,
private val portraitShadeInterpolator: LargeScreenPortraitShadeInterpolator,
) : LargeScreenShadeInterpolator {
private var inSplitShade = false
init {
configurationController.addCallback(
object : ConfigurationController.ConfigurationListener {
override fun onConfigChanged(newConfig: Configuration?) {
updateResources()
}
}
)
updateResources()
}
private fun updateResources() {
inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
}
private val impl: LargeScreenShadeInterpolator
get() =
if (inSplitShade) {
splitShadeInterpolator
} else {
portraitShadeInterpolator
}
override fun getBehindScrimAlpha(fraction: Float) = impl.getBehindScrimAlpha(fraction)
override fun getNotificationScrimAlpha(fraction: Float) =
impl.getNotificationScrimAlpha(fraction)
override fun getNotificationContentAlpha(fraction: Float) =
impl.getNotificationContentAlpha(fraction)
override fun getNotificationFooterAlpha(fraction: Float) =
impl.getNotificationFooterAlpha(fraction)
override fun getQsAlpha(fraction: Float) = impl.getQsAlpha(fraction)
}

View File

@@ -23,6 +23,8 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.shade.PanelState
import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.shade.ShadeExpansionChangeEvent
@@ -45,7 +47,8 @@ constructor(
private val scrimController: ScrimController,
@Main private val resources: Resources,
private val statusBarStateController: SysuiStatusBarStateController,
private val headsUpManager: HeadsUpManager
private val headsUpManager: HeadsUpManager,
private val featureFlags: FeatureFlags,
) {
private var inSplitShade = false
@@ -106,7 +109,8 @@ constructor(
// in case of HUN we can't always use predefined distances to manage scrim
// transition because dragDownPxAmount can start from value bigger than
// splitShadeScrimTransitionDistance
!headsUpManager.isTrackingHeadsUp
!headsUpManager.isTrackingHeadsUp &&
!featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)
private fun isScreenUnlocked() =
statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE

View File

@@ -0,0 +1,63 @@
/*
* 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.shade.transition
import android.util.MathUtils
import javax.inject.Inject
/** Interpolator responsible for the split shade. */
internal class SplitShadeInterpolator @Inject internal constructor() :
LargeScreenShadeInterpolator {
override fun getBehindScrimAlpha(fraction: Float): Float {
// Start delay: 0%
// Duration: 40%
// End: 40%
return mapFraction(start = 0f, end = 0.4f, fraction)
}
override fun getNotificationScrimAlpha(fraction: Float): Float {
// Start delay: 39%
// Duration: 27%
// End: 66%
return mapFraction(start = 0.39f, end = 0.66f, fraction)
}
override fun getNotificationContentAlpha(fraction: Float): Float {
return getNotificationScrimAlpha(fraction)
}
override fun getNotificationFooterAlpha(fraction: Float): Float {
// Start delay: 57.6%
// Duration: 32.1%
// End: 89.7%
return mapFraction(start = 0.576f, end = 0.897f, fraction)
}
override fun getQsAlpha(fraction: Float): Float {
return getNotificationScrimAlpha(fraction)
}
private fun mapFraction(start: Float, end: Float, fraction: Float) =
MathUtils.constrainedMap(
/* rangeMin= */ 0f,
/* rangeMax= */ 1f,
/* valueMin= */ start,
/* valueMax= */ end,
/* value= */ fraction
)
}

View File

@@ -39,7 +39,10 @@ import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.notification.LegacySourceType;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
@@ -216,7 +219,15 @@ public class NotificationShelf extends ActivatableNotificationView implements
if (ambientState.isBouncerInTransit()) {
viewState.setAlpha(aboutToShowBouncerProgress(expansion));
} else {
viewState.setAlpha(ShadeInterpolation.getContentAlpha(expansion));
FeatureFlags flags = ambientState.getFeatureFlags();
if (ambientState.isSmallScreen() || !flags.isEnabled(
Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) {
viewState.setAlpha(ShadeInterpolation.getContentAlpha(expansion));
} else {
LargeScreenShadeInterpolator interpolator =
ambientState.getLargeScreenShadeInterpolator();
viewState.setAlpha(interpolator.getNotificationContentAlpha(expansion));
}
}
} else {
viewState.setAlpha(1f - ambientState.getHideAmount());

View File

@@ -29,6 +29,8 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -55,6 +57,8 @@ public class AmbientState implements Dumpable {
private final SectionProvider mSectionProvider;
private final BypassController mBypassController;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
private final FeatureFlags mFeatureFlags;
/**
* Used to read bouncer states.
*/
@@ -84,7 +88,7 @@ public class AmbientState implements Dumpable {
private float mExpandingVelocity;
private boolean mPanelTracking;
private boolean mExpansionChanging;
private boolean mPanelFullWidth;
private boolean mIsSmallScreen;
private boolean mPulsing;
private boolean mUnlockHintRunning;
private float mHideAmount;
@@ -252,10 +256,14 @@ public class AmbientState implements Dumpable {
@NonNull DumpManager dumpManager,
@NonNull SectionProvider sectionProvider,
@NonNull BypassController bypassController,
@Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
@Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator,
@NonNull FeatureFlags featureFlags) {
mSectionProvider = sectionProvider;
mBypassController = bypassController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mFeatureFlags = featureFlags;
reload(context);
dumpManager.registerDumpable(this);
}
@@ -574,12 +582,12 @@ public class AmbientState implements Dumpable {
return mPanelTracking;
}
public boolean isPanelFullWidth() {
return mPanelFullWidth;
public boolean isSmallScreen() {
return mIsSmallScreen;
}
public void setPanelFullWidth(boolean panelFullWidth) {
mPanelFullWidth = panelFullWidth;
public void setSmallScreen(boolean smallScreen) {
mIsSmallScreen = smallScreen;
}
public void setUnlockHintRunning(boolean unlockHintRunning) {
@@ -736,6 +744,14 @@ public class AmbientState implements Dumpable {
return mIsClosing;
}
public LargeScreenShadeInterpolator getLargeScreenShadeInterpolator() {
return mLargeScreenShadeInterpolator;
}
public FeatureFlags getFeatureFlags() {
return mFeatureFlags;
}
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("mTopPadding=" + mTopPadding);
@@ -751,7 +767,7 @@ public class AmbientState implements Dumpable {
pw.println("mDimmed=" + mDimmed);
pw.println("mStatusBarState=" + mStatusBarState);
pw.println("mExpansionChanging=" + mExpansionChanging);
pw.println("mPanelFullWidth=" + mPanelFullWidth);
pw.println("mPanelFullWidth=" + mIsSmallScreen);
pw.println("mPulsing=" + mPulsing);
pw.println("mPulseHeight=" + mPulseHeight);
pw.println("mTrackedHeadsUpRow.key=" + logKey(mTrackedHeadsUpRow));

View File

@@ -5223,7 +5223,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setIsFullWidth(boolean isFullWidth) {
mAmbientState.setPanelFullWidth(isFullWidth);
mAmbientState.setSmallScreen(isFullWidth);
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)

View File

@@ -70,7 +70,6 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEv
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -172,7 +171,6 @@ public class NotificationStackScrollLayoutController {
private final CentralSurfaces mCentralSurfaces;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final ShadeTransitionController mShadeTransitionController;
private final InteractionJankMonitor mJankMonitor;
private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
private final StackStateLogger mStackStateLogger;
@@ -664,7 +662,6 @@ public class NotificationStackScrollLayoutController {
NotifPipelineFlags notifPipelineFlags,
NotifCollection notifCollection,
LockscreenShadeTransitionController lockscreenShadeTransitionController,
ShadeTransitionController shadeTransitionController,
UiEventLogger uiEventLogger,
NotificationRemoteInputManager remoteInputManager,
VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
@@ -697,7 +694,6 @@ public class NotificationStackScrollLayoutController {
mMetricsLogger = metricsLogger;
mDumpManager = dumpManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mShadeTransitionController = shadeTransitionController;
mFalsingCollector = falsingCollector;
mFalsingManager = falsingManager;
mResources = resources;
@@ -781,7 +777,6 @@ public class NotificationStackScrollLayoutController {
mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
mLockscreenShadeTransitionController.setStackScroller(this);
mShadeTransitionController.setNotificationStackScrollLayoutController(this);
mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);

View File

@@ -29,6 +29,9 @@ import com.android.internal.policy.SystemBarUtils;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.R;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.LegacySourceType;
@@ -135,7 +138,6 @@ public class StackScrollAlgorithm {
AmbientState ambientState) {
for (ExpandableView view : algorithmState.visibleChildren) {
final ViewState viewState = view.getViewState();
final boolean isHunGoingToShade = ambientState.isShadeExpanded()
&& view == ambientState.getTrackedHeadsUpRow();
@@ -148,9 +150,14 @@ public class StackScrollAlgorithm {
} else if (ambientState.isExpansionChanging()) {
// Adjust alpha for shade open & close.
float expansion = ambientState.getExpansionFraction();
viewState.setAlpha(ambientState.isBouncerInTransit()
? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion)
: ShadeInterpolation.getContentAlpha(expansion));
if (ambientState.isBouncerInTransit()) {
viewState.setAlpha(
BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion));
} else if (view instanceof FooterView) {
viewState.setAlpha(interpolateFooterAlpha(ambientState));
} else {
viewState.setAlpha(interpolateNotificationContentAlpha(ambientState));
}
}
// For EmptyShadeView if on keyguard, we need to control the alpha to create
@@ -182,6 +189,28 @@ public class StackScrollAlgorithm {
}
}
private float interpolateFooterAlpha(AmbientState ambientState) {
float expansion = ambientState.getExpansionFraction();
FeatureFlags flags = ambientState.getFeatureFlags();
if (ambientState.isSmallScreen()
|| !flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) {
return ShadeInterpolation.getContentAlpha(expansion);
}
LargeScreenShadeInterpolator interpolator = ambientState.getLargeScreenShadeInterpolator();
return interpolator.getNotificationFooterAlpha(expansion);
}
private float interpolateNotificationContentAlpha(AmbientState ambientState) {
float expansion = ambientState.getExpansionFraction();
FeatureFlags flags = ambientState.getFeatureFlags();
if (ambientState.isSmallScreen()
|| !flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) {
return ShadeInterpolation.getContentAlpha(expansion);
}
LargeScreenShadeInterpolator interpolator = ambientState.getLargeScreenShadeInterpolator();
return interpolator.getNotificationContentAlpha(expansion);
}
/**
* How expanded or collapsed notifications are when pulling down the shade.
*

View File

@@ -54,6 +54,8 @@ import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FeatureFlags;
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;
@@ -62,6 +64,7 @@ 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;
@@ -245,6 +248,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private boolean mWallpaperVisibilityTimedOut;
private int mScrimsVisibility;
private final TriConsumer<ScrimState, Float, GradientColors> mScrimStateListener;
private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
private final FeatureFlags mFeatureFlags;
private Consumer<Integer> mScrimVisibleListener;
private boolean mBlankScreen;
private boolean mScreenBlankingCallbackCalled;
@@ -293,8 +298,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
KeyguardTransitionInteractor keyguardTransitionInteractor,
SysuiStatusBarStateController sysuiStatusBarStateController,
@Main CoroutineDispatcher mainDispatcher) {
@Main CoroutineDispatcher mainDispatcher,
LargeScreenShadeInterpolator largeScreenShadeInterpolator,
FeatureFlags featureFlags) {
mScrimStateListener = lightBarController::setScrimState;
mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
mFeatureFlags = featureFlags;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
mKeyguardStateController = keyguardStateController;
@@ -850,16 +859,21 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
if (!mScreenOffAnimationController.shouldExpandNotifications()
&& !mAnimatingPanelExpansionOnUnlock
&& !occluding) {
if (mClipsQsScrim) {
if (mTransparentScrimBackground) {
mBehindAlpha = 0;
mNotificationsAlpha = 0;
} else if (mClipsQsScrim) {
float behindFraction = getInterpolatedFraction();
behindFraction = (float) Math.pow(behindFraction, 0.8f);
mBehindAlpha = mTransparentScrimBackground ? 0 : 1;
mNotificationsAlpha =
mTransparentScrimBackground ? 0 : behindFraction * mDefaultScrimAlpha;
mBehindAlpha = 1;
mNotificationsAlpha = behindFraction * mDefaultScrimAlpha;
} else {
if (mTransparentScrimBackground) {
mBehindAlpha = 0;
mNotificationsAlpha = 0;
if (mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) {
mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha(
mPanelExpansionFraction * mDefaultScrimAlpha);
mNotificationsAlpha =
mLargeScreenShadeInterpolator.getNotificationScrimAlpha(
mPanelExpansionFraction);
} else {
// Behind scrim will finish fading in at 30% expansion.
float behindFraction = MathUtils

View File

@@ -52,6 +52,8 @@ import com.android.systemui.R;
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.media.controls.ui.MediaHost;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
@@ -60,6 +62,7 @@ import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -103,6 +106,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Mock private QSSquishinessController mSquishinessController;
@Mock private FooterActionsViewModel mFooterActionsViewModel;
@Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
@Mock private FeatureFlags mFeatureFlags;
private View mQsFragmentView;
public QSFragmentTest() {
@@ -148,13 +153,51 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
}
@Test
public void transitionToFullShade_setsAlphaUsingShadeInterpolator() {
public void transitionToFullShade_smallScreen_alphaAlways1() {
QSFragment fragment = resumeAndGetFragment();
setIsSmallScreen();
setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
assertThat(mQsFragmentView.getAlpha()).isEqualTo(1f);
}
@Test
public void transitionToFullShade_largeScreen_flagEnabled_alphaLargeScreenShadeInterpolator() {
when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION))
.thenReturn(true);
QSFragment fragment = resumeAndGetFragment();
setIsLargeScreen();
setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
when(mLargeScreenShadeInterpolator.getQsAlpha(transitionProgress)).thenReturn(123f);
fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
assertThat(mQsFragmentView.getAlpha())
.isEqualTo(123f);
}
@Test
public void transitionToFullShade_largeScreen_flagDisabled_alphaStandardInterpolator() {
when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION))
.thenReturn(false);
QSFragment fragment = resumeAndGetFragment();
setIsLargeScreen();
setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE);
boolean isTransitioningToFullShade = true;
float transitionProgress = 0.5f;
float squishinessFraction = 0.5f;
when(mLargeScreenShadeInterpolator.getQsAlpha(transitionProgress)).thenReturn(123f);
fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress,
squishinessFraction);
@@ -514,7 +557,9 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mock(DumpManager.class),
mock(QSLogger.class),
mock(FooterActionsController.class),
mFooterActionsViewModelFactory);
mFooterActionsViewModelFactory,
mLargeScreenShadeInterpolator,
mFeatureFlags);
}
private void setUpOther() {
@@ -622,4 +667,12 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
return null;
}).when(view).getLocationOnScreen(any(int[].class));
}
private void setIsLargeScreen() {
getFragment().setIsNotificationPanelFullWidth(false);
}
private void setIsSmallScreen() {
getFragment().setIsNotificationPanelFullWidth(true);
}
}

View File

@@ -22,8 +22,6 @@ import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.google.common.truth.Truth.assertThat;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -35,6 +33,8 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
import android.annotation.IdRes;
import android.content.ContentResolver;
import android.content.res.Configuration;

View File

@@ -0,0 +1,144 @@
package com.android.systemui.shade.transition
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.google.common.truth.Expect
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
class LargeScreenShadeInterpolatorImplTest : SysuiTestCase() {
@get:Rule val expect: Expect = Expect.create()
private val portraitShadeInterpolator = LargeScreenPortraitShadeInterpolator()
private val splitShadeInterpolator = SplitShadeInterpolator()
private val configurationController = FakeConfigurationController()
private val impl =
LargeScreenShadeInterpolatorImpl(
configurationController,
context,
splitShadeInterpolator,
portraitShadeInterpolator
)
@Test
fun getBehindScrimAlpha_inSplitShade_usesSplitShadeValue() {
setSplitShadeEnabled(true)
assertInterpolation(
actual = { fraction -> impl.getBehindScrimAlpha(fraction) },
expected = { fraction -> splitShadeInterpolator.getBehindScrimAlpha(fraction) }
)
}
@Test
fun getBehindScrimAlpha_inPortraitShade_usesPortraitShadeValue() {
setSplitShadeEnabled(false)
assertInterpolation(
actual = { fraction -> impl.getBehindScrimAlpha(fraction) },
expected = { fraction -> portraitShadeInterpolator.getBehindScrimAlpha(fraction) }
)
}
@Test
fun getNotificationScrimAlpha_inSplitShade_usesSplitShadeValue() {
setSplitShadeEnabled(true)
assertInterpolation(
actual = { fraction -> impl.getNotificationScrimAlpha(fraction) },
expected = { fraction -> splitShadeInterpolator.getNotificationScrimAlpha(fraction) }
)
}
@Test
fun getNotificationScrimAlpha_inPortraitShade_usesPortraitShadeValue() {
setSplitShadeEnabled(false)
assertInterpolation(
actual = { fraction -> impl.getNotificationScrimAlpha(fraction) },
expected = { fraction -> portraitShadeInterpolator.getNotificationScrimAlpha(fraction) }
)
}
@Test
fun getNotificationContentAlpha_inSplitShade_usesSplitShadeValue() {
setSplitShadeEnabled(true)
assertInterpolation(
actual = { fraction -> impl.getNotificationContentAlpha(fraction) },
expected = { fraction -> splitShadeInterpolator.getNotificationContentAlpha(fraction) }
)
}
@Test
fun getNotificationContentAlpha_inPortraitShade_usesPortraitShadeValue() {
setSplitShadeEnabled(false)
assertInterpolation(
actual = { fraction -> impl.getNotificationContentAlpha(fraction) },
expected = { fraction ->
portraitShadeInterpolator.getNotificationContentAlpha(fraction)
}
)
}
@Test
fun getNotificationFooterAlpha_inSplitShade_usesSplitShadeValue() {
setSplitShadeEnabled(true)
assertInterpolation(
actual = { fraction -> impl.getNotificationFooterAlpha(fraction) },
expected = { fraction -> splitShadeInterpolator.getNotificationFooterAlpha(fraction) }
)
}
@Test
fun getNotificationFooterAlpha_inPortraitShade_usesPortraitShadeValue() {
setSplitShadeEnabled(false)
assertInterpolation(
actual = { fraction -> impl.getNotificationFooterAlpha(fraction) },
expected = { fraction ->
portraitShadeInterpolator.getNotificationFooterAlpha(fraction)
}
)
}
@Test
fun getQsAlpha_inSplitShade_usesSplitShadeValue() {
setSplitShadeEnabled(true)
assertInterpolation(
actual = { fraction -> impl.getQsAlpha(fraction) },
expected = { fraction -> splitShadeInterpolator.getQsAlpha(fraction) }
)
}
@Test
fun getQsAlpha_inPortraitShade_usesPortraitShadeValue() {
setSplitShadeEnabled(false)
assertInterpolation(
actual = { fraction -> impl.getQsAlpha(fraction) },
expected = { fraction -> portraitShadeInterpolator.getQsAlpha(fraction) }
)
}
private fun setSplitShadeEnabled(enabled: Boolean) {
overrideResource(R.bool.config_use_split_notification_shade, enabled)
configurationController.notifyConfigurationChanged()
}
private fun assertInterpolation(
actual: (fraction: Float) -> Float,
expected: (fraction: Float) -> Float
) {
for (i in 0..10) {
val fraction = i / 10f
expect.that(actual(fraction)).isEqualTo(expected(fraction))
}
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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.shade.transition
class LinearLargeScreenShadeInterpolator : LargeScreenShadeInterpolator {
override fun getBehindScrimAlpha(fraction: Float) = fraction
override fun getNotificationScrimAlpha(fraction: Float) = fraction
override fun getNotificationContentAlpha(fraction: Float) = fraction
override fun getNotificationFooterAlpha(fraction: Float) = fraction
override fun getQsAlpha(fraction: Float) = fraction
}

View File

@@ -5,6 +5,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.shade.STATE_CLOSED
import com.android.systemui.shade.STATE_OPEN
import com.android.systemui.shade.STATE_OPENING
@@ -30,6 +32,7 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() {
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var headsUpManager: HeadsUpManager
@Mock private lateinit var featureFlags: FeatureFlags
private val configurationController = FakeConfigurationController()
private lateinit var controller: ScrimShadeTransitionController
@@ -45,7 +48,8 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() {
scrimController,
context.resources,
statusBarStateController,
headsUpManager)
headsUpManager,
featureFlags)
controller.onPanelStateChanged(STATE_OPENING)
}
@@ -106,6 +110,19 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() {
verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
}
@Test
fun onPanelExpansionChanged_inSplitShade_flagTrue_setsFractionEqualToEventFraction() {
whenever(featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION))
.thenReturn(true)
whenever(statusBarStateController.currentOrUpcomingState)
.thenReturn(StatusBarState.SHADE)
setSplitShadeEnabled(true)
controller.onPanelExpansionChanged(EXPANSION_EVENT)
verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction)
}
@Test
fun onPanelExpansionChanged_inSplitShade_onKeyguard_setsFractionEqualToEventFraction() {
whenever(statusBarStateController.currentOrUpcomingState)

View File

@@ -20,6 +20,8 @@ import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.util.mockito.mock
@@ -39,6 +41,8 @@ class AmbientStateTest : SysuiTestCase() {
private val sectionProvider = StackScrollAlgorithm.SectionProvider { _, _ -> false }
private val bypassController = StackScrollAlgorithm.BypassController { false }
private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
private val featureFlags = mock<FeatureFlags>()
private lateinit var sut: AmbientState
@@ -51,6 +55,8 @@ class AmbientStateTest : SysuiTestCase() {
sectionProvider,
bypassController,
statusBarKeyguardViewManager,
largeScreenShadeInterpolator,
featureFlags
)
}

View File

@@ -7,6 +7,9 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.LegacySourceType
@@ -21,7 +24,9 @@ import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
/**
@@ -32,6 +37,9 @@ import org.mockito.Mockito.`when` as whenever
@RunWithLooper
class NotificationShelfTest : SysuiTestCase() {
@Mock private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator
@Mock private lateinit var flags: FeatureFlags
private val shelf = NotificationShelf(
context,
/* attrs */ null,
@@ -50,8 +58,12 @@ class NotificationShelfTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
whenever(ambientState.featureFlags).thenReturn(flags)
shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
whenever(ambientState.isSmallScreen).thenReturn(true)
}
@Test
@@ -295,7 +307,35 @@ class NotificationShelfTest : SysuiTestCase() {
fun updateState_expansionChanging_shelfAlphaUpdated() {
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = 0.6f,
expectedAlpha = ShadeInterpolation.getContentAlpha(0.6f)
expectedAlpha = ShadeInterpolation.getContentAlpha(0.6f),
)
}
@Test
fun updateState_flagTrue_largeScreen_expansionChanging_shelfAlphaUpdated_largeScreenValue() {
val expansionFraction = 0.6f
whenever(flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)).thenReturn(true)
whenever(ambientState.isSmallScreen).thenReturn(false)
whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction))
.thenReturn(0.123f)
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = expansionFraction,
expectedAlpha = 0.123f
)
}
@Test
fun updateState_flagFalse_largeScreen_expansionChanging_shelfAlphaUpdated_standardValue() {
val expansionFraction = 0.6f
whenever(flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)).thenReturn(false)
whenever(ambientState.isSmallScreen).thenReturn(false)
whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction))
.thenReturn(0.123f)
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = expansionFraction,
expectedAlpha = ShadeInterpolation.getContentAlpha(expansionFraction)
)
}
@@ -305,7 +345,17 @@ class NotificationShelfTest : SysuiTestCase() {
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = 0.95f,
expectedAlpha = aboutToShowBouncerProgress(0.95f)
expectedAlpha = aboutToShowBouncerProgress(0.95f),
)
}
@Test
fun updateState_largeScreen_expansionChangingWhileBouncerInTransit_bouncerInterpolatorUsed() {
whenever(ambientState.isBouncerInTransit).thenReturn(true)
updateState_expansionChanging_shelfAlphaUpdated(
expansionFraction = 0.95f,
expectedAlpha = aboutToShowBouncerProgress(0.95f),
)
}

View File

@@ -52,7 +52,6 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
@@ -136,7 +135,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
@Mock private StackStateLogger mStackLogger;
@Mock private NotificationStackScrollLogger mLogger;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private ShadeTransitionController mShadeTransitionController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private NotificationTargetsHelper mNotificationTargetsHelper;
@Mock private SecureSettings mSecureSettings;
@@ -184,7 +182,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
mNotifPipelineFlags,
mNotifCollection,
mLockscreenShadeTransitionController,
mShadeTransitionController,
mUiEventLogger,
mRemoteInputManager,
mVisibilityLocationProviderDelegator,

View File

@@ -68,7 +68,9 @@ import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
@@ -129,6 +131,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Mock private NotificationShelf mNotificationShelf;
@Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
@Mock private FeatureFlags mFeatureFlags;
@Before
@UiThreadTest
@@ -142,7 +146,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
mDumpManager,
mNotificationSectionsManager,
mBypassController,
mStatusBarKeyguardViewManager));
mStatusBarKeyguardViewManager,
mLargeScreenShadeInterpolator,
mFeatureFlags
));
// Inject dependencies before initializing the layout
mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);

View File

@@ -8,6 +8,9 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
import com.android.systemui.statusbar.EmptyShadeView
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarState
@@ -15,11 +18,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
@@ -30,12 +35,19 @@ import org.mockito.Mockito.`when` as whenever
@SmallTest
class StackScrollAlgorithmTest : SysuiTestCase() {
@JvmField @Rule
var expect: Expect = Expect.create()
private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
private val hostView = FrameLayout(context)
private val stackScrollAlgorithm = StackScrollAlgorithm(context, hostView)
private val notificationRow = mock<ExpandableNotificationRow>()
private val dumpManager = mock<DumpManager>()
private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
private val notificationShelf = mock<NotificationShelf>()
private val featureFlags = mock<FeatureFlags>()
private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
}
@@ -44,8 +56,10 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
dumpManager,
/* sectionProvider */ { _, _ -> false },
/* bypassController */ { false },
mStatusBarKeyguardViewManager
)
mStatusBarKeyguardViewManager,
largeScreenShadeInterpolator,
featureFlags,
)
private val testableResources = mContext.getOrCreateTestableResources()
@@ -59,6 +73,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
fun setUp() {
whenever(notificationShelf.viewState).thenReturn(ExpandableViewState())
whenever(notificationRow.viewState).thenReturn(ExpandableViewState())
ambientState.isSmallScreen = true
hostView.addView(notificationRow)
}
@@ -145,11 +160,46 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
}
@Test
fun resetViewStates_expansionChangingWhileBouncerInTransit_notificationAlphaUpdated() {
fun resetViewStates_flagTrue_largeScreen_expansionChanging_alphaUpdated_largeScreenValue() {
val expansionFraction = 0.6f
val surfaceAlpha = 123f
ambientState.isSmallScreen = false
whenever(featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION))
.thenReturn(true)
whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction))
.thenReturn(surfaceAlpha)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = expansionFraction,
expectedAlpha = surfaceAlpha,
)
}
@Test
fun resetViewStates_flagFalse_largeScreen_expansionChanging_alphaUpdated_standardValue() {
val expansionFraction = 0.6f
val surfaceAlpha = 123f
ambientState.isSmallScreen = false
whenever(featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION))
.thenReturn(false)
whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false)
whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction))
.thenReturn(surfaceAlpha)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = expansionFraction,
expectedAlpha = getContentAlpha(expansionFraction),
)
}
@Test
fun expansionChanging_largeScreen_bouncerInTransit_alphaUpdated_bouncerValues() {
ambientState.isSmallScreen = false
whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true)
resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction = 0.95f,
expectedAlpha = aboutToShowBouncerProgress(0.95f)
expectedAlpha = aboutToShowBouncerProgress(0.95f),
)
}
@@ -696,7 +746,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction: Float,
expectedAlpha: Float
expectedAlpha: Float,
) {
ambientState.isExpansionChanging = true
ambientState.expansionFraction = expansionFraction
@@ -704,7 +754,7 @@ class StackScrollAlgorithmTest : SysuiTestCase() {
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
assertThat(notificationRow.viewState.alpha).isEqualTo(expectedAlpha)
expect.that(notificationRow.viewState.alpha).isEqualTo(expectedAlpha)
}
}

View File

@@ -24,8 +24,6 @@ import static com.android.systemui.statusbar.phone.ScrimState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -41,6 +39,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
import android.animation.Animator;
import android.app.AlarmManager;
import android.graphics.Color;
@@ -59,6 +59,8 @@ import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FeatureFlags;
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;
@@ -67,6 +69,8 @@ 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.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;
@@ -104,6 +108,8 @@ public class ScrimControllerTest extends SysuiTestCase {
private final FakeConfigurationController mConfigurationController =
new FakeConfigurationController();
private final LargeScreenShadeInterpolator
mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator();
private ScrimController mScrimController;
private ScrimView mScrimBehind;
@@ -133,6 +139,7 @@ public class ScrimControllerTest extends SysuiTestCase {
// 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.)
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private FeatureFlags mFeatureFlags;
private static class AnimatorListener implements Animator.AnimatorListener {
private int mNumStarts;
@@ -245,17 +252,26 @@ public class ScrimControllerTest extends SysuiTestCase {
when(mPrimaryBouncerToGoneTransitionViewModel.getScrimBehindAlpha())
.thenReturn(emptyFlow());
mScrimController = new ScrimController(mLightBarController,
mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScrimController = new ScrimController(
mLightBarController,
mDozeParameters,
mAlarmManager,
mKeyguardStateController,
mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()),
mKeyguardUpdateMonitor,
mDockManager,
mConfigurationController,
new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mSysuiStatusBarStateController,
mMainDispatcher);
mMainDispatcher,
mLinearLargeScreenShadeInterpolator,
mFeatureFlags);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
@@ -653,7 +669,81 @@ public class ScrimControllerTest extends SysuiTestCase {
}
@Test
public void transitionToUnlocked() {
public void transitionToUnlocked_clippedQs() {
mScrimController.setClipsQsScrim(true);
mScrimController.setRawPanelExpansionFraction(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
finishAnimationsImmediately();
assertScrimTinted(Map.of(
mNotificationsScrim, false,
mScrimInFront, false,
mScrimBehind, true
));
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, TRANSPARENT,
mScrimBehind, OPAQUE));
mScrimController.setRawPanelExpansionFraction(0.25f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, SEMI_TRANSPARENT,
mScrimBehind, OPAQUE));
mScrimController.setRawPanelExpansionFraction(0.5f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, OPAQUE,
mScrimBehind, OPAQUE));
}
@Test
public void transitionToUnlocked_nonClippedQs_flagTrue_followsLargeScreensInterpolator() {
when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION))
.thenReturn(true);
mScrimController.setClipsQsScrim(false);
mScrimController.setRawPanelExpansionFraction(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
finishAnimationsImmediately();
assertScrimTinted(Map.of(
mNotificationsScrim, false,
mScrimInFront, false,
mScrimBehind, true
));
// The large screens interpolator used in this test is a linear one, just for tests.
// Assertions below are based on this assumption, and that the code uses that interpolator
// when on a large screen (QS not clipped).
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, TRANSPARENT,
mScrimBehind, TRANSPARENT));
mScrimController.setRawPanelExpansionFraction(0.5f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, SEMI_TRANSPARENT,
mScrimBehind, SEMI_TRANSPARENT));
mScrimController.setRawPanelExpansionFraction(0.99f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, SEMI_TRANSPARENT,
mScrimBehind, SEMI_TRANSPARENT));
mScrimController.setRawPanelExpansionFraction(1f);
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mNotificationsScrim, OPAQUE,
mScrimBehind, OPAQUE));
}
@Test
public void transitionToUnlocked_nonClippedQs_flagFalse() {
when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION))
.thenReturn(false);
mScrimController.setClipsQsScrim(false);
mScrimController.setRawPanelExpansionFraction(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -691,7 +781,6 @@ public class ScrimControllerTest extends SysuiTestCase {
mScrimBehind, OPAQUE));
}
@Test
public void scrimStateCallback() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
@@ -879,17 +968,26 @@ public class ScrimControllerTest extends SysuiTestCase {
// GIVEN display does NOT need blanking
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
mScrimController = new ScrimController(mLightBarController,
mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScrimController = new ScrimController(
mLightBarController,
mDozeParameters,
mAlarmManager,
mKeyguardStateController,
mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()),
mKeyguardUpdateMonitor,
mDockManager,
mConfigurationController,
new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController,
mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mSysuiStatusBarStateController,
mMainDispatcher);
mMainDispatcher,
mLinearLargeScreenShadeInterpolator,
mFeatureFlags);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);