diff options
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)); |