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