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
This commit is contained in:
Adrian Roos
2016-05-19 15:28:18 -07:00
parent c21f84dddb
commit 18d099a8fb
3 changed files with 72 additions and 23 deletions

View File

@@ -189,6 +189,7 @@ public abstract class BaseStatusBar extends SystemUI implements
protected boolean mVisible;
protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
protected ArraySet<Entry> 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();

View File

@@ -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);

View File

@@ -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();