summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/provider/Settings.java12
-rw-r--r--packages/SystemUI/res/values/config.xml3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java6
19 files changed, 318 insertions, 42 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e95e6e081276..6f0669b15dad 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8276,6 +8276,16 @@ public final class Settings {
BOOLEAN_VALIDATOR;
/**
+ * Whether or not media is shown automatically when bypassing as a heads up.
+ * @hide
+ */
+ public static final String SHOW_MEDIA_WHEN_BYPASSING =
+ "show_media_when_bypassing";
+
+ private static final Validator SHOW_MEDIA_WHEN_BYPASSING_VALIDATOR =
+ BOOLEAN_VALIDATOR;
+
+ /**
* Whether or not face unlock requires attention. This is a cached value, the source of
* truth is obtained through the HAL.
* @hide
@@ -8979,6 +8989,7 @@ public final class Settings {
NFC_PAYMENT_DEFAULT_COMPONENT,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
FACE_UNLOCK_KEYGUARD_ENABLED,
+ SHOW_MEDIA_WHEN_BYPASSING,
FACE_UNLOCK_DISMISSES_KEYGUARD,
FACE_UNLOCK_APP_ENABLED,
FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
@@ -9155,6 +9166,7 @@ public final class Settings {
VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_DISMISSES_KEYGUARD,
FACE_UNLOCK_DISMISSES_KEYGUARD_VALIDATOR);
+ VALIDATORS.put(SHOW_MEDIA_WHEN_BYPASSING, SHOW_MEDIA_WHEN_BYPASSING_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR);
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index e6078a09bcc5..0515d1733c8d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -138,6 +138,9 @@
<!-- The number of milliseconds before the heads up notification auto-dismisses. -->
<integer name="heads_up_notification_decay">5000</integer>
+ <!-- The number of milliseconds before the heads up notification sent automatically by the system auto-dismisses. -->
+ <integer name="auto_heads_up_notification_decay">3000</integer>
+
<!-- The number of milliseconds after a heads up notification is pushed back
before the app can interrupt again. -->
<integer name="heads_up_default_snooze_length_ms">60000</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 06352310b3dd..5136682bb292 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -52,6 +52,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
@@ -103,8 +104,8 @@ public class KeyguardSliceProvider extends SliceProvider implements
private final Date mCurrentTime = new Date();
private final Handler mHandler;
private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
- private final HashSet<Integer> mMediaInvisibleStates;
private final Object mMediaToken = new Object();
+ private DozeParameters mDozeParameters;
@VisibleForTesting
protected SettableWakeLock mMediaWakeLock;
@VisibleForTesting
@@ -184,11 +185,6 @@ public class KeyguardSliceProvider extends SliceProvider implements
mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
mDndUri = Uri.parse(KEYGUARD_DND_URI);
mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
-
- mMediaInvisibleStates = new HashSet<>();
- mMediaInvisibleStates.add(PlaybackState.STATE_NONE);
- mMediaInvisibleStates.add(PlaybackState.STATE_STOPPED);
- mMediaInvisibleStates.add(PlaybackState.STATE_PAUSED);
}
/**
@@ -201,12 +197,14 @@ public class KeyguardSliceProvider extends SliceProvider implements
public void initDependencies(
NotificationMediaManager mediaManager,
StatusBarStateController statusBarStateController,
- KeyguardBypassController keyguardBypassController) {
+ KeyguardBypassController keyguardBypassController,
+ DozeParameters dozeParameters) {
mMediaManager = mediaManager;
mMediaManager.addCallback(this);
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(this);
mKeyguardBypassController = keyguardBypassController;
+ mDozeParameters = dozeParameters;
}
@AnyThread
@@ -231,9 +229,9 @@ public class KeyguardSliceProvider extends SliceProvider implements
}
protected boolean needsMediaLocked() {
- boolean isBypass = mKeyguardBypassController != null
- && mKeyguardBypassController.getBypassEnabled();
- return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && (mDozing || isBypass);
+ boolean keepWhenAwake = mKeyguardBypassController != null
+ && mKeyguardBypassController.getBypassEnabled() && mDozeParameters.getAlwaysOn();
+ return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && (mDozing || keepWhenAwake);
}
protected void addMediaLocked(ListBuilder listBuilder) {
@@ -458,7 +456,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
@Override
public void onMetadataOrStateChanged(MediaMetadata metadata, @PlaybackState.State int state) {
synchronized (this) {
- boolean nextVisible = !mMediaInvisibleStates.contains(state);
+ boolean nextVisible = NotificationMediaManager.isPlayingState(state);
mHandler.removeCallbacksAndMessages(mMediaToken);
if (mMediaIsVisible && !nextVisible) {
// We need to delay this event for a few millis when stopping to avoid jank in the
@@ -477,7 +475,7 @@ public class KeyguardSliceProvider extends SliceProvider implements
}
private void updateMediaStateLocked(MediaMetadata metadata, @PlaybackState.State int state) {
- boolean nextVisible = !mMediaInvisibleStates.contains(state);
+ boolean nextVisible = NotificationMediaManager.isPlayingState(state);
CharSequence title = null;
if (metadata != null) {
title = metadata.getText(MediaMetadata.METADATA_KEY_TITLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index a59d590c9719..f001561754aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -69,6 +69,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -91,6 +92,14 @@ public class NotificationMediaManager implements Dumpable {
private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class);
private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
private final KeyguardBypassController mKeyguardBypassController;
+ private static final HashSet<Integer> PAUSED_MEDIA_STATES = new HashSet<>();
+ static {
+ PAUSED_MEDIA_STATES.add(PlaybackState.STATE_NONE);
+ PAUSED_MEDIA_STATES.add(PlaybackState.STATE_STOPPED);
+ PAUSED_MEDIA_STATES.add(PlaybackState.STATE_PAUSED);
+ PAUSED_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
+ }
+
// Late binding
private NotificationEntryManager mEntryManager;
@@ -207,6 +216,10 @@ public class NotificationMediaManager implements Dumpable {
mPropertiesChangedListener);
}
+ public static boolean isPlayingState(int state) {
+ return !PAUSED_MEDIA_STATES.contains(state);
+ }
+
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0a8b7f8aa0da..4ccd0cd3353b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar;
import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN_REVERSE;
import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
import android.content.res.Configuration;
@@ -48,8 +49,12 @@ import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.ViewState;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
+import javax.inject.Inject;
+import javax.inject.Named;
+
/**
* A notification shelf view that is placed inside the notification scroller. It manages the
* overflow icons that don't fit into the regular list anymore.
@@ -63,6 +68,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
= SystemProperties.getBoolean("debug.icon_scroll_animations", true);
private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
private static final String TAG = "NotificationShelf";
+ private final KeyguardBypassController mBypassController;
private NotificationIconContainer mShelfIcons;
private int[] mTmp = new int[2];
@@ -93,8 +99,12 @@ public class NotificationShelf extends ActivatableNotificationView implements
private int mCutoutHeight;
private int mGapHeight;
- public NotificationShelf(Context context, AttributeSet attrs) {
+ @Inject
+ public NotificationShelf(@Named(VIEW_CONTEXT) Context context,
+ AttributeSet attrs,
+ KeyguardBypassController keyguardBypassController) {
super(context, attrs);
+ mBypassController = keyguardBypassController;
}
@Override
@@ -309,7 +319,10 @@ public class NotificationShelf extends ActivatableNotificationView implements
colorTwoBefore = previousColor;
transitionAmount = inShelfAmount;
}
- if (isLastChild) {
+ // We don't want to modify the color if the notification is hun'd
+ boolean canModifyColor = mAmbientState.isShadeExpanded()
+ && !(mAmbientState.isOnKeyguard() && mBypassController.getBypassEnabled());
+ if (isLastChild && canModifyColor) {
if (colorOfViewBeforeLast == NO_COLOR) {
colorOfViewBeforeLast = ownColorUntinted;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
new file mode 100644
index 000000000000..ea474ced7632
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 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.notification
+
+import android.content.Context
+import android.media.MediaMetadata
+import android.provider.Settings
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.tuner.TunerService
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * A class that automatically creates heads up for important notification when bypassing the
+ * lockscreen
+ */
+@Singleton
+class BypassHeadsUpNotifier @Inject constructor(
+ private val context: Context,
+ private val bypassController: KeyguardBypassController,
+ private val statusBarStateController: StatusBarStateController,
+ private val headsUpManager: HeadsUpManagerPhone,
+ private val mediaManager: NotificationMediaManager,
+ tunerService: TunerService) : StatusBarStateController.StateListener,
+ NotificationMediaManager.MediaListener {
+
+ private lateinit var entryManager: NotificationEntryManager
+ private var currentMediaEntry: NotificationEntry? = null
+ private var enabled = true
+
+ var fullyAwake = false
+ set(value) {
+ field = value
+ if (value) {
+ updateAutoHeadsUp(currentMediaEntry)
+ }
+ }
+
+ init {
+ statusBarStateController.addCallback(this)
+ tunerService.addTunable(
+ TunerService.Tunable { _, _ ->
+ enabled = Settings.Secure.getIntForUser(
+ context.contentResolver,
+ Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING,
+ 1 /* default */,
+ KeyguardUpdateMonitor.getCurrentUser()) != 0
+ }, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING)
+ }
+
+ fun setUp(entryManager: NotificationEntryManager) {
+ this.entryManager = entryManager
+ mediaManager.addCallback(this)
+ }
+
+ override fun onMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
+ val previous = currentMediaEntry
+ var newEntry = entryManager.notificationData.get(mediaManager.mediaNotificationKey)
+ if (!NotificationMediaManager.isPlayingState(state)) {
+ newEntry = null
+ }
+ if (newEntry?.isSensitive == true) {
+ newEntry = null
+ }
+ currentMediaEntry = newEntry
+ updateAutoHeadsUp(previous)
+ updateAutoHeadsUp(currentMediaEntry)
+ }
+
+ private fun updateAutoHeadsUp(entry: NotificationEntry?) {
+ entry?.let {
+ val autoHeadsUp = it == currentMediaEntry && canAutoHeadsUp()
+ it.isAutoHeadsUp = autoHeadsUp
+ if (autoHeadsUp) {
+ headsUpManager.showNotification(it)
+ }
+ }
+ }
+
+ override fun onStatePostChange() {
+ updateAutoHeadsUp(currentMediaEntry)
+ }
+
+ private fun canAutoHeadsUp() : Boolean {
+ if (!enabled) {
+ return false
+ }
+ if (!bypassController.bypassEnabled) {
+ return false
+ }
+ if (statusBarStateController.state != StatusBarState.KEYGUARD) {
+ return false
+ }
+ if (!fullyAwake) {
+ return false
+ }
+ return true
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 8a23f718ef9a..d71d40781f2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -24,7 +24,6 @@ import android.service.notification.StatusBarNotification;
import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -119,12 +118,11 @@ public class NotificationAlertingManager {
shouldAlert = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
final boolean wasAlerting = mHeadsUpManager.isAlerting(entry.key);
if (wasAlerting) {
- if (!shouldAlert) {
- // We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(entry.key,
- false /* ignoreEarliestRemovalTime */);
- } else {
+ if (shouldAlert) {
mHeadsUpManager.updateNotification(entry.key, alertAgain);
+ } else if (!mHeadsUpManager.isEntryAutoHeadsUpped(entry.key)) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(entry.key, false /* removeImmediately */);
}
} else if (shouldAlert && alertAgain) {
// This notification was updated to be alerting, show it!
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 1aa6bc9ae5f9..dfc64508cadf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -24,6 +24,8 @@ import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import androidx.annotation.NonNull;
+
/**
* Listener interface for changes sent by NotificationEntryManager.
*/
@@ -45,7 +47,7 @@ public interface NotificationEntryListener {
/**
* Called when a new entry is created.
*/
- default void onNotificationAdded(NotificationEntry entry) {
+ default void onNotificationAdded(@NonNull NotificationEntry entry) {
}
/**
@@ -61,7 +63,7 @@ public interface NotificationEntryListener {
/**
* Called when a notification was updated, after any filtering of notifications have occurred.
*/
- default void onPostEntryUpdated(NotificationEntry entry) {
+ default void onPostEntryUpdated(@NonNull NotificationEntry entry) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 9db715d129a5..b19d2ca29c96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -175,6 +175,7 @@ public final class NotificationEntry {
private boolean mHighPriority;
private boolean mSensitive = true;
private Runnable mOnSensitiveChangedListener;
+ private boolean mAutoHeadsUp;
public NotificationEntry(StatusBarNotification n) {
this(n, null);
@@ -670,11 +671,25 @@ public final class NotificationEntry {
if (row != null) row.setHeadsUp(shouldHeadsUp);
}
-
public void setHeadsUpAnimatingAway(boolean animatingAway) {
if (row != null) row.setHeadsUpAnimatingAway(animatingAway);
}
+ /**
+ * Set that this notification was automatically heads upped. This happens for example when
+ * the user bypasses the lockscreen and media is playing.
+ */
+ public void setAutoHeadsUp(boolean autoHeadsUp) {
+ mAutoHeadsUp = autoHeadsUp;
+ }
+
+ /**
+ * @return if this notification was automatically heads upped. This happens for example when
+ * * the user bypasses the lockscreen and media is playing.
+ */
+ public boolean isAutoHeadsUp() {
+ return mAutoHeadsUp;
+ }
public boolean mustStayOnScreen() {
return row != null && row.mustStayOnScreen();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index ae534d278116..a8327f63dcf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2698,6 +2698,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.setAlpha(1.0f);
l.setLayerType(LAYER_TYPE_NONE, null);
}
+ } else {
+ setHeadsUpAnimatingAway(false);
}
}
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 192373f5a4c5..58e639924f4f 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
@@ -3402,10 +3402,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
ExpandableNotificationRow row = eventPair.first;
boolean isHeadsUp = eventPair.second;
+ if (isHeadsUp != row.isHeadsUp()) {
+ // For cases where we have a heads up showing and appearing again we shouldn't
+ // do the animations at all.
+ continue;
+ }
int type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER;
boolean onBottom = false;
boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
- if (!mIsExpanded && !isHeadsUp) {
+ boolean performDisappearAnimation = !mIsExpanded
+ // Only animate if we still have pinned heads up, otherwise we just have the
+ // regular collapse animation of the lock screen
+ || (mKeyguardBypassController.getBypassEnabled() && onKeyguard()
+ && mHeadsUpManager.hasPinnedHeadsUp());
+ if (performDisappearAnimation && !isHeadsUp) {
type = row.wasJustClicked()
? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
: AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
@@ -6252,6 +6262,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
mAmbientState.onDragFinished(animView);
updateContinuousShadowDrawing();
updateContinuousBackgroundDrawing();
+ if (animView instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
+ if (row.isPinned() && !canChildBeDismissed(row)
+ && row.getStatusBarNotification().getNotification().fullScreenIntent
+ == null) {
+ mHeadsUpManager.removeNotification(row.getStatusBarNotification().getKey(),
+ true /* removeImmediately */);
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 09c6968867b8..35ba801c75ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -546,12 +546,12 @@ public class StackScrollAlgorithm {
ExpandableViewState topState =
topHeadsUpEntry == null ? null : topHeadsUpEntry.getViewState();
if (topState != null && !isTopEntry && (!mIsExpanded
- || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
+ || unmodifiedEndLocation > topState.yTranslation + topState.height)) {
// Ensure that a headsUp doesn't vertically extend further than the heads-up at
// the top most z-position
childState.height = row.getIntrinsicHeight();
- childState.yTranslation = topState.yTranslation + topState.height
- - childState.height;
+ childState.yTranslation = Math.min(topState.yTranslation + topState.height
+ - childState.height, childState.yTranslation);
}
// heads up notification show and this row is the top entry of heads up
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4f169eb50f88..0996ff27e9a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -28,6 +28,7 @@ import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -451,7 +452,11 @@ public class StackStateAnimator {
if (row.isDismissed()) {
needsAnimation = false;
}
- StatusBarIconView icon = row.getEntry().icon;
+ NotificationEntry entry = row.getEntry();
+ StatusBarIconView icon = entry.icon;
+ if (entry.centeredIcon != null && entry.centeredIcon.getParent() != null) {
+ icon = entry.centeredIcon;
+ }
if (icon.getParent() != null) {
icon.getLocationOnScreen(mTmpLocation);
float iconPosition = mTmpLocation[0] - icon.getTranslationX()
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 ade855e755e3..c44f953615e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -32,7 +32,6 @@ import android.view.ViewTreeObserver;
import androidx.collection.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.ScreenDecorations;
@@ -67,6 +66,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
final int mExtensionTime;
private final StatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mBypassController;
+ private final int mAutoHeadsUpNotificationDecay;
private View mStatusBarWindowView;
private NotificationGroupManager mGroupManager;
private VisualStabilityManager mVisualStabilityManager;
@@ -81,6 +81,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
private boolean mTrackingHeadsUp;
private HashSet<String> mSwipedOutKeys = new HashSet<>();
private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ private HashSet<String> mKeysToRemoveWhenLeavingKeyguard = new HashSet<>();
private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
= new ArraySet<>();
private boolean mIsExpanded;
@@ -121,6 +122,8 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
mAutoDismissNotificationDecayDozing = resources.getInteger(
R.integer.heads_up_notification_decay_dozing);
mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
+ mAutoHeadsUpNotificationDecay = resources.getInteger(
+ R.integer.auto_heads_up_notification_decay);
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(this);
mBypassController = bypassController;
@@ -231,7 +234,16 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
@Override
public void onStateChanged(int newState) {
+ boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
+ boolean isKeyguard = newState == StatusBarState.KEYGUARD;
mStatusBarState = newState;
+ if (wasKeyguard && !isKeyguard && mKeysToRemoveWhenLeavingKeyguard.size() != 0) {
+ String[] keys = mKeysToRemoveWhenLeavingKeyguard.toArray(new String[0]);
+ for (String key : keys) {
+ removeAlertEntry(key);
+ }
+ mKeysToRemoveWhenLeavingKeyguard.clear();
+ }
}
@Override
@@ -245,6 +257,15 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
}
}
+ @Override
+ public boolean isEntryAutoHeadsUpped(String key) {
+ HeadsUpEntryPhone headsUpEntryPhone = getHeadsUpEntryPhone(key);
+ if (headsUpEntryPhone == null) {
+ return false;
+ }
+ return headsUpEntryPhone.isAutoHeadsUp();
+ }
+
/**
* Set that we are exiting the headsUp pinned mode, but some notifications might still be
* animating out. This is used to keep the touchable regions in a sane state.
@@ -420,6 +441,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
+ mKeysToRemoveWhenLeavingKeyguard.remove(alertEntry.mEntry.key);
super.onAlertEntryRemoved(alertEntry);
mEntryPool.release((HeadsUpEntryPhone) alertEntry);
}
@@ -479,6 +501,11 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
*/
private boolean extended;
+ /**
+ * Was this entry received while on keyguard
+ */
+ private boolean mIsAutoHeadsUp;
+
@Override
protected boolean isSticky() {
@@ -494,10 +521,12 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
mEntriesToRemoveWhenReorderingAllowed.add(entry);
mVisualStabilityManager.addReorderingAllowedCallback(
HeadsUpManagerPhone.this);
- } else if (!mTrackingHeadsUp) {
- removeAlertEntry(entry.key);
- } else {
+ } else if (mTrackingHeadsUp) {
mEntriesToRemoveAfterExpand.add(entry);
+ } else if (mIsAutoHeadsUp && mStatusBarState == StatusBarState.KEYGUARD) {
+ mKeysToRemoveWhenLeavingKeyguard.add(entry.key);
+ } else {
+ removeAlertEntry(entry.key);
}
};
@@ -506,6 +535,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
@Override
public void updateEntry(boolean updatePostTime) {
+ mIsAutoHeadsUp = mEntry.isAutoHeadsUp();
super.updateEntry(updatePostTime);
if (mEntriesToRemoveAfterExpand.contains(mEntry)) {
@@ -514,6 +544,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
if (mEntriesToRemoveWhenReorderingAllowed.contains(mEntry)) {
mEntriesToRemoveWhenReorderingAllowed.remove(mEntry);
}
+ mKeysToRemoveWhenLeavingKeyguard.remove(mEntry.key);
}
@Override
@@ -548,6 +579,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
super.reset();
mMenuShownPinned = false;
extended = false;
+ mIsAutoHeadsUp = false;
}
private void extendPulse() {
@@ -558,13 +590,35 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
}
@Override
+ public int compareTo(AlertEntry alertEntry) {
+ HeadsUpEntryPhone headsUpEntry = (HeadsUpEntryPhone) alertEntry;
+ boolean autoShown = isAutoHeadsUp();
+ boolean otherAutoShown = headsUpEntry.isAutoHeadsUp();
+ if (autoShown && !otherAutoShown) {
+ return 1;
+ } else if (!autoShown && otherAutoShown) {
+ return -1;
+ }
+ return super.compareTo(alertEntry);
+ }
+
+ @Override
protected long calculateFinishTime() {
return mPostTime + getDecayDuration() + (extended ? mExtensionTime : 0);
}
private int getDecayDuration() {
- return mStatusBarStateController.isDozing() ? mAutoDismissNotificationDecayDozing
- : getRecommendedHeadsUpTimeoutMs();
+ if (mStatusBarStateController.isDozing()) {
+ return mAutoDismissNotificationDecayDozing;
+ } else if (isAutoHeadsUp()) {
+ return getRecommendedHeadsUpTimeoutMs(mAutoHeadsUpNotificationDecay);
+ } else {
+ return getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay);
+ }
+ }
+
+ private boolean isAutoHeadsUp() {
+ return mIsAutoHeadsUp;
}
}
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 cd9772237d59..d2159ca15b24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -248,7 +248,7 @@ public class NotificationIconAreaController implements DarkReceiver,
if (onlyShowCenteredIcon) {
return isCenteredNotificationIcon;
}
- if (hideCenteredIcon && isCenteredNotificationIcon) {
+ if (hideCenteredIcon && isCenteredNotificationIcon && !entry.isRowHeadsUp()) {
return false;
}
if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
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 b87a692b933d..e756d3a997f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -80,6 +80,7 @@ import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -193,6 +194,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationClicker;
@@ -373,6 +375,8 @@ public class StatusBar extends SystemUI implements DemoMode,
KeyguardBypassController mKeyguardBypassController;
@Inject
protected HeadsUpManagerPhone mHeadsUpManager;
+ @Inject
+ BypassHeadsUpNotifier mBypassHeadsUpNotifier;
@Nullable
@Inject
protected KeyguardLiftController mKeyguardLiftController;
@@ -633,6 +637,7 @@ public class StatusBar extends SystemUI implements DemoMode,
mGutsManager = Dependency.get(NotificationGutsManager.class);
mMediaManager = Dependency.get(NotificationMediaManager.class);
mEntryManager = Dependency.get(NotificationEntryManager.class);
+ mBypassHeadsUpNotifier.setUp(mEntryManager);
mNotificationInterruptionStateProvider =
Dependency.get(NotificationInterruptionStateProvider.class);
mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class);
@@ -649,7 +654,7 @@ public class StatusBar extends SystemUI implements DemoMode,
KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
if (sliceProvider != null) {
sliceProvider.initDependencies(mMediaManager, mStatusBarStateController,
- mKeyguardBypassController);
+ mKeyguardBypassController, DozeParameters.getInstance(mContext));
} else {
Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
}
@@ -1154,8 +1159,9 @@ public class StatusBar extends SystemUI implements DemoMode,
private void inflateShelf() {
mNotificationShelf =
- (NotificationShelf) LayoutInflater.from(mContext).inflate(
- R.layout.status_bar_notification_shelf, mStackScroller, false);
+ (NotificationShelf) mInjectionInflater.injectable(
+ LayoutInflater.from(mContext)).inflate(
+ R.layout.status_bar_notification_shelf, mStackScroller, false);
mNotificationShelf.setOnClickListener(mGoToLockedShadeListener);
}
@@ -3629,6 +3635,7 @@ public class StatusBar extends SystemUI implements DemoMode,
notifyHeadsUpGoingToSleep();
dismissVolumeDialog();
mWakeUpCoordinator.setFullyAwake(false);
+ mBypassHeadsUpNotifier.setFullyAwake(false);
mKeyguardBypassController.onStartedGoingToSleep();
}
@@ -3653,6 +3660,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void onFinishedWakingUp() {
mWakeUpCoordinator.setFullyAwake(true);
+ mBypassHeadsUpNotifier.setFullyAwake(true);
mWakeUpCoordinator.setWakingUp(false);
if (mLaunchCameraWhenFinishedWaking) {
mNotificationPanel.launchCamera(false /* animate */, mLastCameraLaunchSource);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 40d5e4dcfd64..b84dc476dd6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -356,6 +356,10 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
public void onDensityOrFontScaleChanged() {
}
+ public boolean isEntryAutoHeadsUpped(String key) {
+ return false;
+ }
+
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
@@ -416,16 +420,17 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
@Override
protected long calculateFinishTime() {
- return mPostTime + getRecommendedHeadsUpTimeoutMs();
+ return mPostTime + getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay);
}
/**
* Get user-preferred or default timeout duration. The larger one will be returned.
* @return milliseconds before auto-dismiss
+ * @param requestedTimeout
*/
- protected int getRecommendedHeadsUpTimeoutMs() {
+ protected int getRecommendedHeadsUpTimeoutMs(int requestedTimeout) {
return mAccessibilityMgr.getRecommendedTimeoutMillis(
- mAutoDismissNotificationDecay,
+ requestedTimeout,
AccessibilityManager.FLAG_CONTENT_CONTROLS
| AccessibilityManager.FLAG_CONTENT_ICONS
| AccessibilityManager.FLAG_CONTENT_TEXT);
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index d521e5534ad4..ede30046d6c3 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -32,6 +32,7 @@ import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.NotificationPanelView;
@@ -138,6 +139,11 @@ public class InjectionInflationController {
QSCarrierGroup createQSCarrierGroup();
/**
+ * Creates the Shelf.
+ */
+ NotificationShelf creatNotificationShelf();
+
+ /**
* Creates the KeyguardClockSwitch.
*/
KeyguardClockSwitch createKeyguardClockSwitch();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index bf067947f2b9..893f3d184acb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -48,6 +48,7 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.wakelock.SettableWakeLock;
@@ -84,6 +85,8 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
private SettableWakeLock mMediaWakeLock;
@Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private DozeParameters mDozeParameters;
private TestableKeyguardSliceProvider mProvider;
private boolean mIsZenMode;
@@ -94,7 +97,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
mProvider = new TestableKeyguardSliceProvider();
mProvider.attachInfo(getContext(), null);
mProvider.initDependencies(mNotificationMediaManager, mStatusBarStateController,
- mKeyguardBypassController);
+ mKeyguardBypassController, mDozeParameters);
SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
}
@@ -130,6 +133,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase {
MediaMetadata metadata = mock(MediaMetadata.class);
when(metadata.getText(any())).thenReturn("metadata");
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
mProvider.onMetadataOrStateChanged(metadata, PlaybackState.STATE_PLAYING);
mProvider.onBindSlice(mProvider.getUri());
verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));