From 18d099a8fb3eb4332ce7f4cd43b1f00035e16bb4 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Thu, 19 May 2016 15:28:18 -0700 Subject: [PATCH] Keep notifications with active remote inputs Fixes a bad interaction when the app cancels the notification into which the user is typing. This happens for example when the user initiates direct reply from the lock screen and the app is currently open underneath the lockscreen. Also cleans up the code that clears notification effects by moving it into the appropriate place (without functional changes). Fixes: 28745456 Change-Id: I69e076642e93930d41cfb06f2a48836aea34d92d --- .../systemui/statusbar/BaseStatusBar.java | 12 ++- .../statusbar/phone/PhoneStatusBar.java | 73 ++++++++++++++++--- .../stack/NotificationStackScrollLayout.java | 10 --- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index e66dc74c7958b..63933d4124006 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -189,6 +189,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected boolean mVisible; protected ArraySet mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>(); + protected ArraySet mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>(); /** * Notifications with keys in this set are not actually around anymore. We kept them around @@ -968,7 +969,7 @@ public abstract class BaseStatusBar extends SystemUI implements return vetoButton; } - private void performRemoveNotification(StatusBarNotification n, boolean removeView) { + protected void performRemoveNotification(StatusBarNotification n, boolean removeView) { final String pkg = n.getPackageName(); final String tag = n.getTag(); final int id = n.getId(); @@ -980,6 +981,9 @@ public abstract class BaseStatusBar extends SystemUI implements mKeysKeptForRemoteInput.remove(n.getKey()); removeView = true; } + if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) { + removeView = true; + } if (removeView) { removeNotification(n.getKey(), null); } @@ -2085,8 +2089,7 @@ public abstract class BaseStatusBar extends SystemUI implements /** * The LEDs are turned off when the notification panel is shown, even just a little bit. - * See also NotificationStackScrollLayout.setIsExpanded() for another place where we - * attempt to do this. + * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this. */ protected void handleVisibleToUserChanged(boolean visibleToUser) { try { @@ -2326,8 +2329,9 @@ public abstract class BaseStatusBar extends SystemUI implements Entry entry = mNotificationData.get(key); if (entry == null) { return; - } else if (mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) { + } else { mHeadsUpEntriesToRemoveOnSwitch.remove(entry); + mRemoteInputEntriesToRemoveOnCollapse.remove(entry); } Notification n = notification.getNotification(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index e3ce1e27f3e68..35fec51f2fadb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -263,6 +263,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, * This affects the status bar UI. */ private static final boolean FREEFORM_WINDOW_MANAGEMENT; + /** + * How long to wait before auto-dismissing a notification that was kept for remote input, and + * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel + * these given that they technically don't exist anymore. We wait a bit in case the app issues + * an update. + */ + private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200; + static { boolean onlyCoreApps; boolean freeformWindowManagement; @@ -1181,16 +1189,24 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mIconPolicy.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); mRemoteInputController.addCallback(mStatusBarKeyguardViewManager); - if (FORCE_REMOTE_INPUT_HISTORY) { - mRemoteInputController.addCallback(new RemoteInputController.Callback() { - @Override - public void onRemoteInputSent(Entry entry) { - if (mKeysKeptForRemoteInput.contains(entry.key)) { - removeNotification(entry.key, null); - } + mRemoteInputController.addCallback(new RemoteInputController.Callback() { + @Override + public void onRemoteInputSent(Entry entry) { + if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(entry.key)) { + removeNotification(entry.key, null); + } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) { + // We're currently holding onto this notification, but from the apps point of + // view it is already canceled, so we'll need to cancel it on the apps behalf + // after sending - unless the app posts an update in the mean time, so wait a + // bit. + mHandler.postDelayed(() -> { + if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) { + removeNotification(entry.key, null); + } + }, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY); } - }); - } + } + }); mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController); @@ -1507,6 +1523,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return; } Entry entry = mNotificationData.get(key); + + if (entry != null && mRemoteInputController.isRemoteInputActive(entry)) { + mLatestRankingMap = ranking; + mRemoteInputEntriesToRemoveOnCollapse.add(entry); + return; + } + if (entry != null && entry.row != null) { entry.row.setRemoved(true); } @@ -1567,6 +1590,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } } + @Override + protected void performRemoveNotification(StatusBarNotification n, boolean removeView) { + Entry entry = mNotificationData.get(n.getKey()); + if (mRemoteInputController.isRemoteInputActive(entry)) { + mRemoteInputController.removeRemoteInput(entry); + } + super.performRemoveNotification(n, removeView); + } + @Override protected void refreshLayout(int layoutDirection) { if (mNavigationBarView != null) { @@ -2464,6 +2496,26 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void setPanelExpanded(boolean isExpanded) { mStatusBarWindowManager.setPanelExpanded(isExpanded); + + if (isExpanded && getBarState() != StatusBarState.KEYGUARD) { + if (DEBUG) { + Log.v(TAG, "clearing notification effects from setPanelExpanded"); + } + clearNotificationEffects(); + } + + if (!isExpanded) { + removeRemoteInputEntriesKeptUntilCollapsed(); + } + } + + private void removeRemoteInputEntriesKeptUntilCollapsed() { + for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) { + Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i); + mRemoteInputController.removeRemoteInput(entry); + removeNotification(entry.key, mLatestRankingMap); + } + mRemoteInputEntriesToRemoveOnCollapse.clear(); } public void onScreenTurnedOff() { @@ -4206,6 +4258,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, || (state == StatusBarState.SHADE && isGoingToNotificationShade()))) { clearNotificationEffects(); } + if (state == StatusBarState.KEYGUARD) { + removeRemoteInputEntriesKeptUntilCollapsed(); + } mState = state; mGroupManager.setStatusBarState(state); mFalsingManager.setStatusBarState(state); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 70d4aec0f8df0..65fd2fcacd04c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -2927,16 +2927,6 @@ public class NotificationStackScrollLayout extends ViewGroup if (changed) { if (!mIsExpanded) { mGroupManager.collapseAllGroups(); - } else { - // XXX: HACK: we should not be clearing notification effects from way down here. - // But at the moment we don't have a reliable way to know when the window is - // actually exposed to the air, so - if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) { - if (DEBUG) { - Log.v(TAG, "clearing notification effects from scroller"); - } - mPhoneStatusBar.clearNotificationEffects(); - } } updateNotificationAnimationStates(); updateChronometers();