SystemProperties.getBoolean("debug.enable_remote_input", true);
public static final boolean ENABLE_CHILD_NOTIFICATIONS
= SystemProperties.getBoolean("debug.child_notifs", true);
+ public static final boolean FORCE_REMOTE_INPUT_HISTORY =
+ SystemProperties.getBoolean("debug.force_remoteinput_history", false);
protected static final int MSG_SHOW_RECENT_APPS = 1019;
protected static final int MSG_HIDE_RECENT_APPS = 1020;
protected boolean mVisible;
protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
+ /**
+ * Notifications with keys in this set are not actually around anymore. We kept them around
+ * when they were canceled in response to a remote input interaction. This allows us to show
+ * what you replied and allows you to continue typing into it.
+ */
+ protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
+
// mScreenOnFromKeyguard && mVisible.
private boolean mVisibleToUser;
public void run() {
processForRemoteInput(sbn.getNotification());
String key = sbn.getKey();
+ mKeysKeptForRemoteInput.remove(key);
boolean isUpdate = mNotificationData.get(key) != null;
// In case we don't allow child notifications, we ignore children of
// notifications that have a summary, since we're not going to show them
}
}
- protected View bindVetoButtonClickListener(View row, StatusBarNotification n) {
+ protected View bindVetoButtonClickListener(View row, final StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
final String _pkg = n.getPackageName();
final String _tag = n.getTag();
mContext.getString(R.string.accessibility_notification_dismissed));
try {
mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
+ if (FORCE_REMOTE_INPUT_HISTORY
+ && mKeysKeptForRemoteInput.contains(n.getKey())) {
+ removeNotification(n.getKey(), null);
+ mKeysKeptForRemoteInput.remove(n.getKey());
+ }
} catch (RemoteException ex) {
// system process is dead if we're here.
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.RemoteInputView;
+import android.util.ArraySet;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
*/
public class RemoteInputController {
- private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
+ private final ArrayList<WeakReference<NotificationData.Entry>> mOpen = new ArrayList<>();
+ private final ArraySet<String> mSpinning = new ArraySet<>();
private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
private final HeadsUpManager mHeadsUpManager;
boolean found = pruneWeakThenRemoveAndContains(
entry /* contains */, null /* remove */);
if (!found) {
- mRemoteInputs.add(new WeakReference<>(entry));
+ mOpen.add(new WeakReference<>(entry));
}
apply(entry);
apply(entry);
}
+ public void addSpinning(String key) {
+ mSpinning.add(key);
+ }
+
+ public void removeSpinning(String key) {
+ mSpinning.remove(key);
+ }
+
+ public boolean isSpinning(String key) {
+ return mSpinning.contains(key);
+ }
+
private void apply(NotificationData.Entry entry) {
mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
boolean remoteInputActive = isRemoteInputActive();
*/
public boolean isRemoteInputActive() {
pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */);
- return !mRemoteInputs.isEmpty();
+ return !mOpen.isEmpty();
}
/**
private boolean pruneWeakThenRemoveAndContains(
NotificationData.Entry contains, NotificationData.Entry remove) {
boolean found = false;
- for (int i = mRemoteInputs.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = mRemoteInputs.get(i).get();
+ for (int i = mOpen.size() - 1; i >= 0; i--) {
+ NotificationData.Entry item = mOpen.get(i).get();
if (item == null || item == remove) {
- mRemoteInputs.remove(i);
+ mOpen.remove(i);
} else if (item == contains) {
found = true;
}
mCallbacks.add(callback);
}
+ public void remoteInputSent(NotificationData.Entry entry) {
+ int N = mCallbacks.size();
+ for (int i = 0; i < N; i++) {
+ mCallbacks.get(i).onRemoteInputSent(entry);
+ }
+ }
+
public interface Callback {
- void onRemoteInputActive(boolean active);
+ default void onRemoteInputActive(boolean active) {}
+
+ default void onRemoteInputSent(NotificationData.Entry entry) {}
}
}
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
mStatusBarKeyguardViewManager);
mFingerprintUnlockController.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);
+ }
+ }
+ });
+ }
+
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
mLightStatusBarController.setFingerprintUnlockController(mFingerprintUnlockController);
}
clearCurrentMediaNotification();
updateMediaMetaData(true, true);
}
+ if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)) {
+ Entry entry = mNotificationData.get(key);
+ StatusBarNotification sbn = entry.notification;
+
+ Notification.Builder b = Notification.Builder
+ .recoverBuilder(mContext, sbn.getNotification().clone());
+ CharSequence[] oldHistory = sbn.getNotification().extras
+ .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ CharSequence[] newHistory;
+ if (oldHistory == null) {
+ newHistory = new CharSequence[1];
+ } else {
+ newHistory = new CharSequence[oldHistory.length + 1];
+ for (int i = 0; i < oldHistory.length; i++) {
+ newHistory[i + 1] = oldHistory[i];
+ }
+ }
+ newHistory[0] = String.valueOf(entry.remoteInputText);
+ b.setRemoteInputHistory(newHistory);
+
+ Notification newNotification = b.build();
+
+ // Undo any compatibility view inflation
+ newNotification.contentView = sbn.getNotification().contentView;
+ newNotification.bigContentView = sbn.getNotification().bigContentView;
+ newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
+
+ StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
+ 0, newNotification, sbn.getUser(), sbn.getPostTime());
+
+ updateNotification(newSbn, null);
+ mKeysKeptForRemoteInput.add(entry.key);
+ return;
+ }
if (deferRemoval) {
mLatestRankingMap = ranking;
mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
+ mEntry.remoteInputText = mEditText.getText();
+ mController.addSpinning(mEntry.key);
mController.removeRemoteInput(mEntry);
mEditText.mShowImeOnInputConnection = false;
+ mController.remoteInputSent(mEntry);
try {
mPendingIntent.send(mContext, 0, fillInIntent);
return;
}
mController.removeRemoteInput(mEntry);
+ mController.removeSpinning(mEntry.key);
}
public void setPendingIntent(PendingIntent pendingIntent) {
mEditText.setEnabled(true);
mSendButton.setVisibility(VISIBLE);
mProgressBar.setVisibility(INVISIBLE);
+ mController.removeSpinning(mEntry.key);
updateSendButton();
onDefocus();
}