HeadsUpTouchHelper.java revision 31aada4ef4ff37f8cdc110b196c06918841647c7
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    private final int mNotificationsTopPadding;
47
48    public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
49            NotificationStackScrollLayout stackScroller,
50            NotificationPanelView notificationPanelView) {
51        mHeadsUpManager = headsUpManager;
52        mStackScroller = stackScroller;
53        mPanel = notificationPanelView;
54        Context context = stackScroller.getContext();
55        final ViewConfiguration configuration = ViewConfiguration.get(context);
56        mTouchSlop = configuration.getScaledTouchSlop();
57        mNotificationsTopPadding = context.getResources()
58                .getDimensionPixelSize(R.dimen.notifications_top_padding);
59    }
60
61    public boolean isTrackingHeadsUp() {
62        return mTrackingHeadsUp;
63    }
64
65    @Override
66    public boolean onInterceptTouchEvent(MotionEvent event) {
67        if (!mTouchingHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
68            return false;
69        }
70        int pointerIndex = event.findPointerIndex(mTrackingPointer);
71        if (pointerIndex < 0) {
72            pointerIndex = 0;
73            mTrackingPointer = event.getPointerId(pointerIndex);
74        }
75        final float x = event.getX(pointerIndex);
76        final float y = event.getY(pointerIndex);
77        switch (event.getActionMasked()) {
78            case MotionEvent.ACTION_DOWN:
79                mInitialTouchY = y;
80                mInitialTouchX = x;
81                setTrackingHeadsUp(false);
82                ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
83                if (child == null && y < mNotificationsTopPadding) {
84                    // We should also allow drags from the margin above the heads up
85                    child = mStackScroller.getChildAtRawPosition(x, y + mNotificationsTopPadding);
86                }
87                mTouchingHeadsUpView = false;
88                if (child instanceof ExpandableNotificationRow) {
89                    mPickedChild = (ExpandableNotificationRow) child;
90                    mTouchingHeadsUpView = mPickedChild.isHeadsUp() && mPickedChild.isPinned();
91                }
92                break;
93            case MotionEvent.ACTION_POINTER_UP:
94                final int upPointer = event.getPointerId(event.getActionIndex());
95                if (mTrackingPointer == upPointer) {
96                    // gesture is ongoing, find a new pointer to track
97                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
98                    mTrackingPointer = event.getPointerId(newIndex);
99                    mInitialTouchX = event.getX(newIndex);
100                    mInitialTouchY = event.getY(newIndex);
101                }
102                break;
103
104            case MotionEvent.ACTION_MOVE:
105                final float h = y - mInitialTouchY;
106                if (mTouchingHeadsUpView && Math.abs(h) > mTouchSlop
107                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
108                    setTrackingHeadsUp(true);
109                    mCollapseSnoozes = h < 0;
110                    mInitialTouchX = x;
111                    mInitialTouchY = y;
112                    int expandedHeight = mPickedChild.getActualHeight();
113                    mHeadsUpManager.unpinAll();
114                    mPanel.setPanelScrimMinFraction((float) expandedHeight
115                            / mPanel.getMaxPanelHeight());
116                    mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight
117                            + mNotificationsTopPadding);
118                    mPanel.clearNotificattonEffects();
119                    return true;
120                }
121                break;
122
123            case MotionEvent.ACTION_CANCEL:
124            case MotionEvent.ACTION_UP:
125                if (mPickedChild != null && mTouchingHeadsUpView) {
126                    // We may swallow this click if the heads up just came in.
127                    if (mHeadsUpManager.shouldSwallowClick(
128                            mPickedChild.getStatusBarNotification().getKey())) {
129                        endMotion();
130                        return true;
131                    }
132                }
133                endMotion();
134                break;
135        }
136        return false;
137    }
138
139    private void setTrackingHeadsUp(boolean tracking) {
140        mTrackingHeadsUp = tracking;
141        mHeadsUpManager.setTrackingHeadsUp(tracking);
142        mPanel.setTrackingHeadsUp(tracking);
143    }
144
145    public void notifyFling(boolean collapse) {
146        if (collapse && mCollapseSnoozes) {
147            mHeadsUpManager.snooze();
148        }
149        mCollapseSnoozes = false;
150    }
151
152    @Override
153    public boolean onTouchEvent(MotionEvent event) {
154        if (!mTrackingHeadsUp) {
155            return false;
156        }
157        switch (event.getActionMasked()) {
158            case MotionEvent.ACTION_UP:
159            case MotionEvent.ACTION_CANCEL:
160                endMotion();
161                setTrackingHeadsUp(false);
162                break;
163        }
164        return true;
165    }
166
167    private void endMotion() {
168        mTrackingPointer = -1;
169        mPickedChild = null;
170        mTouchingHeadsUpView = false;
171    }
172}
173