Integrate Heads-up notifications into the shade

Change-Id: I4ca0fb4e76e7c974490538c168da0564fe97e0ae
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 02b6c19..6fe609e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
@@ -39,8 +40,8 @@
 import android.widget.TextView;
 
 import com.android.keyguard.KeyguardStatusView;
-import com.android.systemui.EventLogTags;
 import com.android.systemui.EventLogConstants;
+import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSContainer;
 import com.android.systemui.qs.QSPanel;
@@ -48,7 +49,9 @@
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -56,7 +59,8 @@
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
         View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
-        KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener {
+        KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
+        HeadsUpManager.OnHeadsUpChangedListener {
 
     private static final boolean DEBUG = false;
 
@@ -81,7 +85,7 @@
     private TextView mClockView;
     private View mReserveNotificationSpace;
     private View mQsNavbarScrim;
-    private View mNotificationContainerParent;
+    private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private NotificationStackScrollLayout mNotificationStackScroller;
     private int mNotificationTopPadding;
     private boolean mAnimateNextTopPaddingChange;
@@ -177,6 +181,16 @@
 
     private float mKeyguardStatusBarAnimateAlpha = 1f;
     private int mOldLayoutDirection;
+    private HeadsUpTouchHelper mHeadsUpTouchHelper = new HeadsUpTouchHelper();
+    private boolean mPinnedHeadsUpExist;
+    private boolean mExpansionIsFromHeadsUp;
+    private int mBottomBarHeight;
+    private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
+        @Override
+        public void run() {
+            notifyBarPanelExpansionChanged();
+        }
+    };
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -201,7 +215,8 @@
         mScrollView.setListener(this);
         mScrollView.setFocusable(false);
         mReserveNotificationSpace = findViewById(R.id.reserve_notification_space);
-        mNotificationContainerParent = findViewById(R.id.notification_container_parent);
+        mNotificationContainerParent = (NotificationsQuickSettingsContainer)
+                findViewById(R.id.notification_container_parent);
         mNotificationStackScroller = (NotificationStackScrollLayout)
                 findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
@@ -317,6 +332,7 @@
         if (mQsSizeChangeAnimator == null) {
             mQsContainer.setHeightOverride(mQsContainer.getDesiredHeight());
         }
+        updateMaxHeadsUpTranslation();
     }
 
     @Override
@@ -493,22 +509,42 @@
     }
 
     @Override
+    protected void flingToHeight(float vel, boolean expand, float target) {
+        if (!expand && mHeadsUpManager.hasPinnedHeadsUp()) {
+            target = mHeadsUpManager.getHighestPinnedHeadsUp();
+        }
+        mHeadsUpTouchHelper.notifyFling(!expand);
+        super.flingToHeight(vel, expand, target);
+    }
+
+    @Override
     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
         if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
             event.getText().add(getKeyguardOrLockScreenString());
             mLastAnnouncementWasQuickSettings = false;
             return true;
         }
-
         return super.dispatchPopulateAccessibilityEventInternal(event);
     }
 
     @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
+    public boolean
+    onInterceptTouchEvent(MotionEvent event) {
         if (mBlockTouches) {
             return false;
         }
         initDownStates(event);
+        if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+            mExpansionIsFromHeadsUp = true;
+            return true;
+        }
+        if (onQsIntercept(event)) {
+            return true;
+        }
+        return super.onInterceptTouchEvent(event);
+    }
+
+    private boolean onQsIntercept(MotionEvent event) {
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -583,7 +619,7 @@
                 mIntercepting = false;
                 break;
         }
-        return super.onInterceptTouchEvent(event);
+        return false;
     }
 
     @Override
@@ -652,6 +688,15 @@
         if (mOnlyAffordanceInThisMotion) {
             return true;
         }
+        mHeadsUpTouchHelper.onTouchEvent(event);
+        if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) {
+            return true;
+        }
+        super.onTouchEvent(event);
+        return true;
+    }
+
+    private boolean handleQSTouch(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
                 && mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded
                 && mQsExpansionEnabled) {
@@ -691,8 +736,7 @@
             // earlier so the state is already up to date when dragging down.
             setListening(true);
         }
-        super.onTouchEvent(event);
-        return true;
+        return false;
     }
 
     private boolean isInQsArea(float x, float y) {
@@ -834,13 +878,13 @@
         setQsExpansion(mQsExpansionHeight);
         flingSettings(!mQsExpansionEnabled && open ? 0f : velocity, open && mQsExpansionEnabled,
                 new Runnable() {
-            @Override
-            public void run() {
-                mStackScrollerOverscrolling = false;
-                mQsExpansionFromOverscroll = false;
-                updateQsState();
-            }
-        });
+                    @Override
+                    public void run() {
+                        mStackScrollerOverscrolling = false;
+                        mQsExpansionFromOverscroll = false;
+                        updateQsState();
+                    }
+                });
     }
 
     private void onQsExpansionStarted() {
@@ -869,6 +913,7 @@
             mNotificationStackScroller.setInterceptDelegateEnabled(expanded);
             mStatusBar.setQsExpanded(expanded);
             mQsPanel.setExpanded(expanded);
+            mNotificationContainerParent.setQsExpanded(expanded);
         }
     }
 
