HeadsUpTouchHelper.java revision aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40b
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.stack.NotificationStackScrollLayout;
27b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
28b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek/**
29684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A helper class to handle touches on the heads-up views.
30b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */
31b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekpublic class HeadsUpTouchHelper implements Gefingerpoken {
32b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
334e30e763473859bbe1755e446af08fc7325267b4yoshiki iguchi    private HeadsUpManagerPhone mHeadsUpManager;
34b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private NotificationStackScrollLayout mStackScroller;
35b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private int mTrackingPointer;
36b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private float mTouchSlop;
37b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private float mInitialTouchX;
38b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private float mInitialTouchY;
39684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private boolean mTouchingHeadsUpView;
40b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mTrackingHeadsUp;
41b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mCollapseSnoozes;
42b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private NotificationPanelView mPanel;
43b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private ExpandableNotificationRow mPickedChild;
44b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
454e30e763473859bbe1755e446af08fc7325267b4yoshiki iguchi    public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
46684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            NotificationStackScrollLayout stackScroller,
47684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            NotificationPanelView notificationPanelView) {
48684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mHeadsUpManager = headsUpManager;
49684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mStackScroller = stackScroller;
50684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mPanel = notificationPanelView;
51684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        Context context = stackScroller.getContext();
52684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        final ViewConfiguration configuration = ViewConfiguration.get(context);
53684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mTouchSlop = configuration.getScaledTouchSlop();
54684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    }
55684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek
56b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean isTrackingHeadsUp() {
57b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mTrackingHeadsUp;
58b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
59b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
60b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    @Override
61b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean onInterceptTouchEvent(MotionEvent event) {
62684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        if (!mTouchingHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
63b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return false;
64b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
65b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        int pointerIndex = event.findPointerIndex(mTrackingPointer);
66b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (pointerIndex < 0) {
67b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            pointerIndex = 0;
68b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mTrackingPointer = event.getPointerId(pointerIndex);
69b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
70b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        final float x = event.getX(pointerIndex);
71b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        final float y = event.getY(pointerIndex);
72b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        switch (event.getActionMasked()) {
73b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_DOWN:
74b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mInitialTouchY = y;
75b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mInitialTouchX = x;
76b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                setTrackingHeadsUp(false);
7735b7771fcbe57574d812adbc98acbba96ba47ae2Selim Cinek                ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
78684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                mTouchingHeadsUpView = false;
79b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (child instanceof ExpandableNotificationRow) {
80b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mPickedChild = (ExpandableNotificationRow) child;
813776fe007550451bb237c705d333247eab2a291cSelim Cinek                    mTouchingHeadsUpView = !mStackScroller.isExpanded()
823776fe007550451bb237c705d333247eab2a291cSelim Cinek                            && mPickedChild.isHeadsUp() && mPickedChild.isPinned();
83b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
84b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
85b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_POINTER_UP:
86b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final int upPointer = event.getPointerId(event.getActionIndex());
87b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (mTrackingPointer == upPointer) {
88b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    // gesture is ongoing, find a new pointer to track
89b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
90b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mTrackingPointer = event.getPointerId(newIndex);
91b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchX = event.getX(newIndex);
92b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchY = event.getY(newIndex);
93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_MOVE:
97b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final float h = y - mInitialTouchY;
98131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek                if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
99131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
100b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    setTrackingHeadsUp(true);
101b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mCollapseSnoozes = h < 0;
102b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchX = x;
103b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchY = y;
104aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                    int startHeight = (int) (mPickedChild.getActualHeight()
105aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                                                + mPickedChild.getTranslationY());
106aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                    mPanel.setPanelScrimMinFraction((float) startHeight
1073d395c69fa67732ceb2c045d03ea81efa3710afaSelim Cinek                            / mPanel.getMaxPanelHeight());
108aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek                    mPanel.startExpandMotion(x, y, true /* startTracking */, startHeight);
109621933fd7bd276c536e6c5c2dd8f5d411283006cChris Wren                    mPanel.startExpandingFromPeek();
110def35a86530200958384191d43d321dbcda16e2aSelim Cinek                    // This call needs to be after the expansion start otherwise we will get a
111def35a86530200958384191d43d321dbcda16e2aSelim Cinek                    // flicker of one frame as it's not expanded yet.
112def35a86530200958384191d43d321dbcda16e2aSelim Cinek                    mHeadsUpManager.unpinAll();
1139528f43e481e6daf07e1b66aa0740982b3b08903Xiaohui Chen                    mPanel.clearNotificationEffects();
11431f708c4dc0d2ee2db1bc23a16d761275ccedf8cSelim Cinek                    endMotion();
115b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    return true;
116b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
117b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
119b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_CANCEL:
120b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_UP:
121684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                if (mPickedChild != null && mTouchingHeadsUpView) {
122684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                    // We may swallow this click if the heads up just came in.
123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    if (mHeadsUpManager.shouldSwallowClick(
124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                            mPickedChild.getStatusBarNotification().getKey())) {
125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        endMotion();
126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        return true;
127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    }
128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
129b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                endMotion();
130b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
131b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
132b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
133b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
134b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
135b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void setTrackingHeadsUp(boolean tracking) {
136b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingHeadsUp = tracking;
137b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mHeadsUpManager.setTrackingHeadsUp(tracking);
138aa9db1f34fe8b4a2d143c1379ec6c0c304bbd40bSelim Cinek        mPanel.setTrackedHeadsUp(tracking ? mPickedChild : null);
139b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
140b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
141b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void notifyFling(boolean collapse) {
142b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (collapse && mCollapseSnoozes) {
143b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mHeadsUpManager.snooze();
144b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
145b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mCollapseSnoozes = false;
146b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
147b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
148b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    @Override
149b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean onTouchEvent(MotionEvent event) {
150b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (!mTrackingHeadsUp) {
151b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return false;
152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        switch (event.getActionMasked()) {
154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_UP:
155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_CANCEL:
156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                endMotion();
157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                setTrackingHeadsUp(false);
158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return true;
161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void endMotion() {
164b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingPointer = -1;
165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mPickedChild = null;
166684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mTouchingHeadsUpView = false;
167b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
168b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek}
169