16a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler/* 26a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * Copyright (C) 2012 The Android Open Source Project 36a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * 46a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * Licensed under the Apache License, Version 2.0 (the "License"); 56a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * you may not use this file except in compliance with the License. 66a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * You may obtain a copy of the License at 76a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * 86a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * http://www.apache.org/licenses/LICENSE-2.0 96a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * 106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * Unless required by applicable law or agreed to in writing, software 116a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * distributed under the License is distributed on an "AS IS" BASIS, 126a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * See the License for the specific language governing permissions and 146a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * limitations under the License. 156a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler */ 166a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 176a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 186a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerpackage com.android.systemui; 196a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 208900e631940fdffe7b941b56dc0f17e55345441eRomain Guyimport android.animation.Animator; 218900e631940fdffe7b941b56dc0f17e55345441eRomain Guyimport android.animation.AnimatorListenerAdapter; 226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.animation.ObjectAnimator; 236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.content.Context; 247b41467704f941b11af6aace3e40993afc7f6c6fJohn Spurlockimport android.media.AudioAttributes; 25f9e1a0b369740e11ea1ed4f141ffb936fc1a6cdbJohn Spurlockimport android.media.AudioManager; 26b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wrenimport android.os.Vibrator; 27cd686b5b6d4166b510df8e32138479a9559bc117John Spurlockimport android.util.Log; 289b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wrenimport android.view.Gravity; 296a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.MotionEvent; 306a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ScaleGestureDetector; 31ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandlerimport android.view.ScaleGestureDetector.OnScaleGestureListener; 3228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggiimport android.view.VelocityTracker; 336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View; 34b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wrenimport android.view.ViewConfiguration; 356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 36be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggiimport com.android.systemui.statusbar.ExpandableNotificationRow; 374222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggiimport com.android.systemui.statusbar.ExpandableView; 3828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggiimport com.android.systemui.statusbar.FlingAnimationUtils; 39b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516Selim Cinekimport com.android.systemui.statusbar.policy.ScrollAdapter; 40b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516Selim Cinek 414222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggipublic class ExpandHelper implements Gefingerpoken { 426a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public interface Callback { 434222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi ExpandableView getChildAtRawPosition(float x, float y); 444222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi ExpandableView getChildAtPosition(float x, float y); 4580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren boolean canChildBeExpanded(View v); 4651c7510e493680b4aca1ed7695b35c52d2cd63ffChris Wren void setUserExpandedChild(View v, boolean userExpanded); 4751c7510e493680b4aca1ed7695b35c52d2cd63ffChris Wren void setUserLockedChild(View v, boolean userLocked); 481408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek void expansionStateChanged(boolean isExpanding); 496a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 506a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 516a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private static final String TAG = "ExpandHelper"; 52e46647d28467ee9e88aafe2951a5736f494235daChris Wren protected static final boolean DEBUG = false; 533c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren protected static final boolean DEBUG_SCALE = false; 5428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi private static final float EXPAND_DURATION = 0.3f; 55ba925e8ecd9decff5701001a0190042d6797942dChris Wren 564377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // Set to false to disable focus-based gestures (spread-finger vertical pull). 5789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_DRAG = true; 5889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Set to false to disable scale-based gestures (both horizontal and vertical). 5989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_SPAN = true; 6089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Both gestures types may be active at the same time. 6189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // At least one gesture type should be active. 6289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // A variant of the screwdriver gesture will emerge from either gesture type. 636a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 6480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // amount of overstretch for maximum brightness expressed in U 6580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U 6680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float STRETCH_INTERVAL = 2f; 6780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 6880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // level of glow for a touch, without overstretch 6980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // overstretch fills the range (GLOW_BASE, 1.0] 7080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float GLOW_BASE = 0.5f; 7180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 727b41467704f941b11af6aace3e40993afc7f6c6fJohn Spurlock private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 737b41467704f941b11af6aace3e40993afc7f6c6fJohn Spurlock .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 747b41467704f941b11af6aace3e40993afc7f6c6fJohn Spurlock .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) 757b41467704f941b11af6aace3e40993afc7f6c6fJohn Spurlock .build(); 767b41467704f941b11af6aace3e40993afc7f6c6fJohn Spurlock 776a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @SuppressWarnings("unused") 786a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Context mContext; 796a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 804377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private boolean mExpanding; 814377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int NONE = 0; 824377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int BLINDS = 1<<0; 834377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int PULL = 1<<1; 844377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int STRETCH = 1<<2; 854377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private int mExpansionStyle = NONE; 86b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean mWatchingForPull; 87b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean mHasPopped; 885de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren private View mEventSource; 896a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mOldHeight; 906a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mNaturalHeight; 9189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private float mInitialTouchFocusY; 92b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float mInitialTouchY; 936a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mInitialTouchSpan; 94cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren private float mLastFocusY; 95cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren private float mLastSpanY; 96b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mTouchSlop; 971408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek private float mLastMotionY; 98b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mPopDuration; 994377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private float mPullGestureMinXSpan; 1006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Callback mCallback; 1014377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private ScaleGestureDetector mSGD; 1026a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private ViewScaler mScaler; 103ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mScaleAnimation; 104b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private Vibrator mVibrator; 1051408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek private boolean mEnabled = true; 1061408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek private ExpandableView mResizedView; 1071408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek private float mCurrentHeight; 1086a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1096a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mSmallSize; 1106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mLargeSize; 11180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private float mMaximumStretch; 1121408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek private boolean mOnlyMovements; 1136a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1149b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren private int mGravity; 1159b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 116fab078b01fbad026f006744016272327f7ab116bSelim Cinek private ScrollAdapter mScrollAdapter; 11728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi private FlingAnimationUtils mFlingAnimationUtils; 11828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi private VelocityTracker mVelocityTracker; 119b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 120209bede6b9edb9171e5bee4077b48e35004a37b4John Spurlock private OnScaleGestureListener mScaleGestureListener 121ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler = new ScaleGestureDetector.SimpleOnScaleGestureListener() { 122ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler @Override 123ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public boolean onScaleBegin(ScaleGestureDetector detector) { 124cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()"); 125ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 1261408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek startExpanding(mResizedView, STRETCH); 127ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler return mExpanding; 128ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 129ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 130ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler @Override 131ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public boolean onScale(ScaleGestureDetector detector) { 1321408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mResizedView); 133ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler return true; 134ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 135ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 136ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler @Override 137ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public void onScaleEnd(ScaleGestureDetector detector) { 138ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 139ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler }; 140ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 1416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private class ViewScaler { 142be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi ExpandableView mView; 1439b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 1446a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ViewScaler() {} 145be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public void setView(ExpandableView v) { 1466a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView = v; 1476a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1486a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void setHeight(float h) { 149cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h); 150be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mView.setActualHeight((int) h); 1511408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCurrentHeight = h; 1526a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1536a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public float getHeight() { 154be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return mView.getActualHeight(); 1556a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1566a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public int getNaturalHeight(int maximum) { 157be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return Math.min(maximum, mView.getMaxHeight()); 1586a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1596a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1606a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1619b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren /** 1629b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * Handle expansion gestures to expand and contract children of the callback. 1639b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * 1649b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param context application context 1659b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param callback the container that holds the items to be manipulated 1669b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param small the smallest allowable size for the manuipulated items. 1679b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param large the largest allowable size for the manuipulated items. 1689b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren */ 1696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ExpandHelper(Context context, Callback callback, int small, int large) { 1706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mSmallSize = small; 17180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mMaximumStretch = mSmallSize * STRETCH_INTERVAL; 1726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mLargeSize = large; 1736a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mContext = context; 1746a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCallback = callback; 1756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mScaler = new ViewScaler(); 1769b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = Gravity.TOP; 177ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); 1784377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms); 1794377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min); 180ba925e8ecd9decff5701001a0190042d6797942dChris Wren 181b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final ViewConfiguration configuration = ViewConfiguration.get(mContext); 182b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mTouchSlop = configuration.getScaledTouchSlop(); 183b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 184ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler mSGD = new ScaleGestureDetector(context, mScaleGestureListener); 18528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mFlingAnimationUtils = new FlingAnimationUtils(context, EXPAND_DURATION); 1866a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1875de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 1884377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private void updateExpansion() { 189cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "updateExpansion()"); 1904377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // are we scaling or dragging? 191cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren float span = mSGD.getCurrentSpan() - mInitialTouchSpan; 1924377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler span *= USE_SPAN ? 1f : 0f; 1934377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float drag = mSGD.getFocusY() - mInitialTouchFocusY; 1944377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler drag *= USE_DRAG ? 1f : 0f; 1954377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; 1964377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float pull = Math.abs(drag) + Math.abs(span) + 1f; 1974377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; 1984377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float target = hand + mOldHeight; 1994377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float newHeight = clamp(target); 2004377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mScaler.setHeight(newHeight); 201cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastFocusY = mSGD.getFocusY(); 202cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastSpanY = mSGD.getCurrentSpan(); 2034377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 2044377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 205b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float clamp(float target) { 206b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float out = target; 207b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out); 208b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out > mNaturalHeight ? mNaturalHeight : out; 209b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return out; 210b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 211b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 2124222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi private ExpandableView findView(float x, float y) { 2134222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi ExpandableView v; 214b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 215b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 216b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 2174377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x += location[0]; 2184377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y += location[1]; 219b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtRawPosition(x, y); 220b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 221b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtPosition(x, y); 222b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 223b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return v; 224b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 225b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 226b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean isInside(View v, float x, float y) { 227cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "isinside (" + x + ", " + y + ")"); 228b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 229b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (v == null) { 230cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "isinside null subject"); 231b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 232b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 233b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 234b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 235b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 2364377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x += location[0]; 2374377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y += location[1]; 238cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, " to global (" + x + ", " + y + ")"); 239b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 240b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 241b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v.getLocationOnScreen(location); 2424377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x -= location[0]; 2434377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y -= location[1]; 244cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, " to local (" + x + ", " + y + ")"); 245cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")"); 246b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight()); 247b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return inside; 248b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 249b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 2505de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren public void setEventSource(View eventSource) { 2515de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren mEventSource = eventSource; 2525de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren } 2535de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 2549b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren public void setGravity(int gravity) { 2559b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = gravity; 2569b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren } 2579b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 258fab078b01fbad026f006744016272327f7ab116bSelim Cinek public void setScrollAdapter(ScrollAdapter adapter) { 259fab078b01fbad026f006744016272327f7ab116bSelim Cinek mScrollAdapter = adapter; 260b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 261b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 2624377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler @Override 2636a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onInterceptTouchEvent(MotionEvent ev) { 2641408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (!isEnabled()) { 2651408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek return false; 2661408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 26728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi trackVelocity(ev); 2684377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int action = ev.getAction(); 269cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) + 2704377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " expanding=" + mExpanding + 2714377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 2724377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 2734377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 2744377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // check for a spread-finger vertical pull gesture 2754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mSGD.onTouchEvent(ev); 2764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int x = (int) mSGD.getFocusX(); 2774377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int y = (int) mSGD.getFocusY(); 278cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 279cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchFocusY = y; 280cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchSpan = mSGD.getCurrentSpan(); 281cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastFocusY = mInitialTouchFocusY; 282cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastSpanY = mInitialTouchSpan; 283cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.d(TAG, "set initial span: " + mInitialTouchSpan); 284cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 2854377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mExpanding) { 2861408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 28728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi maybeRecycleVelocityTracker(ev); 288b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 289b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 2904377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) { 2914377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // we've begun Venetian blinds style expansion 2924377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return true; 2934377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 294b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren switch (action & MotionEvent.ACTION_MASK) { 295b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 2961408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek final float xspan = mSGD.getCurrentSpanX(); 2971408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (xspan > mPullGestureMinXSpan && 2981408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek xspan > mSGD.getCurrentSpanY() && !mExpanding) { 2991408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek // detect a vertical pulling gesture with fingers somewhat separated 3001408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)"); 3011408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek startExpanding(mResizedView, PULL); 3021408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mWatchingForPull = false; 3031408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 304b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mWatchingForPull) { 3051408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek final float yDiff = ev.getRawY() - mInitialTouchY; 306b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (yDiff > mTouchSlop) { 307cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); 3081408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mWatchingForPull = false; 3091408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (mResizedView != null && !isFullyExpanded(mResizedView)) { 3101408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (startExpanding(mResizedView, BLINDS)) { 3111408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 3121408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mInitialTouchY = ev.getRawY(); 3131408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mHasPopped = false; 3141408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 315b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 316b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 317b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 318b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 319b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 320b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 321b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_DOWN: 322de3b1a2b2d3056cb7caa5cdaac51fc9d80e1e3c5Chris Wren mWatchingForPull = mScrollAdapter != null && 3231408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek isInside(mScrollAdapter.getHostView(), x, y) 3241408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek && mScrollAdapter.isScrolledToTop(); 3251408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mResizedView = findView(x, y); 3261408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mInitialTouchY = ev.getY(); 327b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 328b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 329b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_CANCEL: 330b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_UP: 331cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "up/cancel"); 33228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi finishExpanding(false, getCurrentVelocity()); 3334377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler clearView(); 334b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 335b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 3361408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 33728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi maybeRecycleVelocityTracker(ev); 3384377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return mExpanding; 339b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 3406a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 3416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 34228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi private void trackVelocity(MotionEvent event) { 34328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi int action = event.getActionMasked(); 34428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi switch(action) { 34528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi case MotionEvent.ACTION_DOWN: 34628c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi if (mVelocityTracker == null) { 34728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mVelocityTracker = VelocityTracker.obtain(); 34828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } else { 34928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mVelocityTracker.clear(); 35028c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } 35128c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mVelocityTracker.addMovement(event); 35228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi break; 35328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi case MotionEvent.ACTION_MOVE: 3541b7f51ebc66fbb2cbe8a468790bbbfd397d66964Selim Cinek if (mVelocityTracker == null) { 3551b7f51ebc66fbb2cbe8a468790bbbfd397d66964Selim Cinek mVelocityTracker = VelocityTracker.obtain(); 3561b7f51ebc66fbb2cbe8a468790bbbfd397d66964Selim Cinek } 35728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mVelocityTracker.addMovement(event); 35828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi break; 35928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi default: 36028c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi break; 36128c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } 36228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } 36328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi 36428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi private void maybeRecycleVelocityTracker(MotionEvent event) { 365c503896a4d0cab029bca56cf7ac18ae182729a0aJorim Jaggi if (mVelocityTracker != null && (event.getActionMasked() == MotionEvent.ACTION_CANCEL 366c503896a4d0cab029bca56cf7ac18ae182729a0aJorim Jaggi || event.getActionMasked() == MotionEvent.ACTION_UP)) { 36728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mVelocityTracker.recycle(); 36828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mVelocityTracker = null; 36928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } 37028c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } 37128c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi 37228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi private float getCurrentVelocity() { 37328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi if (mVelocityTracker != null) { 37428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mVelocityTracker.computeCurrentVelocity(1000); 37528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi return mVelocityTracker.getYVelocity(); 37628c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } else { 37728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi return 0f; 37828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } 37928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi } 38028c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi 3811408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek public void setEnabled(boolean enable) { 3821408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mEnabled = enable; 3831408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 3841408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek 3851408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek private boolean isEnabled() { 3861408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek return mEnabled; 3871408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 3881408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek 3891408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek private boolean isFullyExpanded(ExpandableView underFocus) { 3901408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek return underFocus.getIntrinsicHeight() == underFocus.getMaxHeight(); 3911408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 3921408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek 3934377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler @Override 3946a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onTouchEvent(MotionEvent ev) { 3951408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (!isEnabled()) { 3961408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek return false; 3971408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 39828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi trackVelocity(ev); 399cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren final int action = ev.getActionMasked(); 400cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.d(TAG, "touch: act=" + MotionEvent.actionToString(action) + 4014377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " expanding=" + mExpanding + 4024377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 4034377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 4044377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 4054377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4064377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mSGD.onTouchEvent(ev); 4071408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek final int x = (int) mSGD.getFocusX(); 4081408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek final int y = (int) mSGD.getFocusY(); 4094377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4101408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (mOnlyMovements) { 4111408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 4121408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek return false; 4131408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 4146a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler switch (action) { 4151408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek case MotionEvent.ACTION_DOWN: 4161408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mWatchingForPull = mScrollAdapter != null && 4171408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek isInside(mScrollAdapter.getHostView(), x, y); 4181408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mResizedView = findView(x, y); 4191408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mInitialTouchY = ev.getY(); 4201408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek break; 421b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 4221408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (mWatchingForPull) { 4231408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek final float yDiff = ev.getRawY() - mInitialTouchY; 4241408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (yDiff > mTouchSlop) { 4251408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); 4261408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mWatchingForPull = false; 4271408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (mResizedView != null && !isFullyExpanded(mResizedView)) { 4281408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (startExpanding(mResizedView, BLINDS)) { 4291408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mInitialTouchY = ev.getRawY(); 4301408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 4311408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mHasPopped = false; 4321408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 4331408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 4341408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 4351408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 4361408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (mExpanding && 0 != (mExpansionStyle & BLINDS)) { 4371408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek final float rawHeight = ev.getRawY() - mLastMotionY + mCurrentHeight; 43886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final float newHeight = clamp(rawHeight); 43986d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren boolean isFinished = false; 4401408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek boolean expanded = false; 44186d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (rawHeight > mNaturalHeight) { 44286d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren isFinished = true; 4431408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek expanded = true; 44486d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 44586d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (rawHeight < mSmallSize) { 44686d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren isFinished = true; 4471408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek expanded = false; 44886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 44986d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 4501408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (!mHasPopped) { 4511408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek vibrate(mPopDuration); 4521408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mHasPopped = true; 45386d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 45486d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 4551408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mScaler.setHeight(newHeight); 4561408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 4571408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (isFinished) { 4581408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCallback.setUserExpandedChild(mResizedView, expanded); 4591408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCallback.expansionStateChanged(false); 4601408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek return false; 4611408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } else { 4621408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCallback.expansionStateChanged(true); 463b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 464b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 465b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4664377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4674377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mExpanding) { 4681408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek 4691408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek // Gestural expansion is running 4704377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler updateExpansion(); 4711408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 4724377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return true; 4734377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 4744377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 475b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 476b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 477cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 478cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren case MotionEvent.ACTION_POINTER_UP: 479cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren case MotionEvent.ACTION_POINTER_DOWN: 480cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "pointer change"); 481cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchY += mSGD.getFocusY() - mLastFocusY; 482cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchSpan += mSGD.getCurrentSpan() - mLastSpanY; 483cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren break; 484cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 4856a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_UP: 4866a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_CANCEL: 487cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "up/cancel"); 48828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi finishExpanding(false, getCurrentVelocity()); 48980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren clearView(); 4906a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler break; 4916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4921408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mLastMotionY = ev.getRawY(); 49328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi maybeRecycleVelocityTracker(ev); 494787a0af8ebba004a6f1cd3bfe8c78d851003d227Jorim Jaggi return mResizedView != null; 4956a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4964377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 497be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi /** 498be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi * @return True if the view is expandable, false otherwise. 499be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi */ 5004222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi private boolean startExpanding(ExpandableView v, int expandType) { 501be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi if (!(v instanceof ExpandableNotificationRow)) { 502be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return false; 503be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 504cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mExpansionStyle = expandType; 5051408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (mExpanding && v == mResizedView) { 506be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return true; 507cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren } 5084377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpanding = true; 5091408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCallback.expansionStateChanged(true); 510cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v); 5114377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mCallback.setUserLockedChild(v, true); 5121408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mScaler.setView(v); 5134377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mOldHeight = mScaler.getHeight(); 5141408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCurrentHeight = mOldHeight; 5154377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mCallback.canChildBeExpanded(v)) { 516cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "working on an expandable child"); 5174377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); 518b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 519cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "working on a non-expandable child"); 5204377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mNaturalHeight = mOldHeight; 5216a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 522cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight + 5234377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " mNaturalHeight: " + mNaturalHeight); 524be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi return true; 5256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5266a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 52728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi private void finishExpanding(boolean force, float velocity) { 5284377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (!mExpanding) return; 5294377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 5301408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView); 531cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 5323ddab0dcc1039137f05a28ff86477601a223a0faChris Wren float currentHeight = mScaler.getHeight(); 5333ddab0dcc1039137f05a28ff86477601a223a0faChris Wren float targetHeight = mSmallSize; 5346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler float h = mScaler.getHeight(); 5356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler final boolean wasClosed = (mOldHeight == mSmallSize); 5366a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (wasClosed) { 5373ddab0dcc1039137f05a28ff86477601a223a0faChris Wren targetHeight = (force || currentHeight > mSmallSize) ? mNaturalHeight : mSmallSize; 5386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } else { 5393ddab0dcc1039137f05a28ff86477601a223a0faChris Wren targetHeight = (force || currentHeight < mNaturalHeight) ? mSmallSize : mNaturalHeight; 5406a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 541ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mScaleAnimation.isRunning()) { 542ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.cancel(); 543ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 5441408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCallback.setUserExpandedChild(mResizedView, targetHeight == mNaturalHeight); 5451408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCallback.expansionStateChanged(false); 5463ddab0dcc1039137f05a28ff86477601a223a0faChris Wren if (targetHeight != currentHeight) { 5473ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.setFloatValues(targetHeight); 5483ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.setupStartValues(); 5491408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek final View scaledView = mResizedView; 550be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mScaleAnimation.addListener(new AnimatorListenerAdapter() { 551be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi @Override 552be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi public void onAnimationEnd(Animator animation) { 553be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mCallback.setUserLockedChild(scaledView, false); 554be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi mScaleAnimation.removeListener(this); 555be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi } 556be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi }); 55728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi mFlingAnimationUtils.apply(mScaleAnimation, currentHeight, targetHeight, velocity); 5583ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.start(); 559343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek } else { 5601408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mCallback.setUserLockedChild(mResizedView, false); 5613ddab0dcc1039137f05a28ff86477601a223a0faChris Wren } 5624377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 5634377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpanding = false; 5644377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpansionStyle = NONE; 5654377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 566cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "wasClosed is: " + wasClosed); 567cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "currentHeight is: " + currentHeight); 568cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "mSmallSize is: " + mSmallSize); 569cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "targetHeight is: " + targetHeight); 5701408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek if (DEBUG) Log.d(TAG, "scale was finished on view: " + mResizedView); 57180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 57280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 57380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void clearView() { 5741408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mResizedView = null; 5756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5766a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 577b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren /** 578ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler * Use this to abort any pending expansions in progress. 579ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler */ 580ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public void cancel() { 58128c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi finishExpanding(true, 0f /* velocity */); 582ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler clearView(); 583ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 584ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler // reset the gesture detector 585ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler mSGD = new ScaleGestureDetector(mContext, mScaleGestureListener); 586ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 587ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 588ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler /** 5891408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek * Change the expansion mode to only observe movements and don't perform any resizing. 5901408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek * This is needed when the expanding is finished and the scroller kicks in, 5911408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek * performing an overscroll motion. We only want to shrink it again when we are not 5921408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek * overscrolled. 5931408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek * 5941408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek * @param onlyMovements Should only movements be observed? 5951408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek */ 5961408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek public void onlyObserveMovements(boolean onlyMovements) { 5971408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek mOnlyMovements = onlyMovements; 5981408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek } 5991408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek 6001408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek /** 601b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren * Triggers haptic feedback. 602b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren */ 603b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private synchronized void vibrate(long duration) { 604b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mVibrator == null) { 605b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mVibrator = (android.os.Vibrator) 606b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mContext.getSystemService(Context.VIBRATOR_SERVICE); 607b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 6087b41467704f941b11af6aace3e40993afc7f6c6fJohn Spurlock mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES); 6096a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 6106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler} 611b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 612