AOD notification icons placement
Whenever there's a custom clock, notification icons should be on the top
left. The default clock will just show the dark shelf.
Test: atest NotificationStackScrollLayoutTest
Test: atest CollapsedStatusBarFragmentTest
Test: visual - with and w/o clock plugin
Test: visual - with and w/o RTL layout
Bug: 122301289
Bug: 111405682
Fixes: 120563185
Change-Id: I4c513d347f656262ee6f91bd208ab9f219e4419c
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 17cc1d5..3cfd6a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -87,6 +87,13 @@
super(context, attrs);
}
+ /**
+ * Returns if this view is presenting a custom clock, or the default implementation.
+ */
+ public boolean hasCustomClock() {
+ return mClockPlugin != null;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index c6f1726..f0cdc89 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -141,6 +141,13 @@
onDensityOrFontScaleChanged();
}
+ /**
+ * If we're presenting a custom clock of just the default one.
+ */
+ public boolean hasCustomClock() {
+ return mClockView.hasCustomClock();
+ }
+
private void setEnableMarquee(boolean enabled) {
if (DEBUG) Log.v(TAG, "Schedule setEnableMarquee: " + (enabled ? "Enable" : "Disable"));
if (enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index d0111cb..f66a57b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.collection.NotificationData;
@@ -136,8 +137,8 @@
}
public NotificationIconAreaController createNotificationIconAreaController(Context context,
- StatusBar statusBar) {
- return new NotificationIconAreaController(context, statusBar);
+ StatusBar statusBar, StatusBarStateController statusBarStateController) {
+ return new NotificationIconAreaController(context, statusBar, statusBarStateController);
}
public KeyguardIndicationController createKeyguardIndicationController(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 1718cff..4ff27b1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -48,7 +48,7 @@
*
* It does not collect touch events when the bouncer shows up.
*/
-public class FalsingManager implements SensorEventListener {
+public class FalsingManager implements SensorEventListener, StateListener {
private static final String ENFORCE_BOUNCER = "falsing_manager_enforce_bouncer";
private static final int[] CLASSIFIER_SENSORS = new int[] {
@@ -84,8 +84,6 @@
private boolean mShowingAod;
private Runnable mPendingWtf;
- private final StateListener mStateListener = this::setStatusBarState;
-
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -108,7 +106,7 @@
UserHandle.USER_ALL);
updateConfiguration();
- Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
}
public static FalsingManager getInstance(Context context) {
@@ -282,14 +280,15 @@
updateSessionActive();
}
- private void setStatusBarState(int state) {
+ @Override
+ public void onStateChanged(int newState) {
if (FalsingLog.ENABLED) {
FalsingLog.i("setStatusBarState", new StringBuilder()
.append("from=").append(StatusBarState.toShortString(mState))
- .append(" to=").append(StatusBarState.toShortString(state))
+ .append(" to=").append(StatusBarState.toShortString(newState))
.toString());
}
- mState = state;
+ mState = newState;
updateSessionActive();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 91b34fc..bd9ca1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -55,7 +55,7 @@
* overflow icons that don't fit into the regular list anymore.
*/
public class NotificationShelf extends ActivatableNotificationView implements
- View.OnLayoutChangeListener {
+ View.OnLayoutChangeListener, StateListener {
private static final boolean USE_ANIMATIONS_WHEN_OPENING =
SystemProperties.getBoolean("debug.icon_opening_animations", true);
@@ -95,8 +95,6 @@
private int mCutoutHeight;
private int mGapHeight;
- private final StateListener mStateListener = this::setStatusBarState;
-
public NotificationShelf(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -121,13 +119,13 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(StatusBarStateController.class)
- .addCallback(mStateListener, StatusBarStateController.RANK_SHELF);
+ .addCallback(this, StatusBarStateController.RANK_SHELF);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(this);
}
public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
@@ -174,6 +172,24 @@
updateInteractiveness();
}
+ /**
+ * Alpha animation with translation played when this view is visible on AOD.
+ */
+ public void fadeInTranslating() {
+ mShelfIcons.setTranslationY(-mShelfAppearTranslation);
+ mShelfIcons.setAlpha(0);
+ mShelfIcons.animate()
+ .setInterpolator(Interpolators.DECELERATE_QUINT)
+ .translationY(0)
+ .setDuration(SHELF_IN_TRANSLATION_DURATION)
+ .start();
+ mShelfIcons.animate()
+ .alpha(1)
+ .setInterpolator(Interpolators.LINEAR)
+ .setDuration(SHELF_IN_TRANSLATION_DURATION)
+ .start();
+ }
+
@Override
protected View getContentView() {
return mShelfIcons;
@@ -225,7 +241,7 @@
}
viewState.hasItemsInStableShelf = lastViewState.inShelf;
viewState.hidden = !mAmbientState.isShadeExpanded()
- || mAmbientState.isQsCustomizerShowing() || mAmbientState.isFullyDark();
+ || mAmbientState.isQsCustomizerShowing();
viewState.maxShelfEnd = maxShelfEnd;
} else {
viewState.hidden = true;
@@ -420,7 +436,7 @@
float maxTop = row.getTranslationY();
StatusBarIconView icon = row.getEntry().expandedIcon;
float shelfIconPosition = getTranslationY() + icon.getTop() + icon.getTranslationY();
- if (shelfIconPosition < maxTop) {
+ if (shelfIconPosition < maxTop && !mAmbientState.isDark()) {
int top = (int) (maxTop - shelfIconPosition);
Rect clipRect = new Rect(0, top, icon.getWidth(), Math.max(top, icon.getHeight()));
icon.setClipBounds(clipRect);
@@ -431,7 +447,7 @@
private void updateContinuousClipping(final ExpandableNotificationRow row) {
StatusBarIconView icon = row.getEntry().expandedIcon;
- boolean needsContinuousClipping = ViewState.isAnimatingY(icon);
+ boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
final ViewTreeObserver observer = icon.getViewTreeObserver();
@@ -622,7 +638,9 @@
iconState.translateContent = false;
}
float transitionAmount;
- if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
+ if (mAmbientState.isDarkAtAll() && !row.isInShelf()) {
+ transitionAmount = mAmbientState.isFullyDark() ? 1 : 0;
+ } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
|| iconState.useLinearTransitionAmount) {
transitionAmount = iconTransitionAmount;
} else {
@@ -860,8 +878,9 @@
mCollapsedIcons.addOnLayoutChangeListener(this);
}
- private void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
+ @Override
+ public void onStateChanged(int newState) {
+ mStatusBarState = newState;
updateInteractiveness();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 3c13354..19ed13e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -206,6 +206,10 @@
mIconScale = SYSTEM_ICON_SCALE;
}
+ public float getIconScaleFullyDark() {
+ return (float) mStatusBarIconDrawingSizeDark / mStatusBarIconDrawingSize;
+ }
+
public float getIconScale() {
return mIconScale;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 087b655..54bce1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -331,7 +331,8 @@
*
* @param newState the new {@link StatusBarState}
*/
- public void onStateChanged(int newState);
+ default void onStateChanged(int newState) {
+ }
/**
* Callback to be notified when Dozing changes. Dozing is stored separately from state.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 99b0cd2..2d1f989 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -113,6 +113,7 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -458,6 +459,10 @@
private final NotificationGutsManager
mNotificationGutsManager = Dependency.get(NotificationGutsManager.class);
+ /**
+ * If the {@link NotificationShelf} should be visible when dark.
+ */
+ private boolean mShowDarkShelf;
@Inject
public NotificationStackScrollLayout(
@@ -1196,7 +1201,7 @@
mIsClipped = clipped;
}
- if (mPulsing) {
+ if (mPulsing || mAmbientState.isFullyDark() && mShowDarkShelf) {
setClipBounds(null);
} else if (mAmbientState.isDarkAtAll()) {
setClipBounds(mBackgroundAnimationRect);
@@ -4361,6 +4366,9 @@
if (mAmbientState.isDark() == dark) {
return;
}
+ if (!dark) {
+ mShowDarkShelf = false;
+ }
mAmbientState.setDark(dark);
if (animate && mAnimationsEnabled) {
mDarkNeedsAnimation = true;
@@ -4422,9 +4430,9 @@
boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
if (nowFullyDark != wasFullyDark) {
updateContentHeight();
- }
- if (mIconAreaController != null) {
- mIconAreaController.setDarkAmount(interpolatedDarkAmount);
+ if (nowFullyDark && mShowDarkShelf) {
+ updateDarkShelfVisibility();
+ }
}
if (!wasDarkAtAll && nowDarkAtAll) {
resetExposedMenuView(true /* animate */, true /* animate */);
@@ -4435,6 +4443,22 @@
requestChildrenUpdate();
}
+ /**
+ * If the shelf should be visible when the device is in ambient mode (dozing.)
+ */
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void setShowDarkShelf(boolean showDarkShelf) {
+ mShowDarkShelf = showDarkShelf;
+ }
+
+ private void updateDarkShelfVisibility() {
+ DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
+ if (dozeParameters.shouldControlScreenOff()) {
+ mShelf.fadeInTranslating();
+ }
+ updateClipping();
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void notifyDarkAnimationStart(boolean dark) {
// We only swap the scaling factor if we're fully dark or fully awake to avoid
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index f907b65..35763dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -215,7 +215,10 @@
}
}
- if (mStatusBarStateController.isDozing()) {
+ // The shelf will be hidden when dozing with a custom clock, we must show notification
+ // icons in this occasion.
+ if (mStatusBarStateController.isDozing()
+ && mStatusBarComponent.getPanel().hasCustomClock()) {
state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
state &= ~DISABLE_NOTIFICATION_ICONS;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 0fada66..7f75223 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -58,7 +58,7 @@
*/
public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
- OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener {
+ OnHeadsUpChangedListener, ConfigurationController.ConfigurationListener, StateListener {
private static final String TAG = "HeadsUpManagerPhone";
private final View mStatusBarWindowView;
@@ -83,7 +83,6 @@
private boolean mIsObserving;
private int mStatusBarState;
- private final StateListener mStateListener = this::setStatusBarState;
private AnimationStateHandler mAnimationStateHandler;
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
@@ -129,7 +128,7 @@
updateTouchableRegionListener();
}
});
- Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
+ Dependency.get(StatusBarStateController.class).addCallback(this);
mBubbleController.setBubbleStateChangeListener((hasBubbles) -> {
if (!hasBubbles) {
mBubbleGoingAway = true;
@@ -143,7 +142,7 @@
}
public void destroy() {
- Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(this);
}
private void initResources() {
@@ -225,11 +224,9 @@
}
}
- /**
- * Set the current state of the statusbar.
- */
- private void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
+ @Override
+ public void onStateChanged(int newState) {
+ mStatusBarState = newState;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 1fb5484..077fcda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -23,6 +23,7 @@
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -36,13 +37,15 @@
* A controller for the space in the status bar to the left of the system icons. This area is
* normally reserved for notifications.
*/
-public class NotificationIconAreaController implements DarkReceiver {
+public class NotificationIconAreaController implements DarkReceiver,
+ StatusBarStateController.StateListener {
public static final String LOW_PRIORITY = "low_priority";
private final ContrastColorUtil mContrastColorUtil;
private final NotificationEntryManager mEntryManager;
private final Runnable mUpdateStatusBarIcons = this::updateStatusBarIcons;
+ private final StatusBarStateController mStatusBarStateController;
private final TunerService.Tunable mTunable = new TunerService.Tunable() {
@Override
public void onTuningChanged(String key, String newValue) {
@@ -86,11 +89,14 @@
private final ViewClippingUtil.ClippingParameters mClippingParameters =
view -> view instanceof StatusBarWindowView;
- public NotificationIconAreaController(Context context, StatusBar statusBar) {
+ public NotificationIconAreaController(Context context, StatusBar statusBar,
+ StatusBarStateController statusBarStateController) {
mStatusBar = statusBar;
mContrastColorUtil = ContrastColorUtil.getInstance(context);
mContext = context;
mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mStatusBarStateController = statusBarStateController;
+ mStatusBarStateController.addCallback(this);
Dependency.get(TunerService.class).addTunable(mTunable, LOW_PRIORITY);
@@ -373,24 +379,6 @@
v.setDecorColor(mIconTint);
}
- /**
- * Dark amount, from 0 to 1, representing being awake or in AOD.
- */
- public void setDarkAmount(float darkAmount) {
- mDarkAmount = darkAmount;
- if (darkAmount == 0 || darkAmount == 1) {
- ViewClippingUtil.setClippingDeactivated(mNotificationIcons, darkAmount != 0,
- mClippingParameters);
- }
- dozeTimeTick();
-
- boolean fullyDark = darkAmount == 1f;
- if (mFullyDark != fullyDark) {
- mFullyDark = fullyDark;
- updateShelfIcons();
- }
- }
-
public void setDark(boolean dark) {
mNotificationIcons.setDark(dark, false, 0);
mShelfIcons.setDark(dark, false, 0);
@@ -408,10 +396,45 @@
* Moves icons whenever the device wakes up in AOD, to avoid burn in.
*/
public void dozeTimeTick() {
+ if (mNotificationIcons.getVisibility() != View.VISIBLE) {
+ return;
+ }
+
+ if (mDarkAmount == 0 && !mStatusBarStateController.isDozing()) {
+ mNotificationIcons.setTranslationX(0);
+ mNotificationIcons.setTranslationY(0);
+ return;
+ }
+
int yOffset = (mKeyguardStatusBarHeight - getHeight()) / 2;
int translationX = getBurnInOffset(mBurnInOffset, true /* xAxis */);
int translationY = getBurnInOffset(mBurnInOffset, false /* xAxis */) + yOffset;
- mNotificationIcons.setTranslationX(translationX * mDarkAmount);
- mNotificationIcons.setTranslationY(translationY * mDarkAmount);
+ mNotificationIcons.setTranslationX(translationX);
+ mNotificationIcons.setTranslationY(translationY);
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ dozeTimeTick();
+ }
+
+ @Override
+ public void onDozeAmountChanged(float linear, float eased) {
+ boolean wasOrIsAwake = mDarkAmount == 0 || linear == 0;
+ boolean wasOrIsDozing = mDarkAmount == 1 || linear == 1;
+ mDarkAmount = linear;
+ if (wasOrIsAwake) {
+ ViewClippingUtil.setClippingDeactivated(mNotificationIcons, mDarkAmount != 0,
+ mClippingParameters);
+ }
+ if (wasOrIsAwake || wasOrIsDozing) {
+ dozeTimeTick();
+ }
+
+ boolean fullyDark = mDarkAmount == 1f;
+ if (mFullyDark != fullyDark) {
+ mFullyDark = fullyDark;
+ updateShelfIcons();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 964b2210..009afca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -128,6 +128,7 @@
}
}.setDuration(CONTENT_FADE_DURATION);
+ private static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
public static final int MAX_STATIC_ICONS = 4;
private static final int MAX_DOTS = 1;
@@ -371,7 +372,8 @@
float translationX = getActualPaddingStart();
int firstOverflowIndex = -1;
int childCount = getChildCount();
- int maxVisibleIcons = mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
+ int maxVisibleIcons = mDark ? MAX_VISIBLE_ICONS_WHEN_DARK :
+ mIsStaticLayout ? MAX_STATIC_ICONS : childCount;
float layoutEnd = getLayoutEnd();
float overflowStart = getMaxOverflowStart();
mVisualOverflowStart = 0;
@@ -387,6 +389,9 @@
boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
&& iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
boolean noOverflowAfter = i == childCount - 1;
+ float drawingScale = mDark && view instanceof StatusBarIconView
+ ? ((StatusBarIconView) view).getIconScaleFullyDark()
+ : 1f;
if (mOpenedAmount != 0.0f) {
noOverflowAfter = noOverflowAfter && !hasAmbient && !forceOverflow;
}
@@ -402,7 +407,7 @@
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
}
}
- translationX += iconState.iconAppearAmount * view.getWidth();
+ translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
}
mNumDots = 0;
if (firstOverflowIndex != -1) {
@@ -432,6 +437,32 @@
mFirstVisibleIconState = mIconStates.get(getChildAt(0));
}
+ boolean center = mDark;
+ if (center && translationX < getLayoutEnd()) {
+ float initialTranslation =
+ mFirstVisibleIconState == null ? 0 : mFirstVisibleIconState.xTranslation;
+
+ float contentWidth = 0;
+ if (mLastVisibleIconState != null) {
+ contentWidth = mLastVisibleIconState.xTranslation + mIconSize;
+ contentWidth = Math.min(getWidth(), contentWidth) - initialTranslation;
+ }
+ float availableSpace = getLayoutEnd() - getActualPaddingStart();
+ float delta = (availableSpace - contentWidth) / 2;
+
+ if (firstOverflowIndex != -1) {
+ // If we have an overflow, only count those half for centering because the dots
+ // don't have a lot of visual weight.
+ float deltaIgnoringOverflow = (getLayoutEnd() - mVisualOverflowStart) / 2;
+ delta = (deltaIgnoringOverflow + delta) / 2;
+ }
+ for (int i = 0; i < childCount; i++) {
+ View view = getChildAt(i);
+ IconState iconState = mIconStates.get(view);
+ iconState.xTranslation += delta;
+ }
+ }
+
if (isLayoutRtl()) {
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 512f56d..31310f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -50,6 +50,7 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardClockSwitch;
@@ -142,7 +143,8 @@
private KeyguardStatusBarView mKeyguardStatusBar;
private QS mQs;
private FrameLayout mQsFrame;
- private KeyguardStatusView mKeyguardStatusView;
+ @VisibleForTesting
+ protected KeyguardStatusView mKeyguardStatusView;
private View mQsNavbarScrim;
protected NotificationsQuickSettingsContainer mNotificationContainerParent;
protected NotificationStackScrollLayout mNotificationStackScroller;
@@ -327,6 +329,13 @@
mDisplayId = context.getDisplayId();
}
+ /**
+ * Returns if there's a custom clock being presented.
+ */
+ public boolean hasCustomClock() {
+ return mKeyguardStatusView.hasCustomClock();
+ }
+
private void setStatusBar(StatusBar bar) {
mStatusBar = bar;
mKeyguardBottomArea.setStatusBar(mStatusBar);
@@ -2774,6 +2783,9 @@
if (dozing == mDozing) return;
mDozing = dozing;
mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
+ if (mDozing) {
+ mNotificationStackScroller.setShowDarkShelf(!hasCustomClock());
+ }
if (mBarState == StatusBarState.KEYGUARD
|| mBarState == StatusBarState.SHADE_LOCKED) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 021b430..f17145d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -142,8 +142,8 @@
private boolean mIgnoreXTouchSlop;
private boolean mExpandLatencyTracking;
protected final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
- protected final StatusBarStateController
- mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+ protected final StatusBarStateController mStatusBarStateController =
+ Dependency.get(StatusBarStateController.class);
protected void onExpandingFinished() {
mBar.onExpandingFinished();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 9abd86d..3f93192 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -800,7 +800,7 @@
mNotificationLogger.setUpWithContainer(notifListContainer);
mNotificationIconAreaController = SystemUIFactory.getInstance()
- .createNotificationIconAreaController(context, this);
+ .createNotificationIconAreaController(context, this, mStatusBarStateController);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 3ddfc0c..8d58410 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -53,7 +53,8 @@
/**
*/
@Singleton
-public class StatusBarRemoteInputCallback implements Callback, Callbacks {
+public class StatusBarRemoteInputCallback implements Callback, Callbacks,
+ StatusBarStateController.StateListener {
private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
private final StatusBarStateController mStatusBarStateController
@@ -63,7 +64,6 @@
private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
private final Context mContext;
private View mPendingWorkRemoteInputView;
- private final StatusBarStateController.StateListener mStateListener = this::setStatusBarState;
private View mPendingRemoteInputView;
private final ShadeController mShadeController = Dependency.get(ShadeController.class);
private KeyguardManager mKeyguardManager;
@@ -78,13 +78,14 @@
mContext = context;
mContext.registerReceiverAsUser(mChallengeReceiver, UserHandle.ALL,
new IntentFilter(ACTION_DEVICE_LOCKED_CHANGED), null, null);
- mStatusBarStateController.addCallback(mStateListener);
+ mStatusBarStateController.addCallback(this);
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mCommandQueue = getComponent(context, CommandQueue.class);
mCommandQueue.addCallback(this);
}
- private void setStatusBarState(int state) {
+ @Override
+ public void onStateChanged(int state) {
if (state == StatusBarState.SHADE && mStatusBarStateController.leaveOpenOnKeyguardHide()) {
if (!mStatusBarStateController.isKeyguardRequested()) {
if (mPendingRemoteInputView != null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 214404c..c140ba2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -142,6 +142,7 @@
doNothing().when(mGroupManager).collapseAllGroups();
doNothing().when(mExpandHelper).cancelImmediately();
doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+ doNothing().when(notificationShelf).fadeInTranslating();
mOriginalInterruptionModelSetting = Settings.Secure.getInt(mContext.getContentResolver(),
NOTIFICATION_NEW_INTERRUPTION_MODEL, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index add8838..a72be7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -57,13 +57,15 @@
@Before
public void setup() {
mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
- mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+ StatusBar statusBar = mock(StatusBar.class);
+ mSysuiContext.putComponent(StatusBar.class, statusBar);
mSysuiContext.putComponent(TunerService.class, mock(TunerService.class));
mStatusBarStateController = mDependency
.injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
mNotificationAreaInner = mock(View.class);
+ when(statusBar.getPanel()).thenReturn(mock(NotificationPanelView.class));
when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class));
when(mMockNotificiationAreaController.getNotificationInnerAreaView()).thenReturn(
mNotificationAreaInner);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
new file mode 100644
index 0000000..b7b95ef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2018 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.statusbar.phone;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationPanelViewTest extends SysuiTestCase {
+
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private NotificationStackScrollLayout mNotificationStackScrollLayout;
+ @Mock
+ private KeyguardStatusView mKeyguardStatusView;
+ private NotificationPanelView mNotificationPanelView;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mDependency.injectTestDependency(StatusBarStateController.class,
+ mStatusBarStateController);
+ mDependency.injectMockDependency(ShadeController.class);
+ mDependency.injectMockDependency(NotificationLockscreenUserManager.class);
+ mDependency.injectMockDependency(ConfigurationController.class);
+ mDependency.injectMockDependency(ZenModeController.class);
+ mNotificationPanelView = new TestableNotificationPanelView();
+ }
+
+ @Test
+ public void testSetDozing_notifiesNsslAndStateController() {
+ mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+ InOrder inOrder = inOrder(mNotificationStackScrollLayout, mStatusBarStateController);
+ inOrder.verify(mNotificationStackScrollLayout).setDark(eq(true), eq(true), eq(null));
+ inOrder.verify(mNotificationStackScrollLayout).setShowDarkShelf(eq(true));
+ inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true));
+ }
+
+ @Test
+ public void testSetDozing_showsDarkShelfWithDefaultClock() {
+ when(mKeyguardStatusView.hasCustomClock()).thenReturn(false);
+ mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+ verify(mNotificationStackScrollLayout).setShowDarkShelf(eq(true));
+ }
+
+ @Test
+ public void testSetDozing_hidesDarkShelfWhenCustomClock() {
+ when(mKeyguardStatusView.hasCustomClock()).thenReturn(true);
+ mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+ verify(mNotificationStackScrollLayout).setShowDarkShelf(eq(false));
+ }
+
+ private class TestableNotificationPanelView extends NotificationPanelView {
+ TestableNotificationPanelView() {
+ super(NotificationPanelViewTest.this.mContext, null);
+ mNotificationStackScroller = mNotificationStackScrollLayout;
+ mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
+ }
+ }
+}