summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher2/Launcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher2/Launcher.java')
-rw-r--r--src/com/android/launcher2/Launcher.java536
1 files changed, 353 insertions, 183 deletions
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 63fb12cf1..d8a43845c 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -23,12 +23,12 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
-import android.app.StatusBarManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -54,7 +54,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -80,6 +79,7 @@ import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -139,6 +139,7 @@ public final class Launcher extends Activity
static final int DIALOG_RENAME_FOLDER = 2;
private static final String PREFERENCES = "launcher.preferences";
+ static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher.force_enable_rotation";
// Type: int
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
@@ -246,6 +247,11 @@ public final class Launcher extends Activity
private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
static final ArrayList<String> sDumpLogs = new ArrayList<String>();
+ PendingAddWidgetInfo mWidgetBeingConfigured = null;
+
+ // We only want to get the SharedPreferences once since it does an FS stat each time we get
+ // it from the context.
+ private SharedPreferences mSharedPrefs;
private BubbleTextView mWaitingForResume;
@@ -274,6 +280,7 @@ public final class Launcher extends Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LauncherApplication app = ((LauncherApplication)getApplication());
+ mSharedPrefs = getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
mDragController = new DragController(this);
@@ -348,8 +355,11 @@ public final class Launcher extends Activity
}
mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
+ final String forceEnableRotation =
+ SystemProperties.get(FORCE_ENABLE_ROTATION_PROPERTY, "false");
+
// On large interfaces, we want the screen to auto-rotate based on the current orientation
- if (LauncherApplication.isScreenLarge() || Build.TYPE.contentEquals("eng")) {
+ if (LauncherApplication.isScreenLarge() || "true".equalsIgnoreCase(forceEnableRotation)) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
}
}
@@ -495,24 +505,31 @@ public final class Launcher extends Activity
break;
case REQUEST_CREATE_APPWIDGET:
int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
- completeAddAppWidget(appWidgetId, args.container, args.screen);
+ completeAddAppWidget(appWidgetId, args.container, args.screen, null, null);
result = true;
break;
case REQUEST_PICK_WALLPAPER:
// We just wanted the activity result here so we can clear mWaitingForResult
break;
}
- // In any situation where we have a multi-step drop, we should reset the add info only after
- // we complete the drop
- resetAddInfo();
return result;
}
@Override
- protected void onActivityResult(final int requestCode, int resultCode, final Intent data) {
+ protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
boolean delayExitSpringLoadedMode = false;
+ boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
+ requestCode == REQUEST_CREATE_APPWIDGET);
mWaitingForResult = false;
+ // We have special handling for widgets
+ if (isWidgetDrop) {
+ int appWidgetId = data != null ?
+ data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
+ completeTwoStageWidgetDrop(resultCode, appWidgetId);
+ return;
+ }
+
// The pattern used here is that a user PICKs a specific application,
// which, depending on the target, might need to CREATE the actual target.
@@ -526,26 +543,51 @@ public final class Launcher extends Activity
args.screen = mPendingAddInfo.screen;
args.cellX = mPendingAddInfo.cellX;
args.cellY = mPendingAddInfo.cellY;
-
- // If the loader is still running, defer the add until it is done.
if (isWorkspaceLocked()) {
sPendingAddList.add(args);
} else {
delayExitSpringLoadedMode = completeAdd(args);
}
- } else if ((requestCode == REQUEST_PICK_APPWIDGET ||
- requestCode == REQUEST_CREATE_APPWIDGET) && resultCode == RESULT_CANCELED) {
- if (data != null) {
- // Clean up the appWidgetId if we canceled
- int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
- if (appWidgetId != -1) {
- mAppWidgetHost.deleteAppWidgetId(appWidgetId);
- }
- }
}
-
+ mDragLayer.clearAnimatedView();
// Exit spring loaded mode if necessary after cancelling the configuration of a widget
- exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode);
+ exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode,
+ null);
+ }
+
+ private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
+ CellLayout cellLayout = (CellLayout) mWorkspace.getChildAt(mWidgetBeingConfigured.screen);
+ Runnable onCompleteRunnable = null;
+ int animationType = 0;
+
+ if (resultCode == RESULT_OK) {
+ animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
+ final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
+ mWidgetBeingConfigured.info);
+ mWidgetBeingConfigured.boundWidget = layout;
+ onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
+ mPendingAddInfo.screen, layout, null);
+ exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
+ 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);
+ }
+ };
+ }
+ mWorkspace.animateWidgetDrop(mWidgetBeingConfigured, cellLayout,
+ (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
+ animationType, mWidgetBeingConfigured.boundWidget, true);
+ mWidgetBeingConfigured = null;
}
@Override
@@ -558,39 +600,17 @@ public final class Launcher extends Activity
mRestoring = false;
mOnResumeNeedsLoad = false;
}
+
+ // Reset the pressed state of icons that were locked in the press state while activities
+ // were launching
if (mWaitingForResume != null) {
+ // Resets the previous workspace icon press state
mWaitingForResume.setStayPressed(false);
}
- // When we resume Launcher, a different Activity might be responsible for the app
- // market intent, so refresh the icon
- updateAppMarketIcon();
- mAppsCustomizeTabHost.onResume();
- if (!mWorkspaceLoading) {
- final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
- final Workspace workspace = mWorkspace;
- // We want to let Launcher draw itself at least once before we force it to build
- // layers on all the workspace pages, so that transitioning to Launcher from other
- // apps is nice and speedy. Usually the first call to preDraw doesn't correspond to
- // a true draw so we wait until the second preDraw call to be safe
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- boolean mFirstTime = true;
- public boolean onPreDraw() {
- if (mFirstTime) {
- mFirstTime = false;
- } else {
- // We delay the layer building a bit in order to give
- // other message processing a time to run. In particular
- // this avoids a delay in hiding the IME if it was
- // currently shown, because doing that may involve
- // some communication back with the app.
- workspace.postDelayed(mBuildLayersRunnable, 500);
- observer.removeOnPreDrawListener(this);
- }
- return true;
- }
- });
+ if (mAppsCustomizeContent != null) {
+ // Resets the previous all apps icon press state
+ mAppsCustomizeContent.resetDrawableState();
}
- clearTypedText();
}
@Override
@@ -939,7 +959,7 @@ public final class Launcher extends Activity
return getSpanForWidget(info.provider, info.minWidth, info.minHeight, spanXY);
}
- int[] getMinResizeSpanForWidget(AppWidgetProviderInfo info, int[] spanXY) {
+ int[] getMinSpanForWidget(AppWidgetProviderInfo info, int[] spanXY) {
return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight, spanXY);
}
@@ -947,18 +967,27 @@ public final class Launcher extends Activity
return getSpanForWidget(info.componentName, info.minWidth, info.minHeight, spanXY);
}
+ int[] getMinSpanForWidget(PendingAddWidgetInfo info, int[] spanXY) {
+ return getSpanForWidget(info.componentName, info.minResizeWidth,
+ info.minResizeHeight, spanXY);
+ }
+
/**
* Add a widget to the workspace.
*
* @param appWidgetId The app widget id
* @param cellInfo The position on screen where to create the widget.
*/
- private void completeAddAppWidget(final int appWidgetId, long container, int screen) {
- AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+ private void completeAddAppWidget(final int appWidgetId, long container, int screen,
+ AppWidgetHostView hostView, AppWidgetProviderInfo appWidgetInfo) {
+ if (appWidgetInfo == null) {
+ appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+ }
// Calculate the grid spans needed to fit this widget
CellLayout layout = getCellLayout(container, screen);
+ int[] minSpanXY = getMinSpanForWidget(appWidgetInfo, null);
int[] spanXY = getSpanForWidget(appWidgetInfo, null);
// Try finding open space on Launcher screen
@@ -966,18 +995,24 @@ public final class Launcher extends Activity
// if we are placing widgets on a "spring-loaded" screen
int[] cellXY = mTmpAddItemCellCoordinates;
int[] touchXY = mPendingAddInfo.dropPos;
+ int[] finalSpan = new int[2];
boolean foundCellSpan = false;
if (mPendingAddInfo.cellX >= 0 && mPendingAddInfo.cellY >= 0) {
cellXY[0] = mPendingAddInfo.cellX;
cellXY[1] = mPendingAddInfo.cellY;
+ spanXY[0] = mPendingAddInfo.spanX;
+ spanXY[1] = mPendingAddInfo.spanY;
foundCellSpan = true;
} else if (touchXY != null) {
// when dragging and dropping, just find the closest free spot
int[] result = layout.findNearestVacantArea(
- touchXY[0], touchXY[1], spanXY[0], spanXY[1], cellXY);
+ touchXY[0], touchXY[1], minSpanXY[0], minSpanXY[1], spanXY[0],
+ spanXY[1], cellXY, finalSpan);
+ spanXY[0] = finalSpan[0];
+ spanXY[1] = finalSpan[1];
foundCellSpan = (result != null);
} else {
- foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]);
+ foundCellSpan = layout.findCellForSpan(cellXY, minSpanXY[0], minSpanXY[1]);
}
if (!foundCellSpan) {
@@ -998,22 +1033,30 @@ public final class Launcher extends Activity
LauncherAppWidgetInfo launcherInfo = new LauncherAppWidgetInfo(appWidgetId);
launcherInfo.spanX = spanXY[0];
launcherInfo.spanY = spanXY[1];
+ launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
+ launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
LauncherModel.addItemToDatabase(this, launcherInfo,
container, screen, cellXY[0], cellXY[1], false);
if (!mRestoring) {
- // Perform actual inflation because we're live
- launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ if (hostView == null) {
+ // Perform actual inflation because we're live
+ launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
+ } else {
+ // The AppWidgetHostView has already been inflated and instantiated
+ launcherInfo.hostView = hostView;
+ }
- launcherInfo.hostView.setAppWidget(appWidgetId, appWidgetInfo);
launcherInfo.hostView.setTag(launcherInfo);
-
+ launcherInfo.hostView.setVisibility(View.VISIBLE);
mWorkspace.addInScreen(launcherInfo.hostView, container, screen, cellXY[0], cellXY[1],
launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
}
+ resetAddInfo();
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -1068,6 +1111,35 @@ public final class Launcher extends Activity
public void onWindowVisibilityChanged(int visibility) {
mVisible = visibility == View.VISIBLE;
updateRunning();
+ // The following code used to be in onResume, but it turns out onResume is called when
+ // you're in All Apps and click home to go to the workspace. onWindowVisibilityChanged
+ // is a more appropriate event to handle
+ if (mVisible) {
+ mAppsCustomizeTabHost.onWindowVisible();
+ if (!mWorkspaceLoading) {
+ final ViewTreeObserver observer = mWorkspace.getViewTreeObserver();
+ // We want to let Launcher draw itself at least once before we force it to build
+ // layers on all the workspace pages, so that transitioning to Launcher from other
+ // apps is nice and speedy. Usually the first call to preDraw doesn't correspond to
+ // a true draw so we wait until the second preDraw call to be safe
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ // We delay the layer building a bit in order to give
+ // other message processing a time to run. In particular
+ // this avoids a delay in hiding the IME if it was
+ // currently shown, because doing that may involve
+ // some communication back with the app.
+ mWorkspace.postDelayed(mBuildLayersRunnable, 500);
+ observer.removeOnPreDrawListener(this);
+ return true;
+ }
+ });
+ }
+ // When Launcher comes back to foreground, a different Activity might be responsible for
+ // the app market intent, so refresh the icon
+ updateAppMarketIcon();
+ clearTypedText();
+ }
}
private void sendAdvanceMessage(long delay) {
@@ -1408,6 +1480,7 @@ public final class Launcher extends Activity
mPendingAddInfo.screen = -1;
mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
+ mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
mPendingAddInfo.dropPos = null;
}
@@ -1419,9 +1492,9 @@ public final class Launcher extends Activity
addAppWidgetImpl(appWidgetId, null);
}
- void addAppWidgetImpl(int appWidgetId, PendingAddWidgetInfo info) {
- AppWidgetProviderInfo appWidget = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
-
+ void addAppWidgetImpl(final int appWidgetId, final PendingAddWidgetInfo info) {
+ final AppWidgetProviderInfo appWidget = info.info;
+ Runnable configurationActivity = null;
if (appWidget.configure != null) {
// Launch over to configure widget, if needed
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
@@ -1429,9 +1502,8 @@ public final class Launcher extends Activity
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
if (info != null) {
if (info.mimeType != null && !info.mimeType.isEmpty()) {
- intent.putExtra(
- InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA_MIME_TYPE,
- info.mimeType);
+ intent.putExtra(InstallWidgetReceiver.
+ EXTRA_APPWIDGET_CONFIGURATION_DATA_MIME_TYPE, info.mimeType);
final String mimeType = info.mimeType;
final ClipData clipData = (ClipData) info.configurationData;
@@ -1442,8 +1514,8 @@ public final class Launcher extends Activity
final CharSequence stringData = item.getText();
final Uri uriData = item.getUri();
final Intent intentData = item.getIntent();
- final String key =
- InstallWidgetReceiver.EXTRA_APPWIDGET_CONFIGURATION_DATA;
+ final String key = InstallWidgetReceiver.
+ EXTRA_APPWIDGET_CONFIGURATION_DATA;
if (uriData != null) {
intent.putExtra(key, uriData);
} else if (intentData != null) {
@@ -1456,14 +1528,13 @@ public final class Launcher extends Activity
}
}
}
-
startActivityForResultSafely(intent, REQUEST_CREATE_APPWIDGET);
+ mWidgetBeingConfigured = info;
} else {
// Otherwise just add it
- completeAddAppWidget(appWidgetId, info.container, info.screen);
-
+ completeAddAppWidget(appWidgetId, info.container, info.screen, info.boundWidget, appWidget);
// Exit spring loaded mode if necessary after adding the widget
- exitSpringLoadedDragModeDelayed(true, false);
+ exitSpringLoadedDragModeDelayed(true, false, null);
}
}
@@ -1501,18 +1572,31 @@ public final class Launcher extends Activity
* @param position The location on the screen where it was dropped, optional
*/
void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, int screen,
- int[] cell, int[] loc) {
+ int[] cell, int[] span, int[] loc) {
resetAddInfo();
mPendingAddInfo.container = info.container = container;
mPendingAddInfo.screen = info.screen = screen;
mPendingAddInfo.dropPos = loc;
+ mPendingAddInfo.minSpanX = info.minSpanX;
+ mPendingAddInfo.minSpanY = info.minSpanY;
+
if (cell != null) {
mPendingAddInfo.cellX = cell[0];
mPendingAddInfo.cellY = cell[1];
}
+ if (span != null) {
+ mPendingAddInfo.spanX = span[0];
+ mPendingAddInfo.spanY = span[1];
+ }
- int appWidgetId = getAppWidgetHost().allocateAppWidgetId();
- AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName);
+ AppWidgetHostView hostView = info.boundWidget;
+ int appWidgetId;
+ if (hostView != null) {
+ appWidgetId = hostView.getAppWidgetId();
+ } else {
+ appWidgetId = getAppWidgetHost().allocateAppWidgetId();
+ AppWidgetManager.getInstance(this).bindAppWidgetId(appWidgetId, info.componentName);
+ }
addAppWidgetImpl(appWidgetId, info);
}
@@ -1662,7 +1746,7 @@ public final class Launcher extends Activity
return;
}
- if (mWorkspace.isSwitchingState()) {
+ if (!mWorkspace.isFinishedSwitchingState()) {
return;
}
@@ -2160,6 +2244,30 @@ public final class Launcher extends Activity
}
}
+ private void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
+ if (v instanceof LauncherTransitionable) {
+ ((LauncherTransitionable) v).onLauncherTransitionStart(this, animated, toWorkspace);
+ }
+
+ // Update the workspace transition step as well
+ dispatchOnLauncherTransitionStep(v, 0f);
+ }
+
+ private void dispatchOnLauncherTransitionStep(View v, float t) {
+ if (v instanceof LauncherTransitionable) {
+ ((LauncherTransitionable) v).onLauncherTransitionStep(this, t);
+ }
+ }
+
+ private void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
+ if (v instanceof LauncherTransitionable) {
+ ((LauncherTransitionable) v).onLauncherTransitionEnd(this, animated, toWorkspace);
+ }
+
+ // Update the workspace transition step as well
+ dispatchOnLauncherTransitionStep(v, 1f);
+ }
+
/**
* Things to test when changing the following seven functions.
* - Home from workspace
@@ -2205,7 +2313,7 @@ public final class Launcher extends Activity
* Assumes that the view to show is anchored at either the very top or very bottom
* of the screen.
*/
- private void showAppsCustomizeHelper(boolean animated, final boolean springLoaded) {
+ private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded) {
if (mStateAnimation != null) {
mStateAnimation.cancel();
mStateAnimation = null;
@@ -2216,6 +2324,7 @@ public final class Launcher extends Activity
final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
+ final View fromView = mWorkspace;
final View toView = mAppsCustomizeTabHost;
final int startDelay =
res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
@@ -2223,32 +2332,40 @@ public final class Launcher extends Activity
setPivotsForZoom(toView, scale);
// Shrink workspaces away if going to AppsCustomize from workspace
- mWorkspace.changeState(Workspace.State.SMALL, animated);
+ Animator workspaceAnim =
+ mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
if (animated) {
- final ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
- scaleAnim.setInterpolator(new Workspace.ZoomOutInterpolator());
- scaleAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
- public void onAnimationUpdate(float a, float b) {
- toView.setScaleX(a * scale + b * 1f);
- toView.setScaleY(a * scale + b * 1f);
- }
- });
+ toView.setScaleX(scale);
+ toView.setScaleY(scale);
+ final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
+ scaleAnim.
+ scaleX(1f).scaleY(1f).
+ setDuration(duration).
+ setInterpolator(new Workspace.ZoomOutInterpolator());
toView.setVisibility(View.VISIBLE);
toView.setAlpha(0f);
- ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(fadeDuration);
+ final ObjectAnimator alphaAnim = ObjectAnimator
+ .ofFloat(toView, "alpha", 0f, 1f)
+ .setDuration(fadeDuration);
alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
- alphaAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
- public void onAnimationUpdate(float a, float b) {
- // don't need to invalidate because we do so above
- toView.setAlpha(a * 0f + b * 1f);
+ alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = (Float) animation.getAnimatedValue();
+ dispatchOnLauncherTransitionStep(fromView, t);
+ dispatchOnLauncherTransitionStep(toView, t);
}
});
- alphaAnim.setStartDelay(startDelay);
- alphaAnim.start();
- scaleAnim.addListener(new AnimatorListenerAdapter() {
+ // toView should appear right at the end of the workspace shrink
+ // animation
+ mStateAnimation = new AnimatorSet();
+ mStateAnimation.play(scaleAnim).after(startDelay);
+ mStateAnimation.play(alphaAnim).after(startDelay);
+
+ mStateAnimation.addListener(new AnimatorListenerAdapter() {
boolean animationCancelled = false;
@Override
@@ -2262,15 +2379,8 @@ public final class Launcher extends Activity
}
@Override
public void onAnimationEnd(Animator animation) {
- // If we don't set the final scale values here, if this animation is cancelled
- // it will have the wrong scale value and subsequent cameraPan animations will
- // not fix that
- toView.setScaleX(1.0f);
- toView.setScaleY(1.0f);
- if (toView instanceof LauncherTransitionable) {
- ((LauncherTransitionable) toView).onLauncherTransitionEnd(instance,
- scaleAnim, false);
- }
+ dispatchOnLauncherTransitionEnd(fromView, animated, false);
+ dispatchOnLauncherTransitionEnd(toView, animated, false);
if (!springLoaded && !LauncherApplication.isScreenLarge()) {
// Hide the workspace scrollbar
@@ -2288,19 +2398,48 @@ public final class Launcher extends Activity
}
});
- // toView should appear right at the end of the workspace shrink animation
- mStateAnimation = new AnimatorSet();
- mStateAnimation.play(scaleAnim).after(startDelay);
+ if (workspaceAnim != null) {
+ mStateAnimation.play(workspaceAnim);
+ }
boolean delayAnim = false;
- if (toView instanceof LauncherTransitionable) {
- LauncherTransitionable lt = (LauncherTransitionable) toView;
- delayAnim = lt.onLauncherTransitionStart(instance, mStateAnimation, false);
+ final ViewTreeObserver observer;
+
+ dispatchOnLauncherTransitionStart(fromView, animated, false);
+ dispatchOnLauncherTransitionStart(toView, animated, false);
+
+ // If any of the objects being animated haven't been measured/laid out
+ // yet, delay the animation until we get a layout pass
+ if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
+ (mWorkspace.getMeasuredWidth() == 0) ||
+ (toView.getMeasuredWidth() == 0)) {
+ observer = mWorkspace.getViewTreeObserver();
+ delayAnim = true;
+ } else {
+ observer = null;
}
- // if the anim is delayed, the LauncherTransitionable is responsible for starting it
- if (!delayAnim) {
- // TODO: q-- what if this anim is cancelled before being started? or started after
- // being cancelled?
+
+ if (delayAnim) {
+ final AnimatorSet stateAnimation = mStateAnimation;
+ final OnGlobalLayoutListener delayedStart = new OnGlobalLayoutListener() {
+ public void onGlobalLayout() {
+ mWorkspace.post(new Runnable() {
+ public void run() {
+ // Check that mStateAnimation hasn't changed while
+ // we waited for a layout pass
+ if (mStateAnimation == stateAnimation) {
+ // Need to update pivots for zoom if layout changed
+ setPivotsForZoom(toView, scale);
+ mStateAnimation.start();
+ }
+ }
+ });
+ observer.removeGlobalOnLayoutListener(this);
+ }
+ };
+ observer.addOnGlobalLayoutListener(delayedStart);
+ } else {
+ setPivotsForZoom(toView, scale);
mStateAnimation.start();
}
} else {
@@ -2310,16 +2449,16 @@ public final class Launcher extends Activity
toView.setScaleY(1.0f);
toView.setVisibility(View.VISIBLE);
toView.bringToFront();
- if (toView instanceof LauncherTransitionable) {
- ((LauncherTransitionable) toView).onLauncherTransitionStart(instance, null, false);
- ((LauncherTransitionable) toView).onLauncherTransitionEnd(instance, null, false);
-
- if (!springLoaded && !LauncherApplication.isScreenLarge()) {
- // Hide the workspace scrollbar
- mWorkspace.hideScrollingIndicator(true);
- hideDockDivider();
- }
+
+ if (!springLoaded && !LauncherApplication.isScreenLarge()) {
+ // Hide the workspace scrollbar
+ mWorkspace.hideScrollingIndicator(true);
+ hideDockDivider();
}
+ dispatchOnLauncherTransitionStart(fromView, animated, false);
+ dispatchOnLauncherTransitionEnd(fromView, animated, false);
+ dispatchOnLauncherTransitionStart(toView, animated, false);
+ dispatchOnLauncherTransitionEnd(toView, animated, false);
updateWallpaperVisibility(false);
}
}
@@ -2329,18 +2468,32 @@ public final class Launcher extends Activity
* This is the opposite of showAppsCustomizeHelper.
* @param animated If true, the transition will be animated.
*/
- private void hideAppsCustomizeHelper(boolean animated, final boolean springLoaded) {
+ private void hideAppsCustomizeHelper(State toState, final boolean animated,
+ final boolean springLoaded, final Runnable onCompleteRunnable) {
+
if (mStateAnimation != null) {
mStateAnimation.cancel();
mStateAnimation = null;
}
Resources res = getResources();
- final Launcher instance = this;
final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
+ final int fadeOutDuration =
+ res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
final float scaleFactor = (float)
res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
final View fromView = mAppsCustomizeTabHost;
+ final View toView = mWorkspace;
+ Animator workspaceAnim = null;
+
+ if (toState == State.WORKSPACE) {
+ int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
+ workspaceAnim = mWorkspace.getChangeStateAnimation(
+ Workspace.State.NORMAL, animated, stagger);
+ } else if (toState == State.APPS_CUSTOMIZE_SPRING_LOADED) {
+ workspaceAnim = mWorkspace.getChangeStateAnimation(
+ Workspace.State.SPRING_LOADED, animated);
+ }
setPivotsForZoom(fromView, scaleFactor);
updateWallpaperVisibility(true);
@@ -2349,48 +2502,56 @@ public final class Launcher extends Activity
final float oldScaleX = fromView.getScaleX();
final float oldScaleY = fromView.getScaleY();
- ValueAnimator scaleAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(duration);
- scaleAnim.setInterpolator(new Workspace.ZoomInInterpolator());
- scaleAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
- public void onAnimationUpdate(float a, float b) {
- fromView.setScaleX(a * oldScaleX + b * scaleFactor);
- fromView.setScaleY(a * oldScaleY + b * scaleFactor);
- }
- });
- final ValueAnimator alphaAnim = ValueAnimator.ofFloat(0f, 1f);
- alphaAnim.setDuration(res.getInteger(R.integer.config_appsCustomizeFadeOutTime));
+ final LauncherViewPropertyAnimator scaleAnim =
+ new LauncherViewPropertyAnimator(fromView);
+ scaleAnim.
+ scaleX(scaleFactor).scaleY(scaleFactor).
+ setDuration(duration).
+ setInterpolator(new Workspace.ZoomInInterpolator());
+
+ final ObjectAnimator alphaAnim = ObjectAnimator
+ .ofFloat(fromView, "alpha", 1f, 0f)
+ .setDuration(fadeOutDuration);
alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
- alphaAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
- public void onAnimationUpdate(float a, float b) {
- fromView.setAlpha(a * 1f + b * 0f);
+ alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float t = 1f - (Float) animation.getAnimatedValue();
+ dispatchOnLauncherTransitionStep(fromView, t);
+ dispatchOnLauncherTransitionStep(toView, t);
}
});
- if (fromView instanceof LauncherTransitionable) {
- ((LauncherTransitionable) fromView).onLauncherTransitionStart(instance, alphaAnim,
- true);
- }
- alphaAnim.addListener(new AnimatorListenerAdapter() {
+
+ mStateAnimation = new AnimatorSet();
+
+ dispatchOnLauncherTransitionStart(fromView, animated, true);
+ dispatchOnLauncherTransitionStart(toView, animated, true);
+
+ mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
updateWallpaperVisibility(true);
fromView.setVisibility(View.GONE);
- if (fromView instanceof LauncherTransitionable) {
- ((LauncherTransitionable) fromView).onLauncherTransitionEnd(instance,
- alphaAnim, true);
- }
+ dispatchOnLauncherTransitionEnd(fromView, animated, true);
+ dispatchOnLauncherTransitionEnd(toView, animated, true);
mWorkspace.hideScrollingIndicator(false);
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
}
});
- mStateAnimation = new AnimatorSet();
mStateAnimation.playTogether(scaleAnim, alphaAnim);
+ if (workspaceAnim != null) {
+ mStateAnimation.play(workspaceAnim);
+ }
mStateAnimation.start();
} else {
fromView.setVisibility(View.GONE);
- if (fromView instanceof LauncherTransitionable) {
- ((LauncherTransitionable) fromView).onLauncherTransitionStart(instance, null, true);
- ((LauncherTransitionable) fromView).onLauncherTransitionEnd(instance, null, true);
- }
+ dispatchOnLauncherTransitionStart(fromView, animated, true);
+ dispatchOnLauncherTransitionEnd(fromView, animated, true);
+ dispatchOnLauncherTransitionStart(toView, animated, true);
+ dispatchOnLauncherTransitionEnd(toView, animated, true);
mWorkspace.hideScrollingIndicator(false);
}
}
@@ -2404,13 +2565,13 @@ public final class Launcher extends Activity
}
void showWorkspace(boolean animated) {
- Resources res = getResources();
- int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
+ showWorkspace(animated, null);
+ }
- mWorkspace.changeState(Workspace.State.NORMAL, animated, stagger);
+ void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
if (mState != State.WORKSPACE) {
mWorkspace.setVisibility(View.VISIBLE);
- hideAppsCustomizeHelper(animated, false);
+ hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);
// Show the search bar and hotseat
mSearchDropTargetBar.showSearchBar(animated);
@@ -2459,14 +2620,14 @@ public final class Launcher extends Activity
void enterSpringLoadedDragMode() {
if (mState == State.APPS_CUSTOMIZE) {
- mWorkspace.changeState(Workspace.State.SPRING_LOADED);
- hideAppsCustomizeHelper(true, true);
+ hideAppsCustomizeHelper(State.APPS_CUSTOMIZE_SPRING_LOADED, true, true, null);
hideDockDivider();
mState = State.APPS_CUSTOMIZE_SPRING_LOADED;
}
}
- void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay) {
+ void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay,
+ final Runnable onCompleteRunnable) {
if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
mHandler.postDelayed(new Runnable() {
@@ -2478,7 +2639,7 @@ public final class Launcher extends Activity
// clean up our state transition functions
mAppsCustomizeTabHost.setVisibility(View.GONE);
mSearchDropTargetBar.showSearchBar(true);
- showWorkspace(true);
+ showWorkspace(true, onCompleteRunnable);
} else {
exitSpringLoadedDragMode();
}
@@ -2543,8 +2704,10 @@ public final class Launcher extends Activity
void showHotseat(boolean animated) {
if (!LauncherApplication.isScreenLarge()) {
if (animated) {
- int duration = mSearchDropTargetBar.getTransitionInDuration();
- mHotseat.animate().alpha(1f).setDuration(duration);
+ if (mHotseat.getAlpha() != 1f) {
+ int duration = mSearchDropTargetBar.getTransitionInDuration();
+ mHotseat.animate().alpha(1f).setDuration(duration);
+ }
} else {
mHotseat.setAlpha(1f);
}
@@ -2557,8 +2720,10 @@ public final class Launcher extends Activity
void hideHotseat(boolean animated) {
if (!LauncherApplication.isScreenLarge()) {
if (animated) {
- int duration = mSearchDropTargetBar.getTransitionOutDuration();
- mHotseat.animate().alpha(0f).setDuration(duration);
+ if (mHotseat.getAlpha() != 0f) {
+ int duration = mSearchDropTargetBar.getTransitionOutDuration();
+ mHotseat.animate().alpha(0f).setDuration(duration);
+ }
} else {
mHotseat.setAlpha(0f);
}
@@ -3002,6 +3167,11 @@ public final class Launcher extends Activity
item.hostView.setAppWidget(appWidgetId, appWidgetInfo);
item.hostView.setTag(item);
+ // We need to load the minimum span and embed it into the item info
+ int[] minSpan = getMinSpanForWidget(appWidgetInfo, null);
+ item.minSpanX = minSpan[0];
+ item.minSpanY = minSpan[1];
+
workspace.addInScreen(item.hostView, item.container, item.screen, item.cellX,
item.cellY, item.spanX, item.spanY, false);
@@ -3048,7 +3218,7 @@ public final class Launcher extends Activity
// package changes in bindSearchablesChanged()
updateAppMarketIcon();
- mWorkspace.post(mBuildLayersRunnable);
+ mWorkspace.postDelayed(mBuildLayersRunnable, 500);
}
@Override
@@ -3227,11 +3397,14 @@ public final class Launcher extends Activity
public void onAnimationEnd(Animator animation) {
cling.setVisibility(View.GONE);
cling.cleanup();
- SharedPreferences prefs =
- getSharedPreferences("com.android.launcher2.prefs", Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(flag, true);
- editor.commit();
+ // We should update the shared preferences on a background thread
+ new Thread("dismissClingThread") {
+ public void run() {
+ SharedPreferences.Editor editor = mSharedPrefs.edit();
+ editor.putBoolean(flag, true);
+ editor.commit();
+ }
+ }.start();
};
});
anim.start();
@@ -3251,9 +3424,8 @@ public final class Launcher extends Activity
}
public void showFirstRunWorkspaceCling() {
// Enable the clings only if they have not been dismissed before
- SharedPreferences prefs =
- getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
- if (isClingsEnabled() && !prefs.getBoolean(Cling.WORKSPACE_CLING_DISMISSED_KEY, false)) {
+ if (isClingsEnabled() &&
+ !mSharedPrefs.getBoolean(Cling.WORKSPACE_CLING_DISMISSED_KEY, false)) {
initCling(R.id.workspace_cling, null, false, 0);
} else {
removeCling(R.id.workspace_cling);
@@ -3261,9 +3433,8 @@ public final class Launcher extends Activity
}
public void showFirstRunAllAppsCling(int[] position) {
// Enable the clings only if they have not been dismissed before
- SharedPreferences prefs =
- getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
- if (isClingsEnabled() && !prefs.getBoolean(Cling.ALLAPPS_CLING_DISMISSED_KEY, false)) {
+ if (isClingsEnabled() &&
+ !mSharedPrefs.getBoolean(Cling.ALLAPPS_CLING_DISMISSED_KEY, false)) {
initCling(R.id.all_apps_cling, position, true, 0);
} else {
removeCling(R.id.all_apps_cling);
@@ -3271,15 +3442,13 @@ public final class Launcher extends Activity
}
public Cling showFirstRunFoldersCling() {
// Enable the clings only if they have not been dismissed before
- SharedPreferences prefs =
- getSharedPreferences(PREFS_KEY, Context.MODE_PRIVATE);
- Cling cling = null;
- if (isClingsEnabled() && !prefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) {
- cling = initCling(R.id.folder_cling, null, true, 0);
+ if (isClingsEnabled() &&
+ !mSharedPrefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) {
+ return initCling(R.id.folder_cling, null, true, 0);
} else {
removeCling(R.id.folder_cling);
+ return null;
}
- return cling;
}
public boolean isFolderClingVisible() {
Cling cling = (Cling) findViewById(R.id.folder_cling);
@@ -3332,7 +3501,8 @@ public final class Launcher extends Activity
}
interface LauncherTransitionable {
- // return true if the callee will take care of start the animation by itself
- boolean onLauncherTransitionStart(Launcher l, Animator animation, boolean toWorkspace);
- void onLauncherTransitionEnd(Launcher l, Animator animation, boolean toWorkspace);
+ View getContent();
+ void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
+ void onLauncherTransitionStep(Launcher l, float t);
+ void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
}