1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.ui.mediapicker;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.res.Resources;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.os.Handler;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.AttributeSet;
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.MotionEvent;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.View;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewConfiguration;
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.ViewGroup;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.animation.Animation;
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.view.animation.Transformation;
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.widget.LinearLayout;
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.R;
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.ui.PagingAwareViewPager;
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.Assert;
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.OsUtil;
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.UiUtils;
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/**
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Custom layout panel which makes the MediaPicker animations seamless and synchronized
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Designed to be very specific to the MediaPicker's usage
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class MediaPickerPanel extends ViewGroup {
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The window of time in which we might to decide to reinterpret the intent of a gesture.
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final long TOUCH_RECAPTURE_WINDOW_MS = 500L;
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // The two view components to layout
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private LinearLayout mTabStrip;
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private boolean mFullScreenOnly;
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private PagingAwareViewPager mViewPager;
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * True if the MediaPicker is full screen or animating into it
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private boolean mFullScreen;
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * True if the MediaPicker is open at all
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private boolean mExpanded;
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The current desired height of the MediaPicker.  This property may be animated and the
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * measure pass uses it to determine what size the components are.
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private int mCurrentDesiredHeight;
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final Handler mHandler = new Handler();
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The media picker for dispatching events to the MediaPicker's listener
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private MediaPicker mMediaPicker;
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The computed default "half-screen" height of the view pager in px
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final int mDefaultViewPagerHeight;
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The action bar height used to compute the padding on the view pager when it's full screen.
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final int mActionBarHeight;
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private TouchHandler mTouchHandler;
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    static final int PAGE_NOT_SET = -1;
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public MediaPickerPanel(final Context context, final AttributeSet attrs) {
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        super(context, attrs);
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Cache the computed dimension
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mDefaultViewPagerHeight = getResources().getDimensionPixelSize(
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                R.dimen.mediapicker_default_chooser_height);
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mActionBarHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected void onFinishInflate() {
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        super.onFinishInflate();
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mTabStrip = (LinearLayout) findViewById(R.id.mediapicker_tabstrip);
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mViewPager = (PagingAwareViewPager) findViewById(R.id.mediapicker_view_pager);
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mTouchHandler = new TouchHandler();
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        setOnTouchListener(mTouchHandler);
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mViewPager.setOnTouchListener(mTouchHandler);
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Make sure full screen mode is updated in landscape mode change when the panel is open.
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        addOnLayoutChangeListener(new OnLayoutChangeListener() {
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            private boolean mLandscapeMode = UiUtils.isLandscapeMode();
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            @Override
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            public void onLayoutChange(View v, int left, int top, int right, int bottom,
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final boolean newLandscapeMode = UiUtils.isLandscapeMode();
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (mLandscapeMode != newLandscapeMode) {
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mLandscapeMode = newLandscapeMode;
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (mExpanded) {
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        setExpanded(mExpanded, false /* animate */, mViewPager.getCurrentItem(),
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                                true /* force */);
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        });
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int requestedHeight = MeasureSpec.getSize(heightMeasureSpec);
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mMediaPicker.getChooserShowsActionBarInFullScreen()) {
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            requestedHeight -= mActionBarHeight;
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int desiredHeight = Math.min(mCurrentDesiredHeight, requestedHeight);
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mExpanded && desiredHeight == 0) {
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // If we want to be shown, we have to have a non-0 height.  Returning a height of 0 will
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // cause the framework to abort the animation from 0, so we must always have some
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // height once we start expanding
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            desiredHeight = 1;
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else if (!mExpanded && desiredHeight == 0) {
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mViewPager.setVisibility(View.GONE);
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mViewPager.setAdapter(null);
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        measureChild(mTabStrip, widthMeasureSpec, heightMeasureSpec);
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int tabStripHeight;
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (requiresFullScreen()) {
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Ensure that the tab strip is always visible, even in full screen.
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            tabStripHeight = mTabStrip.getMeasuredHeight();
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Slide out the tab strip at the end of the animation to full screen.
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            tabStripHeight = Math.min(mTabStrip.getMeasuredHeight(),
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    requestedHeight - desiredHeight);
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // If we are animating and have an interim desired height, use the default height. We can't
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // take the max here as on some devices the mDefaultViewPagerHeight may be too big in
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // landscape mode after animation.
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int tabAdjustedDesiredHeight = desiredHeight - tabStripHeight;
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int viewPagerHeight =
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                tabAdjustedDesiredHeight <= 1 ? mDefaultViewPagerHeight : tabAdjustedDesiredHeight;
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int viewPagerHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                viewPagerHeight, MeasureSpec.EXACTLY);
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        measureChild(mViewPager, widthMeasureSpec, viewPagerHeightMeasureSpec);
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        setMeasuredDimension(mViewPager.getMeasuredWidth(), desiredHeight);
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected void onLayout(final boolean changed, final int left, final int top, final int right,
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int bottom) {
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        int y = top;
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int width = right - left;
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int viewPagerHeight = mViewPager.getMeasuredHeight();
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mViewPager.layout(0, y, width, y + viewPagerHeight);
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        y += viewPagerHeight;
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mTabStrip.layout(0, y, width, y + mTabStrip.getMeasuredHeight());
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    void onChooserChanged() {
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mFullScreen) {
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            setDesiredHeight(getDesiredHeight(), true);
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    void setFullScreenOnly(boolean fullScreenOnly) {
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mFullScreenOnly = fullScreenOnly;
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    boolean isFullScreen() {
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return mFullScreen;
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    void setMediaPicker(final MediaPicker mediaPicker) {
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mMediaPicker = mediaPicker;
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Get the desired height of the media picker panel for when the panel is not in motion (i.e.
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * not being dragged by the user).
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private int getDesiredHeight() {
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (mFullScreen) {
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            int fullHeight = getContext().getResources().getDisplayMetrics().heightPixels;
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (OsUtil.isAtLeastKLP() && isAttachedToWindow()) {
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // When we're attached to the window, we can get an accurate height, not necessary
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // on older API level devices because they don't include the action bar height
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                View composeContainer =
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        getRootView().findViewById(R.id.conversation_and_compose_container);
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (composeContainer != null) {
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // protect against composeContainer having been unloaded already
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    fullHeight -= UiUtils.getMeasuredBoundsOnScreen(composeContainer).top;
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (mMediaPicker.getChooserShowsActionBarInFullScreen()) {
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return fullHeight - mActionBarHeight;
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } else {
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return fullHeight;
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else if (mExpanded) {
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return LayoutParams.WRAP_CONTENT;
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return 0;
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void setupViewPager(final int startingPage) {
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mViewPager.setVisibility(View.VISIBLE);
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (startingPage >= 0 && startingPage < mMediaPicker.getPagerAdapter().getCount()) {
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mViewPager.setAdapter(mMediaPicker.getPagerAdapter());
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mViewPager.setCurrentItem(startingPage);
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        updateViewPager();
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Expand the media picker panel. Since we always set the pager adapter to null when the panel
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * is collapsed, we need to restore the adapter and the starting page.
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param expanded expanded or collapsed
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param animate need animation
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param startingPage the desired selected page to start
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    void setExpanded(final boolean expanded, final boolean animate, final int startingPage) {
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        setExpanded(expanded, animate, startingPage, false /* force */);
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void setExpanded(final boolean expanded, final boolean animate, final int startingPage,
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final boolean force) {
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (expanded == mExpanded && !force) {
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mFullScreen = false;
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mExpanded = expanded;
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mHandler.post(new Runnable() {
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            @Override
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            public void run() {
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                setDesiredHeight(getDesiredHeight(), animate);
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        });
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (expanded) {
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            setupViewPager(startingPage);
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mMediaPicker.dispatchOpened();
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mMediaPicker.dispatchDismissed();
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Call setFullScreenView() when we are in landscape mode so it can go full screen as
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // soon as it is expanded.
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (expanded && requiresFullScreen()) {
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            setFullScreenView(true, animate);
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private boolean requiresFullScreen() {
275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return mFullScreenOnly || UiUtils.isLandscapeMode();
276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void setDesiredHeight(int height, final boolean animate) {
279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int startHeight = mCurrentDesiredHeight;
280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (height == LayoutParams.WRAP_CONTENT) {
281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            height = measureHeight();
282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        clearAnimation();
284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (animate) {
285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final int deltaHeight = height - startHeight;
286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Animation animation = new Animation() {
287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                @Override
288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                protected void applyTransformation(final float interpolatedTime,
289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        final Transformation t) {
290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mCurrentDesiredHeight = (int) (startHeight + deltaHeight * interpolatedTime);
291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    requestLayout();
292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                @Override
295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                public boolean willChangeBounds() {
296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return true;
297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            };
299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            animation.setDuration(UiUtils.MEDIAPICKER_TRANSITION_DURATION);
300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            animation.setInterpolator(UiUtils.EASE_OUT_INTERPOLATOR);
301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            startAnimation(animation);
302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mCurrentDesiredHeight = height;
304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        requestLayout();
306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return The minimum total height of the view
310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private int measureHeight() {
312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int measureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE, MeasureSpec.AT_MOST);
313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        measureChild(mTabStrip, measureSpec, measureSpec);
314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return mDefaultViewPagerHeight + mTabStrip.getMeasuredHeight();
315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Enters or leaves full screen view
319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param fullScreen True to enter full screen view, false to leave
321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param animate    True to animate the transition
322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    void setFullScreenView(final boolean fullScreen, final boolean animate) {
324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (fullScreen == mFullScreen) {
325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (requiresFullScreen() && !fullScreen) {
329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            setExpanded(false /* expanded */, true /* animate */, PAGE_NOT_SET);
330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mFullScreen = fullScreen;
333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        setDesiredHeight(getDesiredHeight(), animate);
334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mMediaPicker.dispatchFullScreen(mFullScreen);
335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        updateViewPager();
336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * ViewPager should have its paging disabled when in full screen mode.
340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void updateViewPager() {
342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mViewPager.setPagingEnabled(!mFullScreen);
343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public boolean onInterceptTouchEvent(final MotionEvent ev) {
347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return mTouchHandler.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev);
348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Helper class to handle touch events and swipe gestures
352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private class TouchHandler implements OnTouchListener {
354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * The height of the view when the touch press started
356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private int mDownHeight = -1;
358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * True if the panel moved at all (changed height) during the drag
361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private boolean mMoved = false;
363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // The threshold constants converted from DP to px
365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final float mFlingThresholdPx;
366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final float mBigFlingThresholdPx;
367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // The system defined pixel size to determine when a movement is considered a drag.
369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final int mTouchSlop;
370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * A copy of the MotionEvent that started the drag/swipe gesture
373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private MotionEvent mDownEvent;
375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Whether we are currently moving down. We may not be able to move down in full screen
378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * mode when the child view can swipe down (such as a list view).
379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private boolean mMovedDown = false;
381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Indicates whether the child view contained in the panel can swipe down at the beginning
384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * of the drag event (i.e. the initial down). The MediaPanel can contain
385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * scrollable children such as a list view / grid view. If the child view can swipe down,
386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * We want to let the child view handle the scroll first instead of handling it ourselves.
387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private boolean mCanChildViewSwipeDown = false;
389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Necessary direction ratio for a fling to be considered in one direction this prevents
392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * horizontal swipes with small vertical components from triggering vertical swipe actions
393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final float DIRECTION_RATIO = 1.1f;
395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        TouchHandler() {
397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Resources resources = getContext().getResources();
398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final ViewConfiguration configuration = ViewConfiguration.get(getContext());
399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mFlingThresholdPx = resources.getDimensionPixelSize(
400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    R.dimen.mediapicker_fling_threshold);
401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mBigFlingThresholdPx = resources.getDimensionPixelSize(
402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    R.dimen.mediapicker_big_fling_threshold);
403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mTouchSlop = configuration.getScaledTouchSlop();
404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * The media picker panel may contain scrollable children such as a GridView, which eats
408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * all touch events before we get to it. Therefore, we'd like to intercept these events
409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * before the children to determine if we should handle swiping down in full screen mode.
410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * In non-full screen mode, we should handle all vertical scrolling events and leave
411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * horizontal scrolling to the view pager.
412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public boolean onInterceptTouchEvent(final MotionEvent ev) {
414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            switch (ev.getActionMasked()) {
415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case MotionEvent.ACTION_DOWN:
416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // Never capture the initial down, so that the children may handle it
417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // as well. Let the touch handler know about the down event as well.
418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mTouchHandler.onTouch(MediaPickerPanel.this, ev);
419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // Ask the MediaPicker whether the contained view can be swiped down.
421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // We record the value at the start of the drag to decide the swiping mode
422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // for the entire motion.
423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mCanChildViewSwipeDown = mMediaPicker.canSwipeDownChooser();
424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return false;
425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case MotionEvent.ACTION_MOVE: {
427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (mMediaPicker.isChooserHandlingTouch()) {
428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        if (shouldAllowRecaptureTouch(ev)) {
429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            mMediaPicker.stopChooserTouchHandling();
430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            mViewPager.setPagingEnabled(true);
431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            return false;
432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        }
433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // If the chooser is claiming ownership on all touch events, then we
434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // shouldn't try to handle them (neither should the view pager).
435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        mViewPager.setPagingEnabled(false);
436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return false;
437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } else if (mCanChildViewSwipeDown) {
438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // Never capture event if the child view can swipe down.
439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return false;
440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } else if (!mFullScreen && mMoved) {
441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // When we are not fullscreen, we own any vertical drag motion.
442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return true;
443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } else if (mMovedDown) {
444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // We are currently handling the down swipe ourselves, so always
445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // capture this event.
446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return true;
447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } else {
448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // The current interaction mode is undetermined, so always let the
449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // touch handler know about this event. However, don't capture this
450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // event so the child may handle it as well.
451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        mTouchHandler.onTouch(MediaPickerPanel.this, ev);
452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // Capture the touch event from now on if we are handling the drag.
454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return mFullScreen ? mMovedDown : mMoved;
455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Determine whether we think the user is actually trying to expand or slide despite the
463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * fact that they touched first on a chooser that captured the input.
464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private boolean shouldAllowRecaptureTouch(MotionEvent ev) {
466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final long elapsedMs = ev.getEventTime() - ev.getDownTime();
467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (mDownEvent == null || elapsedMs == 0 || elapsedMs > TOUCH_RECAPTURE_WINDOW_MS) {
468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // Either we don't have info to decide or it's been long enough that we no longer
469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // want to reinterpret user intent.
470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final float dx = ev.getRawX() - mDownEvent.getRawX();
473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final float dy = ev.getRawY() - mDownEvent.getRawY();
474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final float dt = elapsedMs / 1000.0f;
475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final float maxAbsDelta = Math.max(Math.abs(dx), Math.abs(dy));
476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final float velocity = maxAbsDelta / dt;
477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return velocity > mFlingThresholdPx;
478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public boolean onTouch(final View view, final MotionEvent motionEvent) {
482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            switch (motionEvent.getAction()) {
483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case MotionEvent.ACTION_UP: {
484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (!mMoved || mDownEvent == null) {
485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return false;
486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final float dx = motionEvent.getRawX() - mDownEvent.getRawX();
488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final float dy = motionEvent.getRawY() - mDownEvent.getRawY();
489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final float dt =
491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            (motionEvent.getEventTime() - mDownEvent.getEventTime()) / 1000.0f;
492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final float yVelocity = dy / dt;
493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    boolean handled = false;
495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // Vertical swipe occurred if the direction is as least mostly in the y
497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // component and has the required velocity (px/sec)
498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if ((dx == 0 || (Math.abs(dy) / Math.abs(dx)) > DIRECTION_RATIO) &&
499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            Math.abs(yVelocity) > mFlingThresholdPx) {
500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        if (yVelocity < 0 && mExpanded) {
501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            setFullScreenView(true, true);
502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            handled = true;
503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        } else if (yVelocity > 0) {
504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            if (mFullScreen && yVelocity < mBigFlingThresholdPx) {
505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                                setFullScreenView(false, true);
506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            } else {
507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                                setExpanded(false, true, PAGE_NOT_SET);
508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            }
509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            handled = true;
510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        }
511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (!handled) {
514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // If they didn't swipe enough, animate back to resting state
515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        setDesiredHeight(getDesiredHeight(), true);
516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    resetState();
518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    break;
519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case MotionEvent.ACTION_DOWN: {
521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mDownHeight = getHeight();
522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mDownEvent = MotionEvent.obtain(motionEvent);
523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // If we are here and care about the return value (i.e. this is not called
524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // from onInterceptTouchEvent), then presumably no children view in the panel
525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // handles the down event. We'd like to handle future ACTION_MOVE events, so
526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // always claim ownership on this event so it doesn't fall through and gets
527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // cancelled by the framework.
528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return true;
529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                case MotionEvent.ACTION_MOVE: {
531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (mDownEvent == null) {
532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        return mMoved;
533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final float dx = mDownEvent.getRawX() - motionEvent.getRawX();
536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    final float dy = mDownEvent.getRawY() - motionEvent.getRawY();
537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // Don't act if the move is mostly horizontal
538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    if (Math.abs(dy) > mTouchSlop &&
539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            (Math.abs(dy) / Math.abs(dx)) > DIRECTION_RATIO) {
540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        setDesiredHeight((int) (mDownHeight + dy), false);
541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        mMoved = true;
542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        if (dy < -mTouchSlop) {
543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                            mMovedDown = true;
544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        }
545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return mMoved;
547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mMoved;
551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private void resetState() {
554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mDownEvent = null;
555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mDownHeight = -1;
556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mMoved = false;
557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mMovedDown = false;
558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mCanChildViewSwipeDown = false;
559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            updateViewPager();
560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
564