HeadsUpTouchHelper.java revision 33d4614dfe2b9f89f653210b8609fca4f27c7fe9
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.phone;
18
19import android.content.Context;
20import android.view.MotionEvent;
21import android.view.ViewConfiguration;
22
23import com.android.systemui.Gefingerpoken;
24import com.android.systemui.R;
25import com.android.systemui.statusbar.ExpandableNotificationRow;
26import com.android.systemui.statusbar.ExpandableView;
27import com.android.systemui.statusbar.policy.HeadsUpManager;
28import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
29
30/**
31 * A helper class to handle touches on the heads-up views.
32 */
33public class HeadsUpTouchHelper implements Gefingerpoken {
34
35    private HeadsUpManager mHeadsUpManager;
36    private NotificationStackScrollLayout mStackScroller;
37    private int mTrackingPointer;
38    private float mTouchSlop;
39    private float mInitialTouchX;
40    private float mInitialTouchY;
41    private boolean mTouchingHeadsUpView;
42    private boolean mTrackingHeadsUp;
43    private boolean mCollapseSnoozes;
44    private NotificationPanelView mPanel;
45    private ExpandableNotificationRow mPickedChild;
46
47    public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
48            NotificationStackScrollLayout stackScroller,
49            NotificationPanelView notificationPanelView) {
50        mHeadsUpManager = headsUpManager;
51        mStackScroller = stackScroller;
52        mPanel = notificationPanelView;
53        Context context = stackScroller.getContext();
54        final ViewConfiguration configuration = ViewConfiguration.get(context);
55        mTouchSlop = configuration.getScaledTouchSlop();
56    }
57
58    public boolean isTrackingHeadsUp() {
59        return mTrackingHeadsUp;
60    }
61
62    @Override
63    public boolean onInterceptTouchEvent(MotionEvent event) {
64        if (!mTouchingHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
65            return false;
66        }
67        int pointerIndex = event.findPointerIndex(mTrackingPointer);
68        if (pointerIndex < 0) {
69            pointerIndex = 0;
70            mTrackingPointer = event.getPointerId(pointerIndex);
71        }
72        final float x = event.getX(pointerIndex);
73        final float y = event.getY(pointerIndex);
74        switch (event.getActionMasked()) {
75            case MotionEvent.ACTION_DOWN:
76                mInitialTouchY = y;
77                mInitialTouchX = x;
78                setTrackingHeadsUp(false);
79                ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
80                mTouchingHeadsUpView = false;
81                if (child instanceof ExpandableNotificationRow) {
82                    mPickedChild = (ExpandableNotificationRow) child;
83                    mTouchingHeadsUpView = mPickedChild.isHeadsUp() && mPickedChild.isPinned();
84                }
85                break;
86            case MotionEvent.ACTION_POINTER_UP:
87                final int upPointer = event.getPointerId(event.getActionIndex());
88                if (mTrackingPointer == upPointer) {
89                    // gesture is ongoing, find a new pointer to track
90                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
91                    mTrackingPointer = event.getPointerId(newIndex);
92                    mInitialTouchX = event.getX(newIndex);
93                    mInitialTouchY = event.getY(newIndex);
94                }
95                break;
96
97            case MotionEvent.ACTION_MOVE:
98                final float h = y - mInitialTouchY;
99                if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
100                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
101                    setTrackingHeadsUp(true);
102                    mCollapseSnoozes = h < 0;
103                    mInitialTouchX = x;
104                    mInitialTouchY = y;
105                    int expandedHeight = mPickedChild.getActualHeight();
106                    mHeadsUpManager.unpinAll();
107                    mPanel.setPanelScrimMinFraction((float) expandedHeight
108                            / mPanel.getMaxPanelHeight());
109                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight);
110                    mPanel.clearNotificattonEffects();
111                    return true;
112                }
113                break;
114
115            case MotionEvent.ACTION_CANCEL:
116            case MotionEvent.ACTION_UP:
117                if (mPickedChild != null && mTouchingHeadsUpView) {
118                    // We may swallow this click if the heads up just came in.
119                    if (mHeadsUpManager.shouldSwallowClick(
120                            mPickedChild.getStatusBarNotification().getKey())) {
121                        endMotion();
122                        return true;
123                    }
124                }
125                endMotion();
126                break;
127        }
128        return false;
129    }
130
131    private void setTrackingHeadsUp(boolean tracking) {
132        mTrackingHeadsUp = tracking;
133        mHeadsUpManager.setTrackingHeadsUp(tracking);
134        mPanel.setTrackingHeadsUp(tracking);
135    }
136
137    public void notifyFling(boolean collapse) {
138        if (collapse && mCollapseSnoozes) {
139            mHeadsUpManager.snooze();
140        }
141        mCollapseSnoozes = false;
142    }
143
144    @Override
145    public boolean onTouchEvent(MotionEvent event) {
146        if (!mTrackingHeadsUp) {
147            return false;
148        }
149        switch (event.getActionMasked()) {
150            case MotionEvent.ACTION_UP:
151            case MotionEvent.ACTION_CANCEL:
152                endMotion();
153                setTrackingHeadsUp(false);
154                break;
155        }
156        return true;
157    }
158
159    private void endMotion() {
160        mTrackingPointer = -1;
161        mPickedChild = null;
162        mTouchingHeadsUpView = false;
163    }
164}
165