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