Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 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 | |
| 17 | package com.android.systemui; |
| 18 | |
| 19 | import android.animation.Animator; |
Chet Haase | 2f2022a | 2011-10-11 06:41:59 -0700 | [diff] [blame] | 20 | import android.animation.AnimatorListenerAdapter; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 21 | import android.animation.ObjectAnimator; |
| 22 | import android.animation.Animator.AnimatorListener; |
| 23 | import android.animation.ValueAnimator; |
| 24 | import android.animation.ValueAnimator.AnimatorUpdateListener; |
| 25 | import android.graphics.RectF; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 26 | import android.os.Handler; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 27 | import android.util.Log; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 28 | import android.view.accessibility.AccessibilityEvent; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 29 | import android.view.animation.LinearInterpolator; |
| 30 | import android.view.MotionEvent; |
| 31 | import android.view.VelocityTracker; |
| 32 | import android.view.View; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 33 | import android.view.ViewConfiguration; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 34 | |
Daniel Sandler | 6a858c3 | 2012-03-12 14:38:58 -0400 | [diff] [blame] | 35 | public class SwipeHelper implements Gefingerpoken { |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 36 | static final String TAG = "com.android.systemui.SwipeHelper"; |
Daniel Sandler | 96f4818 | 2011-08-17 09:50:35 -0400 | [diff] [blame] | 37 | private static final boolean DEBUG = false; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 38 | private static final boolean DEBUG_INVALIDATE = false; |
| 39 | private static final boolean SLOW_ANIMATIONS = false; // DEBUG; |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 40 | private static final boolean CONSTRAIN_SWIPE = true; |
| 41 | private static final boolean FADE_OUT_DURING_SWIPE = true; |
| 42 | private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 43 | |
| 44 | public static final int X = 0; |
| 45 | public static final int Y = 1; |
| 46 | |
Chet Haase | 2f2022a | 2011-10-11 06:41:59 -0700 | [diff] [blame] | 47 | private static LinearInterpolator sLinearInterpolator = new LinearInterpolator(); |
| 48 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 49 | private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec |
Michael Jurka | 0e8063a | 2011-09-09 15:31:55 -0700 | [diff] [blame] | 50 | private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms |
| 51 | private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms |
| 52 | private int MAX_DISMISS_VELOCITY = 2000; // dp/sec |
| 53 | private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 54 | |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 55 | public static float ALPHA_FADE_START = 0f; // fraction of thumbnail width |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 56 | // where fade starts |
| 57 | static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width |
| 58 | // beyond which alpha->0 |
Michael Jurka | 4eaa983 | 2012-02-29 15:51:49 -0800 | [diff] [blame] | 59 | private float mMinAlpha = 0f; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 60 | |
| 61 | private float mPagingTouchSlop; |
| 62 | private Callback mCallback; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 63 | private Handler mHandler; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 64 | private int mSwipeDirection; |
| 65 | private VelocityTracker mVelocityTracker; |
| 66 | |
| 67 | private float mInitialTouchPos; |
| 68 | private boolean mDragging; |
| 69 | private View mCurrView; |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 70 | private View mCurrAnimView; |
| 71 | private boolean mCanCurrViewBeDimissed; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 72 | private float mDensityScale; |
| 73 | |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 74 | private boolean mLongPressSent; |
| 75 | private View.OnLongClickListener mLongPressListener; |
| 76 | private Runnable mWatchLongPress; |
Daniel Sandler | 469e96e | 2012-05-04 15:56:19 -0400 | [diff] [blame] | 77 | private long mLongPressTimeout; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 78 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 79 | public SwipeHelper(int swipeDirection, Callback callback, float densityScale, |
| 80 | float pagingTouchSlop) { |
| 81 | mCallback = callback; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 82 | mHandler = new Handler(); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 83 | mSwipeDirection = swipeDirection; |
| 84 | mVelocityTracker = VelocityTracker.obtain(); |
| 85 | mDensityScale = densityScale; |
| 86 | mPagingTouchSlop = pagingTouchSlop; |
Daniel Sandler | 469e96e | 2012-05-04 15:56:19 -0400 | [diff] [blame] | 87 | |
| 88 | mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); // extra long-press! |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 89 | } |
| 90 | |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 91 | public void setLongPressListener(View.OnLongClickListener listener) { |
| 92 | mLongPressListener = listener; |
| 93 | } |
| 94 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 95 | public void setDensityScale(float densityScale) { |
| 96 | mDensityScale = densityScale; |
| 97 | } |
| 98 | |
| 99 | public void setPagingTouchSlop(float pagingTouchSlop) { |
| 100 | mPagingTouchSlop = pagingTouchSlop; |
| 101 | } |
| 102 | |
| 103 | private float getPos(MotionEvent ev) { |
| 104 | return mSwipeDirection == X ? ev.getX() : ev.getY(); |
| 105 | } |
| 106 | |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 107 | private float getTranslation(View v) { |
| 108 | return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY(); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | private float getVelocity(VelocityTracker vt) { |
| 112 | return mSwipeDirection == X ? vt.getXVelocity() : |
| 113 | vt.getYVelocity(); |
| 114 | } |
| 115 | |
| 116 | private ObjectAnimator createTranslationAnimation(View v, float newPos) { |
| 117 | ObjectAnimator anim = ObjectAnimator.ofFloat(v, |
| 118 | mSwipeDirection == X ? "translationX" : "translationY", newPos); |
| 119 | return anim; |
| 120 | } |
| 121 | |
| 122 | private float getPerpendicularVelocity(VelocityTracker vt) { |
| 123 | return mSwipeDirection == X ? vt.getYVelocity() : |
| 124 | vt.getXVelocity(); |
| 125 | } |
| 126 | |
| 127 | private void setTranslation(View v, float translate) { |
| 128 | if (mSwipeDirection == X) { |
| 129 | v.setTranslationX(translate); |
| 130 | } else { |
| 131 | v.setTranslationY(translate); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | private float getSize(View v) { |
| 136 | return mSwipeDirection == X ? v.getMeasuredWidth() : |
| 137 | v.getMeasuredHeight(); |
| 138 | } |
| 139 | |
Michael Jurka | 4eaa983 | 2012-02-29 15:51:49 -0800 | [diff] [blame] | 140 | public void setMinAlpha(float minAlpha) { |
| 141 | mMinAlpha = minAlpha; |
| 142 | } |
| 143 | |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 144 | private float getAlphaForOffset(View view) { |
| 145 | float viewSize = getSize(view); |
| 146 | final float fadeSize = ALPHA_FADE_END * viewSize; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 147 | float result = 1.0f; |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 148 | float pos = getTranslation(view); |
| 149 | if (pos >= viewSize * ALPHA_FADE_START) { |
| 150 | result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize; |
| 151 | } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) { |
| 152 | result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 153 | } |
Michael Jurka | 4eaa983 | 2012-02-29 15:51:49 -0800 | [diff] [blame] | 154 | return Math.max(mMinAlpha, result); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 155 | } |
| 156 | |
Michael Jurka | 67b0370 | 2013-02-15 17:35:48 +0100 | [diff] [blame] | 157 | private void updateAlphaFromOffset(View animView, boolean dismissable) { |
| 158 | if (FADE_OUT_DURING_SWIPE && dismissable) { |
Michael Jurka | 499293f | 2013-03-06 18:08:45 +0100 | [diff] [blame] | 159 | float alpha = getAlphaForOffset(animView); |
| 160 | if (alpha != 0f && alpha != 1f) { |
| 161 | animView.setLayerType(View.LAYER_TYPE_HARDWARE, null); |
| 162 | } else { |
| 163 | animView.setLayerType(View.LAYER_TYPE_NONE, null); |
| 164 | } |
Michael Jurka | 67b0370 | 2013-02-15 17:35:48 +0100 | [diff] [blame] | 165 | animView.setAlpha(getAlphaForOffset(animView)); |
| 166 | } |
| 167 | invalidateGlobalRegion(animView); |
| 168 | } |
| 169 | |
Daniel Sandler | a375c94 | 2011-07-29 00:33:53 -0400 | [diff] [blame] | 170 | // invalidate the view's own bounds all the way up the view hierarchy |
| 171 | public static void invalidateGlobalRegion(View view) { |
| 172 | invalidateGlobalRegion( |
| 173 | view, |
| 174 | new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); |
| 175 | } |
| 176 | |
| 177 | // invalidate a rectangle relative to the view's coordinate system all the way up the view |
| 178 | // hierarchy |
| 179 | public static void invalidateGlobalRegion(View view, RectF childBounds) { |
Daniel Sandler | 96f4818 | 2011-08-17 09:50:35 -0400 | [diff] [blame] | 180 | //childBounds.offset(view.getTranslationX(), view.getTranslationY()); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 181 | if (DEBUG_INVALIDATE) |
| 182 | Log.v(TAG, "-------------"); |
| 183 | while (view.getParent() != null && view.getParent() instanceof View) { |
| 184 | view = (View) view.getParent(); |
| 185 | view.getMatrix().mapRect(childBounds); |
| 186 | view.invalidate((int) Math.floor(childBounds.left), |
| 187 | (int) Math.floor(childBounds.top), |
| 188 | (int) Math.ceil(childBounds.right), |
| 189 | (int) Math.ceil(childBounds.bottom)); |
| 190 | if (DEBUG_INVALIDATE) { |
| 191 | Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left) |
| 192 | + "," + (int) Math.floor(childBounds.top) |
| 193 | + "," + (int) Math.ceil(childBounds.right) |
| 194 | + "," + (int) Math.ceil(childBounds.bottom)); |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | |
Daniel Sandler | 469e96e | 2012-05-04 15:56:19 -0400 | [diff] [blame] | 199 | public void removeLongPressCallback() { |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 200 | if (mWatchLongPress != null) { |
| 201 | mHandler.removeCallbacks(mWatchLongPress); |
Daniel Sandler | 491d3a9 | 2012-05-16 13:04:06 -0400 | [diff] [blame] | 202 | mWatchLongPress = null; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 203 | } |
| 204 | } |
| 205 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 206 | public boolean onInterceptTouchEvent(MotionEvent ev) { |
| 207 | final int action = ev.getAction(); |
| 208 | |
| 209 | switch (action) { |
| 210 | case MotionEvent.ACTION_DOWN: |
| 211 | mDragging = false; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 212 | mLongPressSent = false; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 213 | mCurrView = mCallback.getChildAtPosition(ev); |
| 214 | mVelocityTracker.clear(); |
Michael Jurka | 21ce2d8 | 2011-09-02 15:28:06 -0700 | [diff] [blame] | 215 | if (mCurrView != null) { |
| 216 | mCurrAnimView = mCallback.getChildContentView(mCurrView); |
| 217 | mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView); |
| 218 | mVelocityTracker.addMovement(ev); |
| 219 | mInitialTouchPos = getPos(ev); |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 220 | |
| 221 | if (mLongPressListener != null) { |
| 222 | if (mWatchLongPress == null) { |
| 223 | mWatchLongPress = new Runnable() { |
| 224 | @Override |
| 225 | public void run() { |
| 226 | if (mCurrView != null && !mLongPressSent) { |
| 227 | mLongPressSent = true; |
| 228 | mCurrView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); |
| 229 | mLongPressListener.onLongClick(mCurrView); |
| 230 | } |
| 231 | } |
| 232 | }; |
| 233 | } |
Daniel Sandler | 469e96e | 2012-05-04 15:56:19 -0400 | [diff] [blame] | 234 | mHandler.postDelayed(mWatchLongPress, mLongPressTimeout); |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 235 | } |
| 236 | |
Michael Jurka | 21ce2d8 | 2011-09-02 15:28:06 -0700 | [diff] [blame] | 237 | } |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 238 | break; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 239 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 240 | case MotionEvent.ACTION_MOVE: |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 241 | if (mCurrView != null && !mLongPressSent) { |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 242 | mVelocityTracker.addMovement(ev); |
| 243 | float pos = getPos(ev); |
| 244 | float delta = pos - mInitialTouchPos; |
| 245 | if (Math.abs(delta) > mPagingTouchSlop) { |
| 246 | mCallback.onBeginDrag(mCurrView); |
| 247 | mDragging = true; |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 248 | mInitialTouchPos = getPos(ev) - getTranslation(mCurrAnimView); |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 249 | |
| 250 | removeLongPressCallback(); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 251 | } |
| 252 | } |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 253 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 254 | break; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 255 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 256 | case MotionEvent.ACTION_UP: |
Jeff Brown | 68ebcdf | 2011-09-12 14:12:17 -0700 | [diff] [blame] | 257 | case MotionEvent.ACTION_CANCEL: |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 258 | mDragging = false; |
| 259 | mCurrView = null; |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 260 | mCurrAnimView = null; |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 261 | mLongPressSent = false; |
Daniel Sandler | 491d3a9 | 2012-05-16 13:04:06 -0400 | [diff] [blame] | 262 | removeLongPressCallback(); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 263 | break; |
| 264 | } |
| 265 | return mDragging; |
| 266 | } |
| 267 | |
Chet Haase | 2f2022a | 2011-10-11 06:41:59 -0700 | [diff] [blame] | 268 | /** |
| 269 | * @param view The view to be dismissed |
| 270 | * @param velocity The desired pixels/second speed at which the view should move |
| 271 | */ |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 272 | public void dismissChild(final View view, float velocity) { |
| 273 | final View animView = mCallback.getChildContentView(view); |
| 274 | final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 275 | float newPos; |
Michael Jurka | c6461ca | 2011-09-02 12:12:15 -0700 | [diff] [blame] | 276 | |
| 277 | if (velocity < 0 |
| 278 | || (velocity == 0 && getTranslation(animView) < 0) |
| 279 | // if we use the Menu to dismiss an item in landscape, animate up |
| 280 | || (velocity == 0 && getTranslation(animView) == 0 && mSwipeDirection == Y)) { |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 281 | newPos = -getSize(animView); |
| 282 | } else { |
| 283 | newPos = getSize(animView); |
| 284 | } |
| 285 | int duration = MAX_ESCAPE_ANIMATION_DURATION; |
| 286 | if (velocity != 0) { |
| 287 | duration = Math.min(duration, |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 288 | (int) (Math.abs(newPos - getTranslation(animView)) * 1000f / Math |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 289 | .abs(velocity))); |
Michael Jurka | 0e8063a | 2011-09-09 15:31:55 -0700 | [diff] [blame] | 290 | } else { |
| 291 | duration = DEFAULT_ESCAPE_ANIMATION_DURATION; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 292 | } |
Michael Jurka | 0e8063a | 2011-09-09 15:31:55 -0700 | [diff] [blame] | 293 | |
Chet Haase | 2f2022a | 2011-10-11 06:41:59 -0700 | [diff] [blame] | 294 | animView.setLayerType(View.LAYER_TYPE_HARDWARE, null); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 295 | ObjectAnimator anim = createTranslationAnimation(animView, newPos); |
Chet Haase | 2f2022a | 2011-10-11 06:41:59 -0700 | [diff] [blame] | 296 | anim.setInterpolator(sLinearInterpolator); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 297 | anim.setDuration(duration); |
Chet Haase | 2f2022a | 2011-10-11 06:41:59 -0700 | [diff] [blame] | 298 | anim.addListener(new AnimatorListenerAdapter() { |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 299 | public void onAnimationEnd(Animator animation) { |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 300 | mCallback.onChildDismissed(view); |
Chet Haase | 2f2022a | 2011-10-11 06:41:59 -0700 | [diff] [blame] | 301 | animView.setLayerType(View.LAYER_TYPE_NONE, null); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 302 | } |
| 303 | }); |
| 304 | anim.addUpdateListener(new AnimatorUpdateListener() { |
| 305 | public void onAnimationUpdate(ValueAnimator animation) { |
Michael Jurka | 67b0370 | 2013-02-15 17:35:48 +0100 | [diff] [blame] | 306 | updateAlphaFromOffset(animView, canAnimViewBeDismissed); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 307 | } |
| 308 | }); |
| 309 | anim.start(); |
| 310 | } |
| 311 | |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 312 | public void snapChild(final View view, float velocity) { |
| 313 | final View animView = mCallback.getChildContentView(view); |
| 314 | final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(animView); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 315 | ObjectAnimator anim = createTranslationAnimation(animView, 0); |
| 316 | int duration = SNAP_ANIM_LEN; |
| 317 | anim.setDuration(duration); |
| 318 | anim.addUpdateListener(new AnimatorUpdateListener() { |
| 319 | public void onAnimationUpdate(ValueAnimator animation) { |
Michael Jurka | 67b0370 | 2013-02-15 17:35:48 +0100 | [diff] [blame] | 320 | updateAlphaFromOffset(animView, canAnimViewBeDismissed); |
| 321 | } |
| 322 | }); |
| 323 | anim.addListener(new AnimatorListenerAdapter() { |
| 324 | public void onAnimationEnd(Animator animator) { |
| 325 | updateAlphaFromOffset(animView, canAnimViewBeDismissed); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 326 | } |
| 327 | }); |
| 328 | anim.start(); |
| 329 | } |
| 330 | |
| 331 | public boolean onTouchEvent(MotionEvent ev) { |
Daniel Sandler | f7a1956 | 2012-04-04 14:04:21 -0400 | [diff] [blame] | 332 | if (mLongPressSent) { |
| 333 | return true; |
| 334 | } |
| 335 | |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 336 | if (!mDragging) { |
Dianne Hackborn | 7f3b379 | 2012-06-26 16:39:02 -0700 | [diff] [blame] | 337 | // We are not doing anything, make sure the long press callback |
| 338 | // is not still ticking like a bomb waiting to go off. |
| 339 | removeLongPressCallback(); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 340 | return false; |
| 341 | } |
| 342 | |
| 343 | mVelocityTracker.addMovement(ev); |
| 344 | final int action = ev.getAction(); |
| 345 | switch (action) { |
| 346 | case MotionEvent.ACTION_OUTSIDE: |
| 347 | case MotionEvent.ACTION_MOVE: |
| 348 | if (mCurrView != null) { |
| 349 | float delta = getPos(ev) - mInitialTouchPos; |
| 350 | // don't let items that can't be dismissed be dragged more than |
| 351 | // maxScrollDistance |
| 352 | if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) { |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 353 | float size = getSize(mCurrAnimView); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 354 | float maxScrollDistance = 0.15f * size; |
| 355 | if (Math.abs(delta) >= size) { |
| 356 | delta = delta > 0 ? maxScrollDistance : -maxScrollDistance; |
| 357 | } else { |
| 358 | delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2)); |
| 359 | } |
| 360 | } |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 361 | setTranslation(mCurrAnimView, delta); |
Michael Jurka | 67b0370 | 2013-02-15 17:35:48 +0100 | [diff] [blame] | 362 | |
| 363 | updateAlphaFromOffset(mCurrAnimView, mCanCurrViewBeDimissed); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 364 | } |
| 365 | break; |
| 366 | case MotionEvent.ACTION_UP: |
| 367 | case MotionEvent.ACTION_CANCEL: |
| 368 | if (mCurrView != null) { |
Daniel Sandler | 0761e4c | 2011-08-11 00:19:49 -0400 | [diff] [blame] | 369 | float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale; |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 370 | mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity); |
| 371 | float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale; |
| 372 | float velocity = getVelocity(mVelocityTracker); |
| 373 | float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker); |
| 374 | |
| 375 | // Decide whether to dismiss the current view |
| 376 | boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH && |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 377 | Math.abs(getTranslation(mCurrAnimView)) > 0.4 * getSize(mCurrAnimView); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 378 | boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) && |
| 379 | (Math.abs(velocity) > Math.abs(perpendicularVelocity)) && |
Michael Jurka | 3cd0a59 | 2011-08-16 12:40:30 -0700 | [diff] [blame] | 380 | (velocity > 0) == (getTranslation(mCurrAnimView) > 0); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 381 | |
| 382 | boolean dismissChild = mCallback.canChildBeDismissed(mCurrView) && |
| 383 | (childSwipedFastEnough || childSwipedFarEnough); |
| 384 | |
| 385 | if (dismissChild) { |
| 386 | // flingadingy |
| 387 | dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f); |
| 388 | } else { |
| 389 | // snappity |
Peter Ng | 622a976 | 2011-08-29 10:56:53 -0700 | [diff] [blame] | 390 | mCallback.onDragCancelled(mCurrView); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 391 | snapChild(mCurrView, velocity); |
| 392 | } |
| 393 | } |
| 394 | break; |
| 395 | } |
| 396 | return true; |
| 397 | } |
| 398 | |
| 399 | public interface Callback { |
| 400 | View getChildAtPosition(MotionEvent ev); |
| 401 | |
| 402 | View getChildContentView(View v); |
| 403 | |
| 404 | boolean canChildBeDismissed(View v); |
| 405 | |
| 406 | void onBeginDrag(View v); |
| 407 | |
| 408 | void onChildDismissed(View v); |
Peter Ng | 622a976 | 2011-08-29 10:56:53 -0700 | [diff] [blame] | 409 | |
| 410 | void onDragCancelled(View v); |
Michael Jurka | 07d4046 | 2011-07-19 10:54:38 -0700 | [diff] [blame] | 411 | } |
| 412 | } |