DragDownHelper.java revision 1408eb5a58d669933c701e347fd3498ceab70f3c
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 Jaggiimport com.android.systemui.ExpandHelper; 29ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport com.android.systemui.Gefingerpoken; 30ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggiimport com.android.systemui.R; 31ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi 32ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi/** 33ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand 34ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi * the notification where the drag started. 35ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi */ 36ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggipublic class DragDownHelper implements Gefingerpoken { 37ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi 384222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi private static final float RUBBERBAND_FACTOR_EXPANDABLE = 0.5f; 394222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi private static final float RUBBERBAND_FACTOR_STATIC = 0.15f; 404222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi 414222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi private static final int SPRING_BACK_ANIMATION_LENGTH_MS = 375; 424222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi 43ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private int mMinDragDistance; 44ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private ExpandHelper.Callback mCallback; 45ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private float mInitialTouchX; 46ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private float mInitialTouchY; 47ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private boolean mDraggingDown; 48ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private float mTouchSlop; 49ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private OnDragDownListener mOnDragDownListener; 50ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private View mHost; 51ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private final int[] mTemp2 = new int[2]; 52ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi private boolean mDraggedFarEnough; 534222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi private ExpandableView mStartingChild; 544222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi private Interpolator mInterpolator; 55ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi 56ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi public DragDownHelper(Context context, View host, ExpandHelper.Callback callback, 57ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi OnDragDownListener onDragDownListener) { 58ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mMinDragDistance = context.getResources().getDimensionPixelSize( 59ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi R.dimen.keyguard_drag_down_min_distance); 604222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi mInterpolator = 614222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); 62ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 63ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mCallback = callback; 64ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mOnDragDownListener = onDragDownListener; 65ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mHost = host; 66ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi } 67ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi 68ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi @Override 69ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi public boolean onInterceptTouchEvent(MotionEvent event) { 70ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi final float x = event.getX(); 71ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi final float y = event.getY(); 72ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi 73ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi switch (event.getActionMasked()) { 74ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi case MotionEvent.ACTION_DOWN: 75ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mDraggedFarEnough = false; 76ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mDraggingDown = false; 77ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mStartingChild = null; 78ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mInitialTouchY = y; 79ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mInitialTouchX = x; 80ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi break; 81ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi 82ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi case MotionEvent.ACTION_MOVE: 83ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi final float h = y - mInitialTouchY; 84ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { 85ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mDraggingDown = true; 864222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi captureStartingChild(mInitialTouchX, mInitialTouchY); 87ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mInitialTouchY = y; 88ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi mInitialTouchX = x; 891408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mOnDragDownListener.onTouchSlopExceeded(); 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(); 2051408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek void onTouchSlopExceeded(); 206ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi } 207ecbab3662d4474bbb45477939aaa167eb883212bJorim Jaggi} 208