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; 22ba925e8ecd9decff5701001a0190042d6797942dChris Wrenimport android.animation.AnimatorSet; 236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.animation.ObjectAnimator; 246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.content.Context; 25b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wrenimport android.os.Vibrator; 26cd686b5b6d4166b510df8e32138479a9559bc117John Spurlockimport android.util.Log; 279b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wrenimport android.view.Gravity; 286a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.MotionEvent; 296a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ScaleGestureDetector; 30ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandlerimport android.view.ScaleGestureDetector.OnScaleGestureListener; 316a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View; 32de84f0e77ea2bf713d15c290264059a413c2486aJohn Spurlockimport android.view.View.OnClickListener; 33b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wrenimport android.view.ViewConfiguration; 346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ViewGroup; 356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 366a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerpublic class ExpandHelper implements Gefingerpoken, OnClickListener { 376a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public interface Callback { 385de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren View getChildAtRawPosition(float x, float y); 396a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View getChildAtPosition(float x, float y); 4080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren boolean canChildBeExpanded(View v); 4151c7510e493680b4aca1ed7695b35c52d2cd63ffChris Wren void setUserExpandedChild(View v, boolean userExpanded); 4251c7510e493680b4aca1ed7695b35c52d2cd63ffChris Wren void setUserLockedChild(View v, boolean userLocked); 436a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 446a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 456a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private static final String TAG = "ExpandHelper"; 46e46647d28467ee9e88aafe2951a5736f494235daChris Wren protected static final boolean DEBUG = false; 473c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren protected static final boolean DEBUG_SCALE = false; 48e46647d28467ee9e88aafe2951a5736f494235daChris Wren protected static final boolean DEBUG_GLOW = false; 496a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private static final long EXPAND_DURATION = 250; 50ba925e8ecd9decff5701001a0190042d6797942dChris Wren private static final long GLOW_DURATION = 150; 51ba925e8ecd9decff5701001a0190042d6797942dChris Wren 524377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // Set to false to disable focus-based gestures (spread-finger vertical pull). 5389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_DRAG = true; 5489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Set to false to disable scale-based gestures (both horizontal and vertical). 5589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_SPAN = true; 5689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Both gestures types may be active at the same time. 5789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // At least one gesture type should be active. 5889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // A variant of the screwdriver gesture will emerge from either gesture type. 596a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 6080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // amount of overstretch for maximum brightness expressed in U 6180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U 6280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float STRETCH_INTERVAL = 2f; 6380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 6480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // level of glow for a touch, without overstretch 6580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // overstretch fills the range (GLOW_BASE, 1.0] 6680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float GLOW_BASE = 0.5f; 6780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 686a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @SuppressWarnings("unused") 696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Context mContext; 706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 714377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private boolean mExpanding; 724377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int NONE = 0; 734377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int BLINDS = 1<<0; 744377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int PULL = 1<<1; 754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private static final int STRETCH = 1<<2; 764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private int mExpansionStyle = NONE; 77b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean mWatchingForPull; 78b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean mHasPopped; 795de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren private View mEventSource; 806a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private View mCurrView; 8180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private View mCurrViewTopGlow; 8280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private View mCurrViewBottomGlow; 836a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mOldHeight; 846a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mNaturalHeight; 8589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private float mInitialTouchFocusY; 86b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float mInitialTouchY; 876a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mInitialTouchSpan; 88cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren private float mLastFocusY; 89cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren private float mLastSpanY; 90b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mTouchSlop; 91b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mLastMotionY; 92b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float mPopLimit; 93b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mPopDuration; 944377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private float mPullGestureMinXSpan; 956a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Callback mCallback; 964377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private ScaleGestureDetector mSGD; 976a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private ViewScaler mScaler; 98ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mScaleAnimation; 99ba925e8ecd9decff5701001a0190042d6797942dChris Wren private AnimatorSet mGlowAnimationSet; 100ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mGlowTopAnimation; 101ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mGlowBottomAnimation; 102b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private Vibrator mVibrator; 1036a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1046a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mSmallSize; 1056a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mLargeSize; 10680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private float mMaximumStretch; 1076a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1089b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren private int mGravity; 1099b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 110b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private View mScrollView; 111b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 112209bede6b9edb9171e5bee4077b48e35004a37b4John Spurlock private OnScaleGestureListener mScaleGestureListener 113ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler = new ScaleGestureDetector.SimpleOnScaleGestureListener() { 114ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler @Override 115ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public boolean onScaleBegin(ScaleGestureDetector detector) { 116cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()"); 117ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler float focusX = detector.getFocusX(); 118ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler float focusY = detector.getFocusY(); 119ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 120ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler final View underFocus = findView(focusX, focusY); 121ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler if (underFocus != null) { 122ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler startExpanding(underFocus, STRETCH); 123ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 124ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler return mExpanding; 125ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 126ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 127ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler @Override 128ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public boolean onScale(ScaleGestureDetector detector) { 129cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mCurrView); 130ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler return true; 131ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 132ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 133ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler @Override 134ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public void onScaleEnd(ScaleGestureDetector detector) { 135ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 136ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler }; 137ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 1386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private class ViewScaler { 1396a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View mView; 1409b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 1416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ViewScaler() {} 1426a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void setView(View v) { 1436a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView = v; 1446a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1456a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void setHeight(float h) { 146cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h); 1476a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler ViewGroup.LayoutParams lp = mView.getLayoutParams(); 1486a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = (int)h; 1496a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 15089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mView.requestLayout(); 1516a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1526a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public float getHeight() { 1536a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler int height = mView.getLayoutParams().height; 1546a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (height < 0) { 1556a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler height = mView.getMeasuredHeight(); 1566a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1574377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return height; 1586a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1596a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public int getNaturalHeight(int maximum) { 1606a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler ViewGroup.LayoutParams lp = mView.getLayoutParams(); 161cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "Inspecting a child of type: " + 1623c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mView.getClass().getName()); 1636a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler int oldHeight = lp.height; 1646a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 1656a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 1666a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.measure( 1676a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View.MeasureSpec.makeMeasureSpec(mView.getMeasuredWidth(), 16889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren View.MeasureSpec.EXACTLY), 1696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View.MeasureSpec.makeMeasureSpec(maximum, 17089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren View.MeasureSpec.AT_MOST)); 1716a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = oldHeight; 1726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 1736a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return mView.getMeasuredHeight(); 1746a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1766a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1779b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren /** 1789b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * Handle expansion gestures to expand and contract children of the callback. 1799b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * 1809b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param context application context 1819b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param callback the container that holds the items to be manipulated 1829b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param small the smallest allowable size for the manuipulated items. 1839b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param large the largest allowable size for the manuipulated items. 1849b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren */ 1856a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ExpandHelper(Context context, Callback callback, int small, int large) { 1866a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mSmallSize = small; 18780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mMaximumStretch = mSmallSize * STRETCH_INTERVAL; 1886a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mLargeSize = large; 1896a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mContext = context; 1906a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCallback = callback; 1916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mScaler = new ViewScaler(); 1929b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = Gravity.TOP; 193ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); 194ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setDuration(EXPAND_DURATION); 1954377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold); 1964377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms); 1974377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min); 198ba925e8ecd9decff5701001a0190042d6797942dChris Wren 1998900e631940fdffe7b941b56dc0f17e55345441eRomain Guy AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() { 2008900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 2018900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationStart(Animator animation) { 2028900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 2038900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 2048900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.VISIBLE); 2058900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2068900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2078900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 2088900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 2098900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationEnd(Animator animation) { 2108900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 2118900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 2128900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.INVISIBLE); 2138900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2148900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2158900e631940fdffe7b941b56dc0f17e55345441eRomain Guy }; 2168900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 217ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 2188900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowTopAnimation.addListener(glowVisibilityController); 219ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 2208900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowBottomAnimation.addListener(glowVisibilityController); 221ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet = new AnimatorSet(); 222ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.play(mGlowTopAnimation).with(mGlowBottomAnimation); 223ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setDuration(GLOW_DURATION); 224ba925e8ecd9decff5701001a0190042d6797942dChris Wren 225b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final ViewConfiguration configuration = ViewConfiguration.get(mContext); 226b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mTouchSlop = configuration.getScaledTouchSlop(); 227b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 228ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler mSGD = new ScaleGestureDetector(context, mScaleGestureListener); 2296a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2305de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 2314377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private void updateExpansion() { 232cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "updateExpansion()"); 2334377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // are we scaling or dragging? 234cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren float span = mSGD.getCurrentSpan() - mInitialTouchSpan; 2354377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler span *= USE_SPAN ? 1f : 0f; 2364377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float drag = mSGD.getFocusY() - mInitialTouchFocusY; 2374377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler drag *= USE_DRAG ? 1f : 0f; 2384377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; 2394377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float pull = Math.abs(drag) + Math.abs(span) + 1f; 2404377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; 2414377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float target = hand + mOldHeight; 2424377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float newHeight = clamp(target); 2434377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mScaler.setHeight(newHeight); 2444377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 2454377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler setGlow(calculateGlow(target, newHeight)); 246cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastFocusY = mSGD.getFocusY(); 247cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastSpanY = mSGD.getCurrentSpan(); 2484377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 2494377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 250b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float clamp(float target) { 251b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float out = target; 252b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out); 253b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out > mNaturalHeight ? mNaturalHeight : out; 254b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return out; 255b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 256b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 257b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private View findView(float x, float y) { 258b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren View v = null; 259b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 260b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 261b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 2624377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x += location[0]; 2634377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y += location[1]; 264b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtRawPosition(x, y); 265b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 266b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtPosition(x, y); 267b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 268b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return v; 269b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 270b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 271b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean isInside(View v, float x, float y) { 272cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "isinside (" + x + ", " + y + ")"); 273b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 274b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (v == null) { 275cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "isinside null subject"); 276b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 277b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 278b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 279b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 280b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 2814377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x += location[0]; 2824377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y += location[1]; 283cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, " to global (" + x + ", " + y + ")"); 284b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 285b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 286b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v.getLocationOnScreen(location); 2874377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x -= location[0]; 2884377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y -= location[1]; 289cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, " to local (" + x + ", " + y + ")"); 290cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")"); 291b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight()); 292b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return inside; 293b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 294b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 2955de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren public void setEventSource(View eventSource) { 2965de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren mEventSource = eventSource; 2975de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren } 2985de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 2999b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren public void setGravity(int gravity) { 3009b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = gravity; 3019b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren } 3029b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 303b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren public void setScrollView(View scrollView) { 304b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mScrollView = scrollView; 305b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 306b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 307b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float calculateGlow(float target, float actual) { 308b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren // glow if overscale 309cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_GLOW) Log.d(TAG, "target: " + target + " actual: " + actual); 3104377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float stretch = Math.abs((target - actual) / mMaximumStretch); 311b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f))); 312cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_GLOW) Log.d(TAG, "stretch: " + stretch + " strength: " + strength); 313b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return (GLOW_BASE + strength * (1f - GLOW_BASE)); 314b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 315b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 31680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren public void setGlow(float glow) { 31789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (!mGlowAnimationSet.isRunning() || glow == 0f) { 31889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (mGlowAnimationSet.isRunning()) { 3193c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mGlowAnimationSet.end(); 32089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren } 321ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mCurrViewTopGlow != null && mCurrViewBottomGlow != null) { 322b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) { 323ba925e8ecd9decff5701001a0190042d6797942dChris Wren // animate glow in and out 324ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setTarget(mCurrViewTopGlow); 325ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setTarget(mCurrViewBottomGlow); 326ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setFloatValues(glow); 327ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setFloatValues(glow); 328ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setupStartValues(); 329ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.start(); 330ba925e8ecd9decff5701001a0190042d6797942dChris Wren } else { 331ba925e8ecd9decff5701001a0190042d6797942dChris Wren // set it explicitly in reponse to touches. 332ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewTopGlow.setAlpha(glow); 333ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewBottomGlow.setAlpha(glow); 3348900e631940fdffe7b941b56dc0f17e55345441eRomain Guy handleGlowVisibility(); 335ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 336ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 33780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 33880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 3398900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 3408900e631940fdffe7b941b56dc0f17e55345441eRomain Guy private void handleGlowVisibility() { 3418900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewTopGlow.setVisibility(mCurrViewTopGlow.getAlpha() <= 0.0f ? 3428900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 3438900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewBottomGlow.setVisibility(mCurrViewBottomGlow.getAlpha() <= 0.0f ? 3448900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 3458900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 3468900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 3474377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler @Override 3486a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onInterceptTouchEvent(MotionEvent ev) { 3494377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int action = ev.getAction(); 350cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) + 3514377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " expanding=" + mExpanding + 3524377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 3534377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 3544377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 3554377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // check for a spread-finger vertical pull gesture 3564377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mSGD.onTouchEvent(ev); 3574377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int x = (int) mSGD.getFocusX(); 3584377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int y = (int) mSGD.getFocusY(); 359cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 360cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchFocusY = y; 361cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchSpan = mSGD.getCurrentSpan(); 362cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastFocusY = mInitialTouchFocusY; 363cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastSpanY = mInitialTouchSpan; 364cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.d(TAG, "set initial span: " + mInitialTouchSpan); 365cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 3664377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mExpanding) { 367b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 368b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 3694377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) { 3704377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // we've begun Venetian blinds style expansion 3714377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return true; 3724377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 3734377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final float xspan = mSGD.getCurrentSpanX(); 3744377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if ((action == MotionEvent.ACTION_MOVE && 3754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler xspan > mPullGestureMinXSpan && 3764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler xspan > mSGD.getCurrentSpanY())) { 3774377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // detect a vertical pulling gesture with fingers somewhat separated 378cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)"); 3794377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 3804377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final View underFocus = findView(x, y); 3814377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (underFocus != null) { 3824377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(underFocus, PULL); 3834377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 384b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 385b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 386b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mScrollView != null && mScrollView.getScrollY() > 0) { 387b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 388b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 3894377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // Now look for other gestures 390b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren switch (action & MotionEvent.ACTION_MASK) { 391b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 392b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mWatchingForPull) { 393b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final int yDiff = y - mLastMotionY; 394b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (yDiff > mTouchSlop) { 395cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); 396b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mLastMotionY = y; 3974377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final View underFocus = findView(x, y); 3984377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (underFocus != null) { 3994377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(underFocus, BLINDS); 4003c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mInitialTouchY = mLastMotionY; 401b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mHasPopped = false; 402b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 403b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 404b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 405b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 406b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 407b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 408b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_DOWN: 4094377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mWatchingForPull = isInside(mScrollView, x, y); 4104377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mLastMotionY = y; 411b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 412b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 413b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_CANCEL: 414b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_UP: 415cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "up/cancel"); 4164377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(false); 4174377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler clearView(); 418b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 419b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4204377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return mExpanding; 421b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 4244377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler @Override 4256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onTouchEvent(MotionEvent ev) { 426cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren final int action = ev.getActionMasked(); 427cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG_SCALE) Log.d(TAG, "touch: act=" + MotionEvent.actionToString(action) + 4284377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " expanding=" + mExpanding + 4294377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 4304377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 4314377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 4324377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4334377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mSGD.onTouchEvent(ev); 4344377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler switch (action) { 436b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 4374377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (0 != (mExpansionStyle & BLINDS)) { 43886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight; 43986d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final float newHeight = clamp(rawHeight); 44086d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final boolean wasClosed = (mOldHeight == mSmallSize); 44186d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren boolean isFinished = false; 44286d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (rawHeight > mNaturalHeight) { 44386d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren isFinished = true; 44486d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 44586d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (rawHeight < mSmallSize) { 44686d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren isFinished = true; 44786d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 44886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 44986d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final float pull = Math.abs(ev.getY() - mInitialTouchY); 45086d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (mHasPopped || pull > mPopLimit) { 451b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (!mHasPopped) { 452b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren vibrate(mPopDuration); 453b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mHasPopped = true; 454b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 45586d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 45686d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 45786d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (mHasPopped) { 458b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mScaler.setHeight(newHeight); 45986d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren setGlow(GLOW_BASE); 460b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 46186d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren setGlow(calculateGlow(4f * pull, 0f)); 46286d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 46386d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 4644377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int x = (int) mSGD.getFocusX(); 4654377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int y = (int) mSGD.getFocusY(); 4664377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler View underFocus = findView(x, y); 4674377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (isFinished && underFocus != null && underFocus != mCurrView) { 4684377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(false); // @@@ needed? 4694377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(underFocus, BLINDS); 4704377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mInitialTouchY = y; 47186d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren mHasPopped = false; 472b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 473b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 474b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mExpanding) { 4774377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler updateExpansion(); 4784377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return true; 4794377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 4804377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 481b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 482b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 483cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 484cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren case MotionEvent.ACTION_POINTER_UP: 485cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren case MotionEvent.ACTION_POINTER_DOWN: 486cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "pointer change"); 487cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchY += mSGD.getFocusY() - mLastFocusY; 488cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchSpan += mSGD.getCurrentSpan() - mLastSpanY; 489cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren break; 490cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 4916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_UP: 4926a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_CANCEL: 493cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "up/cancel"); 4944377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(false); 49580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren clearView(); 4966a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler break; 4976a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4986a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return true; 4996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5004377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 5014377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private void startExpanding(View v, int expandType) { 502cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mExpansionStyle = expandType; 503cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (mExpanding && v == mCurrView) { 504cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren return; 505cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren } 5064377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpanding = true; 507cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v); 5084377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mCallback.setUserLockedChild(v, true); 5094377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler setView(v); 5104377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler setGlow(GLOW_BASE); 5114377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mScaler.setView(v); 5124377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mOldHeight = mScaler.getHeight(); 5134377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mCallback.canChildBeExpanded(v)) { 514cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "working on an expandable child"); 5154377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); 516b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 517cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "working on a non-expandable child"); 5184377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mNaturalHeight = mOldHeight; 5196a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 520cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight + 5214377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " mNaturalHeight: " + mNaturalHeight); 5224377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler v.getParent().requestDisallowInterceptTouchEvent(true); 5236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 5254377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private void finishExpanding(boolean force) { 5264377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (!mExpanding) return; 5274377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 528cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mCurrView); 529cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 5303ddab0dcc1039137f05a28ff86477601a223a0faChris Wren float currentHeight = mScaler.getHeight(); 5313ddab0dcc1039137f05a28ff86477601a223a0faChris Wren float targetHeight = mSmallSize; 5326a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler float h = mScaler.getHeight(); 5336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler final boolean wasClosed = (mOldHeight == mSmallSize); 5346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (wasClosed) { 5353ddab0dcc1039137f05a28ff86477601a223a0faChris Wren targetHeight = (force || currentHeight > mSmallSize) ? mNaturalHeight : mSmallSize; 5366a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } else { 5373ddab0dcc1039137f05a28ff86477601a223a0faChris Wren targetHeight = (force || currentHeight < mNaturalHeight) ? mSmallSize : mNaturalHeight; 5386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 539ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mScaleAnimation.isRunning()) { 540ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.cancel(); 541ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 54280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setGlow(0f); 5438fd12657e353a4a6f6d875a0d86850426fec00e8Chris Wren mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight); 5443ddab0dcc1039137f05a28ff86477601a223a0faChris Wren if (targetHeight != currentHeight) { 5453ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.setFloatValues(targetHeight); 5463ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.setupStartValues(); 5473ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.start(); 5483ddab0dcc1039137f05a28ff86477601a223a0faChris Wren } 5493ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mCallback.setUserLockedChild(mCurrView, false); 5504377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 5514377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpanding = false; 5524377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpansionStyle = NONE; 5534377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 554cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "wasClosed is: " + wasClosed); 555cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "currentHeight is: " + currentHeight); 556cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "mSmallSize is: " + mSmallSize); 557cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "targetHeight is: " + targetHeight); 558cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock if (DEBUG) Log.d(TAG, "scale was finished on view: " + mCurrView); 55980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 56080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 56180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void clearView() { 5626a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCurrView = null; 56380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = null; 56480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = null; 56580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 56680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 56780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void setView(View v) { 56889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mCurrView = v; 56980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (v instanceof ViewGroup) { 57080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren ViewGroup g = (ViewGroup) v; 57180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = g.findViewById(R.id.top_glow); 57280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = g.findViewById(R.id.bottom_glow); 57389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) { 574b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren String debugLog = "Looking for glows: " + 57580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewTopGlow != null ? "found top " : "didn't find top") + 57680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom"); 577cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock Log.v(TAG, debugLog); 57880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 57980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 5806a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5816a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 5826a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 5836a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void onClick(View v) { 5844377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(v, STRETCH); 5854377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(true); 5863c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren clearView(); 587b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 5886a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 589b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren /** 590ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler * Use this to abort any pending expansions in progress. 591ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler */ 592ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public void cancel() { 593ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler finishExpanding(true); 594ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler clearView(); 595ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 596ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler // reset the gesture detector 597ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler mSGD = new ScaleGestureDetector(mContext, mScaleGestureListener); 598ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 599ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 600ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler /** 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 } 608b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mVibrator.vibrate(duration); 6096a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 6106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler} 611b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 612