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;
268032708409dbc7dfd072823b185cd5118368309fSelim Cinekimport com.android.systemui.statusbar.NotificationData;
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
344e30e763473859bbe1755e446af08fc7325267b4yoshiki iguchi    private HeadsUpManagerPhone 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
464e30e763473859bbe1755e446af08fc7325267b4yoshiki iguchi    public HeadsUpTouchHelper(HeadsUpManagerPhone 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();
848032708409dbc7dfd072823b185cd5118368309fSelim Cinek                } else if (child == null && !mStackScroller.isExpanded()) {
858032708409dbc7dfd072823b185cd5118368309fSelim Cinek                    // We might touch above the visible heads up child, but then we still would
868032708409dbc7dfd072823b185cd5118368309fSelim Cinek                    // like to capture it.
878032708409dbc7dfd072823b185cd5118368309fSelim Cinek                    NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
888032708409dbc7dfd072823b185cd5118368309fSelim Cinek                    if (topEntry != null && topEntry.row.isPinned()) {
898032708409dbc7dfd072823b185cd5118368309fSelim Cinek                        mPickedChild = topEntry.row;
908032708409dbc7dfd072823b185cd5118368309fSelim Cinek                        mTouchingHeadsUpView = true;
918032708409dbc7dfd072823b185cd5118368309fSelim Cinek                    }
92b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_POINTER_UP:
95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final int upPointer = event.getPointerId(event.getActionIndex());
96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (mTrackingPointer == upPointer) {
97b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    // gesture is ongoing, find a new pointer to track
98b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
99b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mTrackingPointer = event.getPointerId(newIndex);
100b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchX = event.getX(newIndex);
101b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchY = event.getY(newIndex);
102b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
103b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
104b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
105b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_MOVE:
106b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final float h = y - mInitialTouchY;
107131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek                if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
108131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
109b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    setTrackingHeadsUp(true);
110b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mCollapseSnoozes = h < 0;
111b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchX = x;
112b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchY = y;
113aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                    int startHeight = (int) (mPickedChild.getActualHeight()
114aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                                                + mPickedChild.getTranslationY());
115aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                    mPanel.setPanelScrimMinFraction((float) startHeight
1163d395c69fa67732ceb2c045d03ea81efa3710afaSelim Cinek                            / mPanel.getMaxPanelHeight());
117aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                    mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
118621933fd7bd276c536e6c5c2dd8f5d411283006cChris Wren                    mPanel.startExpandingFromPeek();
119def35a86530200958384191d43d321dbcda16e2aSelim Cinek                    // This call needs to be after the expansion start otherwise we will get a
120def35a86530200958384191d43d321dbcda16e2aSelim Cinek                    // flicker of one frame as it's not expanded yet.
121def35a86530200958384191d43d321dbcda16e2aSelim Cinek                    mHeadsUpManager.unpinAll();
1229528f43e481e6daf07e1b66aa0740982b3b08903Xiaohui Chen                    mPanel.clearNotificationEffects();
12331f708c4dc0d2ee2db1bc23a16d761275ccedf8cSelim Cinek                    endMotion();
124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    return true;
125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_CANCEL:
129b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_UP:
130684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                if (mPickedChild != null && mTouchingHeadsUpView) {
131684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                    // We may swallow this click if the heads up just came in.
132b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    if (mHeadsUpManager.shouldSwallowClick(
133b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                            mPickedChild.getStatusBarNotification().getKey())) {
134b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        endMotion();
135b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        return true;
136b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    }
137b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
138b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                endMotion();
139b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
140b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
141b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
142b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
143b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
144b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void setTrackingHeadsUp(boolean tracking) {
145b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingHeadsUp = tracking;
146b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mHeadsUpManager.setTrackingHeadsUp(tracking);
147aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek        mPanel.setTrackedHeadsUp(tracking ? mPickedChild : null);
148b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
149b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
150b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void notifyFling(boolean collapse) {
151b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (collapse && mCollapseSnoozes) {
152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mHeadsUpManager.snooze();
153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mCollapseSnoozes = false;
155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    @Override
158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean onTouchEvent(MotionEvent event) {
159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (!mTrackingHeadsUp) {
160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return false;
161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        switch (event.getActionMasked()) {
163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_UP:
164b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_CANCEL:
165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                endMotion();
166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                setTrackingHeadsUp(false);
167b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
168b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
169b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return true;
170b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
171b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
172b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void endMotion() {
173b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingPointer = -1;
174b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mPickedChild = null;
175684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mTouchingHeadsUpView = false;
176b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
177b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek}
178