1b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek/* 2b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Copyright (C) 2015 The Android Open Source Project 3b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * 4b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Licensed under the Apache License, Version 2.0 (the "License"); 5b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * you may not use this file except in compliance with the License. 6b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * You may obtain a copy of the License at 7b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * 8b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * http://www.apache.org/licenses/LICENSE-2.0 9b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * 10b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Unless required by applicable law or agreed to in writing, software 11b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * distributed under the License is distributed on an "AS IS" BASIS, 12b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * See the License for the specific language governing permissions and 14b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * limitations under the License 15b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 16b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 17b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekpackage com.android.systemui.statusbar.phone; 18b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 19b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.content.Context; 20b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.view.MotionEvent; 21b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.view.ViewConfiguration; 22b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 23b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.Gefingerpoken; 24b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow; 25b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.ExpandableView; 26b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.policy.HeadsUpManager; 27b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 28b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 29b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek/** 30684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A helper class to handle touches on the heads-up views. 31b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 32b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekpublic class HeadsUpTouchHelper implements Gefingerpoken { 33b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 34b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HeadsUpManager mHeadsUpManager; 35b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private NotificationStackScrollLayout mStackScroller; 36b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private int mTrackingPointer; 37b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private float mTouchSlop; 38b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private float mInitialTouchX; 39b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private float mInitialTouchY; 40684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private boolean mTouchingHeadsUpView; 41b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mTrackingHeadsUp; 42b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mCollapseSnoozes; 43b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private NotificationPanelView mPanel; 44b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private ExpandableNotificationRow mPickedChild; 45b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 46684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public HeadsUpTouchHelper(HeadsUpManager headsUpManager, 47684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek NotificationStackScrollLayout stackScroller, 48684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek NotificationPanelView notificationPanelView) { 49684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mHeadsUpManager = headsUpManager; 50684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mStackScroller = stackScroller; 51684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mPanel = notificationPanelView; 52684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek Context context = stackScroller.getContext(); 53684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek final ViewConfiguration configuration = ViewConfiguration.get(context); 54684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mTouchSlop = configuration.getScaledTouchSlop(); 55684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek } 56684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 57b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean isTrackingHeadsUp() { 58b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mTrackingHeadsUp; 59b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 60b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 61b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 62b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean onInterceptTouchEvent(MotionEvent event) { 63684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (!mTouchingHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) { 64b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 65b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 66b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek int pointerIndex = event.findPointerIndex(mTrackingPointer); 67b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (pointerIndex < 0) { 68b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pointerIndex = 0; 69b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTrackingPointer = event.getPointerId(pointerIndex); 70b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 71b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final float x = event.getX(pointerIndex); 72b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final float y = event.getY(pointerIndex); 73b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek switch (event.getActionMasked()) { 74b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek case MotionEvent.ACTION_DOWN: 75b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mInitialTouchY = y; 76b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mInitialTouchX = x; 77b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek setTrackingHeadsUp(false); 7835b7771fcbe57574d812adbc98acbba96ba47ae2Selim Cinek ExpandableView child = mStackScroller.getChildAtRawPosition(x, y); 79684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mTouchingHeadsUpView = false; 80b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (child instanceof ExpandableNotificationRow) { 81b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mPickedChild = (ExpandableNotificationRow) child; 823776fe007550451bb237c705d333247eab2a291cSelim Cinek mTouchingHeadsUpView = !mStackScroller.isExpanded() 833776fe007550451bb237c705d333247eab2a291cSelim Cinek && mPickedChild.isHeadsUp() && mPickedChild.isPinned(); 84b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 85b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek break; 86b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek case MotionEvent.ACTION_POINTER_UP: 87b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final int upPointer = event.getPointerId(event.getActionIndex()); 88b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mTrackingPointer == upPointer) { 89b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // gesture is ongoing, find a new pointer to track 90b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; 91b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTrackingPointer = event.getPointerId(newIndex); 92b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mInitialTouchX = event.getX(newIndex); 93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mInitialTouchY = event.getY(newIndex); 94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek break; 96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 97b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek case MotionEvent.ACTION_MOVE: 98b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final float h = y - mInitialTouchY; 99131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop 100131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek && Math.abs(h) > Math.abs(x - mInitialTouchX)) { 101b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek setTrackingHeadsUp(true); 102b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mCollapseSnoozes = h < 0; 103b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mInitialTouchX = x; 104b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mInitialTouchY = y; 105b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek int expandedHeight = mPickedChild.getActualHeight(); 1063d395c69fa67732ceb2c045d03ea81efa3710afaSelim Cinek mPanel.setPanelScrimMinFraction((float) expandedHeight 1073d395c69fa67732ceb2c045d03ea81efa3710afaSelim Cinek / mPanel.getMaxPanelHeight()); 10833d4614dfe2b9f89f653210b8609fca4f27c7fe9Selim Cinek mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight); 109def35a86530200958384191d43d321dbcda16e2aSelim Cinek // This call needs to be after the expansion start otherwise we will get a 110def35a86530200958384191d43d321dbcda16e2aSelim Cinek // flicker of one frame as it's not expanded yet. 111def35a86530200958384191d43d321dbcda16e2aSelim Cinek mHeadsUpManager.unpinAll(); 1129528f43e481e6daf07e1b66aa0740982b3b08903Xiaohui Chen mPanel.clearNotificationEffects(); 113b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 114b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 115b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek break; 116b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 117b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek case MotionEvent.ACTION_CANCEL: 118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek case MotionEvent.ACTION_UP: 119684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (mPickedChild != null && mTouchingHeadsUpView) { 120684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek // We may swallow this click if the heads up just came in. 121b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mHeadsUpManager.shouldSwallowClick( 122b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mPickedChild.getStatusBarNotification().getKey())) { 123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek endMotion(); 124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek endMotion(); 128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek break; 129b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 130b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 131b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 132b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 133b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private void setTrackingHeadsUp(boolean tracking) { 134b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTrackingHeadsUp = tracking; 135b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHeadsUpManager.setTrackingHeadsUp(tracking); 136b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mPanel.setTrackingHeadsUp(tracking); 137b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 138b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 139b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void notifyFling(boolean collapse) { 140b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (collapse && mCollapseSnoozes) { 141b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHeadsUpManager.snooze(); 142b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 143b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mCollapseSnoozes = false; 144b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 145b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 146b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 147b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean onTouchEvent(MotionEvent event) { 148b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (!mTrackingHeadsUp) { 149b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 150b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 151b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek switch (event.getActionMasked()) { 152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek case MotionEvent.ACTION_UP: 153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek case MotionEvent.ACTION_CANCEL: 154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek endMotion(); 155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek setTrackingHeadsUp(false); 156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek break; 157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private void endMotion() { 162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTrackingPointer = -1; 163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mPickedChild = null; 164684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mTouchingHeadsUpView = false; 165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek} 167