16a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler/*
26a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * Copyright (C) 2012 The Android Open Source Project
36a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler *
46a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * Licensed under the Apache License, Version 2.0 (the "License");
56a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * you may not use this file except in compliance with the License.
66a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * You may obtain a copy of the License at
76a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler *
86a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler *      http://www.apache.org/licenses/LICENSE-2.0
96a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler *
106a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * Unless required by applicable law or agreed to in writing, software
116a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * distributed under the License is distributed on an "AS IS" BASIS,
126a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * See the License for the specific language governing permissions and
146a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler * limitations under the License.
156a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler */
166a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
176a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
186a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerpackage com.android.systemui;
196a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
208900e631940fdffe7b941b56dc0f17e55345441eRomain Guyimport android.animation.Animator;
218900e631940fdffe7b941b56dc0f17e55345441eRomain Guyimport android.animation.AnimatorListenerAdapter;
226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.animation.ObjectAnimator;
236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.content.Context;
24cd686b5b6d4166b510df8e32138479a9559bc117John Spurlockimport android.util.Log;
259b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wrenimport android.view.Gravity;
269ff1510aeba910c22b2e31c29a93b08281feae4fJorim Jaggiimport android.view.HapticFeedbackConstants;
276a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.MotionEvent;
286a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.ScaleGestureDetector;
29ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandlerimport android.view.ScaleGestureDetector.OnScaleGestureListener;
3028c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggiimport android.view.VelocityTracker;
316a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandlerimport android.view.View;
32b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wrenimport android.view.ViewConfiguration;
336a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
343acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinekimport com.android.internal.annotations.VisibleForTesting;
35be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggiimport com.android.systemui.statusbar.ExpandableNotificationRow;
364222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggiimport com.android.systemui.statusbar.ExpandableView;
3728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggiimport com.android.systemui.statusbar.FlingAnimationUtils;
38b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516Selim Cinekimport com.android.systemui.statusbar.policy.ScrollAdapter;
39b6d85ebfe4f9f5d3b7d7ab7b6123af02a0deb516Selim Cinek
404222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggipublic class ExpandHelper implements Gefingerpoken {
416a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    public interface Callback {
424222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        ExpandableView getChildAtRawPosition(float x, float y);
434222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        ExpandableView getChildAtPosition(float x, float y);
4480a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren        boolean canChildBeExpanded(View v);
4551c7510e493680b4aca1ed7695b35c52d2cd63ffChris Wren        void setUserExpandedChild(View v, boolean userExpanded);
4651c7510e493680b4aca1ed7695b35c52d2cd63ffChris Wren        void setUserLockedChild(View v, boolean userLocked);
471408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        void expansionStateChanged(boolean isExpanding);
481b2a05eb00746756c4d37ad76d597d909019e56fSelim Cinek        int getMaxExpandHeight(ExpandableView view);
49b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor        void setExpansionCancelled(View view);
506a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    }
516a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
526a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private static final String TAG = "ExpandHelper";
53e46647d28467ee9e88aafe2951a5736f494235daChris Wren    protected static final boolean DEBUG = false;
543c148f106f6625ce247a2c7211682c3a1df89bc9Chris Wren    protected static final boolean DEBUG_SCALE = false;
5528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    private static final float EXPAND_DURATION = 0.3f;
56ba925e8ecd9decff5701001a0190042d6797942dChris Wren
574377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    // Set to false to disable focus-based gestures (spread-finger vertical pull).
5889139d74b27305a29ca082c75d94dcbed5f84625Chris Wren    private static final boolean USE_DRAG = true;
5989139d74b27305a29ca082c75d94dcbed5f84625Chris Wren    // Set to false to disable scale-based gestures (both horizontal and vertical).
6089139d74b27305a29ca082c75d94dcbed5f84625Chris Wren    private static final boolean USE_SPAN = true;
6189139d74b27305a29ca082c75d94dcbed5f84625Chris Wren    // Both gestures types may be active at the same time.
6289139d74b27305a29ca082c75d94dcbed5f84625Chris Wren    // At least one gesture type should be active.
6389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren    // A variant of the screwdriver gesture will emerge from either gesture type.
646a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
6580a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren    // amount of overstretch for maximum brightness expressed in U
6680a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren    // 2f: maximum brightness is stretching a 1U to 3U, or a 4U to 6U
6780a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren    private static final float STRETCH_INTERVAL = 2f;
6880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren
696a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    @SuppressWarnings("unused")
706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private Context mContext;
716a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
724377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private boolean mExpanding;
734377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private static final int NONE    = 0;
744377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private static final int BLINDS  = 1<<0;
754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private static final int PULL    = 1<<1;
764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private static final int STRETCH = 1<<2;
774377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private int mExpansionStyle = NONE;
78b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    private boolean mWatchingForPull;
79b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    private boolean mHasPopped;
805de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren    private View mEventSource;
816a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private float mOldHeight;
826a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private float mNaturalHeight;
8389139d74b27305a29ca082c75d94dcbed5f84625Chris Wren    private float mInitialTouchFocusY;
84821d6a773554565be391b22a050288260cf209d5Selim Cinek    private float mInitialTouchX;
85b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    private float mInitialTouchY;
866a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private float mInitialTouchSpan;
87cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren    private float mLastFocusY;
88cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren    private float mLastSpanY;
89b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    private int mTouchSlop;
901408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    private float mLastMotionY;
914377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private float mPullGestureMinXSpan;
926a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private Callback mCallback;
934377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    private ScaleGestureDetector mSGD;
946a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private ViewScaler mScaler;
95ba925e8ecd9decff5701001a0190042d6797942dChris Wren    private ObjectAnimator mScaleAnimation;
961408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    private boolean mEnabled = true;
971408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    private ExpandableView mResizedView;
981408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    private float mCurrentHeight;
996a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
1006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private int mSmallSize;
1016a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private int mLargeSize;
10280a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren    private float mMaximumStretch;
1031408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    private boolean mOnlyMovements;
1046a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
1059b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren    private int mGravity;
1069b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren
107fab078b01fbad026f006744016272327f7ab116bSelim Cinek    private ScrollAdapter mScrollAdapter;
10828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    private FlingAnimationUtils mFlingAnimationUtils;
10928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    private VelocityTracker mVelocityTracker;
110b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
111209bede6b9edb9171e5bee4077b48e35004a37b4John Spurlock    private OnScaleGestureListener mScaleGestureListener
112ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler            = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
113ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        @Override
114ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        public boolean onScaleBegin(ScaleGestureDetector detector) {
115cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock            if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()");
116ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler
1175e8e1c63774da8c013fad18a63eac691cdc9646fJorim Jaggi            if (!mOnlyMovements) {
1185e8e1c63774da8c013fad18a63eac691cdc9646fJorim Jaggi                startExpanding(mResizedView, STRETCH);
1195e8e1c63774da8c013fad18a63eac691cdc9646fJorim Jaggi            }
120ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler            return mExpanding;
121ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        }
122ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler
123ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        @Override
124ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        public boolean onScale(ScaleGestureDetector detector) {
1251408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mResizedView);
126ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler            return true;
127ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        }
128ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler
129ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        @Override
130ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        public void onScaleEnd(ScaleGestureDetector detector) {
131ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        }
132ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler    };
133ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler
1343acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    @VisibleForTesting
1353acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    ObjectAnimator getScaleAnimation() {
1363acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek        return mScaleAnimation;
1373acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    }
1383acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek
1396a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    private class ViewScaler {
140be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi        ExpandableView mView;
1419b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren
1426a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        public ViewScaler() {}
143be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi        public void setView(ExpandableView v) {
1446a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler            mView = v;
1456a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        }
1466a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        public void setHeight(float h) {
147cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock            if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
148eef842851026a90b6a217d8bc423454fa48df4feSelim Cinek            mView.setActualHeight((int) h);
1491408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            mCurrentHeight = h;
1506a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        }
1516a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        public float getHeight() {
152eef842851026a90b6a217d8bc423454fa48df4feSelim Cinek            return mView.getActualHeight();
1536a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        }
154388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek        public int getNaturalHeight() {
1551b2a05eb00746756c4d37ad76d597d909019e56fSelim Cinek            return mCallback.getMaxExpandHeight(mView);
1566a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        }
1576a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    }
1586a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
1599b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren    /**
1609b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren     * Handle expansion gestures to expand and contract children of the callback.
1619b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren     *
1629b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren     * @param context application context
1639b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren     * @param callback the container that holds the items to be manipulated
1649b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren     * @param small the smallest allowable size for the manuipulated items.
1659b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren     * @param large the largest allowable size for the manuipulated items.
1669b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren     */
1676a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    public ExpandHelper(Context context, Callback callback, int small, int large) {
1686a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        mSmallSize = small;
16980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren        mMaximumStretch = mSmallSize * STRETCH_INTERVAL;
1706a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        mLargeSize = large;
1716a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        mContext = context;
1726a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        mCallback = callback;
1736a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        mScaler = new ViewScaler();
1749b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren        mGravity = Gravity.TOP;
175ba925e8ecd9decff5701001a0190042d6797942dChris Wren        mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
1764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
177ba925e8ecd9decff5701001a0190042d6797942dChris Wren
178b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        final ViewConfiguration configuration = ViewConfiguration.get(mContext);
179b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        mTouchSlop = configuration.getScaledTouchSlop();
180b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
181ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        mSGD = new ScaleGestureDetector(context, mScaleGestureListener);
18228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        mFlingAnimationUtils = new FlingAnimationUtils(context, EXPAND_DURATION);
1836a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    }
1845de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren
1853acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    @VisibleForTesting
1863acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    void updateExpansion() {
187cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG_SCALE) Log.v(TAG, "updateExpansion()");
1884377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        // are we scaling or dragging?
189cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        float span = mSGD.getCurrentSpan() - mInitialTouchSpan;
1904377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        span *= USE_SPAN ? 1f : 0f;
1914377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        float drag = mSGD.getFocusY() - mInitialTouchFocusY;
1924377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        drag *= USE_DRAG ? 1f : 0f;
1934377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        drag *= mGravity == Gravity.BOTTOM ? -1f : 1f;
1944377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        float pull = Math.abs(drag) + Math.abs(span) + 1f;
1954377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        float hand = drag * Math.abs(drag) / pull + span * Math.abs(span) / pull;
1964377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        float target = hand + mOldHeight;
1974377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        float newHeight = clamp(target);
1984377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mScaler.setHeight(newHeight);
199cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        mLastFocusY = mSGD.getFocusY();
200cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        mLastSpanY = mSGD.getCurrentSpan();
2014377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    }
2024377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
203b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    private float clamp(float target) {
204b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        float out = target;
205388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek        out = out < mSmallSize ? mSmallSize : out;
206b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        out = out > mNaturalHeight ? mNaturalHeight : out;
207b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        return out;
208b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    }
209b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
2104222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi    private ExpandableView findView(float x, float y) {
2114222d9a7fb87d73e1443ec1a2de9782b05741af6Jorim Jaggi        ExpandableView v;
212b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        if (mEventSource != null) {
213b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            int[] location = new int[2];
214b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            mEventSource.getLocationOnScreen(location);
2154377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            x += location[0];
2164377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            y += location[1];
217b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            v = mCallback.getChildAtRawPosition(x, y);
218b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        } else {
219b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            v = mCallback.getChildAtPosition(x, y);
220b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        }
221b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        return v;
222b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    }
223b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
224b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    private boolean isInside(View v, float x, float y) {
225cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "isinside (" + x + ", " + y + ")");
226b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
227b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        if (v == null) {
228cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock            if (DEBUG) Log.d(TAG, "isinside null subject");
229b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            return false;
230b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        }
231b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        if (mEventSource != null) {
232b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            int[] location = new int[2];
233b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            mEventSource.getLocationOnScreen(location);
2344377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            x += location[0];
2354377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            y += location[1];
236cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock            if (DEBUG) Log.d(TAG, "  to global (" + x + ", " + y + ")");
237b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        }
238b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        int[] location = new int[2];
239b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        v.getLocationOnScreen(location);
2404377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        x -= location[0];
2414377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        y -= location[1];
242cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "  to local (" + x + ", " + y + ")");
243cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "  inside (" + v.getWidth() + ", " + v.getHeight() + ")");
244b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        boolean inside = (x > 0f && y > 0f && x < v.getWidth() & y < v.getHeight());
245b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        return inside;
246b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    }
247b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
2485de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren    public void setEventSource(View eventSource) {
2495de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren        mEventSource = eventSource;
2505de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren    }
2515de6e94e36e2adbdd4ebfb5c1903c23c9ea3c388Chris Wren
2529b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren    public void setGravity(int gravity) {
2539b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren        mGravity = gravity;
2549b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren    }
2559b2cd15f0fed990f532f35590c2a2896b90dc7fcChris Wren
256fab078b01fbad026f006744016272327f7ab116bSelim Cinek    public void setScrollAdapter(ScrollAdapter adapter) {
257fab078b01fbad026f006744016272327f7ab116bSelim Cinek        mScrollAdapter = adapter;
258b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    }
259b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
2604377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    @Override
2616a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    public boolean onInterceptTouchEvent(MotionEvent ev) {
2621408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        if (!isEnabled()) {
2631408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            return false;
2641408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        }
26528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        trackVelocity(ev);
2664377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        final int action = ev.getAction();
267cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG_SCALE) Log.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) +
2684377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                         " expanding=" + mExpanding +
2694377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                         (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
2704377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                         (0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
2714377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                         (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
2724377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        // check for a spread-finger vertical pull gesture
2734377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mSGD.onTouchEvent(ev);
2744377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        final int x = (int) mSGD.getFocusX();
2754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        final int y = (int) mSGD.getFocusY();
276cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren
277cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        mInitialTouchFocusY = y;
278cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        mInitialTouchSpan = mSGD.getCurrentSpan();
279cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        mLastFocusY = mInitialTouchFocusY;
280cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        mLastSpanY = mInitialTouchSpan;
281cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG_SCALE) Log.d(TAG, "set initial span: " + mInitialTouchSpan);
282cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren
2834377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        if (mExpanding) {
2841408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            mLastMotionY = ev.getRawY();
28528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            maybeRecycleVelocityTracker(ev);
286b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            return true;
287b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        } else {
2884377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) {
2894377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                // we've begun Venetian blinds style expansion
2904377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                return true;
2914377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            }
292b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            switch (action & MotionEvent.ACTION_MASK) {
293b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            case MotionEvent.ACTION_MOVE: {
2941408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                final float xspan = mSGD.getCurrentSpanX();
2951408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                if (xspan > mPullGestureMinXSpan &&
2961408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        xspan > mSGD.getCurrentSpanY() && !mExpanding) {
2971408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    // detect a vertical pulling gesture with fingers somewhat separated
2981408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
2991408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    startExpanding(mResizedView, PULL);
3001408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    mWatchingForPull = false;
3011408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                }
302b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                if (mWatchingForPull) {
3031408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    final float yDiff = ev.getRawY() - mInitialTouchY;
304821d6a773554565be391b22a050288260cf209d5Selim Cinek                    final float xDiff = ev.getRawX() - mInitialTouchX;
305821d6a773554565be391b22a050288260cf209d5Selim Cinek                    if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
306cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock                        if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
3071408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        mWatchingForPull = false;
3081408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        if (mResizedView != null && !isFullyExpanded(mResizedView)) {
3091408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                            if (startExpanding(mResizedView, BLINDS)) {
3101408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                                mLastMotionY = ev.getRawY();
3111408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                                mInitialTouchY = ev.getRawY();
3121408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                                mHasPopped = false;
3131408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                            }
314b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                        }
315b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                    }
316b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                }
317b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                break;
318b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            }
319b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
320b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            case MotionEvent.ACTION_DOWN:
321de3b1a2b2d3056cb7caa5cdaac51fc9d80e1e3c5Chris Wren                mWatchingForPull = mScrollAdapter != null &&
3221408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        isInside(mScrollAdapter.getHostView(), x, y)
3231408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        && mScrollAdapter.isScrolledToTop();
3241408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                mResizedView = findView(x, y);
325e53e6bbb82b411f99083e4a6d2071fde45d68d53Selim Cinek                if (mResizedView != null && !mCallback.canChildBeExpanded(mResizedView)) {
326d2281151ee18f5fd530bc062c59b3f77082317bfSelim Cinek                    mResizedView = null;
327d2281151ee18f5fd530bc062c59b3f77082317bfSelim Cinek                    mWatchingForPull = false;
328d2281151ee18f5fd530bc062c59b3f77082317bfSelim Cinek                }
329fe090658b691f8a960bf0492371ec8b52ed4430fSelim Cinek                mInitialTouchY = ev.getRawY();
330fe090658b691f8a960bf0492371ec8b52ed4430fSelim Cinek                mInitialTouchX = ev.getRawX();
331b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                break;
332b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
333b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            case MotionEvent.ACTION_CANCEL:
334b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            case MotionEvent.ACTION_UP:
335cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock                if (DEBUG) Log.d(TAG, "up/cancel");
336c25af40002eb9de4e30b801b35e9e5b2474aafdfSelim Cinek                finishExpanding(ev.getActionMasked() == MotionEvent.ACTION_CANCEL /* forceAbort */,
337740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek                        getCurrentVelocity());
3384377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                clearView();
339b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                break;
340b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            }
3411408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            mLastMotionY = ev.getRawY();
34228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            maybeRecycleVelocityTracker(ev);
3434377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            return mExpanding;
344b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        }
3456a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    }
3466a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
34728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    private void trackVelocity(MotionEvent event) {
34828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        int action = event.getActionMasked();
34928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        switch(action) {
35028c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            case MotionEvent.ACTION_DOWN:
35128c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                if (mVelocityTracker == null) {
35228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                    mVelocityTracker = VelocityTracker.obtain();
35328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                } else {
35428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                    mVelocityTracker.clear();
35528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                }
35628c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                mVelocityTracker.addMovement(event);
35728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                break;
35828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            case MotionEvent.ACTION_MOVE:
3591b7f51ebc66fbb2cbe8a468790bbbfd397d66964Selim Cinek                if (mVelocityTracker == null) {
3601b7f51ebc66fbb2cbe8a468790bbbfd397d66964Selim Cinek                    mVelocityTracker = VelocityTracker.obtain();
3611b7f51ebc66fbb2cbe8a468790bbbfd397d66964Selim Cinek                }
36228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                mVelocityTracker.addMovement(event);
36328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                break;
36428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            default:
36528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi                break;
36628c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        }
36728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    }
36828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi
36928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    private void maybeRecycleVelocityTracker(MotionEvent event) {
370c503896a4d0cab029bca56cf7ac18ae182729a0aJorim Jaggi        if (mVelocityTracker != null && (event.getActionMasked() == MotionEvent.ACTION_CANCEL
371c503896a4d0cab029bca56cf7ac18ae182729a0aJorim Jaggi                || event.getActionMasked() == MotionEvent.ACTION_UP)) {
37228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            mVelocityTracker.recycle();
37328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            mVelocityTracker = null;
37428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        }
37528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    }
37628c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi
37728c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    private float getCurrentVelocity() {
37828c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        if (mVelocityTracker != null) {
37928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            mVelocityTracker.computeCurrentVelocity(1000);
38028c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            return mVelocityTracker.getYVelocity();
38128c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        } else {
38228c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            return 0f;
38328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        }
38428c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi    }
38528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi
3861408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    public void setEnabled(boolean enable) {
3871408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mEnabled = enable;
3881408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    }
3891408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek
3901408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    private boolean isEnabled() {
3911408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        return mEnabled;
3921408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    }
3931408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek
3941408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    private boolean isFullyExpanded(ExpandableView underFocus) {
395388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek        return underFocus.getIntrinsicHeight() == underFocus.getMaxContentHeight()
396388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek                && (!underFocus.isSummaryWithChildren() || underFocus.areChildrenExpanded());
3971408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    }
3981408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek
3994377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler    @Override
4006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    public boolean onTouchEvent(MotionEvent ev) {
401740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek        if (!isEnabled() && !mExpanding) {
402740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek            // In case we're expanding we still want to finish the current motion.
4031408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            return false;
4041408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        }
40528c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        trackVelocity(ev);
406cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        final int action = ev.getActionMasked();
407cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG_SCALE) Log.d(TAG, "touch: act=" + MotionEvent.actionToString(action) +
4084377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                " expanding=" + mExpanding +
4094377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                (0 != (mExpansionStyle & BLINDS) ? " (blinds)" : "") +
4104377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                (0 != (mExpansionStyle & PULL) ? " (pull)" : "") +
4114377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                (0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
4124377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
4134377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mSGD.onTouchEvent(ev);
4141408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        final int x = (int) mSGD.getFocusX();
4151408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        final int y = (int) mSGD.getFocusY();
4164377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
4171408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        if (mOnlyMovements) {
4181408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            mLastMotionY = ev.getRawY();
4191408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            return false;
4201408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        }
4216a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        switch (action) {
4221408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            case MotionEvent.ACTION_DOWN:
4231408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                mWatchingForPull = mScrollAdapter != null &&
4241408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        isInside(mScrollAdapter.getHostView(), x, y);
4251408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                mResizedView = findView(x, y);
426fe090658b691f8a960bf0492371ec8b52ed4430fSelim Cinek                mInitialTouchX = ev.getRawX();
427fe090658b691f8a960bf0492371ec8b52ed4430fSelim Cinek                mInitialTouchY = ev.getRawY();
4281408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                break;
429b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            case MotionEvent.ACTION_MOVE: {
4301408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                if (mWatchingForPull) {
4311408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    final float yDiff = ev.getRawY() - mInitialTouchY;
432821d6a773554565be391b22a050288260cf209d5Selim Cinek                    final float xDiff = ev.getRawX() - mInitialTouchX;
433821d6a773554565be391b22a050288260cf209d5Selim Cinek                    if (yDiff > mTouchSlop && yDiff > Math.abs(xDiff)) {
4341408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
4351408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        mWatchingForPull = false;
4361408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        if (mResizedView != null && !isFullyExpanded(mResizedView)) {
4371408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                            if (startExpanding(mResizedView, BLINDS)) {
4381408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                                mInitialTouchY = ev.getRawY();
4391408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                                mLastMotionY = ev.getRawY();
4401408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                                mHasPopped = false;
4411408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                            }
4421408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        }
4431408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    }
4441408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                }
4451408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                if (mExpanding && 0 != (mExpansionStyle & BLINDS)) {
4461408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    final float rawHeight = ev.getRawY() - mLastMotionY + mCurrentHeight;
44786d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                    final float newHeight = clamp(rawHeight);
44886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                    boolean isFinished = false;
4491408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    boolean expanded = false;
45086d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                    if (rawHeight > mNaturalHeight) {
45186d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                        isFinished = true;
4521408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        expanded = true;
45386d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                    }
45486d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                    if (rawHeight < mSmallSize) {
45586d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                        isFinished = true;
4561408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        expanded = false;
45786d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                    }
45886d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren
4591408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    if (!mHasPopped) {
4609ff1510aeba910c22b2e31c29a93b08281feae4fJorim Jaggi                        if (mEventSource != null) {
4619ff1510aeba910c22b2e31c29a93b08281feae4fJorim Jaggi                            mEventSource.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
4629ff1510aeba910c22b2e31c29a93b08281feae4fJorim Jaggi                        }
4631408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        mHasPopped = true;
46486d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren                    }
46586d00fb40ae5cd01ce5a2e228e6de777eae6dee8Chris Wren
4661408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    mScaler.setHeight(newHeight);
4671408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    mLastMotionY = ev.getRawY();
4681408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    if (isFinished) {
4691408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        mCallback.expansionStateChanged(false);
4701408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    } else {
4711408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                        mCallback.expansionStateChanged(true);
472b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                    }
473b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                    return true;
474b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                }
4754377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
4764377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                if (mExpanding) {
4771408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek
4781408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    // Gestural expansion is running
4794377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                    updateExpansion();
4801408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek                    mLastMotionY = ev.getRawY();
4814377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                    return true;
4824377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                }
4834377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
484b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren                break;
485b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren            }
486cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren
487cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren            case MotionEvent.ACTION_POINTER_UP:
488cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren            case MotionEvent.ACTION_POINTER_DOWN:
489cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock                if (DEBUG) Log.d(TAG, "pointer change");
490cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren                mInitialTouchY += mSGD.getFocusY() - mLastFocusY;
491cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren                mInitialTouchSpan += mSGD.getCurrentSpan() - mLastSpanY;
492cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren                break;
493cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren
4946a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler            case MotionEvent.ACTION_UP:
4956a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler            case MotionEvent.ACTION_CANCEL:
496cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock                if (DEBUG) Log.d(TAG, "up/cancel");
497740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek                finishExpanding(!isEnabled() || ev.getActionMasked() == MotionEvent.ACTION_CANCEL,
498740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek                        getCurrentVelocity());
49980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren                clearView();
5006a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler                break;
5016a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        }
5021408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mLastMotionY = ev.getRawY();
50328c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi        maybeRecycleVelocityTracker(ev);
504787a0af8ebba004a6f1cd3bfe8c78d851003d227Jorim Jaggi        return mResizedView != null;
5056a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    }
5064377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
507be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi    /**
508be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi     * @return True if the view is expandable, false otherwise.
509be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi     */
5103acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    @VisibleForTesting
5113acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    boolean startExpanding(ExpandableView v, int expandType) {
512be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi        if (!(v instanceof ExpandableNotificationRow)) {
513be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi            return false;
514be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi        }
515cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        mExpansionStyle = expandType;
5161408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        if (mExpanding && v == mResizedView) {
517be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi            return true;
518cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren        }
5194377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mExpanding = true;
5201408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mCallback.expansionStateChanged(true);
521cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v);
5224377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mCallback.setUserLockedChild(v, true);
5231408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mScaler.setView(v);
5244377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mOldHeight = mScaler.getHeight();
5251408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mCurrentHeight = mOldHeight;
526388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek        boolean canBeExpanded = mCallback.canChildBeExpanded(v);
527388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek        if (canBeExpanded) {
528cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock            if (DEBUG) Log.d(TAG, "working on an expandable child");
529388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek            mNaturalHeight = mScaler.getNaturalHeight();
530567e845d99840a6e556595739a15e16132eb2f1eSelim Cinek            mSmallSize = v.getCollapsedHeight();
531b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren        } else {
532cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock            if (DEBUG) Log.d(TAG, "working on a non-expandable child");
5334377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler            mNaturalHeight = mOldHeight;
5346a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        }
535cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
5364377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler                    " mNaturalHeight: " + mNaturalHeight);
537be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi        return true;
5386a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    }
5396a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
540740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek    /**
541740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek     * Finish the current expand motion
542740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek     * @param forceAbort whether the expansion should be forcefully aborted and returned to the old
543740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek     *                   state
544740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek     * @param velocity the velocity this was expanded/ collapsed with
545740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek     */
5463acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    @VisibleForTesting
5473acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek    void finishExpanding(boolean forceAbort, float velocity) {
5484377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        if (!mExpanding) return;
5494377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
5501408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView);
551cea520747f344204a78db9d3f7f1abe3f695f49fChris Wren
5523ddab0dcc1039137f05a28ff86477601a223a0faChris Wren        float currentHeight = mScaler.getHeight();
5536a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        final boolean wasClosed = (mOldHeight == mSmallSize);
554388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek        boolean nowExpanded;
555740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek        if (!forceAbort) {
556740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek            if (wasClosed) {
557740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek                nowExpanded = currentHeight > mOldHeight && velocity >= 0;
558740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek            } else {
559740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek                nowExpanded = currentHeight >= mOldHeight || velocity > 0;
560740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek            }
561740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek            nowExpanded |= mNaturalHeight == mSmallSize;
5626a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        } else {
563740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek            nowExpanded = !wasClosed;
5646a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler        }
565ba925e8ecd9decff5701001a0190042d6797942dChris Wren        if (mScaleAnimation.isRunning()) {
566ba925e8ecd9decff5701001a0190042d6797942dChris Wren            mScaleAnimation.cancel();
567ba925e8ecd9decff5701001a0190042d6797942dChris Wren        }
5681408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mCallback.expansionStateChanged(false);
569740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek        int naturalHeight = mScaler.getNaturalHeight();
570388df6dd3d07376ecd7446cae36e1486cd313171Selim Cinek        float targetHeight = nowExpanded ? naturalHeight : mSmallSize;
571740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek        if (targetHeight != currentHeight && mEnabled) {
5723ddab0dcc1039137f05a28ff86477601a223a0faChris Wren            mScaleAnimation.setFloatValues(targetHeight);
5733ddab0dcc1039137f05a28ff86477601a223a0faChris Wren            mScaleAnimation.setupStartValues();
5741408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            final View scaledView = mResizedView;
575d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek            final boolean expand = nowExpanded;
576be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi            mScaleAnimation.addListener(new AnimatorListenerAdapter() {
577d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                public boolean mCancelled;
578d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek
579be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi                @Override
580be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi                public void onAnimationEnd(Animator animation) {
581d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                    if (!mCancelled) {
582d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                        mCallback.setUserExpandedChild(scaledView, expand);
5833acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek                        if (!mExpanding) {
5843acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek                            mScaler.setView(null);
5853acdc8e7fc7e4e21f2ffb13b55d258f4a9cc460eSelim Cinek                        }
586b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                    } else {
587b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                        mCallback.setExpansionCancelled(scaledView);
588d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                    }
589be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi                    mCallback.setUserLockedChild(scaledView, false);
590be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi                    mScaleAnimation.removeListener(this);
591be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi                }
592d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek
593d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                @Override
594d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                public void onAnimationCancel(Animator animation) {
595d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                    mCancelled = true;
596d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek                }
597be565dfc1c17b7ddafa9753851b8f82849fd3f42Jorim Jaggi            });
598d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek            velocity = nowExpanded == velocity >= 0 ? velocity : 0;
59928c0b714ac7f6c98d63aab106447bfd1e727fae2Jorim Jaggi            mFlingAnimationUtils.apply(mScaleAnimation, currentHeight, targetHeight, velocity);
6003ddab0dcc1039137f05a28ff86477601a223a0faChris Wren            mScaleAnimation.start();
601343e6e258ab6a9f647eabebaed05ce3acafd2ff1Selim Cinek        } else {
602c25af40002eb9de4e30b801b35e9e5b2474aafdfSelim Cinek            if (targetHeight != currentHeight) {
603c25af40002eb9de4e30b801b35e9e5b2474aafdfSelim Cinek                mScaler.setHeight(targetHeight);
604c25af40002eb9de4e30b801b35e9e5b2474aafdfSelim Cinek            }
605d191a1789e7bcd890557fbc6fb1d4efda97601eeSelim Cinek            mCallback.setUserExpandedChild(mResizedView, nowExpanded);
6061408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek            mCallback.setUserLockedChild(mResizedView, false);
607f306d9b498c2c81ad52083a0825e037f2757daf7Selim Cinek            mScaler.setView(null);
6083ddab0dcc1039137f05a28ff86477601a223a0faChris Wren        }
6094377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
6104377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mExpanding = false;
6114377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler        mExpansionStyle = NONE;
6124377d1494cab30ab299f6065cf6857df7367db3bDaniel Sandler
613cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "wasClosed is: " + wasClosed);
614cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "currentHeight is: " + currentHeight);
615cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "mSmallSize is: " + mSmallSize);
616cd686b5b6d4166b510df8e32138479a9559bc117John Spurlock        if (DEBUG) Log.d(TAG, "targetHeight is: " + targetHeight);
6171408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        if (DEBUG) Log.d(TAG, "scale was finished on view: " + mResizedView);
61880a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren    }
61980a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren
62080a76276dc9440ffad30dc4c820eb7d65f4df368Chris Wren    private void clearView() {
6211408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mResizedView = null;
6226a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler    }
6236a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler
624b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren    /**
625ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler     * Use this to abort any pending expansions in progress.
626ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler     */
627ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler    public void cancel() {
628740c1114764e341857d32b486d3b1ad985033a3bSelim Cinek        finishExpanding(true /* forceAbort */, 0f /* velocity */);
629ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        clearView();
630ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler
631ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        // reset the gesture detector
632ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler        mSGD = new ScaleGestureDetector(mContext, mScaleGestureListener);
633ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler    }
634ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler
635ac47ff70c322614ff2ca9ad82fe41338daf55877Daniel Sandler    /**
6361408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek     * Change the expansion mode to only observe movements and don't perform any resizing.
6371408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek     * This is needed when the expanding is finished and the scroller kicks in,
6381408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek     * performing an overscroll motion. We only want to shrink it again when we are not
6391408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek     * overscrolled.
6401408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek     *
6411408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek     * @param onlyMovements Should only movements be observed?
6421408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek     */
6431408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    public void onlyObserveMovements(boolean onlyMovements) {
6441408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek        mOnlyMovements = onlyMovements;
6451408eb5a58d669933c701e347fd3498ceab70f3cSelim Cinek    }
6466a858c347f4d4e5db4c8f00d5e285967631b71caDaniel Sandler}
647b4e2c48b4d75e7d68209412152011441fb6deda3Chris Wren
648