HeadsUpTouchHelper.java revision 3d395c69fa67732ceb2c045d03ea81efa3710afa
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;
2479d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinekimport com.android.systemui.R;
25b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow;
26b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.ExpandableView;
27b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.policy.HeadsUpManager;
28b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
29b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
30b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek/**
31684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A helper class to handle touches on the heads-up views.
32b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */
33b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekpublic class HeadsUpTouchHelper implements Gefingerpoken {
34b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
35b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private HeadsUpManager mHeadsUpManager;
36b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private NotificationStackScrollLayout mStackScroller;
37b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private int mTrackingPointer;
38b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private float mTouchSlop;
39b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private float mInitialTouchX;
40b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private float mInitialTouchY;
41684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private boolean mTouchingHeadsUpView;
42b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mTrackingHeadsUp;
43b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mCollapseSnoozes;
44b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private NotificationPanelView mPanel;
45b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private ExpandableNotificationRow mPickedChild;
4679d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek    private final int mNotificationsTopPadding;
47b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
48684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
49684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            NotificationStackScrollLayout stackScroller,
50684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            NotificationPanelView notificationPanelView) {
51684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mHeadsUpManager = headsUpManager;
52684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mStackScroller = stackScroller;
53684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mPanel = notificationPanelView;
54684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        Context context = stackScroller.getContext();
55684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        final ViewConfiguration configuration = ViewConfiguration.get(context);
56684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mTouchSlop = configuration.getScaledTouchSlop();
5779d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek        mNotificationsTopPadding = context.getResources()
5879d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                .getDimensionPixelSize(R.dimen.notifications_top_padding);
59684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    }
60684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek
61b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean isTrackingHeadsUp() {
62b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mTrackingHeadsUp;
63b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
64b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
65b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    @Override
66b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean onInterceptTouchEvent(MotionEvent event) {
67684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        if (!mTouchingHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
68b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return false;
69b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
70b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        int pointerIndex = event.findPointerIndex(mTrackingPointer);
71b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (pointerIndex < 0) {
72b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            pointerIndex = 0;
73b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mTrackingPointer = event.getPointerId(pointerIndex);
74b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
75b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        final float x = event.getX(pointerIndex);
76b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        final float y = event.getY(pointerIndex);
77b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        switch (event.getActionMasked()) {
78b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_DOWN:
79b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mInitialTouchY = y;
80b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mInitialTouchX = x;
81b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                setTrackingHeadsUp(false);
8235b7771fcbe57574d812adbc98acbba96ba47ae2Selim Cinek                ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
8379d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                if (child == null && y < mNotificationsTopPadding) {
8479d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                    // We should also allow drags from the margin above the heads up
8579d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                    child = mStackScroller.getChildAtRawPosition(x, y + mNotificationsTopPadding);
8679d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                }
87684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                mTouchingHeadsUpView = false;
88b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (child instanceof ExpandableNotificationRow) {
89b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mPickedChild = (ExpandableNotificationRow) child;
90684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                    mTouchingHeadsUpView = mPickedChild.isHeadsUp() && mPickedChild.isPinned();
91b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
92b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_POINTER_UP:
94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final int upPointer = event.getPointerId(event.getActionIndex());
95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (mTrackingPointer == upPointer) {
96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    // gesture is ongoing, find a new pointer to track
97b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
98b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mTrackingPointer = event.getPointerId(newIndex);
99b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchX = event.getX(newIndex);
100b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchY = event.getY(newIndex);
101b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
102b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
103b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
104b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_MOVE:
105b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final float h = y - mInitialTouchY;
106131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek                if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
107131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
108b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    setTrackingHeadsUp(true);
109b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mCollapseSnoozes = h < 0;
110b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchX = x;
111b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mInitialTouchY = y;
112b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    int expandedHeight = mPickedChild.getActualHeight();
1133d395c69fa67732ceb2c045d03ea81efa3710afaSelim Cinek                    mPanel.setPanelScrimMinFraction((float) expandedHeight
1143d395c69fa67732ceb2c045d03ea81efa3710afaSelim Cinek                            / mPanel.getMaxPanelHeight());
11579d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight
11679d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                            + mNotificationsTopPadding);
117131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek                    mHeadsUpManager.unpinAll();
118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    return true;
119b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
120b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
121b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
122b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_CANCEL:
123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_UP:
124684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                if (mPickedChild != null && mTouchingHeadsUpView) {
125684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                    // We may swallow this click if the heads up just came in.
126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    if (mHeadsUpManager.shouldSwallowClick(
127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                            mPickedChild.getStatusBarNotification().getKey())) {
128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        endMotion();
129b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        return true;
130b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    }
131b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
132b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                endMotion();
133b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
134b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
135b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
136b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
137b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
138b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void setTrackingHeadsUp(boolean tracking) {
139b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingHeadsUp = tracking;
140b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mHeadsUpManager.setTrackingHeadsUp(tracking);
141b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mPanel.setTrackingHeadsUp(tracking);
142b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
143b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
144b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void notifyFling(boolean collapse) {
145b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (collapse && mCollapseSnoozes) {
146b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mHeadsUpManager.snooze();
147b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
148b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mCollapseSnoozes = false;
149b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
150b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
151b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    @Override
152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean onTouchEvent(MotionEvent event) {
153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (!mTrackingHeadsUp) {
154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return false;
155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        switch (event.getActionMasked()) {
157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_UP:
158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            case MotionEvent.ACTION_CANCEL:
159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                endMotion();
160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                setTrackingHeadsUp(false);
161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                break;
162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return true;
164b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void endMotion() {
167b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingPointer = -1;
168b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mPickedChild = null;
169684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mTouchingHeadsUpView = false;
170b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
171b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek}
172