DragDownHelper.java revision ecbab3662d4474bbb45477939aaa167eb883212b
1/*
2 * Copyright (C) 2014 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;
18
19import android.content.Context;
20import android.util.ArraySet;
21import android.view.MotionEvent;
22import android.view.View;
23import android.view.ViewConfiguration;
24
25import com.android.systemui.ExpandHelper;
26import com.android.systemui.Gefingerpoken;
27import com.android.systemui.R;
28
29import java.util.HashSet;
30
31/**
32 * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
33 * the notification where the drag started.
34 */
35public class DragDownHelper implements Gefingerpoken {
36
37    private int mMinDragDistance;
38    private ExpandHelper.Callback mCallback;
39    private float mInitialTouchX;
40    private float mInitialTouchY;
41    private boolean mDraggingDown;
42    private float mTouchSlop;
43    private OnDragDownListener mOnDragDownListener;
44    private View mHost;
45    private final int[] mTemp2 = new int[2];
46    private final ArraySet<View> mHoveredChildren = new ArraySet<View>();
47    private boolean mDraggedFarEnough;
48    private View mStartingChild;
49
50    public DragDownHelper(Context context, View host, ExpandHelper.Callback callback,
51            OnDragDownListener onDragDownListener) {
52        mMinDragDistance = context.getResources().getDimensionPixelSize(
53                R.dimen.keyguard_drag_down_min_distance);
54        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
55        mCallback = callback;
56        mOnDragDownListener = onDragDownListener;
57        mHost = host;
58    }
59
60    @Override
61    public boolean onInterceptTouchEvent(MotionEvent event) {
62        final float x = event.getX();
63        final float y = event.getY();
64
65        switch (event.getActionMasked()) {
66            case MotionEvent.ACTION_DOWN:
67                mHoveredChildren.clear();
68                mDraggedFarEnough = false;
69                mDraggingDown = false;
70                mStartingChild = null;
71                mInitialTouchY = y;
72                mInitialTouchX = x;
73                break;
74
75            case MotionEvent.ACTION_MOVE:
76                final float h = y - mInitialTouchY;
77                if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
78                    mDraggingDown = true;
79                    mInitialTouchY = y;
80                    mInitialTouchX = x;
81                    return true;
82                }
83                break;
84        }
85        return false;
86    }
87
88    @Override
89    public boolean onTouchEvent(MotionEvent event) {
90        if (!mDraggingDown) {
91            return false;
92        }
93        final float x = event.getX();
94        final float y = event.getY();
95
96        switch (event.getActionMasked()) {
97            case MotionEvent.ACTION_MOVE:
98                final float h = y - mInitialTouchY;
99                View child = findView(x, y);
100                if (child != null) {
101                    hoverChild(findView(x, y));
102                }
103                if (h > mMinDragDistance) {
104                    if (!mDraggedFarEnough) {
105                        mDraggedFarEnough = true;
106                        mOnDragDownListener.onThresholdReached();
107                    }
108                } else {
109                    if (mDraggedFarEnough) {
110                        mDraggedFarEnough = false;
111                        mOnDragDownListener.onReset();
112                    }
113                }
114                return true;
115            case MotionEvent.ACTION_UP:
116                if (mDraggedFarEnough) {
117                    mOnDragDownListener.onDraggedDown(mStartingChild);
118                } else {
119                    stopDragging();
120                    return false;
121                }
122                break;
123            case MotionEvent.ACTION_CANCEL:
124                stopDragging();
125                return false;
126        }
127        return false;
128    }
129
130    private void stopDragging() {
131        mDraggingDown = false;
132        mOnDragDownListener.onReset();
133    }
134
135    private void hoverChild(View child) {
136        if (mHoveredChildren.isEmpty()) {
137            mStartingChild = child;
138        }
139        if (!mHoveredChildren.contains(child)) {
140            mOnDragDownListener.onHover(child);
141            mHoveredChildren.add(child);
142        }
143    }
144
145    private View findView(float x, float y) {
146        mHost.getLocationOnScreen(mTemp2);
147        x += mTemp2[0];
148        y += mTemp2[1];
149        return mCallback.getChildAtRawPosition(x, y);
150    }
151
152    public interface OnDragDownListener {
153        void onHover(View child);
154        void onDraggedDown(View startingChild);
155        void onReset();
156        void onThresholdReached();
157    }
158}
159