Close guts/gear after a timeout and if there's been no interaction

On the lock screen the inline controls and gear behind the
notifications may be activated by accident. This CL alters them
to close after a certain period of time has occurred without
interaction.

Bug: 27335311
Change-Id: Ib3b0575571076a5912e23c7d2da5209e9a523fc8
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index c459da9..29b6908 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -91,6 +91,7 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -100,6 +101,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener;
 import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -114,12 +116,12 @@
 import java.util.Locale;
 
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
-import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
-        ExpandableNotificationRow.OnExpandClickListener {
+        ExpandableNotificationRow.OnExpandClickListener,
+        OnGutsClosedListener {
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
@@ -1010,6 +1012,7 @@
         PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
         row.setTag(sbn.getPackageName());
         final NotificationGuts guts = row.getGuts();
+        guts.setClosedListener(this);
         final String pkg = sbn.getPackageName();
         String appname = pkg;
         Drawable pkgicon = null;
@@ -1037,6 +1040,7 @@
             settingsButton.setOnClickListener(new View.OnClickListener() {
                 public void onClick(View v) {
                     MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
+                    guts.resetFalsingCheck();
                     startAppNotificationSettingsActivity(pkg, appUidF);
                 }
             });
@@ -1069,6 +1073,7 @@
 
     private void saveImportanceCloseControls(StatusBarNotification sbn,
             ExpandableNotificationRow row, NotificationGuts guts, View done) {
+        guts.resetFalsingCheck();
         guts.saveImportance(sbn);
 
         int[] rowLocation = new int[2];
@@ -1137,7 +1142,8 @@
                             }
                         });
                         a.start();
-                        guts.setExposed(true);
+                        guts.setExposed(true /* exposed */,
+                                mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
                         row.closeRemoteInput();
                         mStackScroller.onHeightChanged(null, true /* needsAnimation */);
                         mNotificationGutsExposed = guts;
@@ -1165,31 +1171,7 @@
 
     public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
         if (mNotificationGutsExposed != null) {
-            final NotificationGuts v = mNotificationGutsExposed;
-            mNotificationGutsExposed = null;
-
-            if (v.getWindowToken() == null) return;
-            if (x == -1 || y == -1) {
-                x = (v.getLeft() + v.getRight()) / 2;
-                y = (v.getTop() + v.getHeight() / 2);
-            }
-            final double horz = Math.max(v.getWidth() - x, x);
-            final double vert = Math.max(v.getHeight() - y, y);
-            final float r = (float) Math.hypot(horz, vert);
-            final Animator a = ViewAnimationUtils.createCircularReveal(v,
-                    x, y, r, 0);
-            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-            a.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    v.setVisibility(View.GONE);
-                }
-            });
-            a.start();
-            v.setExposed(false);
-            mStackScroller.onHeightChanged(null, true /* needsAnimation */);
+            mNotificationGutsExposed.closeControls(x, y, true /* notify */);
         }
         if (resetGear) {
             mStackScroller.resetExposedGearView(animate, true /* force */);
@@ -1197,6 +1179,12 @@
     }
 
     @Override
+    public void onGutsClosed(NotificationGuts guts) {
+        mStackScroller.onHeightChanged(null, true /* needsAnimation */);
+        mNotificationGutsExposed = null;
+    }
+
+    @Override
     public void showRecentApps(boolean triggeredFromAltTab) {
         int msg = MSG_SHOW_RECENT_APPS;
         mHandler.removeMessages(msg);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 3e0542f..3c464d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -16,28 +16,35 @@
 
 package com.android.systemui.statusbar;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.app.INotificationManager;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewAnimationUtils;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RadioButton;
+import android.widget.RadioGroup;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 import com.android.systemui.tuner.TunerService;
 
 /**
@@ -46,6 +53,8 @@
 public class NotificationGuts extends LinearLayout implements TunerService.Tunable {
     public static final String SHOW_SLIDER = "show_importance_slider";
 
+    private static final long CLOSE_GUTS_DELAY = 8000;
+
     private Drawable mBackground;
     private int mClipTopAmount;
     private int mActualHeight;
@@ -59,10 +68,35 @@
     private RadioButton mSilent;
     private RadioButton mReset;
 
+    private Handler mHandler;
+    private Runnable mFalsingCheck;
+    private boolean mNeedsFalsingProtection;
+    private OnGutsClosedListener mListener;
+
+    public interface OnGutsClosedListener {
+        public void onGutsClosed(NotificationGuts guts);
+    }
+
     public NotificationGuts(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(false);
         TunerService.get(mContext).addTunable(this, SHOW_SLIDER);
+        mHandler = new Handler();
+        mFalsingCheck = new Runnable() {
+            @Override
+            public void run() {
+                if (mNeedsFalsingProtection && mExposed) {
+                    closeControls(-1 /* x */, -1 /* y */, true /* notify */);
+                }
+            }
+        };
+    }
+
+    public void resetFalsingCheck() {
+        mHandler.removeCallbacks(mFalsingCheck);
+        if (mNeedsFalsingProtection && mExposed) {
+            mHandler.postDelayed(mFalsingCheck, CLOSE_GUTS_DELAY);
+        }
     }
 
     @Override
