Push back notification means snooze the package.

Bug: 18451923
Change-Id: I90160f9d14cde12a5bc03e46e64eedf770149a90
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1152eea..0457b34 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -139,6 +139,10 @@
     <!-- milliseconds before the heads up notification auto-dismisses. -->
     <integer name="heads_up_notification_decay">10000</integer>
 
+    <!-- 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>
+
     <!-- milliseconds before the heads up notification accepts touches. -->
     <integer name="heads_up_sensitivity_delay">700</integer>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 5613a6e..a2796c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -600,6 +600,7 @@
         }
 
         mCurrentUserId = ActivityManager.getCurrentUser();
+        setHeadsUpUser(mCurrentUserId);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_SWITCHED);
@@ -662,7 +663,13 @@
     }
 
     public void userSwitched(int newUserId) {
-        // should be overridden
+        setHeadsUpUser(newUserId);
+    }
+
+    private void setHeadsUpUser(int newUserId) {
+        if (mHeadsUpNotificationView != null) {
+            mHeadsUpNotificationView.setUser(newUserId);
+        }
     }
 
     public boolean isHeadsUp(String key) {
@@ -2065,6 +2072,10 @@
             return false;
         }
 
+        if (mHeadsUpNotificationView.isSnoozed(sbn.getPackageName())) {
+            return false;
+        }
+
         Notification notification = sbn.getNotification();
         // some predicates to make the boolean logic legible
         boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
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 9a1ac49..ba2bcb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3181,6 +3181,7 @@
 
     @Override
     public void userSwitched(int newUserId) {
+        super.userSwitched(newUserId);
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
         animateCollapsePanels();
         updateNotifications();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 11ff272..8933d00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -18,8 +18,13 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.graphics.Outline;
 import android.graphics.Rect;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -39,17 +44,23 @@
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.phone.PhoneStatusBar;
 
+import java.util.ArrayList;
+
 public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
         ViewTreeObserver.OnComputeInternalInsetsListener {
     private static final String TAG = "HeadsUpNotificationView";
     private static final boolean DEBUG = false;
     private static final boolean SPEW = DEBUG;
+    private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
 
     Rect mTmpRect = new Rect();
     int[] mTmpTwoArray = new int[2];
 
     private final int mTouchSensitivityDelay;
     private final float mMaxAlpha = 1f;
+    private final ArrayMap<String, Long> mSnoozedPackages;
+    private final int mDefaultSnoozeLengthMs;
+
     private SwipeHelper mSwipeHelper;
     private EdgeSwipeHelper mEdgeSwipeHelper;
 
@@ -57,8 +68,11 @@
 
     private long mStartTouchTime;
     private ViewGroup mContentHolder;
+    private int mSnoozeLengthMs;
+    private ContentObserver mSettingsObserver;
 
     private NotificationData.Entry mHeadsUp;
+    private int mUser;
 
     public HeadsUpNotificationView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -66,8 +80,12 @@
 
     public HeadsUpNotificationView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mTouchSensitivityDelay = getResources().getInteger(R.integer.heads_up_sensitivity_delay);
+        Resources resources = context.getResources();
+        mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
         if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
+        mSnoozedPackages = new ArrayMap<>();
+        mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+        mSnoozeLengthMs = mDefaultSnoozeLengthMs;
     }
 
     public void updateResources() {
@@ -115,7 +133,7 @@
             sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
 
             mSwipeHelper.snapChild(mContentHolder, 1f);
-            mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay;
+            mStartTouchTime = SystemClock.elapsedRealtime() + mTouchSensitivityDelay;
 
             mHeadsUp.setInterruption();
 
@@ -166,6 +184,29 @@
         mHeadsUp = null;
     }
 
+    public boolean isSnoozed(String packageName) {
+        final String key = snoozeKey(packageName, mUser);
+        Long snoozedUntil = mSnoozedPackages.get(key);
+        if (snoozedUntil != null) {
+            if (snoozedUntil > SystemClock.elapsedRealtime()) {
+                if (DEBUG) Log.v(TAG, key + " snoozed");
+                return true;
+            }
+            mSnoozedPackages.remove(packageName);
+        }
+        return false;
+    }
+
+    private void snooze() {
+        mSnoozedPackages.put(snoozeKey(mHeadsUp.notification.getPackageName(), mUser),
+                SystemClock.elapsedRealtime() + mSnoozeLengthMs);
+        releaseAndClose();
+    }
+
+    private static String snoozeKey(String packageName, int user) {
+        return user + "," + packageName;
+    }
+
     public void releaseAndClose() {
         release();
         mBar.scheduleHeadsUpClose();
@@ -209,6 +250,24 @@
         mContentHolder = (ViewGroup) findViewById(R.id.content_holder);
         mContentHolder.setOutlineProvider(CONTENT_HOLDER_OUTLINE_PROVIDER);
 
+        mSnoozeLengthMs = Settings.Global.getInt(mContext.getContentResolver(),
+                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
+        mSettingsObserver = new ContentObserver(getHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                final int packageSnoozeLengthMs = Settings.Global.getInt(
+                        mContext.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
+                if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
+                    mSnoozeLengthMs = packageSnoozeLengthMs;
+                    if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
+                }
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
+                mSettingsObserver);
+        if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
+
         if (mHeadsUp != null) {
             // whoops, we're on already!
             showNotification(mHeadsUp);
@@ -218,9 +277,14 @@
     }
 
     @Override
+    protected void onDetachedFromWindow() {
+        mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (DEBUG) Log.v(TAG, "onInterceptTouchEvent()");
-        if (System.currentTimeMillis() < mStartTouchTime) {
+        if (SystemClock.elapsedRealtime() < mStartTouchTime) {
             return true;
         }
         return mEdgeSwipeHelper.onInterceptTouchEvent(ev)
@@ -246,7 +310,7 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        if (System.currentTimeMillis() < mStartTouchTime) {
+        if (SystemClock.elapsedRealtime() < mStartTouchTime) {
             return false;
         }
         mBar.resetHeadsUpDecayTimer();
@@ -370,6 +434,10 @@
         return mHeadsUp == null ? null : mHeadsUp.notification.getKey();
     }
 
+    public void setUser(int user) {
+        mUser = user;
+    }
+
     private class EdgeSwipeHelper implements Gefingerpoken {
         private static final boolean DEBUG_EDGE_SWIPE = false;
         private final float mTouchSlop;
@@ -397,7 +465,7 @@
                     final float daX = Math.abs(ev.getX() - mFirstX);
                     final float daY = Math.abs(dY);
                     if (!mConsuming && daX < daY && daY > mTouchSlop) {
-                        releaseAndClose();
+                        snooze();
                         if (dY > 0) {
                             if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found an open");
                             mBar.animateExpandNotificationsPanel();