ExpandHelper.java revision e46647d28467ee9e88aafe2951a5736f494235da
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; 267a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandlerimport android.util.Slog; 279b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wrenimport android.view.Gravity; 286a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.MotionEvent; 296a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ScaleGestureDetector; 306a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View; 31b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wrenimport android.view.ViewConfiguration; 326a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ViewGroup; 336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View.OnClickListener; 346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 353c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wrenimport java.util.Stack; 363c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren 376a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerpublic class ExpandHelper implements Gefingerpoken, OnClickListener { 386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public interface Callback { 395de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren View getChildAtRawPosition(float x, float y); 406a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View getChildAtPosition(float x, float y); 413c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren View getPreviousChild(View currentChild); 4280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren boolean canChildBeExpanded(View v); 438fd12657e353a4a6f6d875a0d86850426fec00e8Chris Wren boolean setUserExpandedChild(View v, boolean userxpanded); 446a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 456a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 466a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private static final String TAG = "ExpandHelper"; 47e46647d28467ee9e88aafe2951a5736f494235daChris Wren protected static final boolean DEBUG = false; 483c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren protected static final boolean DEBUG_SCALE = false; 49e46647d28467ee9e88aafe2951a5736f494235daChris Wren protected static final boolean DEBUG_GLOW = false; 506a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private static final long EXPAND_DURATION = 250; 51ba925e8ecd9decff5701001a0190042d6797942dChris Wren private static final long GLOW_DURATION = 150; 52ba925e8ecd9decff5701001a0190042d6797942dChris Wren 5389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Set to false to disable focus-based gestures (two-finger pull). 5489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_DRAG = true; 5589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Set to false to disable scale-based gestures (both horizontal and vertical). 5689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_SPAN = true; 5789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Both gestures types may be active at the same time. 5889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // At least one gesture type should be active. 5989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // A variant of the screwdriver gesture will emerge from either gesture type. 606a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 6180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // amount of overstretch for maximum brightness expressed in U 6280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U 6380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float STRETCH_INTERVAL = 2f; 6480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 6580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // level of glow for a touch, without overstretch 6680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // overstretch fills the range (GLOW_BASE, 1.0] 6780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float GLOW_BASE = 0.5f; 6880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @SuppressWarnings("unused") 706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Context mContext; 716a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private boolean mStretching; 73b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean mPullingWithOneFinger; 74b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean mWatchingForPull; 75b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean mHasPopped; 765de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren private View mEventSource; 776a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private View mCurrView; 7880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private View mCurrViewTopGlow; 7980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private View mCurrViewBottomGlow; 806a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mOldHeight; 816a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mNaturalHeight; 8289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private float mInitialTouchFocusY; 83b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float mInitialTouchY; 846a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mInitialTouchSpan; 85b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mTouchSlop; 86b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mLastMotionY; 87b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float mPopLimit; 88b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private int mPopDuration; 896a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Callback mCallback; 906a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private ScaleGestureDetector mDetector; 916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private ViewScaler mScaler; 92ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mScaleAnimation; 93ba925e8ecd9decff5701001a0190042d6797942dChris Wren private AnimatorSet mGlowAnimationSet; 94ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mGlowTopAnimation; 95ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mGlowBottomAnimation; 96b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private Vibrator mVibrator; 976a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 986a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mSmallSize; 996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mLargeSize; 10080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private float mMaximumStretch; 1016a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1029b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren private int mGravity; 1039b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 104b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private View mScrollView; 105b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 1066a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private class ViewScaler { 1076a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View mView; 1089b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 1096a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ViewScaler() {} 1106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void setView(View v) { 1116a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView = v; 1126a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1136a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void setHeight(float h) { 1143c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.v(TAG, "SetHeight: setting to " + h); 1156a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler ViewGroup.LayoutParams lp = mView.getLayoutParams(); 1166a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = (int)h; 1176a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 11889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mView.requestLayout(); 1196a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1206a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public float getHeight() { 1216a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler int height = mView.getLayoutParams().height; 1226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (height < 0) { 1236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler height = mView.getMeasuredHeight(); 1246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return (float) height; 1266a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1276a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public int getNaturalHeight(int maximum) { 1286a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler ViewGroup.LayoutParams lp = mView.getLayoutParams(); 1293c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.v(TAG, "Inspecting a child of type: " + 1303c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mView.getClass().getName()); 1316a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler int oldHeight = lp.height; 1326a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 1336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 1346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.measure( 1356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View.MeasureSpec.makeMeasureSpec(mView.getMeasuredWidth(), 13689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren View.MeasureSpec.EXACTLY), 1376a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View.MeasureSpec.makeMeasureSpec(maximum, 13889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren View.MeasureSpec.AT_MOST)); 1396a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = oldHeight; 1406a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 1416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return mView.getMeasuredHeight(); 1426a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1436a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1446a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1453c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren class PopState { 1463c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren View mCurrView; 1473c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren View mCurrViewTopGlow; 1483c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren View mCurrViewBottomGlow; 1493c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren float mOldHeight; 1503c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren float mNaturalHeight; 1513c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren float mInitialTouchY; 1523c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 1533c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren 1543c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren private Stack<PopState> popStack; 1553c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren 1569b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren /** 1579b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * Handle expansion gestures to expand and contract children of the callback. 1589b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * 1599b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param context application context 1609b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param callback the container that holds the items to be manipulated 1619b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param small the smallest allowable size for the manuipulated items. 1629b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param large the largest allowable size for the manuipulated items. 1639b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param scoller if non-null also manipulate the scroll position to obey the gravity. 1649b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren */ 1656a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ExpandHelper(Context context, Callback callback, int small, int large) { 1666a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mSmallSize = small; 16780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mMaximumStretch = mSmallSize * STRETCH_INTERVAL; 1686a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mLargeSize = large; 1696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mContext = context; 1706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCallback = callback; 1713c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren popStack = new Stack<PopState>(); 1726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mScaler = new ViewScaler(); 1739b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = Gravity.TOP; 174ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); 175ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setDuration(EXPAND_DURATION); 176b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mPopLimit = mContext.getResources().getDimension(R.dimen.one_finger_pop_limit); 177b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mPopDuration = mContext.getResources().getInteger(R.integer.one_finger_pop_duration_ms); 178ba925e8ecd9decff5701001a0190042d6797942dChris Wren 1798900e631940fdffe7b941b56dc0f17e55345441eRomain Guy AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() { 1808900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 1818900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationStart(Animator animation) { 1828900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 1838900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 1848900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.VISIBLE); 1858900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1868900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1878900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 1888900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 1898900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationEnd(Animator animation) { 1908900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 1918900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 1928900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.INVISIBLE); 1938900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1948900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1958900e631940fdffe7b941b56dc0f17e55345441eRomain Guy }; 1968900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 197ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 1988900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowTopAnimation.addListener(glowVisibilityController); 199ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 2008900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowBottomAnimation.addListener(glowVisibilityController); 201ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet = new AnimatorSet(); 202ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.play(mGlowTopAnimation).with(mGlowBottomAnimation); 203ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setDuration(GLOW_DURATION); 204ba925e8ecd9decff5701001a0190042d6797942dChris Wren 205b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final ViewConfiguration configuration = ViewConfiguration.get(mContext); 206b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mTouchSlop = configuration.getScaledTouchSlop(); 207b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 2086a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mDetector = 2096a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler new ScaleGestureDetector(context, 2106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler new ScaleGestureDetector.SimpleOnScaleGestureListener() { 2116a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 2126a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onScaleBegin(ScaleGestureDetector detector) { 2133c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.v(TAG, "onscalebegin()"); 2145de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren float x = detector.getFocusX(); 2155de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren float y = detector.getFocusY(); 2165de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 2176a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler // your fingers have to be somewhat close to the bounds of the view in question 21889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mInitialTouchFocusY = detector.getFocusY(); 21989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mInitialTouchSpan = Math.abs(detector.getCurrentSpan()); 2203c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")"); 2216a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 222b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mStretching = initScale(findView(x, y)); 2236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return mStretching; 2246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 2266a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 2276a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onScale(ScaleGestureDetector detector) { 2283c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.v(TAG, "onscale() on " + mCurrView); 22989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren 23089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // are we scaling or dragging? 23189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float span = Math.abs(detector.getCurrentSpan()) - mInitialTouchSpan; 23289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren span *= USE_SPAN ? 1f : 0f; 23389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float drag = detector.getFocusY() - mInitialTouchFocusY; 23489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren drag *= USE_DRAG ? 1f : 0f; 2359b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; 23689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float pull = Math.abs(drag) + Math.abs(span) + 1f; 23789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; 238b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float target = hand + mOldHeight; 239b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float newHeight = clamp(target); 240b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mScaler.setHeight(newHeight); 241b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 242b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren setGlow(calculateGlow(target, newHeight)); 2436a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return true; 2446a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2456a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 2466a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 2476a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void onScaleEnd(ScaleGestureDetector detector) { 2483c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.v(TAG, "onscaleend()"); 2496a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler // I guess we're alone now 2503c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.d(TAG, "scale end"); 2516a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler finishScale(false); 2523c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren clearView(); 253b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mStretching = false; 2546a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2556a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler }); 2566a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2575de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 258b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float clamp(float target) { 259b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float out = target; 260b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out); 261b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out > mNaturalHeight ? mNaturalHeight : out; 262b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return out; 263b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 264b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 265b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private View findView(float x, float y) { 266b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren View v = null; 267b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 268b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 269b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 270b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren x += (float) location[0]; 271b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren y += (float) location[1]; 272b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtRawPosition(x, y); 273b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 274b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtPosition(x, y); 275b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 276b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return v; 277b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 278b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 279b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean isInside(View v, float x, float y) { 280b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, "isinside (" + x + ", " + y + ")"); 281b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 282b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (v == null) { 283b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, "isinside null subject"); 284b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 285b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 286b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 287b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 288b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 289b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren x += (float) location[0]; 290b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren y += (float) location[1]; 291b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, " to global (" + x + ", " + y + ")"); 292b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 293b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 294b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v.getLocationOnScreen(location); 295b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren x -= (float) location[0]; 296b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren y -= (float) location[1]; 297b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, " to local (" + x + ", " + y + ")"); 298b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")"); 299b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight()); 300b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return inside; 301b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 302b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 3035de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren public void setEventSource(View eventSource) { 3045de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren mEventSource = eventSource; 3055de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren } 3065de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 3079b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren public void setGravity(int gravity) { 3089b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = gravity; 3099b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren } 3109b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 311b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren public void setScrollView(View scrollView) { 312b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mScrollView = scrollView; 313b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 314b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 315b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float calculateGlow(float target, float actual) { 316b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren // glow if overscale 3173c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_GLOW) Slog.d(TAG, "target: " + target + " actual: " + actual); 318b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float stretch = (float) Math.abs((target - actual) / mMaximumStretch); 319b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f))); 3203c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_GLOW) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength); 321b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return (GLOW_BASE + strength * (1f - GLOW_BASE)); 322b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 323b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 32480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren public void setGlow(float glow) { 32589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (!mGlowAnimationSet.isRunning() || glow == 0f) { 32689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (mGlowAnimationSet.isRunning()) { 3273c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mGlowAnimationSet.end(); 32889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren } 329ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mCurrViewTopGlow != null && mCurrViewBottomGlow != null) { 330b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) { 331ba925e8ecd9decff5701001a0190042d6797942dChris Wren // animate glow in and out 332ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setTarget(mCurrViewTopGlow); 333ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setTarget(mCurrViewBottomGlow); 334ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setFloatValues(glow); 335ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setFloatValues(glow); 336ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setupStartValues(); 337ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.start(); 338ba925e8ecd9decff5701001a0190042d6797942dChris Wren } else { 339ba925e8ecd9decff5701001a0190042d6797942dChris Wren // set it explicitly in reponse to touches. 340ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewTopGlow.setAlpha(glow); 341ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewBottomGlow.setAlpha(glow); 3428900e631940fdffe7b941b56dc0f17e55345441eRomain Guy handleGlowVisibility(); 343ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 344ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 34580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 34680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 3478900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 3488900e631940fdffe7b941b56dc0f17e55345441eRomain Guy private void handleGlowVisibility() { 3498900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewTopGlow.setVisibility(mCurrViewTopGlow.getAlpha() <= 0.0f ? 3508900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 3518900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewBottomGlow.setVisibility(mCurrViewBottomGlow.getAlpha() <= 0.0f ? 3528900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 3538900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 3548900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 3556a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onInterceptTouchEvent(MotionEvent ev) { 3567a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "interceptTouch: act=" + (ev.getAction()) + 357b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren " stretching=" + mStretching + 358b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren " onefinger=" + mPullingWithOneFinger); 359b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren // check for a two-finger gesture 3606a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mDetector.onTouchEvent(ev); 361b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mStretching) { 362b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 363b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 364b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final int action = ev.getAction(); 365b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if ((action == MotionEvent.ACTION_MOVE) && mPullingWithOneFinger) { 366b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 367b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 368b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mScrollView != null && mScrollView.getScrollY() > 0) { 369b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 370b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 371b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren switch (action & MotionEvent.ACTION_MASK) { 372b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 373b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mWatchingForPull) { 374b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final int x = (int) ev.getX(); 375b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final int y = (int) ev.getY(); 376b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final int yDiff = y - mLastMotionY; 377b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (yDiff > mTouchSlop) { 378b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mLastMotionY = y; 379b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mPullingWithOneFinger = initScale(findView(x, y)); 380b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mPullingWithOneFinger) { 3813c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mInitialTouchY = mLastMotionY; 382b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mHasPopped = false; 383b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 384b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 385b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 386b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 387b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 388b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 389b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_DOWN: 390b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mWatchingForPull = isInside(mScrollView, ev.getX(), ev.getY()); 391b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mLastMotionY = (int) ev.getY(); 392b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 393b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 394b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_CANCEL: 395b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_UP: 396b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mPullingWithOneFinger) { 397b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren finishScale(false); 3983c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren clearView(); 399b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 400b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mPullingWithOneFinger = false; 401b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mWatchingForPull = false; 402b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 403b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 404b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return mPullingWithOneFinger; 405b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4066a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4076a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 4086a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onTouchEvent(MotionEvent ev) { 4096a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler final int action = ev.getAction(); 4103c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + (action) + 411b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren " stretching=" + mStretching + 412b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren " onefinger=" + mPullingWithOneFinger); 4136a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (mStretching) { 4146a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mDetector.onTouchEvent(ev); 4156a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4166a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler switch (action) { 417b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 418b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mPullingWithOneFinger) { 4193c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren float target = ev.getY() - mInitialTouchY + mOldHeight; 4203c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren float newHeight = clamp(target); 4213c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (mHasPopped || target > mPopLimit) { 422b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (!mHasPopped) { 423b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren vibrate(mPopDuration); 424b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mHasPopped = true; 425b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 426b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mScaler.setHeight(newHeight); 427b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren // glow if overscale 4283c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (target > mNaturalHeight) { 4293c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren View previous = mCallback.getPreviousChild(mCurrView); 4303c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (previous != null) { 4313c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(0f); 4323c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren pushView(previous); 4333c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren initScale(previous); 4343c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mInitialTouchY = ev.getY(); 4353c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren target = mOldHeight; 4363c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren newHeight = clamp(target); 4373c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mHasPopped = false; 4383c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } else { 4393c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(calculateGlow(target, newHeight)); 4403c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 4413c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } else if (target < mSmallSize && !popStack.empty()) { 4423c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(0f); 4433c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren initScale(popView()); 4443c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mInitialTouchY = ev.getY(); 4453c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(GLOW_BASE); 4463c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } else { 4473c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(calculateGlow(target, newHeight)); 4483c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 449b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 4503c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (target < mSmallSize && !popStack.empty()) { 4513c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(0f); 4523c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren initScale(popView()); 4533c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mInitialTouchY = ev.getY(); 4543c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(GLOW_BASE); 4553c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } else { 4563c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren setGlow(calculateGlow(4f * target, mSmallSize)); 4573c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 458b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 459b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 460b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 461b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 462b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4636a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_UP: 4646a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_CANCEL: 4657a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "cancel"); 4666a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mStretching = false; 467b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mPullingWithOneFinger) { 468b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren finishScale(false); 469b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mPullingWithOneFinger = false; 470b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 47180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren clearView(); 4726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler break; 4736a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4746a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return true; 4756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4766a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private boolean initScale(View v) { 4776a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (v != null) { 4787a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "scale begins on view: " + v); 47980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setView(v); 48080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setGlow(GLOW_BASE); 4816a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mScaler.setView(v); 4826a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mOldHeight = mScaler.getHeight(); 48380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (mCallback.canChildBeExpanded(v)) { 4847a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "working on an expandable child"); 48580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); 48680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } else { 4877a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "working on a non-expandable child"); 48880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mNaturalHeight = mOldHeight; 48980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 4907a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight + 4916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler " mNaturalHeight: " + mNaturalHeight); 4926a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler v.getParent().requestDisallowInterceptTouchEvent(true); 493b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 494b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 495b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 4966a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4976a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4986a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 4996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private void finishScale(boolean force) { 5006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler float h = mScaler.getHeight(); 5016a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler final boolean wasClosed = (mOldHeight == mSmallSize); 5026a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (wasClosed) { 5036a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler h = (force || h > mSmallSize) ? mNaturalHeight : mSmallSize; 5046a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } else { 5056a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler h = (force || h < mNaturalHeight) ? mSmallSize : mNaturalHeight; 5066a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 507ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mScaleAnimation.isRunning()) { 508ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.cancel(); 509ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 510ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setFloatValues(h); 511ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setupStartValues(); 512ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.start(); 51380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setGlow(0f); 5148fd12657e353a4a6f6d875a0d86850426fec00e8Chris Wren mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight); 5157a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView); 51680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 51780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 51880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void clearView() { 5193c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren while (!popStack.empty()) { 5203c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren popStack.pop(); 5213c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 5226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCurrView = null; 52380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = null; 52480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = null; 52580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 52680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 52780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void setView(View v) { 52889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mCurrView = v; 52980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (v instanceof ViewGroup) { 53080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren ViewGroup g = (ViewGroup) v; 53180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = g.findViewById(R.id.top_glow); 53280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = g.findViewById(R.id.bottom_glow); 53389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) { 534b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren String debugLog = "Looking for glows: " + 53580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewTopGlow != null ? "found top " : "didn't find top") + 53680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom"); 5377a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler Slog.v(TAG, debugLog); 53880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 53980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 5406a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 5423c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren private void pushView(View v) { 5433c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren PopState state = new PopState(); 5443c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren state.mCurrView = mCurrView; 5453c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren state.mCurrViewTopGlow = mCurrViewTopGlow; 5463c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren state.mCurrViewBottomGlow = mCurrViewBottomGlow; 5473c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren state.mOldHeight = mOldHeight; 5483c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren state.mNaturalHeight = mNaturalHeight; 5493c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren state.mInitialTouchY = mInitialTouchY; 5503c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren popStack.push(state); 5513c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 5523c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren 5533c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren private View popView() { 5543c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (popStack.empty()) { 5553c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren return null; 5563c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 5573c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren 5583c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren PopState state = popStack.pop(); 5593c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mCurrView = state.mCurrView; 5603c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mCurrViewTopGlow = state.mCurrViewTopGlow; 5613c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mCurrViewBottomGlow = state.mCurrViewBottomGlow; 5623c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mOldHeight = state.mOldHeight; 5633c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mNaturalHeight = state.mNaturalHeight; 5643c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mInitialTouchY = state.mInitialTouchY; 5653c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren 5663c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren return mCurrView; 5673c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren } 5683c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren 5696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 5706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void onClick(View v) { 5716a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler initScale(v); 5726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler finishScale(true); 5733c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren clearView(); 574b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 5756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 576b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren /** 577b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren * Triggers haptic feedback. 578b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren */ 579b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private synchronized void vibrate(long duration) { 580b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mVibrator == null) { 581b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mVibrator = (android.os.Vibrator) 582b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mContext.getSystemService(Context.VIBRATOR_SERVICE); 583b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 584b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mVibrator.vibrate(duration); 5856a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5866a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler} 587b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 588