DragDownHelper.java revision 98fb09c2b2dbf57803a8737ee7b73cf167721312
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.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.content.Context; 23import android.view.MotionEvent; 24import android.view.View; 25import android.view.ViewConfiguration; 26import android.view.animation.AnimationUtils; 27import android.view.animation.Interpolator; 28 29import com.android.systemui.ExpandHelper; 30import com.android.systemui.Gefingerpoken; 31import com.android.systemui.R; 32 33/** 34 * A utility class to enable the downward swipe on the lockscreen to go to the full shade and expand 35 * the notification where the drag started. 36 */ 37public class DragDownHelper implements Gefingerpoken { 38 39 private static final float RUBBERBAND_FACTOR_EXPANDABLE = 0.5f; 40 private static final float RUBBERBAND_FACTOR_STATIC = 0.15f; 41 42 private static final int SPRING_BACK_ANIMATION_LENGTH_MS = 375; 43 44 private int mMinDragDistance; 45 private ExpandHelper.Callback mCallback; 46 private float mInitialTouchX; 47 private float mInitialTouchY; 48 private boolean mDraggingDown; 49 private float mTouchSlop; 50 private OnDragDownListener mOnDragDownListener; 51 private View mHost; 52 private final int[] mTemp2 = new int[2]; 53 private boolean mDraggedFarEnough; 54 private ExpandableView mStartingChild; 55 private Interpolator mInterpolator; 56 57 public DragDownHelper(Context context, View host, ExpandHelper.Callback callback, 58 OnDragDownListener onDragDownListener) { 59 mMinDragDistance = context.getResources().getDimensionPixelSize( 60 R.dimen.keyguard_drag_down_min_distance); 61 mInterpolator = 62 AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in); 63 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 64 mCallback = callback; 65 mOnDragDownListener = onDragDownListener; 66 mHost = host; 67 } 68 69 @Override 70 public boolean onInterceptTouchEvent(MotionEvent event) { 71 final float x = event.getX(); 72 final float y = event.getY(); 73 74 switch (event.getActionMasked()) { 75 case MotionEvent.ACTION_DOWN: 76 mDraggedFarEnough = false; 77 mDraggingDown = false; 78 mStartingChild = null; 79 mInitialTouchY = y; 80 mInitialTouchX = x; 81 break; 82 83 case MotionEvent.ACTION_MOVE: 84 final float h = y - mInitialTouchY; 85 if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { 86 mDraggingDown = true; 87 captureStartingChild(mInitialTouchX, mInitialTouchY); 88 mInitialTouchY = y; 89 mInitialTouchX = x; 90 return true; 91 } 92 break; 93 } 94 return false; 95 } 96 97 @Override 98 public boolean onTouchEvent(MotionEvent event) { 99 if (!mDraggingDown) { 100 return false; 101 } 102 final float x = event.getX(); 103 final float y = event.getY(); 104 105 switch (event.getActionMasked()) { 106 case MotionEvent.ACTION_MOVE: 107 final float h = y - mInitialTouchY; 108 captureStartingChild(mInitialTouchX, mInitialTouchY); 109 if (mStartingChild != null) { 110 handleExpansion(h, mStartingChild); 111 } 112 if (h > mMinDragDistance) { 113 if (!mDraggedFarEnough) { 114 mDraggedFarEnough = true; 115 mOnDragDownListener.onThresholdReached(); 116 } 117 } else { 118 if (mDraggedFarEnough) { 119 mDraggedFarEnough = false; 120 mOnDragDownListener.onReset(); 121 } 122 } 123 return true; 124 case MotionEvent.ACTION_UP: 125 if (mDraggedFarEnough) { 126 if (mStartingChild != null) { 127 mCallback.setUserLockedChild(mStartingChild, false); 128 } 129 mOnDragDownListener.onDraggedDown(mStartingChild); 130 mDraggingDown = false; 131 } else { 132 stopDragging(); 133 return false; 134 } 135 break; 136 case MotionEvent.ACTION_CANCEL: 137 stopDragging(); 138 return false; 139 } 140 return false; 141 } 142 143 private void captureStartingChild(float x, float y) { 144 if (mStartingChild == null) { 145 mStartingChild = findView(x, y); 146 if (mStartingChild != null) { 147 mCallback.setUserLockedChild(mStartingChild, true); 148 } 149 } 150 } 151 152 private void handleExpansion(float heightDelta, ExpandableView child) { 153 if (heightDelta < 0) { 154 heightDelta = 0; 155 } 156 boolean expandable = child.isContentExpandable(); 157 float rubberbandFactor = expandable 158 ? RUBBERBAND_FACTOR_EXPANDABLE 159 : RUBBERBAND_FACTOR_STATIC; 160 float rubberband = heightDelta * rubberbandFactor; 161 if (expandable && (rubberband + child.getMinHeight()) > child.getMaxHeight()) { 162 float overshoot = (rubberband + child.getMinHeight()) - child.getMaxHeight(); 163 overshoot *= (1 - RUBBERBAND_FACTOR_STATIC); 164 rubberband -= overshoot; 165 } 166 child.setActualHeight((int) (child.getMinHeight() + rubberband)); 167 } 168 169 private void cancelExpansion(final ExpandableView child) { 170 if (child.getActualHeight() == child.getMinHeight()) { 171 return; 172 } 173 ObjectAnimator anim = ObjectAnimator.ofInt(child, "actualHeight", 174 child.getActualHeight(), child.getMinHeight()); 175 anim.setInterpolator(mInterpolator); 176 anim.setDuration(SPRING_BACK_ANIMATION_LENGTH_MS); 177 anim.addListener(new AnimatorListenerAdapter() { 178 @Override 179 public void onAnimationEnd(Animator animation) { 180 mCallback.setUserLockedChild(child, false); 181 } 182 }); 183 anim.start(); 184 } 185 186 private void stopDragging() { 187 if (mStartingChild != null) { 188 cancelExpansion(mStartingChild); 189 } 190 mDraggingDown = false; 191 mOnDragDownListener.onReset(); 192 } 193 194 private ExpandableView findView(float x, float y) { 195 mHost.getLocationOnScreen(mTemp2); 196 x += mTemp2[0]; 197 y += mTemp2[1]; 198 return mCallback.getChildAtRawPosition(x, y); 199 } 200 201 public interface OnDragDownListener { 202 void onDraggedDown(View startingChild); 203 void onReset(); 204 void onThresholdReached(); 205 } 206} 207