From ad4e15cae4b628677fc249628a9ff661e67add78 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Thu, 17 Oct 2013 16:21:35 -0700 Subject: Cleaning some page animations -> Fix jump when last page gets delted (issue 10908427) -> Fade out empty screen -> If the final page is empty, and that is the current page when spring loaded mode ends, animate back to the previous page and then fade out the final page. Examples: cancel widget or shortcut drop on the final page, scroll to final page and drop an icon into a the hotseat, etc. Change-Id: I13438fb0af6555b6f0b511b7aff51b3972431438 --- src/com/android/launcher3/Launcher.java | 97 +++++++++++------ src/com/android/launcher3/SmoothPagedView.java | 2 - src/com/android/launcher3/Workspace.java | 137 +++++++++++++++++++++---- 3 files changed, 183 insertions(+), 53 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index af58f79df..f35daaade 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -197,8 +197,8 @@ public class Launcher extends Activity private AnimatorSet mStateAnimation; static final int APPWIDGET_HOST_ID = 1024; - private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300; - private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600; + public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300; + private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500; private static final int SHOW_CLING_DURATION = 250; private static final int DISMISS_CLING_DURATION = 200; @@ -709,13 +709,24 @@ public class Launcher extends Activity // Reset the startActivity waiting flag mWaitingForResult = false; + Runnable exitSpringLoaded = new Runnable() { + @Override + public void run() { + exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), + EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + }; + if (requestCode == REQUEST_BIND_APPWIDGET) { - int appWidgetId = data != null ? + final int appWidgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; if (resultCode == RESULT_CANCELED) { completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId); + mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded, + ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_OK) { - addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo); + addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, + mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); } return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { @@ -725,22 +736,37 @@ public class Launcher extends Activity return; } - boolean delayExitSpringLoadedMode = false; boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET || requestCode == REQUEST_CREATE_APPWIDGET); // We have special handling for widgets if (isWidgetDrop) { - int appWidgetId = data != null ? + final int appWidgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; + final int result; + final Runnable onComplete; if (appWidgetId < 0) { Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" + "widget configuration activity."); - completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId); - mWorkspace.stripEmptyScreens(); + result = RESULT_CANCELED; + completeTwoStageWidgetDrop(result, appWidgetId); + onComplete = new Runnable() { + @Override + public void run() { + exitSpringLoadedDragModeDelayed(false, 0, null); + } + }; } else { - completeTwoStageWidgetDrop(resultCode, appWidgetId); + result = resultCode; + onComplete = new Runnable() { + @Override + public void run() { + completeTwoStageWidgetDrop(result, appWidgetId); + } + }; } + mWorkspace.removeExtraEmptyScreen(true, onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY, + false); return; } @@ -760,15 +786,15 @@ public class Launcher extends Activity if (isWorkspaceLocked()) { sPendingAddList.add(args); } else { - delayExitSpringLoadedMode = completeAdd(args); + completeAdd(args); } + mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded, + ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } else if (resultCode == RESULT_CANCELED) { - mWorkspace.stripEmptyScreens(); + mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded, + ON_ACTIVITY_RESULT_ANIMATION_DELAY, false); } mDragLayer.clearAnimatedView(); - // Exit spring loaded mode if necessary after cancelling the configuration of a widget - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode, - null); } private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) { @@ -788,25 +814,18 @@ public class Launcher extends Activity public void run() { completeAddAppWidget(appWidgetId, mPendingAddInfo.container, mPendingAddInfo.screenId, layout, null); - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, - null); + exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), + EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); } }; } else if (resultCode == RESULT_CANCELED) { animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION; - onCompleteRunnable = new Runnable() { - @Override - public void run() { - exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false, - null); - } - }; } if (mDragLayer.getAnimatedView() != null) { mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout, (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable, animationType, boundWidget, true); - } else { + } else if (onCompleteRunnable != null) { // The animated view may be null in the case of a rotation during widget configuration onCompleteRunnable.run(); } @@ -1900,8 +1919,14 @@ public class Launcher extends Activity mPendingAddInfo.dropPos = null; } - void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, - AppWidgetProviderInfo appWidgetInfo) { + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, + final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) { + addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0); + } + + void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, + final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int + delay) { if (appWidgetInfo.configure != null) { mPendingAddWidgetInfo = appWidgetInfo; @@ -1912,10 +1937,17 @@ public class Launcher extends Activity Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET); } else { // Otherwise just add it + Runnable onComplete = new Runnable() { + @Override + public void run() { + // Exit spring loaded mode if necessary after adding the widget + exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, + null); + } + }; completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget, appWidgetInfo); - // Exit spring loaded mode if necessary after adding the widget - exitSpringLoadedDragModeDelayed(true, false, null); + mWorkspace.removeExtraEmptyScreen(true, onComplete, delay, false); } } @@ -3104,7 +3136,7 @@ public class Launcher extends Activity } } - void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay, + void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay, final Runnable onCompleteRunnable) { if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return; @@ -3121,9 +3153,8 @@ public class Launcher extends Activity exitSpringLoadedDragMode(); } } - }, (extendedDelay ? - EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT : - EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT)); + }, delay); + } void exitSpringLoadedDragMode() { @@ -3662,7 +3693,7 @@ public class Launcher extends Activity } // Remove the extra empty screen - mWorkspace.removeExtraEmptyScreen(); + mWorkspace.removeExtraEmptyScreen(false, null); if (!AppsCustomizePagedView.DISABLE_ALL_APPS && addedApps != null && mAppsCustomizeContent != null) { diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java index a45dbbf6e..64dcb34ca 100644 --- a/src/com/android/launcher3/SmoothPagedView.java +++ b/src/com/android/launcher3/SmoothPagedView.java @@ -52,8 +52,6 @@ public abstract class SmoothPagedView extends PagedView { } public float getInterpolation(float t) { - // _o(t) = t * t * ((tension + 1) * t + tension) - // o(t) = _o(t - 1) + 1 t -= 1.0f; return t * t * ((mTension + 1) * t + mTension) + 1.0f; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index e9d41d521..d742d429a 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -89,6 +90,9 @@ public class Workspace extends SmoothPagedView private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375; private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100; + protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400; + protected static final int FADE_EMPTY_SCREEN_DURATION = 150; + private static final int BACKGROUND_FADE_OUT_DURATION = 350; private static final int ADJACENT_SCREEN_DROP_DURATION = 300; private static final int FLING_THRESHOLD_VELOCITY = 500; @@ -128,6 +132,8 @@ public class Workspace extends SmoothPagedView private HashMap mWorkspaceScreens = new HashMap(); private ArrayList mScreenOrder = new ArrayList(); + private Runnable mRemoveEmptyScreenRunnable; + /** * CellInfo for the cell that is currently being dragged */ @@ -392,7 +398,6 @@ public class Workspace extends SmoothPagedView InstallShortcutReceiver.disableAndFlushInstallQueue(getContext()); UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext()); - removeExtraEmptyScreen(); mDragSourceInternal = null; mLauncher.onInteractionEnd(); } @@ -627,6 +632,9 @@ public class Workspace extends SmoothPagedView boolean lastChildOnScreen = false; boolean childOnFinalScreen = false; + // Cancel any pending removal of empty screen + mRemoveEmptyScreenRunnable = null; + if (mDragSourceInternal != null) { if (mDragSourceInternal.getChildCount() == 1) { lastChildOnScreen = true; @@ -654,13 +662,95 @@ public class Workspace extends SmoothPagedView return false; } - public void removeExtraEmptyScreen() { + private void convertFinalScreenToEmptyScreenIfNecessary() { + if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return; + long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1); + + if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return; + CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId); + + // If the final screen is empty, convert it to the extra empty screen + if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0) { + mWorkspaceScreens.remove(finalScreenId); + mScreenOrder.remove(finalScreenId); + + // if this is the last non-custom content screen, convert it to the empty screen + mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen); + mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); + } + } + + public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete) { + removeExtraEmptyScreen(animate, onComplete, 0, false); + } + + public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete, + final int delay, final boolean stripEmptyScreens) { + if (delay > 0) { + postDelayed(new Runnable() { + @Override + public void run() { + removeExtraEmptyScreen(animate, onComplete, 0, stripEmptyScreens); + } + + }, delay); + return; + } + + convertFinalScreenToEmptyScreenIfNecessary(); if (hasExtraEmptyScreen()) { - CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); - mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); - mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID); - removeView(cl); + int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID); + if (getNextPage() == emptyIndex) { + snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION); + fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION, + onComplete, stripEmptyScreens); + } else { + fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION, + onComplete, stripEmptyScreens); + } + return; } + if (onComplete != null) { + onComplete.run(); + } + } + + private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete, + final boolean stripEmptyScreens) { + PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f); + PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f); + + final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID); + + mRemoveEmptyScreenRunnable = new Runnable() { + @Override + public void run() { + if (hasExtraEmptyScreen()) { + mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID); + mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID); + removeView(cl); + if (stripEmptyScreens) { + stripEmptyScreens(); + } + } + } + }; + + ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha); + oa.setDuration(duration); + oa.setStartDelay(delay); + oa.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mRemoveEmptyScreenRunnable != null) { + mRemoveEmptyScreenRunnable.run(); + } + if (onComplete != null) { + onComplete.run(); + } + } + }); + oa.start(); } public boolean hasExtraEmptyScreen() { @@ -753,6 +843,7 @@ public class Workspace extends SmoothPagedView removeView(cl); } else { // if this is the last non-custom content screen, convert it to the empty screen + mRemoveEmptyScreenRunnable = null; mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl); mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID); } @@ -1103,11 +1194,15 @@ public class Workspace extends SmoothPagedView } protected void snapToPage(int whichPage, Runnable r) { + snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r); + } + + protected void snapToPage(int whichPage, int duration, Runnable r) { if (mDelayedSnapToPageRunnable != null) { mDelayedSnapToPageRunnable.run(); } mDelayedSnapToPageRunnable = r; - snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION); + snapToPage(whichPage, duration); } protected void snapToScreenId(long screenId, Runnable r) { @@ -2726,13 +2821,13 @@ public class Workspace extends SmoothPagedView // cell also contains a shortcut, then create a folder with the two shortcuts. if (!mInScrollArea && createUserFolderIfNecessary(cell, container, dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) { - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); return; } if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell, distance, d, false)) { - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); return; } @@ -2840,7 +2935,7 @@ public class Workspace extends SmoothPagedView if (finalResizeRunnable != null) { finalResizeRunnable.run(); } - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); } }; mAnimatingViewIntoPlace = true; @@ -3491,7 +3586,13 @@ public class Workspace extends SmoothPagedView final Runnable exitSpringLoadedRunnable = new Runnable() { @Override public void run() { - mLauncher.exitSpringLoadedDragModeDelayed(true, false, null); + removeExtraEmptyScreen(false, new Runnable() { + @Override + public void run() { + mLauncher.exitSpringLoadedDragModeDelayed(true, + Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null); + } + }); } }; @@ -3727,7 +3828,7 @@ public class Workspace extends SmoothPagedView external, scalePreview); Resources res = mLauncher.getResources(); - int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200; + final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200; // In the case where we've prebound the widget, we remove it from the DragLayer if (finalView instanceof AppWidgetHostView && external) { @@ -3835,11 +3936,11 @@ public class Workspace extends SmoothPagedView final boolean isFlingToDelete, final boolean success) { if (mDeferDropAfterUninstall) { mDeferredAction = new Runnable() { - public void run() { - onDropCompleted(target, d, isFlingToDelete, success); - mDeferredAction = null; - } - }; + public void run() { + onDropCompleted(target, d, isFlingToDelete, success); + mDeferredAction = null; + } + }; return; } @@ -3857,7 +3958,7 @@ public class Workspace extends SmoothPagedView // If we move the item to anything not on the Workspace, check if any empty // screens need to be removed. If we dropped back on the workspace, this will // be done post drop animation. - stripEmptyScreens(); + removeExtraEmptyScreen(true, null, 0, true); } } else if (mDragInfo != null) { CellLayout cellLayout; -- cgit v1.2.3