diff options
author | Hyunyoung Song <hyunyoungs@google.com> | 2016-06-06 21:20:45 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-06-06 21:20:45 +0000 |
commit | 63741779e564ea9c6814800199b994f7464906db (patch) | |
tree | 2fa66db52a63ab1c67b2ea05766f7a2daaa38ce2 | |
parent | 02d3d43d97a06b8066495de8eeb63a46ee98b7f2 (diff) | |
parent | 645764e3e5fa34d9adcddfc722d726b76f048306 (diff) | |
download | android_packages_apps_Trebuchet-63741779e564ea9c6814800199b994f7464906db.tar.gz android_packages_apps_Trebuchet-63741779e564ea9c6814800199b994f7464906db.tar.bz2 android_packages_apps_Trebuchet-63741779e564ea9c6814800199b994f7464906db.zip |
Merge "Pull up all apps interaction First phase implementation: dragging and animation interaction is implemented namely in two classes. ScrollGestureDetector and AllAppsTransitionController." into ub-launcher3-calgary
12 files changed, 852 insertions, 79 deletions
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java index d0cacd374..a9ef43dbf 100644 --- a/src/com/android/launcher3/BaseContainerView.java +++ b/src/com/android/launcher3/BaseContainerView.java @@ -24,6 +24,9 @@ import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; +import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.config.FeatureFlags; + /** * A base container view, which supports resizing. */ @@ -48,7 +51,11 @@ public abstract class BaseContainerView extends FrameLayout { super(context, attrs, defStyleAttr); int width = ((Launcher) context).getDeviceProfile().availableWidthPx; - mHorizontalPadding = DeviceProfile.getContainerPadding(context, width); + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && (this instanceof AllAppsContainerView)) { + mHorizontalPadding = 0; + } else { + mHorizontalPadding = DeviceProfile.getContainerPadding(context, width); + } TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BaseContainerView, defStyleAttr, 0); diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java index f4bfa4549..61edc0f7f 100644 --- a/src/com/android/launcher3/InsettableFrameLayout.java +++ b/src/com/android/launcher3/InsettableFrameLayout.java @@ -9,6 +9,9 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; +import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.config.FeatureFlags; + public class InsettableFrameLayout extends FrameLayout implements ViewGroup.OnHierarchyChangeListener, Insettable { @@ -31,6 +34,9 @@ public class InsettableFrameLayout extends FrameLayout implements lp.rightMargin += (newInsets.right - oldInsets.right); lp.bottomMargin += (newInsets.bottom - oldInsets.bottom); } + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && child instanceof AllAppsContainerView) { + lp.setMargins(0, 0, 0, lp.bottomMargin); + } child.setLayoutParams(lp); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ca31ea54d..13690b43a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -92,6 +92,7 @@ import android.widget.Toast; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DefaultAppSearchController; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherActivityInfoCompat; @@ -256,6 +257,7 @@ public class Launcher extends Activity // Main container view for the all apps screen. @Thunk AllAppsContainerView mAppsView; + AllAppsTransitionController mAllAppsController; // Main container view and the model for the widget tray screen. @Thunk WidgetsContainerView mWidgetsView; @@ -413,7 +415,8 @@ public class Launcher extends Activity mIconCache = app.getIconCache(); mDragController = new DragController(this); - mStateTransitionAnimation = new LauncherStateTransitionAnimation(this); + mAllAppsController = new AllAppsTransitionController(this); + mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); @@ -1339,8 +1342,6 @@ public class Launcher extends Activity * Finds all the views we need and configure them properly. */ private void setupViews() { - final DragController dragController = mDragController; - mLauncherView = findViewById(R.id.launcher); mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator); mDragLayer = (DragLayer) findViewById(R.id.drag_layer); @@ -1353,7 +1354,8 @@ public class Launcher extends Activity mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg); // Setup the drag layer - mDragLayer.setup(this, dragController); + + mDragLayer.setup(this, mDragController, mAllAppsController); // Setup the hotseat mHotseat = (Hotseat) findViewById(R.id.hotseat); @@ -1367,9 +1369,9 @@ public class Launcher extends Activity // Setup the workspace mWorkspace.setHapticFeedbackEnabled(false); mWorkspace.setOnLongClickListener(this); - mWorkspace.setup(dragController); + mWorkspace.setup(mDragController); mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */); - dragController.addDragListener(mWorkspace); + mDragController.addDragListener(mWorkspace); // Get the search/delete/uninstall bar mSearchDropTargetBar = (SearchDropTargetBar) @@ -1388,15 +1390,15 @@ public class Launcher extends Activity } // Setup the drag controller (drop targets have to be added in reverse order in priority) - dragController.setDragScoller(mWorkspace); - dragController.setScrollView(mDragLayer); - dragController.setMoveTarget(mWorkspace); - dragController.addDropTarget(mWorkspace); + mDragController.setDragScoller(mWorkspace); + mDragController.setScrollView(mDragLayer); + mDragController.setMoveTarget(mWorkspace); + mDragController.addDropTarget(mWorkspace); if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.setup(this, dragController); + mSearchDropTargetBar.setup(this, mDragController); } if (mAppInfoDropTargetBar != null) { - mAppInfoDropTargetBar.setup(this, dragController); + mAppInfoDropTargetBar.setup(this, mDragController); } if (TestingUtils.MEMORY_DUMP_ENABLED) { @@ -1940,6 +1942,7 @@ public class Launcher extends Activity if (mWorkspace.getChildCount() > 0) { outState.putInt(RUNTIME_STATE_CURRENT_SCREEN, mWorkspace.getCurrentPageOffsetFromCustomContent()); + } super.onSaveInstanceState(outState); @@ -3163,6 +3166,8 @@ public class Launcher extends Activity mWorkspace.startReordering(v); } else { showOverviewMode(true); + mHotseat.requestDisallowInterceptTouchEvent(true); + } } else { final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank( @@ -3318,7 +3323,7 @@ public class Launcher extends Activity /** * Shows the apps view. */ - void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps, + public void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps, boolean focusSearchBar) { if (resetListToTop) { mAppsView.scrollToTop(); diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 17a5424a4..41e30b129 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -33,6 +33,8 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.allapps.AllAppsTransitionController; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.UiThreadCircularReveal; import com.android.launcher3.widget.WidgetsContainerView; @@ -82,6 +84,17 @@ import java.util.HashMap; */ public class LauncherStateTransitionAnimation { + /** + * animation used for all apps and widget tray when + *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code false} + */ + public static final int CIRCULAR_REVEAL = 0; + /** + * animation used for all apps and not widget tray when + *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code true} + */ + public static final int PULLUP = 1; + private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f; /** @@ -113,9 +126,11 @@ public class LauncherStateTransitionAnimation { @Thunk Launcher mLauncher; @Thunk AnimatorSet mCurrentAnimation; + AllAppsTransitionController mAllAppsController; - public LauncherStateTransitionAnimation(Launcher l) { + public LauncherStateTransitionAnimation(Launcher l, AllAppsTransitionController allAppsController) { mLauncher = l; + mAllAppsController = allAppsController; } /** @@ -154,9 +169,13 @@ public class LauncherStateTransitionAnimation { } } }; + int animType = CIRCULAR_REVEAL; + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + animType = PULLUP; + } // Only animate the search bar if animating from spring loaded mode back to all apps mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState, - Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, cb); + Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb); } /** @@ -167,7 +186,7 @@ public class LauncherStateTransitionAnimation { final WidgetsContainerView toView = mLauncher.getWidgetsView(); final View buttonView = mLauncher.getWidgetsButton(); mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState, - Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, + Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL, new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){ @Override void onTransitionComplete() { @@ -189,8 +208,12 @@ public class LauncherStateTransitionAnimation { } if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { + int animType = CIRCULAR_REVEAL; + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + animType = PULLUP; + } startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, - animated, onCompleteRunnable); + animated, animType, onCompleteRunnable); } else if (fromState == Launcher.State.WIDGETS || fromState == Launcher.State.WIDGETS_SPRING_LOADED) { startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, @@ -208,7 +231,7 @@ public class LauncherStateTransitionAnimation { private AnimatorSet startAnimationToOverlay( final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, final View buttonView, final BaseContainerView toView, - final boolean animated, final PrivateTransitionCallbacks pCb) { + final boolean animated, int animType, final PrivateTransitionCallbacks pCb) { final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final Resources res = mLauncher.getResources(); final boolean material = Utilities.ATLEAST_LOLLIPOP; @@ -225,12 +248,35 @@ public class LauncherStateTransitionAnimation { // Cancel the current animation cancelAnimation(); - playCommonTransitionAnimations(toWorkspaceState, fromView, toView, - animated, initialized, animation, revealDuration, layerViews); - + if (!FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + playCommonTransitionAnimations(toWorkspaceState, fromView, toView, + animated, initialized, animation, revealDuration, layerViews); + } final View contentView = toView.getContentView(); - if (animated && initialized) { + if (!animated || !initialized) { + toView.setTranslationX(0.0f); + toView.setTranslationY(0.0f); + toView.setScaleX(1.0f); + toView.setScaleY(1.0f); + toView.setAlpha(1.0f); + toView.setVisibility(View.VISIBLE); + toView.bringToFront(); + + // Show the content view + contentView.setVisibility(View.VISIBLE); + + dispatchOnLauncherTransitionPrepare(fromView, animated, false); + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionPrepare(toView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + pCb.onTransitionComplete(); + + return null; + } + if (animType == CIRCULAR_REVEAL) { // Setup the reveal view animation final View revealView = toView.getRevealView(); @@ -366,27 +412,40 @@ public class LauncherStateTransitionAnimation { toView.post(startAnimRunnable); return animation; - } else { - toView.setTranslationX(0.0f); - toView.setTranslationY(0.0f); - toView.setScaleX(1.0f); - toView.setScaleY(1.0f); - toView.setVisibility(View.VISIBLE); - toView.bringToFront(); + } else if (animType == PULLUP) { + animation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + cleanupAnimation(); + pCb.onTransitionComplete(); + } - // Show the content view - contentView.setVisibility(View.VISIBLE); + }); + mAllAppsController.animateToAllApps(animation); dispatchOnLauncherTransitionPrepare(fromView, animated, false); - dispatchOnLauncherTransitionStart(fromView, animated, false); - dispatchOnLauncherTransitionEnd(fromView, animated, false); dispatchOnLauncherTransitionPrepare(toView, animated, false); - dispatchOnLauncherTransitionStart(toView, animated, false); - dispatchOnLauncherTransitionEnd(toView, animated, false); - pCb.onTransitionComplete(); - return null; + final AnimatorSet stateAnimation = animation; + final Runnable startAnimRunnable = new Runnable() { + public void run() { + // Check that mCurrentAnimation hasn't changed while + // we waited for a layout/draw pass + if (mCurrentAnimation != stateAnimation) + return; + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + + toView.requestFocus(); + stateAnimation.start(); + } + }; + toView.post(startAnimRunnable); + return animation; } + return null; } /** @@ -439,7 +498,7 @@ public class LauncherStateTransitionAnimation { * Starts an animation to the workspace from the apps view. */ private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState, - final Workspace.State toWorkspaceState, final boolean animated, + final Workspace.State toWorkspaceState, final boolean animated, int type, final Runnable onCompleteRunnable) { AllAppsContainerView appsView = mLauncher.getAppsView(); // No alpha anim from all apps @@ -476,7 +535,7 @@ public class LauncherStateTransitionAnimation { // Only animate the search bar if animating to spring loaded mode from all apps mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState, mLauncher.getAllAppsButton(), appsView, - animated, onCompleteRunnable, cb); + animated, type, onCompleteRunnable, cb); } /** @@ -506,7 +565,7 @@ public class LauncherStateTransitionAnimation { mCurrentAnimation = startAnimationToWorkspaceFromOverlay( fromWorkspaceState, toWorkspaceState, mLauncher.getWidgetsButton(), widgetsView, - animated, onCompleteRunnable, cb); + animated, CIRCULAR_REVEAL, onCompleteRunnable, cb); } /** @@ -598,7 +657,7 @@ public class LauncherStateTransitionAnimation { private AnimatorSet startAnimationToWorkspaceFromOverlay( final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, final View buttonView, final BaseContainerView fromView, - final boolean animated, final Runnable onCompleteRunnable, + final boolean animated, int animType, final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) { final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final Resources res = mLauncher.getResources(); @@ -619,10 +678,29 @@ public class LauncherStateTransitionAnimation { boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages; - playCommonTransitionAnimations(toWorkspaceState, fromView, toView, - animated, initialized, animation, revealDuration, layerViews); + if (!FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + playCommonTransitionAnimations(toWorkspaceState, fromView, toView, + animated, initialized, animation, revealDuration, layerViews); + } + if (!animated || !initialized) { + mAllAppsController.finishPullDown(); + fromView.setVisibility(View.GONE); + dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible); + dispatchOnLauncherTransitionStart(fromView, animated, true); + dispatchOnLauncherTransitionEnd(fromView, animated, true); + dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible); + dispatchOnLauncherTransitionStart(toView, animated, true); + dispatchOnLauncherTransitionEnd(toView, animated, true); + pCb.onTransitionComplete(); - if (animated && initialized) { + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + return null; + } + if (animType == CIRCULAR_REVEAL) { final View revealView = fromView.getRevealView(); final View contentView = fromView.getContentView(); @@ -791,23 +869,59 @@ public class LauncherStateTransitionAnimation { fromView.post(startAnimRunnable); return animation; - } else /* if (!(animated && initialized)) */ { - fromView.setVisibility(View.GONE); - dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible); - dispatchOnLauncherTransitionStart(fromView, animated, true); - dispatchOnLauncherTransitionEnd(fromView, animated, true); - dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible); - dispatchOnLauncherTransitionStart(toView, animated, true); - dispatchOnLauncherTransitionEnd(toView, animated, true); - pCb.onTransitionComplete(); + } else if (animType == PULLUP) { + animation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromView, animated, false); + dispatchOnLauncherTransitionEnd(toView, animated, false); + cleanupAnimation(); + pCb.onTransitionComplete(); + } - // Run any queued runnables - if (onCompleteRunnable != null) { - onCompleteRunnable.run(); - } + }); + mAllAppsController.animateToWorkspace(animation); - return null; + // Dispatch the prepare transition signal + dispatchOnLauncherTransitionPrepare(fromView, animated, false); + dispatchOnLauncherTransitionPrepare(toView, animated, false); + + animation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dispatchOnLauncherTransitionEnd(fromView, animated, true); + dispatchOnLauncherTransitionEnd(toView, animated, true); + + // Run any queued runnables + if (onCompleteRunnable != null) { + onCompleteRunnable.run(); + } + + // This can hold unnecessary references to views. + cleanupAnimation(); + pCb.onTransitionComplete(); + } + }); + + final AnimatorSet stateAnimation = animation; + final Runnable startAnimRunnable = new Runnable() { + public void run() { + // Check that mCurrentAnimation hasn't changed while + // we waited for a layout/draw pass + if (mCurrentAnimation != stateAnimation) + return; + dispatchOnLauncherTransitionStart(fromView, animated, false); + dispatchOnLauncherTransitionStart(toView, animated, false); + + // Focus the new view + toView.requestFocus(); + stateAnimation.start(); + } + }; + fromView.post(startAnimRunnable); + return animation; } + return null; } /** diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java index 0c8568e5e..6ee96fc79 100644 --- a/src/com/android/launcher3/PinchToOverviewListener.java +++ b/src/com/android/launcher3/PinchToOverviewListener.java @@ -21,6 +21,8 @@ import android.content.Context; import android.view.MotionEvent; import android.view.ScaleGestureDetector; +import com.android.launcher3.util.TouchController; + /** * Detects pinches and animates the Workspace to/from overview mode. * @@ -30,7 +32,8 @@ import android.view.ScaleGestureDetector; * @see PinchThresholdManager * @see PinchAnimationManager */ -public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { +public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener + implements TouchController { private static final float OVERVIEW_PROGRESS = 0f; private static final float WORKSPACE_PROGRESS = 1f; /** @@ -63,15 +66,16 @@ public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleG return mPinchStarted; } - public void onTouchEvent(MotionEvent ev) { + public boolean onTouchEvent(MotionEvent ev) { if (mPinchStarted) { if (ev.getPointerCount() > 2) { // Using more than two fingers causes weird behavior, so just cancel the pinch. cancelPinch(mPreviousProgress, -1); } else { - mPinchDetector.onTouchEvent(ev); + return mPinchDetector.onTouchEvent(ev); } } + return false; } @Override diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index c9bd02c22..95ab286d9 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -20,16 +20,20 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; +import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.method.TextKeyListener; import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; +import android.widget.LinearLayout; import com.android.launcher3.AppInfo; import com.android.launcher3.BaseContainerView; @@ -40,6 +44,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.ExtendedEditText; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; @@ -178,9 +183,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mApps.setAdapter(mAdapter); mLayoutManager = mAdapter.getLayoutManager(); mItemDecoration = mAdapter.getItemDecoration(); - mRecyclerViewTopBottomPadding = - res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding); - + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + mRecyclerViewTopBottomPadding = 0; + setPadding(0, 0, 0, 0); + } else { + mRecyclerViewTopBottomPadding = + res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding); + } mSearchQueryBuilder = new SpannableStringBuilder(); Selection.setSelection(mSearchQueryBuilder, 0); } @@ -220,6 +229,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mApps.removeApps(apps); } + public void setSearchBarVisible(boolean visible) { + if (visible) { + mSearchBarController.setVisibility(View.VISIBLE); + } else { + mSearchBarController.setVisibility(View.INVISIBLE); + } + } /** * Sets the search bar that shows above the a-z list. */ @@ -239,6 +255,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mAppsRecyclerView.scrollToTop(); } + public boolean isScrollAtTop() { + return ((LinearLayoutManager) mAppsRecyclerView.getLayoutManager()) + .findFirstVisibleItemPosition() == 1; + } /** * Focuses the search field and begins an app search. */ @@ -321,13 +341,33 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc MeasureSpec.getSize(widthMeasureSpec) - mHorizontalPadding, MeasureSpec.getSize(heightMeasureSpec)); + DeviceProfile grid = mLauncher.getDeviceProfile(); + int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() : + MeasureSpec.getSize(widthMeasureSpec)) + - 2 * mAppsRecyclerView.getMaxScrollbarWidth(); + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + if (mNumAppsPerRow != grid.inv.numColumns || + mNumPredictedAppsPerRow != grid.inv.numColumns) { + mNumAppsPerRow = grid.inv.numColumns; + mNumPredictedAppsPerRow = grid.inv.numColumns; + + mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow); + mAdapter.setNumAppsPerRow(mNumAppsPerRow); + mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm()); + if (mNumAppsPerRow > 0) { + int iconSize = availableWidth / mNumAppsPerRow; + int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2; + } + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; + } + + // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. --- + // Update the number of items in the grid before we measure the view // TODO: mSectionNamesMargin is currently 0, but also account for it, // if it's enabled in the future. - int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() : - MeasureSpec.getSize(widthMeasureSpec)) - - 2 * mAppsRecyclerView.getMaxScrollbarWidth(); - DeviceProfile grid = mLauncher.getDeviceProfile(); grid.updateAppsViewNumCols(getResources(), availableWidth); if (mNumAppsPerRow != grid.allAppsNumCols || mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) { @@ -346,6 +386,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mAdapter.setNumAppsPerRow(mNumAppsPerRow); mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm); + // TODO: should we not do all this complicated computation but just match the + // numAppsPerRow with the workspace? if (mNumAppsPerRow > 0) { int iconSize = availableWidth / mNumAppsPerRow; int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2; @@ -353,6 +395,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc } } + // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. --- super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -385,6 +428,24 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams(); lp.leftMargin = bgPadding.left; lp.rightMargin = bgPadding.right; + + if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) { + MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams(); + + int navBarHeight = 84; /* replace with mInset.height() in dragLayer */ + DeviceProfile grid = mLauncher.getDeviceProfile(); + int height = navBarHeight + grid.hotseatCellHeightPx; + + mlp.topMargin = height; + mAppsRecyclerView.setLayoutParams(mlp); + + LinearLayout.LayoutParams llp = + (LinearLayout.LayoutParams) mSearchInput.getLayoutParams(); + llp.topMargin = navBarHeight; + mSearchInput.setLayoutParams(llp); + + lp.height = height; + } mSearchContainer.setLayoutParams(lp); } @@ -437,6 +498,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // Return early if this is not initiated from a touch if (!v.isInTouchMode()) return false; // When we have exited all apps or are in transition, disregard long clicks + if (!mLauncher.isAppsViewVisible() || mLauncher.getWorkspace().isSwitchingState()) return false; // Return if global dragging is not enabled diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java index a4bea8dbd..ac3593238 100644 --- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java @@ -49,6 +49,9 @@ public abstract class AllAppsSearchBarController protected DefaultAppSearchAlgorithm mSearchAlgorithm; protected InputMethodManager mInputMethodManager; + public void setVisibility(int visibility) { + mInput.setVisibility(visibility); + } /** * Sets the references to the apps model and the search result callback. */ diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java new file mode 100644 index 000000000..1428c2f32 --- /dev/null +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -0,0 +1,347 @@ +package com.android.launcher3.allapps; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.android.launcher3.Hotseat; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAnimUtils; +import com.android.launcher3.util.TouchController; + +/** + * Handles AllApps view transition. + * 1) Slides all apps view using direct manipulation + * 2) When finger is released, animate to either top or bottom accordingly. + * + * Algorithm: + * If release velocity > THRES1, snap according to the direction of movement. + * If release velocity < THRES1, snap according to either top or bottom depending on whether it's + * closer to top or closer to the page indicator. + */ +public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener { + + private static final String TAG = "AllAppsTrans"; + private static final boolean DBG = false; + + private static final float ANIMATION_DURATION = 500; + + private AllAppsContainerView mAppsView; + private Hotseat mHotseat; + private Drawable mHotseatBackground; + private float mHotseatAlpha; + private View mWorkspaceCurPage; + + private final Launcher mLauncher; + private final VerticalPullDetector mDetector; + + // Animation in this class is controlled by a single variable {@link mProgressTransY}. + // Visually, it represents top y coordinate of the all apps container. Using the + // {@link mTranslation} as the denominator, this fraction value ranges in [0, 1]. + private float mProgressTransY; // numerator + private float mTranslation = -1; // denominator + + private float mAnimationDuration; + private float mCurY; + + private AnimatorSet mCurrentAnimation; + + public AllAppsTransitionController(Launcher launcher) { + mLauncher = launcher; + mDetector = new VerticalPullDetector(launcher); + mDetector.setListener(this); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + init(); + if (mLauncher.getWorkspace().isInOverviewMode() || + mLauncher.isWidgetsViewVisible()) { + return false; + } + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mDetector.setAllAppsState(mLauncher.isAllAppsVisible(), mAppsView.isScrollAtTop()); + } + mDetector.onTouchEvent(ev); + return mDetector.mScrolling; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return mDetector.onTouchEvent(ev); + } + + private void init() { + if (mAppsView != null) { + return; + } + mAppsView = mLauncher.getAppsView(); + mHotseat = mLauncher.getHotseat(); + mWorkspaceCurPage = mLauncher.getWorkspace().getChildAt( + mLauncher.getWorkspace().getCurrentPage()); + + if (mHotseatBackground == null) { + mHotseatBackground = mHotseat.getBackground(); + mHotseatAlpha = mHotseatBackground.getAlpha() / 255f; + } + } + + @Override + public void onScrollStart(boolean start) { + cancelAnimation(); + mCurrentAnimation = LauncherAnimUtils.createAnimatorSet(); + mCurY = mAppsView.getTranslationY(); + preparePull(start); + + } + + /** + * @param start {@code true} if start of new drag. + */ + public void preparePull(boolean start) { + if (start) { + if (!mLauncher.isAllAppsVisible()) { + mHotseat.setBackground(null); + mAppsView.setVisibility(View.VISIBLE); + mAppsView.getContentView().setVisibility(View.VISIBLE); + mAppsView.setAlpha(mHotseatAlpha); + mAppsView.setSearchBarVisible(false); + + if (mTranslation < 0) { + mTranslation = mHotseat.getTop(); + setProgress(mTranslation); + } + } else { + mLauncher.getWorkspace().setVisibility(View.VISIBLE); + mLauncher.getWorkspace().setAlpha(1f); + mLauncher.getWorkspace().onLauncherTransitionPrepare(mLauncher, false, false); + mWorkspaceCurPage.setVisibility(View.VISIBLE); + mAppsView.setSearchBarVisible(false); + setLightStatusBar(false); + } + } + mHotseat.setVisibility(View.VISIBLE); + mHotseat.bringToFront(); + } + + private void setLightStatusBar(boolean enable) { + int systemUiFlags = mLauncher.getWindow().getDecorView().getSystemUiVisibility(); + if (enable) { + mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags + | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + + } else { + mLauncher.getWindow().getDecorView().setSystemUiVisibility(systemUiFlags + & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + + } + } + + private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(.5f); + + @Override + public boolean onScroll(float displacement, float velocity) { + if (mAppsView == null) { + return false; // early termination. + } + if (0 <= mCurY + displacement && mCurY + displacement < mTranslation) { + setProgress(mCurY + displacement); + } + return true; + } + + /** + * @param progress y value of the border between hotseat and all apps + */ + public void setProgress(float progress) { + mProgressTransY = progress; + float alpha = calcAlphaAllApps(progress); + float workspaceHotseatAlpha = Math.max(mHotseatAlpha, 1 - alpha); + setTransAndAlpha(mAppsView, progress, Math.max(mHotseatAlpha, alpha)); + setTransAndAlpha(mWorkspaceCurPage, -mTranslation + progress, workspaceHotseatAlpha); + setTransAndAlpha(mHotseat, -mTranslation + progress, workspaceHotseatAlpha); + } + + public float getProgress() { + return mProgressTransY; + } + + private float calcAlphaAllApps(float progress) { + return mAlphaInterpolator.getInterpolation((mTranslation - progress)/mTranslation); + } + + private void setTransAndAlpha(View v, float transY, float alpha) { + if (v != null) { + v.setTranslationY(transY); + v.setAlpha(alpha); + } + } + + @Override + public void onScrollEnd(float velocity, boolean fling) { + if (mAppsView == null) { + return; // early termination. + } + + if (fling) { + if (velocity < 0) { + calculateDuration(velocity, mAppsView.getTranslationY()); + showAppsView(); // Flinging in UP direction + } else { + calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY())); + showWorkspace(); // Flinging in DOWN direction + } + // snap to top or bottom using the release velocity + } else { + if (mAppsView.getTranslationY() > mTranslation / 2) { + calculateDuration(velocity, Math.abs(mTranslation - mAppsView.getTranslationY())); + showWorkspace(); // Released in the bottom half + } else { + calculateDuration(velocity, Math.abs(mAppsView.getTranslationY())); + showAppsView(); // Released in the top half + } + } + } + + private void calculateDuration(float velocity, float disp) { + // TODO: make these values constants after tuning. + float velocityDivisor = Math.max(1, 0.75f * velocity); + float travelDistance = Math.max(0.2f, disp / mTranslation); + mAnimationDuration = Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance); + if (true) { // MERONG + Log.d(TAG, String.format("calculateDuration=%f, v=%f, d=%f", mAnimationDuration, velocity, disp)); + } + } + + /** + * Depending on the current state of the launcher, either just + * 1) animate + * 2) animate and do all the state updates. + */ + private void showAppsView() { + if (mLauncher.isAllAppsVisible()) { + animateToAllApps(mCurrentAnimation); + mCurrentAnimation.start(); + } else { + mLauncher.showAppsView(true /* animated */, true /* resetListToTop */, + true /* updatePredictedApps */, false /* focusSearchBar */); + } + } + + /** + * Depending on the current state of the launcher, either just + * 1) animate + * 2) animate and do all the state updates. + */ + private void showWorkspace() { + if (mLauncher.isAllAppsVisible()) { + mLauncher.showWorkspace(true /* animated */); + } else { + animateToWorkspace(mCurrentAnimation); + mCurrentAnimation.start(); + } + } + + public void animateToAllApps(AnimatorSet animationOut) { + if ((mAppsView = mLauncher.getAppsView()) == null || animationOut == null){ + return; + } + if (!mDetector.mScrolling) { + preparePull(true); + } + mCurY = mAppsView.getTranslationY(); + final float fromAllAppsTop = mAppsView.getTranslationY(); + final float toAllAppsTop = 0; + + ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", + fromAllAppsTop, toAllAppsTop); + driftAndAlpha.setDuration((long) mAnimationDuration); + animationOut.play(driftAndAlpha); + + animationOut.addListener(new AnimatorListenerAdapter() { + boolean canceled = false; + @Override + public void onAnimationCancel(Animator animation) { + canceled = true; + } + @Override + public void onAnimationEnd(Animator animation) { + if (canceled) { + return; + } else { + finishPullUp(); + cleanUpAnimation(); + mDetector.finishedScrolling(); + } + }}); + mCurrentAnimation = animationOut; + } + + private void finishPullUp() { + mAppsView.setSearchBarVisible(true); + mHotseat.setVisibility(View.INVISIBLE); + setProgress(0f); + setLightStatusBar(true); + } + + public void animateToWorkspace(AnimatorSet animationOut) { + if ((mAppsView = mLauncher.getAppsView()) == null || animationOut == null){ + return; + } + if(!mDetector.mScrolling) { + preparePull(true); + } + final float fromAllAppsTop = mAppsView.getTranslationY(); + final float toAllAppsTop = mTranslation; + + ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress", + fromAllAppsTop, toAllAppsTop); + driftAndAlpha.setDuration((long) mAnimationDuration); + animationOut.play(driftAndAlpha); + + animationOut.addListener(new AnimatorListenerAdapter() { + boolean canceled = false; + @Override + public void onAnimationCancel(Animator animation) { + canceled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (canceled) { + return; + } else { + finishPullDown(); + cleanUpAnimation(); + mDetector.finishedScrolling(); + } + }}); + mCurrentAnimation = animationOut; + } + + public void finishPullDown() { + mAppsView.setVisibility(View.INVISIBLE); + mHotseat.setBackground(mHotseatBackground); + mHotseat.setVisibility(View.VISIBLE); + setProgress(mTranslation); + setLightStatusBar(false); + } + + private void cancelAnimation() { + if (mCurrentAnimation != null) { + mCurrentAnimation.cancel(); + mCurrentAnimation = null; + } + } + + private void cleanUpAnimation() { + mCurrentAnimation = null; + } +} diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java new file mode 100644 index 000000000..4cc921c33 --- /dev/null +++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java @@ -0,0 +1,196 @@ +package com.android.launcher3.allapps; + +import android.content.Context; +import android.util.Log; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +/** + * One dimensional scroll gesture detector for all apps container pull up interaction. + */ +public class VerticalPullDetector { + + private static final String TAG = "ScrollGesture"; + private static final boolean DBG = false; + + private float mTouchSlop; + + private boolean mAllAppsVisible; + private boolean mAllAppsScrollAtTop; + + /** + * The minimum release velocity in pixels per millisecond that triggers fling.. + */ + private static final float RELEASE_VELOCITY_PX_MS = 1.7f; + + /** + * The time constant used to calculate dampening in the low-pass filter of scroll velocity. + * Cutoff frequency is set at 10 Hz. + */ + public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10); + + /* Scroll state, this is set to true during dragging and animation. */ + boolean mScrolling; + + + float mDownY; + float mDownMillis; + + float mLastY; + float mLastMillis; + + float mVelocity; + float mLastDisplacement; + float mDisplacement; + + /* scroll started during previous animation */ + boolean mSubtractSlop = true; + + /* Client of this gesture detector can register a callback. */ + Listener mListener; + + public void setListener(Listener l) { + mListener = l; + } + + interface Listener{ + /** + * @param start when should + */ + void onScrollStart(boolean start); + boolean onScroll(float displacement, float velocity); + void onScrollEnd(float velocity, boolean fling); + } + + public VerticalPullDetector(Context context) { + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + public void setAllAppsState(boolean allAppsVisible, boolean scrollAtTop) { + mAllAppsVisible = allAppsVisible; + mAllAppsScrollAtTop = scrollAtTop; + } + + private boolean shouldScrollStart() { + if (mAllAppsVisible && mDisplacement > mTouchSlop && mAllAppsScrollAtTop) { + return true; + } + if (!mAllAppsVisible && mDisplacement < -mTouchSlop) { + return true; + } + return false; + } + + public boolean onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mDownMillis = ev.getDownTime(); + mDownY = ev.getY(); + mLastDisplacement = 0; + mVelocity = 0; + + if (mScrolling) { + reportScrollStart(true /* recatch */); + } + break; + case MotionEvent.ACTION_MOVE: + mDisplacement = computeDisplacement(ev); + mVelocity = computeVelocity(ev, mVelocity); + + if (!mScrolling && shouldScrollStart()) { + mScrolling = true; + reportScrollStart(false /* recatch */); + } + if (mScrolling && mListener != null) { + reportScroll(); + } + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + // These are synthetic events and there is no need to update internal values. + if (mScrolling && mListener != null) { + reportScrollEnd(); + } + break; + default: + //TODO: add multi finger tracking by tracking active pointer. + break; + } + // Do house keeping. + mLastDisplacement = mDisplacement; + + mLastY = ev.getY(); + mLastMillis = ev.getEventTime(); + + return true; + } + + public void finishedScrolling() { + mScrolling = false; + } + + private boolean reportScrollStart(boolean recatch) { + mListener.onScrollStart(!recatch); + if (DBG) { + Log.d(TAG, "onScrollStart recatch:" + recatch); + } + return true; + } + + private boolean reportScroll() { + float delta = mDisplacement - mLastDisplacement; + if (delta != 0) { + if (DBG) { + Log.d(TAG, String.format("onScroll disp=%.1f, velocity=%.1f", + mDisplacement, mVelocity)); + } + return mListener.onScroll(mDisplacement - (mSubtractSlop? mTouchSlop : 0), mVelocity); + } + return true; + } + + private void reportScrollEnd() { + if (DBG) { + Log.d(TAG, String.format("onScrolEnd disp=%.1f, velocity=%.1f", + mDisplacement, mVelocity)); + } + mListener.onScrollEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS); + } + /** + * Computes the damped velocity using the two motion events and the previous velocity. + */ + private float computeVelocity(MotionEvent to, float previousVelocity) { + float delta = computeDelta(to); + + float deltaTimeMillis = to.getEventTime() - mLastMillis; + float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0; + if (Math.abs(previousVelocity) < 0.001f) { + return velocity; + } + + float alpha = computeDampeningFactor(deltaTimeMillis); + return interpolate(previousVelocity, velocity, alpha); + } + + private float computeDisplacement(MotionEvent to) { + return to.getY() - mDownY; + } + + private float computeDelta(MotionEvent to) { + return to.getY() - mLastY; + } + + /** + * Returns a time-dependent dampening factor using delta time. + */ + private static float computeDampeningFactor(float deltaTime) { + return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime); + } + + /** + * Returns the linear interpolation between two values + */ + private static float interpolate(float from, float to, float alpha) { + return (1.0f - alpha) * from + alpha * to; + } +} diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java index f4470f320..af5ff587a 100644 --- a/src/com/android/launcher3/dragndrop/DragController.java +++ b/src/com/android/launcher3/dragndrop/DragController.java @@ -46,6 +46,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.TouchController; import java.util.ArrayList; import java.util.HashSet; @@ -53,7 +54,7 @@ import java.util.HashSet; /** * Class for initiating a drag within a view or across multiple views. */ -public class DragController implements DragDriver.EventListener { +public class DragController implements DragDriver.EventListener, TouchController { private static final String TAG = "Launcher.DragController"; /** Indicates the drag is a move. */ diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 33ce68313..e4c84360c 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -43,6 +43,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import com.android.launcher3.AppWidgetResizeFrame; +import com.android.launcher3.BaseContainerView; import com.android.launcher3.CellLayout; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.ItemInfo; @@ -56,10 +57,13 @@ import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; +import com.android.launcher3.allapps.AllAppsContainerView; +import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.TouchController; import java.util.ArrayList; @@ -117,6 +121,11 @@ public class DragLayer extends InsettableFrameLayout { // Related to pinch-to-go-to-overview gesture. private PinchToOverviewListener mPinchListener = null; + + // Handles all apps pull up interaction + private AllAppsTransitionController mAllAppsController; + + private TouchController mActiveController; /** * Used to create a new DragLayer from XML. * @@ -138,9 +147,11 @@ public class DragLayer extends InsettableFrameLayout { mIsRtl = Utilities.isRtl(res); } - public void setup(Launcher launcher, DragController controller) { + public void setup(Launcher launcher, DragController dragController, + AllAppsTransitionController allAppsTransitionController) { mLauncher = launcher; - mDragController = controller; + mDragController = dragController; + mAllAppsController = allAppsTransitionController; boolean isAccessibilityEnabled = ((AccessibilityManager) mLauncher.getSystemService( Context.ACCESSIBILITY_SERVICE)).isEnabled(); @@ -246,6 +257,7 @@ public class DragLayer extends InsettableFrameLayout { public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); + if (action == MotionEvent.ACTION_DOWN) { if (handleTouchDown(ev, true)) { return true; @@ -258,11 +270,21 @@ public class DragLayer extends InsettableFrameLayout { } clearAllResizeFrames(); + if (mDragController.onInterceptTouchEvent(ev)) { + mActiveController = mDragController; + return true; + } + if (mAllAppsController.onInterceptTouchEvent(ev)) { + mActiveController = mAllAppsController; + return true; + } + if (mPinchListener != null && mPinchListener.onInterceptTouchEvent(ev)) { // Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.) + mActiveController = mPinchListener; return true; } - return mDragController.onInterceptTouchEvent(ev); + return false; } @Override @@ -375,11 +397,6 @@ public class DragLayer extends InsettableFrameLayout { int x = (int) ev.getX(); int y = (int) ev.getY(); - // This is only reached if a pinch was started from onInterceptTouchEvent(); - // this continues sending events for it. - if (mPinchListener != null) { - mPinchListener.onTouchEvent(ev); - } if (action == MotionEvent.ACTION_DOWN) { if (handleTouchDown(ev, false)) { @@ -406,7 +423,10 @@ public class DragLayer extends InsettableFrameLayout { } } if (handled) return true; - return mDragController.onTouchEvent(ev); + if (mActiveController != null) { + return mActiveController.onTouchEvent(ev); + } + return false; } @Override diff --git a/src/com/android/launcher3/util/TouchController.java b/src/com/android/launcher3/util/TouchController.java new file mode 100644 index 000000000..d1409c8b9 --- /dev/null +++ b/src/com/android/launcher3/util/TouchController.java @@ -0,0 +1,8 @@ +package com.android.launcher3.util; + +import android.view.MotionEvent; + +public interface TouchController { + boolean onTouchEvent(MotionEvent ev); + boolean onInterceptTouchEvent(MotionEvent ev); +} |