@@ -1056,7 +1101,7 @@
             mKeyguardBottomArea.animate()
                     .alpha(0f)
                     .setStartDelay(mStatusBar.getKeyguardFadingAwayDelay())
-                    .setDuration(mStatusBar.getKeyguardFadingAwayDuration()/2)
+                    .setDuration(mStatusBar.getKeyguardFadingAwayDuration() / 2)
                     .setInterpolator(PhoneStatusBar.ALPHA_OUT)
                     .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
                     .start();
@@ -1402,6 +1447,8 @@
         updateHeader();
         updateUnlockIcon();
         updateNotificationTranslucency();
+        mHeadsUpManager.setIsExpanded(!isShadeCollapsed());
+        mNotificationStackScroller.setShadeExpanded(!isShadeCollapsed());
         if (DEBUG) {
             invalidate();
         }
@@ -1544,7 +1591,14 @@
                 return mExpandedHeight / HEADER_RUBBERBAND_FACTOR - mQsMinExpansionHeight;
             }
         }
-        return Math.min(0, mNotificationStackScroller.getTranslationY()) / HEADER_RUBBERBAND_FACTOR;
+        float paddingOffset = mNotificationStackScroller.getPaddingOffset();
+        float translation = paddingOffset / HEADER_RUBBERBAND_FACTOR;
+        if (mHeadsUpManager.hasPinnedHeadsUp() || mExpansionIsFromHeadsUp) {
+            translation = mNotificationStackScroller.getTopPadding()
+                    + mNotificationStackScroller.getPaddingOffset()
+                    - mNotificationTopPadding - mQsMinExpansionHeight;
+        }
+        return Math.min(0, translation);
     }
 
     /**
@@ -1605,8 +1659,10 @@
     protected void onExpandingFinished() {
         super.onExpandingFinished();
         mNotificationStackScroller.onExpansionStopped();
+        mHeadsUpManager.onExpandingFinished();
         mIsExpanding = false;
         mScrollYOverride = -1;
+        // TODO: look into whether this is still correct
         if (mExpandedHeight == 0f) {
             setListening(false);
         } else {
@@ -1614,6 +1670,8 @@
         }
         mQsExpandImmediate = false;
         mTwoFingerQsExpandPossible = false;
+        mExpansionIsFromHeadsUp = false;
+        mNotificationStackScroller.setTrackingHeadsUp(mHeadsUpTouchHelper.isTrackingHeadsUp());
     }
 
     private void setListening(boolean listening) {
@@ -1709,6 +1767,17 @@
     }
 
     @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+        mBottomBarHeight = insets.getSystemWindowInsetBottom();
+        updateMaxHeadsUpTranslation();
+        return insets;
+    }
+
+    private void updateMaxHeadsUpTranslation() {
+        mNotificationStackScroller.setMaxHeadsUpTranslation(getHeight() - mBottomBarHeight);
+    }
+
+    @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
         if (layoutDirection != mOldLayoutDirection) {
             mAfforanceHelper.onRtlPropertiesChanged();
@@ -2066,4 +2135,44 @@
                     mNotificationStackScroller.getTopPadding(), p);
         }
     }
+
+    @Override
+    public void OnPinnedHeadsUpExistChanged(final boolean exist, boolean changeImmediatly) {
+        if (exist != mPinnedHeadsUpExist) {
+            mPinnedHeadsUpExist = exist;
+            if (!exist) {
+                mNotificationStackScroller.performOnAnimationFinished(
+                        mHeadsUpExistenceChangedRunnable);
+            } else {
+                mHeadsUpExistenceChangedRunnable.run();
+            }
+        }
+    }
+
+    @Override
+    public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+        // TODO: figure out the conditions when not to generate an animation
+        mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
+        if (isShadeCollapsed()) {
+            setExpandedHeight(mHeadsUpManager.getHighestPinnedHeadsUp());
+        }
+    }
+
+    private boolean isShadeCollapsed() {
+        // TODO: handle this cleaner
+        return mHeader.getTranslationY() + mHeader.getCollapsedHeight() <= 0;
+    }
+
+    @Override
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        super.setHeadsUpManager(headsUpManager);
+        mHeadsUpTouchHelper.bind(headsUpManager, mNotificationStackScroller, this);
+    }
+
+    public void setTrackingHeadsUp(boolean tracking) {
+        if (tracking) {
+            // otherwise we update the state when the expansion is finished
+            mNotificationStackScroller.setTrackingHeadsUp(true);
+        }
+    }
 }