diff options
Diffstat (limited to 'src/com/android/launcher2/Launcher.java')
-rw-r--r-- | src/com/android/launcher2/Launcher.java | 536 |
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); } |