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
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
|
||||
private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
|
||||
|
||||
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,15 +662,97 @@ public class Workspace extends SmoothPagedView
|
||||
return false;
|
||||
}
|
||||
|
||||
public void removeExtraEmptyScreen() {
|
||||
if (hasExtraEmptyScreen()) {
|
||||
CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
|
||||
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
|
||||
mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
|
||||
removeView(cl);
|
||||
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()) {
|
||||
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() {
|
||||
int nScreens = getChildCount();
|
||||
nScreens = nScreens - numCustomPages();
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user