ExpandHelper.java revision 9b2cd15f0fed990f532f35590c2a2896b90dc7fc
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; 256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.util.Log; 269b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wrenimport android.view.Gravity; 276a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.MotionEvent; 286a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ScaleGestureDetector; 296a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View; 306a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ViewGroup; 316a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View.OnClickListener; 329b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wrenimport android.widget.ScrollView; 339b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wrenimport android.widget.FrameLayout; 346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerpublic class ExpandHelper implements Gefingerpoken, OnClickListener { 366a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public interface Callback { 375de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren View getChildAtRawPosition(float x, float y); 386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View getChildAtPosition(float x, float y); 3980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren boolean canChildBeExpanded(View v); 408fd12657e353a4a6f6d875a0d86850426fec00e8Chris Wren boolean setUserExpandedChild(View v, boolean userxpanded); 416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 426a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 436a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private static final String TAG = "ExpandHelper"; 449b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren protected static final boolean DEBUG = true; 456a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private static final long EXPAND_DURATION = 250; 46ba925e8ecd9decff5701001a0190042d6797942dChris Wren private static final long GLOW_DURATION = 150; 47ba925e8ecd9decff5701001a0190042d6797942dChris Wren 4889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Set to false to disable focus-based gestures (two-finger pull). 4989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_DRAG = true; 5089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Set to false to disable scale-based gestures (both horizontal and vertical). 5189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private static final boolean USE_SPAN = true; 5289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // Both gestures types may be active at the same time. 5389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // At least one gesture type should be active. 5489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // A variant of the screwdriver gesture will emerge from either gesture type. 556a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 5680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // amount of overstretch for maximum brightness expressed in U 5780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U 5880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float STRETCH_INTERVAL = 2f; 5980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 6080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // level of glow for a touch, without overstretch 6180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren // overstretch fills the range (GLOW_BASE, 1.0] 6280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private static final float GLOW_BASE = 0.5f; 6380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 646a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @SuppressWarnings("unused") 656a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Context mContext; 666a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 676a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private boolean mStretching; 685de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren private View mEventSource; 696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private View mCurrView; 7080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private View mCurrViewTopGlow; 7180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private View mCurrViewBottomGlow; 726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mOldHeight; 736a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mNaturalHeight; 7489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren private float mInitialTouchFocusY; 756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private float mInitialTouchSpan; 766a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private Callback mCallback; 776a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private ScaleGestureDetector mDetector; 786a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private ViewScaler mScaler; 79ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mScaleAnimation; 80ba925e8ecd9decff5701001a0190042d6797942dChris Wren private AnimatorSet mGlowAnimationSet; 81ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mGlowTopAnimation; 82ba925e8ecd9decff5701001a0190042d6797942dChris Wren private ObjectAnimator mGlowBottomAnimation; 836a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 846a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mSmallSize; 856a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private int mLargeSize; 8680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private float mMaximumStretch; 876a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 889b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren private int mGravity; 899b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 906a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private class ViewScaler { 916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View mView; 929b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 936a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ViewScaler() {} 946a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void setView(View v) { 956a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView = v; 966a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 976a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void setHeight(float h) { 9880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (DEBUG) Log.v(TAG, "SetHeight: setting to " + h); 996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler ViewGroup.LayoutParams lp = mView.getLayoutParams(); 1006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = (int)h; 1016a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 10289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mView.requestLayout(); 1036a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1046a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public float getHeight() { 1056a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler int height = mView.getLayoutParams().height; 1066a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (height < 0) { 1076a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler height = mView.getMeasuredHeight(); 1086a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1096a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return (float) height; 1106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1116a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public int getNaturalHeight(int maximum) { 1126a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler ViewGroup.LayoutParams lp = mView.getLayoutParams(); 11389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) Log.v(TAG, "Inspecting a child of type: " + mView.getClass().getName()); 1146a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler int oldHeight = lp.height; 1156a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; 1166a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 1176a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.measure( 1186a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View.MeasureSpec.makeMeasureSpec(mView.getMeasuredWidth(), 11989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren View.MeasureSpec.EXACTLY), 1206a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler View.MeasureSpec.makeMeasureSpec(maximum, 12189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren View.MeasureSpec.AT_MOST)); 1226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler lp.height = oldHeight; 1236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mView.setLayoutParams(lp); 1246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return mView.getMeasuredHeight(); 1256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1266a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 1276a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1289b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren /** 1299b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * Handle expansion gestures to expand and contract children of the callback. 1309b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * 1319b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param context application context 1329b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param callback the container that holds the items to be manipulated 1339b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param small the smallest allowable size for the manuipulated items. 1349b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param large the largest allowable size for the manuipulated items. 1359b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren * @param scoller if non-null also manipulate the scroll position to obey the gravity. 1369b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren */ 1376a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public ExpandHelper(Context context, Callback callback, int small, int large) { 1386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mSmallSize = small; 13980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mMaximumStretch = mSmallSize * STRETCH_INTERVAL; 1406a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mLargeSize = large; 1416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mContext = context; 1426a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCallback = callback; 1436a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mScaler = new ViewScaler(); 1449b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = Gravity.TOP; 145ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f); 146ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setDuration(EXPAND_DURATION); 147ba925e8ecd9decff5701001a0190042d6797942dChris Wren 1488900e631940fdffe7b941b56dc0f17e55345441eRomain Guy AnimatorListenerAdapter glowVisibilityController = new AnimatorListenerAdapter() { 1498900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 1508900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationStart(Animator animation) { 1518900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 1528900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 1538900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.VISIBLE); 1548900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1558900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1568900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 1578900e631940fdffe7b941b56dc0f17e55345441eRomain Guy @Override 1588900e631940fdffe7b941b56dc0f17e55345441eRomain Guy public void onAnimationEnd(Animator animation) { 1598900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View target = (View) ((ObjectAnimator) animation).getTarget(); 1608900e631940fdffe7b941b56dc0f17e55345441eRomain Guy if (target.getAlpha() <= 0.0f) { 1618900e631940fdffe7b941b56dc0f17e55345441eRomain Guy target.setVisibility(View.INVISIBLE); 1628900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1638900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 1648900e631940fdffe7b941b56dc0f17e55345441eRomain Guy }; 1658900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 166ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 1678900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowTopAnimation.addListener(glowVisibilityController); 168ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation = ObjectAnimator.ofFloat(null, "alpha", 0f); 1698900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mGlowBottomAnimation.addListener(glowVisibilityController); 170ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet = new AnimatorSet(); 171ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.play(mGlowTopAnimation).with(mGlowBottomAnimation); 172ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setDuration(GLOW_DURATION); 173ba925e8ecd9decff5701001a0190042d6797942dChris Wren 1746a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mDetector = 1756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler new ScaleGestureDetector(context, 1766a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler new ScaleGestureDetector.SimpleOnScaleGestureListener() { 1776a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 1786a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onScaleBegin(ScaleGestureDetector detector) { 1796a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.v(TAG, "onscalebegin()"); 1805de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren float x = detector.getFocusX(); 1815de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren float y = detector.getFocusY(); 1825de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 1835de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren View v = null; 1845de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren if (mEventSource != null) { 1855de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren int[] location = new int[2]; 1865de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren mEventSource.getLocationOnScreen(location); 1875de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren x += (float) location[0]; 1885de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren y += (float) location[1]; 1895de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren v = mCallback.getChildAtRawPosition(x, y); 1905de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren } else { 1915de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren v = mCallback.getChildAtPosition(x, y); 1925de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren } 1936a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1946a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler // your fingers have to be somewhat close to the bounds of the view in question 19589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mInitialTouchFocusY = detector.getFocusY(); 19689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mInitialTouchSpan = Math.abs(detector.getCurrentSpan()); 19789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) Log.d(TAG, "got mInitialTouchSpan: (" + mInitialTouchSpan + ")"); 1986a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 1996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mStretching = initScale(v); 2006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return mStretching; 2016a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2026a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 2036a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 2046a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onScale(ScaleGestureDetector detector) { 2056a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.v(TAG, "onscale() on " + mCurrView); 20689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren 20789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // are we scaling or dragging? 20889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float span = Math.abs(detector.getCurrentSpan()) - mInitialTouchSpan; 20989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren span *= USE_SPAN ? 1f : 0f; 21089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float drag = detector.getFocusY() - mInitialTouchFocusY; 21189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren drag *= USE_DRAG ? 1f : 0f; 2129b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren drag *= mGravity == Gravity.BOTTOM ? -1f : 1f; 21389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float pull = Math.abs(drag) + Math.abs(span) + 1f; 21489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull; 21589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) Log.d(TAG, "current span handle is: " + hand); 21689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren hand = hand + mOldHeight; 21789139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float target = hand; 21880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (DEBUG) Log.d(TAG, "target is: " + target); 21989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren hand = hand < mSmallSize ? mSmallSize : (hand > mLargeSize ? mLargeSize : hand); 22089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren hand = hand > mNaturalHeight ? mNaturalHeight : hand; 22189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) Log.d(TAG, "scale continues: hand =" + hand); 22289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mScaler.setHeight(hand); 22389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren 22489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren // glow if overscale 22589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren float stretch = (float) Math.abs((target - hand) / mMaximumStretch); 22680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren float strength = 1f / (1f + (float) Math.pow(Math.E, -1 * ((8f * stretch) - 5f))); 22780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (DEBUG) Log.d(TAG, "stretch: " + stretch + " strength: " + strength); 22880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setGlow(GLOW_BASE + strength * (1f - GLOW_BASE)); 2296a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return true; 2306a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2316a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 2326a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 2336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void onScaleEnd(ScaleGestureDetector detector) { 2346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.v(TAG, "onscaleend()"); 2356a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler // I guess we're alone now 2366a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.d(TAG, "scale end"); 2376a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler finishScale(false); 2386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2396a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler }); 2406a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2415de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 2425de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren public void setEventSource(View eventSource) { 2435de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren mEventSource = eventSource; 2445de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren } 2455de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren 2469b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren public void setGravity(int gravity) { 2479b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren mGravity = gravity; 2489b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren } 2499b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren 25080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren public void setGlow(float glow) { 25189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (!mGlowAnimationSet.isRunning() || glow == 0f) { 25289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (mGlowAnimationSet.isRunning()) { 25389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mGlowAnimationSet.cancel(); 25489139d74b27305a29ca082c75d94dcbed5f84625Chris Wren } 255ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mCurrViewTopGlow != null && mCurrViewBottomGlow != null) { 256ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (glow == 0f || mCurrViewTopGlow.getAlpha() == 0f) { 257ba925e8ecd9decff5701001a0190042d6797942dChris Wren // animate glow in and out 258ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setTarget(mCurrViewTopGlow); 259ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setTarget(mCurrViewBottomGlow); 260ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowTopAnimation.setFloatValues(glow); 261ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowBottomAnimation.setFloatValues(glow); 262ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.setupStartValues(); 263ba925e8ecd9decff5701001a0190042d6797942dChris Wren mGlowAnimationSet.start(); 264ba925e8ecd9decff5701001a0190042d6797942dChris Wren } else { 265ba925e8ecd9decff5701001a0190042d6797942dChris Wren // set it explicitly in reponse to touches. 266ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewTopGlow.setAlpha(glow); 267ba925e8ecd9decff5701001a0190042d6797942dChris Wren mCurrViewBottomGlow.setAlpha(glow); 2688900e631940fdffe7b941b56dc0f17e55345441eRomain Guy handleGlowVisibility(); 269ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 270ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 27180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 27280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 2738900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 2748900e631940fdffe7b941b56dc0f17e55345441eRomain Guy private void handleGlowVisibility() { 2758900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewTopGlow.setVisibility(mCurrViewTopGlow.getAlpha() <= 0.0f ? 2768900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 2778900e631940fdffe7b941b56dc0f17e55345441eRomain Guy mCurrViewBottomGlow.setVisibility(mCurrViewBottomGlow.getAlpha() <= 0.0f ? 2788900e631940fdffe7b941b56dc0f17e55345441eRomain Guy View.INVISIBLE : View.VISIBLE); 2798900e631940fdffe7b941b56dc0f17e55345441eRomain Guy } 2808900e631940fdffe7b941b56dc0f17e55345441eRomain Guy 2816a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onInterceptTouchEvent(MotionEvent ev) { 2826a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.d(TAG, "interceptTouch: act=" + (ev.getAction()) + 2836a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler " stretching=" + mStretching); 2846a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mDetector.onTouchEvent(ev); 2856a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return mStretching; 2866a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2876a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 2886a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public boolean onTouchEvent(MotionEvent ev) { 2896a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler final int action = ev.getAction(); 2906a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.d(TAG, "touch: act=" + (action) + " stretching=" + mStretching); 2916a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (mStretching) { 2925de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren if (DEBUG) Log.d(TAG, "detector ontouch"); 2936a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mDetector.onTouchEvent(ev); 2946a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 2956a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler switch (action) { 2966a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_UP: 2976a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler case MotionEvent.ACTION_CANCEL: 2985de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren if (DEBUG) Log.d(TAG, "cancel"); 2996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mStretching = false; 30080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren clearView(); 3016a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler break; 3026a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 3036a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return true; 3046a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 3056a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private boolean initScale(View v) { 3066a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (v != null) { 3076a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.d(TAG, "scale begins on view: " + v); 3086a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mStretching = true; 30980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setView(v); 31080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setGlow(GLOW_BASE); 3116a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mScaler.setView(v); 3126a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mOldHeight = mScaler.getHeight(); 31380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (mCallback.canChildBeExpanded(v)) { 31480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (DEBUG) Log.d(TAG, "working on an expandable child"); 31580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mNaturalHeight = mScaler.getNaturalHeight(mLargeSize); 31680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } else { 31780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (DEBUG) Log.d(TAG, "working on a non-expandable child"); 31880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mNaturalHeight = mOldHeight; 31980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 3206a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight + 3216a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler " mNaturalHeight: " + mNaturalHeight); 3226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler v.getParent().requestDisallowInterceptTouchEvent(true); 3236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 3246a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler return mStretching; 3256a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 3266a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 3276a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler private void finishScale(boolean force) { 3286a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler float h = mScaler.getHeight(); 3296a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler final boolean wasClosed = (mOldHeight == mSmallSize); 3306a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler if (wasClosed) { 3316a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler h = (force || h > mSmallSize) ? mNaturalHeight : mSmallSize; 3326a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } else { 3336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler h = (force || h < mNaturalHeight) ? mSmallSize : mNaturalHeight; 3346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 33580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (DEBUG && mCurrView != null) mCurrView.setBackgroundColor(0); 336ba925e8ecd9decff5701001a0190042d6797942dChris Wren if (mScaleAnimation.isRunning()) { 337ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.cancel(); 338ba925e8ecd9decff5701001a0190042d6797942dChris Wren } 339ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setFloatValues(h); 340ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.setupStartValues(); 341ba925e8ecd9decff5701001a0190042d6797942dChris Wren mScaleAnimation.start(); 3426a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mStretching = false; 34380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren setGlow(0f); 3448fd12657e353a4a6f6d875a0d86850426fec00e8Chris Wren mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight); 34589139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) Log.d(TAG, "scale was finished on view: " + mCurrView); 34680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren clearView(); 34780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 34880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 34980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void clearView() { 3506a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler mCurrView = null; 35180a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = null; 35280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = null; 35380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 35480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren 35580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren private void setView(View v) { 35689139d74b27305a29ca082c75d94dcbed5f84625Chris Wren mCurrView = v; 35780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren if (v instanceof ViewGroup) { 35880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren ViewGroup g = (ViewGroup) v; 35980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewTopGlow = g.findViewById(R.id.top_glow); 36080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren mCurrViewBottomGlow = g.findViewById(R.id.bottom_glow); 36189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren if (DEBUG) { 36280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren String debugLog = "Looking for glows: " + 36380a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewTopGlow != null ? "found top " : "didn't find top") + 36480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren (mCurrViewBottomGlow != null ? "found bottom " : "didn't find bottom"); 36580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren Log.v(TAG, debugLog); 36680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 36780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren } 3686a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 3696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 3706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler @Override 3716a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler public void onClick(View v) { 3726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler initScale(v); 3736a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler finishScale(true); 3746a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler 3756a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler } 3766a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler} 377