DragDownHelper.java revision d552d9d8e964c102e6832610be46cf2c041e8829
1ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi/*
2ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * Copyright (C) 2014 The Android Open Source Project
3ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi *
4ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * Licensed under the Apache License, Version 2.0 (the "License");
5ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * you may not use this file except in compliance with the License.
6ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * You may obtain a copy of the License at
7ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi *
8ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi *      http://www.apache.org/licenses/LICENSE-2.0
9ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi *
10ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * Unless required by applicable law or agreed to in writing, software
11ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * distributed under the License is distributed on an "AS IS" BASIS,
12ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * See the License for the specific language governing permissions and
14ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * limitations under the License
15ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi */
16ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
17ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggipackage com.android.systemui.statusbar;
18ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
194222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggiimport android.animation.Animator;
204222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggiimport android.animation.AnimatorListenerAdapter;
214222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggiimport android.animation.ObjectAnimator;
22ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport android.content.Context;
23ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport android.view.MotionEvent;
24ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport android.view.View;
25ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport android.view.ViewConfiguration;
264222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggiimport android.view.animation.AnimationUtils;
274222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggiimport android.view.animation.Interpolator;
28ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
29ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport com.android.systemui.ExpandHelper;
30ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport com.android.systemui.Gefingerpoken;
31ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport com.android.systemui.R;
32ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
33ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi/**
34ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand
35ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * the notification where the drag started.
36ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi */
37ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggipublic class DragDownHelper implements Gefingerpoken {
38ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
394222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private static final float RUBBERBAND_FACTOR_EXPANDABLE = 0.5f;
404222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private static final float RUBBERBAND_FACTOR_STATIC = 0.15f;
414222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi
424222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private static final int SPRING_BACK_ANIMATION_LENGTH_MS = 375;
434222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi
44ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private int mMinDragDistance;
45ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private ExpandHelper.Callback mCallback;
46ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private float mInitialTouchX;
47ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private float mInitialTouchY;
48ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private boolean mDraggingDown;
49ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private float mTouchSlop;
50ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private OnDragDownListener mOnDragDownListener;
51ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private View mHost;
52ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private final int[] mTemp2 = new int[2];
53ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    private boolean mDraggedFarEnough;
544222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private ExpandableView mStartingChild;
554222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private Interpolator mInterpolator;
56ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
57ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    public DragDownHelper(Context context, View host, ExpandHelper.Callback callback,
58ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi            OnDragDownListener onDragDownListener) {
59ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        mMinDragDistance = context.getResources().getDimensionPixelSize(
60ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                R.dimen.keyguard_drag_down_min_distance);
614222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        mInterpolator =
624222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
63ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
64ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        mCallback = callback;
65ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        mOnDragDownListener = onDragDownListener;
66ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        mHost = host;
67ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    }
68ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
69ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    @Override
70ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    public boolean onInterceptTouchEvent(MotionEvent event) {
71ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        final float x = event.getX();
72ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        final float y = event.getY();
73ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
74ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        switch (event.getActionMasked()) {
75ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi            case MotionEvent.ACTION_DOWN:
76ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                mDraggedFarEnough = false;
77ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                mDraggingDown = false;
78ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                mStartingChild = null;
79ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                mInitialTouchY = y;
80ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                mInitialTouchX = x;
81ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                break;
82ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
83ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi            case MotionEvent.ACTION_MOVE:
84ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                final float h = y - mInitialTouchY;
85ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) {
86ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    mDraggingDown = true;
874222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                    captureStartingChild(mInitialTouchX, mInitialTouchY);
88ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    mInitialTouchY = y;
89ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    mInitialTouchX = x;
90ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    return true;
91ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                }
92ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                break;
93ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        }
94ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        return false;
95ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    }
96ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
97ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    @Override
98ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    public boolean onTouchEvent(MotionEvent event) {
99ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        if (!mDraggingDown) {
100ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi            return false;
101ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        }
102ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        final float x = event.getX();
103ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        final float y = event.getY();
104ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
105ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        switch (event.getActionMasked()) {
106ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi            case MotionEvent.ACTION_MOVE:
107ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                final float h = y - mInitialTouchY;
1084222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                captureStartingChild(mInitialTouchX, mInitialTouchY);
1094222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                if (mStartingChild != null) {
1104222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                    handleExpansion(h, mStartingChild);
111ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                }
112ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                if (h > mMinDragDistance) {
113ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    if (!mDraggedFarEnough) {
114ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                        mDraggedFarEnough = true;
115ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                        mOnDragDownListener.onThresholdReached();
116ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    }
117ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                } else {
118ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    if (mDraggedFarEnough) {
119ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                        mDraggedFarEnough = false;
120d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                        mOnDragDownListener.onDragDownReset();
121ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    }
122ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                }
123ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                return true;
124ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi            case MotionEvent.ACTION_UP:
125ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                if (mDraggedFarEnough) {
1264222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                    if (mStartingChild != null) {
1274222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                        mCallback.setUserLockedChild(mStartingChild, false);
1284222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                    }
129ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    mOnDragDownListener.onDraggedDown(mStartingChild);
13098fb09c2b2dbf57803a8737ee7b73cf167721312Jorim Jaggi                    mDraggingDown = false;
131ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                } else {
132ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    stopDragging();
133ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                    return false;
134ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                }
135ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                break;
136ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi            case MotionEvent.ACTION_CANCEL:
137ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                stopDragging();
138ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi                return false;
139ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        }
140ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        return false;
141ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    }
142ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
1434222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private void captureStartingChild(float x, float y) {
1444222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        if (mStartingChild == null) {
1454222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            mStartingChild = findView(x, y);
1464222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            if (mStartingChild != null) {
1474222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                mCallback.setUserLockedChild(mStartingChild, true);
1484222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            }
1494222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        }
150ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    }
151ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
1524222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private void handleExpansion(float heightDelta, ExpandableView child) {
1534222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        if (heightDelta < 0) {
1544222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            heightDelta = 0;
155ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        }
1564222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        boolean expandable = child.isContentExpandable();
1574222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        float rubberbandFactor = expandable
1584222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                ? RUBBERBAND_FACTOR_EXPANDABLE
1594222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                : RUBBERBAND_FACTOR_STATIC;
1604222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        float rubberband = heightDelta * rubberbandFactor;
1614222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        if (expandable && (rubberband + child.getMinHeight()) > child.getMaxHeight()) {
1624222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            float overshoot = (rubberband + child.getMinHeight()) - child.getMaxHeight();
1634222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);
1644222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            rubberband -= overshoot;
165ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        }
1664222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        child.setActualHeight((int) (child.getMinHeight() + rubberband));
1674222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    }
1684222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi
1694222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private void cancelExpansion(final ExpandableView child) {
1704222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        if (child.getActualHeight() == child.getMinHeight()) {
1714222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            return;
1724222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        }
1734222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight",
1744222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                child.getActualHeight(), child.getMinHeight());
1754222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        anim.setInterpolator(mInterpolator);
1764222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS);
1774222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        anim.addListener(new AnimatorListenerAdapter() {
1784222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            @Override
1794222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            public void onAnimationEnd(Animator animation) {
1804222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi                mCallback.setUserLockedChild(child, false);
1814222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            }
1824222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        });
1834222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        anim.start();
1844222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    }
1854222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi
1864222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private void stopDragging() {
1874222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        if (mStartingChild != null) {
1884222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi            cancelExpansion(mStartingChild);
1894222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        }
1904222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        mDraggingDown = false;
191d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        mOnDragDownListener.onDragDownReset();
192ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    }
193ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
1944222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private ExpandableView findView(float x, float y) {
195ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        mHost.getLocationOnScreen(mTemp2);
196ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        x += mTemp2[0];
197ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        y += mTemp2[1];
198ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        return mCallback.getChildAtRawPosition(x, y);
199ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    }
200ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi
201ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    public interface OnDragDownListener {
202ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        void onDraggedDown(View startingChild);
203d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        void onDragDownReset();
204ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi        void onThresholdReached();
205ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi    }
206ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi}
207