@@ -172,6 +206,13 @@
 
     private void bindToggles(final View importanceButtons, final int importance,
             final boolean systemApp) {
+        ((RadioGroup) importanceButtons).setOnCheckedChangeListener(
+                new RadioGroup.OnCheckedChangeListener() {
+                    @Override
+                    public void onCheckedChanged(RadioGroup group, int checkedId) {
+                        resetFalsingCheck();
+                    }
+                });
         mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
         mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
         mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance);
@@ -205,6 +246,7 @@
         mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                resetFalsingCheck();
                 if (progress < minProgress) {
                     seekBar.setProgress(minProgress);
                     progress = minProgress;
@@ -217,7 +259,7 @@
 
             @Override
             public void onStartTrackingTouch(SeekBar seekBar) {
-                // no-op
+                resetFalsingCheck();
             }
 
             @Override
@@ -263,6 +305,38 @@
         mSeekBar.setProgress(mStartingImportance);
     }
 
+    public void closeControls(int x, int y, boolean notify) {
+        if (getWindowToken() == null) {
+            if (notify && mListener != null) {
+                mListener.onGutsClosed(this);
+            }
+            return;
+        }
+        if (x == -1 || y == -1) {
+            x = (getLeft() + getRight()) / 2;
+            y = (getTop() + getHeight() / 2);
+        }
+        final double horz = Math.max(getWidth() - x, x);
+        final double vert = Math.max(getHeight() - y, y);
+        final float r = (float) Math.hypot(horz, vert);
+        final Animator a = ViewAnimationUtils.createCircularReveal(this,
+                x, y, r, 0);
+        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                setVisibility(View.GONE);
+            }
+        });
+        a.start();
+        setExposed(false, mNeedsFalsingProtection);
+        if (notify && mListener != null) {
+            mListener.onGutsClosed(this);
+        }
+    }
+
     public void setActualHeight(int actualHeight) {
         mActualHeight = actualHeight;
         invalidate();
@@ -284,8 +358,18 @@
         return false;
     }
 
-    public void setExposed(boolean exposed) {
+    public void setClosedListener(OnGutsClosedListener listener) {
+        mListener = listener;
+    }
+
+    public void setExposed(boolean exposed, boolean needsFalsingProtection) {
         mExposed = exposed;
+        mNeedsFalsingProtection = needsFalsingProtection;
+        if (mExposed && mNeedsFalsingProtection) {
+            resetFalsingCheck();
+        } else {
+            mHandler.removeCallbacks(mFalsingCheck);
+        }
     }
 
     public boolean areGutsExposed() {
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 6bb60f7..cc646f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3456,8 +3456,10 @@
     }
 
     private class NotificationSwipeHelper extends SwipeHelper {
-        private static final long GEAR_SHOW_DELAY = 60;
+        private static final long SHOW_GEAR_DELAY = 60;
+        private static final long COVER_GEAR_DELAY = 4000;
         private CheckForDrag mCheckForDrag;
+        private Runnable mFalsingCheck;
         private Handler mHandler;
         private boolean mGearSnappedTo;
         private boolean mGearSnappedOnLeft;
@@ -3465,6 +3467,12 @@
         public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
             super(swipeDirection, callback, context);
             mHandler = new Handler();
+            mFalsingCheck = new Runnable() {
+                @Override
+                public void run() {
+                    resetExposedGearView(true /* animate */, true /* force */);
+                }
+            };
         }
 
         @Override
@@ -3479,6 +3487,7 @@
             }
             mCheckForDrag = null;
             mCurrIconRow = null;
+            mHandler.removeCallbacks(mFalsingCheck);
 
             // Slide back any notifications that might be showing a gear
             resetExposedGearView(true /* animate */, false /* force */);
@@ -3492,6 +3501,8 @@
 
         @Override
         public void onMoveUpdate(View view, float translation, float delta) {
+            mHandler.removeCallbacks(mFalsingCheck);
+
             if (mCurrIconRow != null) {
                 mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
 
@@ -3617,6 +3628,12 @@
                 setSnappedToGear(true);
             }
             onDragCancelled(animView);
+
+            // If we're on the lockscreen we want to false this.
+            if (mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+                mHandler.removeCallbacks(mFalsingCheck);
+                mHandler.postDelayed(mFalsingCheck, COVER_GEAR_DELAY);
+            }
             super.snapChild(animView, target, velocity);
         }
 
@@ -3733,7 +3750,7 @@
         private void checkForDrag() {
             if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
                 mCheckForDrag = new CheckForDrag();
-                mHandler.postDelayed(mCheckForDrag, GEAR_SHOW_DELAY);
+                mHandler.postDelayed(mCheckForDrag, SHOW_GEAR_DELAY);
             }
         }