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; 30ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandlerimport android.view.ScaleGestureDetector.OnScaleGestureListener; 316a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View; 32b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wrenimport android.view.ViewConfiguration; 336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ViewGroup; 346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View.OnClickListener; 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); 413ddab0dcc1039137f05a28ff86477601a223a0faChris Wren boolean setUserExpandedChild(View v, boolean userExpanded); 423ddab0dcc1039137f05a28ff86477601a223a0faChris Wren boolean 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 112ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler private OnScaleGestureListener mScaleGestureListener 113ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler = new ScaleGestureDetector.SimpleOnScaleGestureListener() { 114ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler @Override 115ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public boolean onScaleBegin(ScaleGestureDetector detector) { 116ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler if (DEBUG_SCALE) Slog.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) { 129ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler if (DEBUG_SCALE) Slog.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) { 1463c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.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(); 1613c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_SCALE) Slog.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 * @param scoller if non-null also manipulate the scroll position to obey the gravity. 1859b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren */ 1866a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ExpandHelper(Context context, Callback callback, int small, int large) { 1876a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mSmallSize = small; 18880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mMaximumStretch = mSmallSize * STRETCH_INTERVAL; 1896a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mLargeSize = large; 1906a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mContext = context; 1916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCallback = callback; 1926a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mScaler = new ViewScaler(); 1939b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = Gravity.TOP; 194ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); 195ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setDuration(EXPAND_DURATION); 1964377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold); 1974377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms); 1984377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min); 199ba925e8ecd9decff5701001a0190042d6797942dChris Wren 2008900e631940fdffe7b941b56dc0f17e55345441eRomain Guy AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() { 2018900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 2028900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationStart(Animator animation) { 2038900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 2048900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 2058900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.VISIBLE); 2068900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2078900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2088900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 2098900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 2108900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationEnd(Animator animation) { 2118900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 2128900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 2138900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.INVISIBLE); 2148900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2158900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2168900e631940fdffe7b941b56dc0f17e55345441eRomain Guy }; 2178900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 218ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 2198900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowTopAnimation.addListener(glowVisibilityController); 220ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 2218900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowBottomAnimation.addListener(glowVisibilityController); 222ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet = new AnimatorSet(); 223ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.play(mGlowTopAnimation).with(mGlowBottomAnimation); 224ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setDuration(GLOW_DURATION); 225ba925e8ecd9decff5701001a0190042d6797942dChris Wren 226b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final ViewConfiguration configuration = ViewConfiguration.get(mContext); 227b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mTouchSlop = configuration.getScaledTouchSlop(); 228b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 229ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler mSGD = new ScaleGestureDetector(context, mScaleGestureListener); 2306a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2315de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 2324377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private void updateExpansion() { 233cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG_SCALE) Slog.v(TAG, "updateExpansion()"); 2344377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // are we scaling or dragging? 235cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren float span = mSGD.getCurrentSpan() - mInitialTouchSpan; 2364377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler span *= USE_SPAN ? 1f : 0f; 2374377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float drag = mSGD.getFocusY() - mInitialTouchFocusY; 2384377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler drag *= USE_DRAG ? 1f : 0f; 2394377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; 2404377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float pull = Math.abs(drag) + Math.abs(span) + 1f; 2414377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; 2424377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float target = hand + mOldHeight; 2434377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float newHeight = clamp(target); 2444377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mScaler.setHeight(newHeight); 2454377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 2464377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler setGlow(calculateGlow(target, newHeight)); 247cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastFocusY = mSGD.getFocusY(); 248cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastSpanY = mSGD.getCurrentSpan(); 2494377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 2504377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 251b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float clamp(float target) { 252b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float out = target; 253b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out < mSmallSize ? mSmallSize : (out > mLargeSize ? mLargeSize : out); 254b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren out = out > mNaturalHeight ? mNaturalHeight : out; 255b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return out; 256b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 257b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 258b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private View findView(float x, float y) { 259b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren View v = null; 260b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 261b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 262b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 2634377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x += location[0]; 2644377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y += location[1]; 265b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtRawPosition(x, y); 266b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 267b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v = mCallback.getChildAtPosition(x, y); 268b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 269b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return v; 270b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 271b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 272b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private boolean isInside(View v, float x, float y) { 273b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, "isinside (" + x + ", " + y + ")"); 274b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 275b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (v == null) { 276b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, "isinside null subject"); 277b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 278b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 279b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mEventSource != null) { 280b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 281b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mEventSource.getLocationOnScreen(location); 2824377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x += location[0]; 2834377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y += location[1]; 284b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, " to global (" + x + ", " + y + ")"); 285b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 286b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren int[] location = new int[2]; 287b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren v.getLocationOnScreen(location); 2884377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler x -= location[0]; 2894377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler y -= location[1]; 290b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, " to local (" + x + ", " + y + ")"); 291b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (DEBUG) Slog.d(TAG, " inside (" + v.getWidth() + ", " + v.getHeight() + ")"); 292b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight()); 293b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return inside; 294b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 295b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 2965de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren public void setEventSource(View eventSource) { 2975de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren mEventSource = eventSource; 2985de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren } 2995de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 3009b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren public void setGravity(int gravity) { 3019b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = gravity; 3029b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren } 3039b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 304b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren public void setScrollView(View scrollView) { 305b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mScrollView = scrollView; 306b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 307b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 308b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private float calculateGlow(float target, float actual) { 309b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren // glow if overscale 3103c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_GLOW) Slog.d(TAG, "target: " + target + " actual: " + actual); 3114377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler float stretch = Math.abs((target - actual) / mMaximumStretch); 312b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f))); 3133c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren if (DEBUG_GLOW) Slog.d(TAG, "stretch: " + stretch + " strength: " + strength); 314b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return (GLOW_BASE + strength * (1f - GLOW_BASE)); 315b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 316b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 31780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren public void setGlow(float glow) { 31889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (!mGlowAnimationSet.isRunning() || glow == 0f) { 31989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (mGlowAnimationSet.isRunning()) { 3203c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mGlowAnimationSet.end(); 32189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren } 322ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mCurrViewTopGlow != null && mCurrViewBottomGlow != null) { 323b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) { 324ba925e8ecd9decff5701001a0190042d6797942dChris Wren // animate glow in and out 325ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setTarget(mCurrViewTopGlow); 326ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setTarget(mCurrViewBottomGlow); 327ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setFloatValues(glow); 328ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setFloatValues(glow); 329ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setupStartValues(); 330ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.start(); 331ba925e8ecd9decff5701001a0190042d6797942dChris Wren } else { 332ba925e8ecd9decff5701001a0190042d6797942dChris Wren // set it explicitly in reponse to touches. 333ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewTopGlow.setAlpha(glow); 334ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewBottomGlow.setAlpha(glow); 3358900e631940fdffe7b941b56dc0f17e55345441eRomain Guy handleGlowVisibility(); 336ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 337ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 33880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 33980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 3408900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 3418900e631940fdffe7b941b56dc0f17e55345441eRomain Guy private void handleGlowVisibility() { 3428900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewTopGlow.setVisibility(mCurrViewTopGlow.getAlpha() <= 0.0f ? 3438900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 3448900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewBottomGlow.setVisibility(mCurrViewBottomGlow.getAlpha() <= 0.0f ? 3458900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 3468900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 3478900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 3484377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler @Override 3496a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onInterceptTouchEvent(MotionEvent ev) { 3504377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int action = ev.getAction(); 3514377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG_SCALE) Slog.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) + 3524377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " expanding=" + mExpanding + 3534377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 3544377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 3554377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 3564377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // check for a spread-finger vertical pull gesture 3574377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mSGD.onTouchEvent(ev); 3584377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int x = (int) mSGD.getFocusX(); 3594377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int y = (int) mSGD.getFocusY(); 360cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 361cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchFocusY = y; 362cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchSpan = mSGD.getCurrentSpan(); 363cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastFocusY = mInitialTouchFocusY; 364cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mLastSpanY = mInitialTouchSpan; 365cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG_SCALE) Slog.d(TAG, "set initial span: " + mInitialTouchSpan); 366cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 3674377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mExpanding) { 368b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 369b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 3704377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) { 3714377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // we've begun Venetian blinds style expansion 3724377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return true; 3734377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 3744377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final float xspan = mSGD.getCurrentSpanX(); 3754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if ((action == MotionEvent.ACTION_MOVE && 3764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler xspan > mPullGestureMinXSpan && 3774377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler xspan > mSGD.getCurrentSpanY())) { 3784377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // detect a vertical pulling gesture with fingers somewhat separated 3794377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG_SCALE) Slog.v(TAG, "got pull gesture (xspan=" + xspan + "px)"); 3804377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 3814377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final View underFocus = findView(x, y); 3824377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (underFocus != null) { 3834377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(underFocus, PULL); 3844377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 385b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 386b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 387b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mScrollView != null && mScrollView.getScrollY() > 0) { 388b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return false; 389b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 3904377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler // Now look for other gestures 391b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren switch (action & MotionEvent.ACTION_MASK) { 392b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 393b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mWatchingForPull) { 394b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren final int yDiff = y - mLastMotionY; 395b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (yDiff > mTouchSlop) { 3964377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG) Slog.v(TAG, "got venetian gesture (dy=" + yDiff + "px)"); 397b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mLastMotionY = y; 3984377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final View underFocus = findView(x, y); 3994377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (underFocus != null) { 4004377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(underFocus, BLINDS); 4013c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren mInitialTouchY = mLastMotionY; 402b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mHasPopped = false; 403b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 404b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 405b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 406b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 407b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 408b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 409b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_DOWN: 4104377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mWatchingForPull = isInside(mScrollView, x, y); 4114377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mLastMotionY = y; 412b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 413b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 414b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_CANCEL: 415b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_UP: 4164377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG) Slog.d(TAG, "up/cancel"); 4174377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(false); 4184377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler clearView(); 419b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 420b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4214377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return mExpanding; 422b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 4254377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler @Override 4266a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onTouchEvent(MotionEvent ev) { 427cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren final int action = ev.getActionMasked(); 4284377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG_SCALE) Slog.d(TAG, "touch: act=" + MotionEvent.actionToString(action) + 4294377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " expanding=" + mExpanding + 4304377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") + 4314377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & PULL) ? " (pull)" : "") + 4324377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : "")); 4334377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4344377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mSGD.onTouchEvent(ev); 4354377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4366a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler switch (action) { 437b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren case MotionEvent.ACTION_MOVE: { 4384377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (0 != (mExpansionStyle & BLINDS)) { 43986d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight; 44086d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final float newHeight = clamp(rawHeight); 44186d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final boolean wasClosed = (mOldHeight == mSmallSize); 44286d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren boolean isFinished = false; 44386d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (rawHeight > mNaturalHeight) { 44486d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren isFinished = true; 44586d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 44686d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (rawHeight < mSmallSize) { 44786d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren isFinished = true; 44886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 44986d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 45086d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren final float pull = Math.abs(ev.getY() - mInitialTouchY); 45186d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (mHasPopped || pull > mPopLimit) { 452b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (!mHasPopped) { 453b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren vibrate(mPopDuration); 454b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mHasPopped = true; 455b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 45686d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 45786d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 45886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren if (mHasPopped) { 459b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mScaler.setHeight(newHeight); 46086d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren setGlow(GLOW_BASE); 461b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 46286d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren setGlow(calculateGlow(4f * pull, 0f)); 46386d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren } 46486d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren 4654377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int x = (int) mSGD.getFocusX(); 4664377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler final int y = (int) mSGD.getFocusY(); 4674377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler View underFocus = findView(x, y); 4684377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (isFinished && underFocus != null && underFocus != mCurrView) { 4694377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(false); // @@@ needed? 4704377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(underFocus, BLINDS); 4714377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mInitialTouchY = y; 47286d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren mHasPopped = false; 473b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 474b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren return true; 475b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 4764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 4774377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mExpanding) { 4784377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler updateExpansion(); 4794377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler return true; 4804377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler } 4814377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 482b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren break; 483b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 484cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 485cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren case MotionEvent.ACTION_POINTER_UP: 486cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren case MotionEvent.ACTION_POINTER_DOWN: 487cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG) Slog.d(TAG, "pointer change"); 488cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchY += mSGD.getFocusY() - mLastFocusY; 489cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mInitialTouchSpan += mSGD.getCurrentSpan() - mLastSpanY; 490cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren break; 491cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 4926a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_UP: 4936a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_CANCEL: 4944377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG) Slog.d(TAG, "up/cancel"); 4954377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(false); 49680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren clearView(); 4976a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler break; 4986a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 4996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return true; 5006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5014377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 5024377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private void startExpanding(View v, int expandType) { 503cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren mExpansionStyle = expandType; 504cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (mExpanding && v == mCurrView) { 505cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren return; 506cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren } 5074377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpanding = true; 5084377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG) Slog.d(TAG, "scale type " + expandType + " beginning on view: " + v); 5094377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mCallback.setUserLockedChild(v, true); 5104377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler setView(v); 5114377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler setGlow(GLOW_BASE); 5124377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mScaler.setView(v); 5134377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mOldHeight = mScaler.getHeight(); 5144377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (mCallback.canChildBeExpanded(v)) { 5154377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG) Slog.d(TAG, "working on an expandable child"); 5164377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); 517b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } else { 5184377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG) Slog.d(TAG, "working on a non-expandable child"); 5194377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mNaturalHeight = mOldHeight; 5206a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5214377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (DEBUG) Slog.d(TAG, "got mOldHeight: " + mOldHeight + 5224377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler " mNaturalHeight: " + mNaturalHeight); 5234377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler v.getParent().requestDisallowInterceptTouchEvent(true); 5246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 5264377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler private void finishExpanding(boolean force) { 5274377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler if (!mExpanding) return; 5284377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 529cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG) Slog.d(TAG, "scale in finishing on view: " + mCurrView); 530cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren 5313ddab0dcc1039137f05a28ff86477601a223a0faChris Wren float currentHeight = mScaler.getHeight(); 5323ddab0dcc1039137f05a28ff86477601a223a0faChris Wren float targetHeight = mSmallSize; 5336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler float h = mScaler.getHeight(); 5346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler final boolean wasClosed = (mOldHeight == mSmallSize); 5356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (wasClosed) { 5363ddab0dcc1039137f05a28ff86477601a223a0faChris Wren targetHeight = (force || currentHeight > mSmallSize) ? mNaturalHeight : mSmallSize; 5376a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } else { 5383ddab0dcc1039137f05a28ff86477601a223a0faChris Wren targetHeight = (force || currentHeight < mNaturalHeight) ? mSmallSize : mNaturalHeight; 5396a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 540ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mScaleAnimation.isRunning()) { 541ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.cancel(); 542ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 54380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setGlow(0f); 5448fd12657e353a4a6f6d875a0d86850426fec00e8Chris Wren mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight); 5453ddab0dcc1039137f05a28ff86477601a223a0faChris Wren if (targetHeight != currentHeight) { 5463ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.setFloatValues(targetHeight); 5473ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.setupStartValues(); 5483ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mScaleAnimation.start(); 5493ddab0dcc1039137f05a28ff86477601a223a0faChris Wren } 5503ddab0dcc1039137f05a28ff86477601a223a0faChris Wren mCallback.setUserLockedChild(mCurrView, false); 5514377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 5524377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpanding = false; 5534377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler mExpansionStyle = NONE; 5544377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler 555cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG) Slog.d(TAG, "wasClosed is: " + wasClosed); 556cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG) Slog.d(TAG, "currentHeight is: " + currentHeight); 557cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG) Slog.d(TAG, "mSmallSize is: " + mSmallSize); 558cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren if (DEBUG) Slog.d(TAG, "targetHeight is: " + targetHeight); 5597a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler if (DEBUG) Slog.d(TAG, "scale was finished on view: " + mCurrView); 56080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 56180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 56280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void clearView() { 5636a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCurrView = null; 56480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = null; 56580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = null; 56680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 56780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 56880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void setView(View v) { 56989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mCurrView = v; 57080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (v instanceof ViewGroup) { 57180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren ViewGroup g = (ViewGroup) v; 57280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = g.findViewById(R.id.top_glow); 57380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = g.findViewById(R.id.bottom_glow); 57489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) { 575b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren String debugLog = "Looking for glows: " + 57680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewTopGlow != null ? "found top " : "didn't find top") + 57780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom"); 5787a1a406b24c235ce59c13126d20cfb101c4e2777Daniel Sandler Slog.v(TAG, debugLog); 57980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 58080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 5816a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 5826a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 5836a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 5846a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void onClick(View v) { 5854377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler startExpanding(v, STRETCH); 5864377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler finishExpanding(true); 5873c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren clearView(); 588b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 5896a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 590b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren /** 591ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler * Use this to abort any pending expansions in progress. 592ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler */ 593ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler public void cancel() { 594ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler finishExpanding(true); 595ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler clearView(); 596ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 597ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler // reset the gesture detector 598ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler mSGD = new ScaleGestureDetector(mContext, mScaleGestureListener); 599ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler } 600ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler 601ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler /** 602b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren * Triggers haptic feedback. 603b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren */ 604b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren private synchronized void vibrate(long duration) { 605b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren if (mVibrator == null) { 606b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mVibrator = (android.os.Vibrator) 607b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mContext.getSystemService(Context.VIBRATOR_SERVICE); 608b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren } 609b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren mVibrator.vibrate(duration); 6106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 6116a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler} 612b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren 613