diff options
-rw-r--r-- | res/values/strings.xml | 2 | ||||
-rw-r--r-- | src/com/android/launcher2/InstallShortcutReceiver.java | 76 | ||||
-rw-r--r-- | src/com/android/launcher2/Launcher.java | 128 | ||||
-rw-r--r-- | src/com/android/launcher2/LauncherApplication.java | 10 | ||||
-rw-r--r-- | src/com/android/launcher2/LauncherModel.java | 23 | ||||
-rw-r--r-- | src/com/android/launcher2/PagedView.java | 3 | ||||
-rw-r--r-- | src/com/android/launcher2/SmoothPagedView.java | 10 | ||||
-rw-r--r-- | src/com/android/launcher2/Workspace.java | 20 |
8 files changed, 215 insertions, 57 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml index a9b14d05a..509a67029 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -82,7 +82,7 @@ s --> <!-- Options in "Add to Home" dialog box; Title of the group containing the list of apps that can set the wallpaper--> <string name="group_wallpapers">Wallpapers</string> <!-- Error message when user has filled a home screen, possibly not used --> - <string name="out_of_space">No more room on this Home screen.</string> + <string name="out_of_space">No more room on your Home screens.</string> <!-- Error message when user tries to drop an invalid item on the hotseat --> <string name="invalid_hotseat_item">This widget is too large for the hotseat.</string> <!-- Message displayed when a shortcut is created by an external application --> diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java index e04ce6419..4c0974fd3 100644 --- a/src/com/android/launcher2/InstallShortcutReceiver.java +++ b/src/com/android/launcher2/InstallShortcutReceiver.java @@ -17,9 +17,9 @@ package com.android.launcher2; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.widget.Toast; @@ -27,10 +27,21 @@ import android.widget.Toast; import com.android.launcher.R; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; public class InstallShortcutReceiver extends BroadcastReceiver { public static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; + public static final String NEW_APPS_PAGE_KEY = "apps.new.page"; + public static final String NEW_APPS_LIST_KEY = "apps.new.list"; + + public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; + public static final int NEW_SHORTCUT_STAGGER_DELAY = 75; + + private static final int INSTALL_SHORTCUT_SUCCESSFUL = 0; + private static final int INSTALL_SHORTCUT_IS_DUPLICATE = -1; + private static final int INSTALL_SHORTCUT_NO_SPACE = -2; // A mime-type representing shortcut data public static final String SHORTCUT_MIMETYPE = @@ -42,6 +53,8 @@ public class InstallShortcutReceiver extends BroadcastReceiver { if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) { return; } + String spKey = LauncherApplication.getSharedPreferencesKey(); + SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE); final int screen = Launcher.getScreen(); final Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); @@ -62,26 +75,35 @@ public class InstallShortcutReceiver extends BroadcastReceiver { } final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context); - final boolean shortcutExists = LauncherModel.shortcutExists(context, name, intent); - final String[] errorMsgs = {""}; - - if (!installShortcut(context, data, items, name, intent, screen, shortcutExists, - errorMsgs)) { - // The target screen is full, let's try the other screens - for (int i = 0; i < Launcher.SCREEN_COUNT; i++) { - if (i != screen && installShortcut(context, data, items, name, intent, i, - shortcutExists, errorMsgs)) break; + final boolean exists = LauncherModel.shortcutExists(context, name, intent); + final int[] result = {INSTALL_SHORTCUT_SUCCESSFUL}; + + // Try adding the target to the workspace screens incrementally, starting at the current + // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1)) + boolean found = false; + for (int i = 0; i < (2 * Launcher.SCREEN_COUNT) + 1 && !found; ++i) { + int si = screen + (int) ((i / 2f) + 0.5f) * ((i % 2 == 1) ? 1 : -1); + if (0 <= si && si < Launcher.SCREEN_COUNT) { + found = installShortcut(context, data, items, name, intent, si, exists, sp, result); } } - if (!errorMsgs[0].isEmpty()) { - Toast.makeText(context, errorMsgs[0], - Toast.LENGTH_SHORT).show(); + // We only report error messages (duplicate shortcut or out of space) as the add-animation + // will provide feedback otherwise + if (!found) { + if (result[0] == INSTALL_SHORTCUT_NO_SPACE) { + Toast.makeText(context, context.getString(R.string.out_of_space), + Toast.LENGTH_SHORT).show(); + } else if (result[0] == INSTALL_SHORTCUT_IS_DUPLICATE) { + Toast.makeText(context, context.getString(R.string.shortcut_duplicate, name), + Toast.LENGTH_SHORT).show(); + } } } private boolean installShortcut(Context context, Intent data, ArrayList<ItemInfo> items, - String name, Intent intent, int screen, boolean shortcutExists, String[] errorMsgs) { + String name, Intent intent, int screen, boolean shortcutExists, + SharedPreferences sharedPrefs, int[] result) { if (findEmptyCell(context, items, mCoordinates, screen)) { if (intent != null) { if (intent.getAction() == null) { @@ -92,23 +114,35 @@ public class InstallShortcutReceiver extends BroadcastReceiver { // different places) boolean duplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true); if (duplicate || !shortcutExists) { + // If the new app is going to fall into the same page as before, then just + // continue adding to the current page + int newAppsScreen = sharedPrefs.getInt(NEW_APPS_PAGE_KEY, screen); + Set<String> newApps = new HashSet<String>(); + if (newAppsScreen == screen) { + newApps = sharedPrefs.getStringSet(NEW_APPS_LIST_KEY, newApps); + } + newApps.add(intent.toUri(0).toString()); + sharedPrefs.edit() + .putInt(NEW_APPS_PAGE_KEY, screen) + .putStringSet(NEW_APPS_LIST_KEY, newApps) + .commit(); + + // Update the Launcher db LauncherApplication app = (LauncherApplication) context.getApplicationContext(); ShortcutInfo info = app.getModel().addShortcut(context, data, - LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, mCoordinates[0], - mCoordinates[1], true); - if (info != null) { - errorMsgs[0] = context.getString(R.string.shortcut_installed, name); - } else { + LauncherSettings.Favorites.CONTAINER_DESKTOP, screen, + mCoordinates[0], mCoordinates[1], true); + if (info == null) { return false; } } else { - errorMsgs[0] = context.getString(R.string.shortcut_duplicate, name); + result[0] = INSTALL_SHORTCUT_IS_DUPLICATE; } return true; } } else { - errorMsgs[0] = context.getString(R.string.out_of_space); + result[0] = INSTALL_SHORTCUT_NO_SPACE; } return false; diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java index 0c1b76f8e..9494d2792 100644 --- a/src/com/android/launcher2/Launcher.java +++ b/src/com/android/launcher2/Launcher.java @@ -55,6 +55,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Debug; import android.os.Environment; import android.os.Handler; import android.os.Message; @@ -84,6 +85,7 @@ import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.AccelerateInterpolator; +import android.view.animation.BounceInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.inputmethod.InputMethodManager; import android.widget.Advanceable; @@ -103,7 +105,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; /** * Default launcher application. @@ -253,6 +260,10 @@ public final class Launcher extends Activity // it from the context. private SharedPreferences mSharedPrefs; + // Holds the page that we need to animate to, and the icon views that we need to animate up + // when we scroll to that page on resume. + private int mNewShortcutAnimatePage = -1; + private ArrayList<View> mNewShortcutAnimateViews = new ArrayList<View>(); private BubbleTextView mWaitingForResume; @@ -280,7 +291,8 @@ 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); + mSharedPrefs = getSharedPreferences(LauncherApplication.getSharedPreferencesKey(), + Context.MODE_PRIVATE); mModel = app.setLauncher(this); mIconCache = app.getIconCache(); mDragController = new DragController(this); @@ -317,7 +329,7 @@ public final class Launcher extends Activity } if (!mRestoring) { - mModel.startLoader(this, true); + mModel.startLoader(true); } if (!mModel.isAllAppsLoaded()) { @@ -593,10 +605,11 @@ public final class Launcher extends Activity @Override protected void onResume() { super.onResume(); + mPaused = false; if (mRestoring || mOnResumeNeedsLoad) { mWorkspaceLoading = true; - mModel.startLoader(this, true); + mModel.startLoader(true); mRestoring = false; mOnResumeNeedsLoad = false; } @@ -725,7 +738,7 @@ public final class Launcher extends Activity showAllApps(false); } - final int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1); + int currentScreen = savedState.getInt(RUNTIME_STATE_CURRENT_SCREEN, -1); if (currentScreen > -1) { mWorkspace.setCurrentPage(currentScreen); } @@ -2184,7 +2197,7 @@ public final class Launcher extends Activity if (mWorkspaceLoading) { lockAllApps(); - mModel.startLoader(Launcher.this, false); + mModel.startLoader(false); } else { final FolderIcon folderIcon = (FolderIcon) mWorkspace.getViewForTag(mFolderInfo); @@ -2196,7 +2209,7 @@ public final class Launcher extends Activity } else { lockAllApps(); mWorkspaceLoading = true; - mModel.startLoader(Launcher.this, false); + mModel.startLoader(false); } } } @@ -3076,7 +3089,6 @@ public final class Launcher extends Activity } } - /** * Refreshes the shortcuts shown on the workspace. * @@ -3085,6 +3097,8 @@ public final class Launcher extends Activity public void startBinding() { final Workspace workspace = mWorkspace; + mNewShortcutAnimatePage = -1; + mNewShortcutAnimateViews.clear(); mWorkspace.clearDropTargets(); int count = workspace.getChildCount(); for (int i = 0; i < count; i++) { @@ -3106,8 +3120,12 @@ public final class Launcher extends Activity public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end) { setLoadOnResume(); - final Workspace workspace = mWorkspace; - for (int i=start; i<end; i++) { + // Get the list of added shortcuts and intersect them with the set of shortcuts here + Set<String> newApps = new HashSet<String>(); + newApps = mSharedPrefs.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, newApps); + + Workspace workspace = mWorkspace; + for (int i = start; i < end; i++) { final ItemInfo item = shortcuts.get(i); // Short circuit if we are loading dock items for a configuration which has no dock @@ -3119,9 +3137,23 @@ public final class Launcher extends Activity switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - View shortcut = createShortcut((ShortcutInfo)item); + ShortcutInfo info = (ShortcutInfo) item; + String uri = info.intent.toUri(0).toString(); + View shortcut = createShortcut(info); workspace.addInScreen(shortcut, item.container, item.screen, item.cellX, item.cellY, 1, 1, false); + if (newApps.contains(uri)) { + newApps.remove(uri); + + // Prepare the view to be animated up + shortcut.setAlpha(0f); + shortcut.setScaleX(0f); + shortcut.setScaleY(0f); + mNewShortcutAnimatePage = item.screen; + if (!mNewShortcutAnimateViews.contains(shortcut)) { + mNewShortcutAnimateViews.add(shortcut); + } + } break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, @@ -3132,6 +3164,7 @@ public final class Launcher extends Activity break; } } + workspace.requestLayout(); } @@ -3169,11 +3202,6 @@ 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); @@ -3207,8 +3235,6 @@ public final class Launcher extends Activity mSavedInstanceState = null; } - mWorkspaceLoading = false; - // If we received the result of any pending adds while the loader was running (e.g. the // widget configuration forced an orientation change), process them now. for (int i = 0; i < sPendingAddList.size(); i++) { @@ -3220,7 +3246,72 @@ public final class Launcher extends Activity // package changes in bindSearchablesChanged() updateAppMarketIcon(); - mWorkspace.postDelayed(mBuildLayersRunnable, 500); + // Animate up any icons as necessary + if (mVisible || mWorkspaceLoading) { + Runnable newAppsRunnable = new Runnable() { + @Override + public void run() { + runNewAppsAnimation(); + } + }; + if (mNewShortcutAnimatePage > -1 && + mNewShortcutAnimatePage != mWorkspace.getCurrentPage()) { + mWorkspace.snapToPage(mNewShortcutAnimatePage, newAppsRunnable); + } else { + newAppsRunnable.run(); + } + } + + mWorkspaceLoading = false; + } + + /** + * Runs a new animation that scales up icons that were added while Launcher was in the + * background. + */ + private void runNewAppsAnimation() { + AnimatorSet anim = new AnimatorSet(); + Collection<Animator> bounceAnims = new ArrayList<Animator>(); + Collections.sort(mNewShortcutAnimateViews, new Comparator<View>() { + @Override + public int compare(View a, View b) { + CellLayout.LayoutParams alp = (CellLayout.LayoutParams) a.getLayoutParams(); + CellLayout.LayoutParams blp = (CellLayout.LayoutParams) b.getLayoutParams(); + int cellCountX = LauncherModel.getCellCountX(); + return (alp.cellY * cellCountX + alp.cellX) - (blp.cellY * cellCountX + blp.cellX); + } + }); + for (int i = 0; i < mNewShortcutAnimateViews.size(); ++i) { + View v = mNewShortcutAnimateViews.get(i); + ValueAnimator bounceAnim = ObjectAnimator.ofPropertyValuesHolder(v, + PropertyValuesHolder.ofFloat("alpha", 1f), + PropertyValuesHolder.ofFloat("scaleX", 1f), + PropertyValuesHolder.ofFloat("scaleY", 1f)); + bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION); + bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY); + bounceAnim.setInterpolator(new SmoothPagedView.OvershootInterpolator()); + bounceAnims.add(bounceAnim); + } + anim.playTogether(bounceAnims); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mWorkspace.postDelayed(mBuildLayersRunnable, 500); + } + }); + anim.start(); + + // Clean up + mNewShortcutAnimatePage = -1; + mNewShortcutAnimateViews.clear(); + new Thread("clearNewAppsThread") { + public void run() { + mSharedPrefs.edit() + .putInt(InstallShortcutReceiver.NEW_APPS_PAGE_KEY, -1) + .putStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY, null) + .commit(); + } + }.start(); } @Override @@ -3363,7 +3454,6 @@ public final class Launcher extends Activity } /* Cling related */ - private static final String PREFS_KEY = "com.android.launcher2.prefs"; private boolean isClingsEnabled() { // disable clings when running in a test harness if(ActivityManager.isRunningInTestHarness()) return false; diff --git a/src/com/android/launcher2/LauncherApplication.java b/src/com/android/launcher2/LauncherApplication.java index 47ce0b722..ef1eb5fcd 100644 --- a/src/com/android/launcher2/LauncherApplication.java +++ b/src/com/android/launcher2/LauncherApplication.java @@ -37,6 +37,7 @@ public class LauncherApplication extends Application { private static boolean sIsScreenLarge; private static float sScreenDensity; private static int sLongPressTimeout = 300; + private static final String sSharedPreferencesKey = "com.android.launcher2.prefs"; WeakReference<LauncherProvider> mLauncherProvider; @Override @@ -94,7 +95,10 @@ public class LauncherApplication extends Application { private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { - mModel.startLoader(LauncherApplication.this, false); + // If the database has ever changed, then we really need to force a reload of the + // workspace on the next load + mModel.resetLoadedState(false, true); + mModel.startLoaderFromBackground(); } }; @@ -119,6 +123,10 @@ public class LauncherApplication extends Application { return mLauncherProvider.get(); } + public static String getSharedPreferencesKey() { + return sSharedPreferencesKey; + } + public static boolean isScreenLarge() { return sIsScreenLarge; } diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java index 159ddb016..30eb86c5a 100644 --- a/src/com/android/launcher2/LauncherModel.java +++ b/src/com/android/launcher2/LauncherModel.java @@ -649,17 +649,22 @@ public class LauncherModel extends BroadcastReceiver { } private void forceReload() { + resetLoadedState(true, true); + + // Do this here because if the launcher activity is running it will be restarted. + // If it's not running startLoaderFromBackground will merely tell it that it needs + // to reload. + startLoaderFromBackground(); + } + + public void resetLoadedState(boolean resetAllAppsLoaded, boolean resetWorkspaceLoaded) { synchronized (mLock) { // Stop any existing loaders first, so they don't set mAllAppsLoaded or // mWorkspaceLoaded to true later stopLoaderLocked(); - mAllAppsLoaded = false; - mWorkspaceLoaded = false; + if (resetAllAppsLoaded) mAllAppsLoaded = false; + if (resetWorkspaceLoaded) mWorkspaceLoaded = false; } - // Do this here because if the launcher activity is running it will be restarted. - // If it's not running startLoaderFromBackground will merely tell it that it needs - // to reload. - startLoaderFromBackground(); } /** @@ -680,7 +685,7 @@ public class LauncherModel extends BroadcastReceiver { } } if (runLoader) { - startLoader(mApp, false); + startLoader(false); } } @@ -698,7 +703,7 @@ public class LauncherModel extends BroadcastReceiver { return isLaunching; } - public void startLoader(Context context, boolean isLaunching) { + public void startLoader(boolean isLaunching) { synchronized (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); @@ -709,7 +714,7 @@ public class LauncherModel extends BroadcastReceiver { // If there is already one running, tell it to stop. // also, don't downgrade isLaunching if we're already running isLaunching = isLaunching || stopLoaderLocked(); - mLoaderTask = new LoaderTask(context, isLaunching); + mLoaderTask = new LoaderTask(mApp, isLaunching); sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); } diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java index 0854508e4..5434704ea 100644 --- a/src/com/android/launcher2/PagedView.java +++ b/src/com/android/launcher2/PagedView.java @@ -62,7 +62,8 @@ public abstract class PagedView extends ViewGroup { // the min drag distance for a fling to register, to prevent random page shifts private static final int MIN_LENGTH_FOR_FLING = 25; - private static final int PAGE_SNAP_ANIMATION_DURATION = 550; + protected static final int PAGE_SNAP_ANIMATION_DURATION = 550; + protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950; protected static final float NANOTIME_DIV = 1000000000.0f; private static final float OVERSCROLL_ACCELERATE_FACTOR = 2; diff --git a/src/com/android/launcher2/SmoothPagedView.java b/src/com/android/launcher2/SmoothPagedView.java index fe763f508..e6414d960 100644 --- a/src/com/android/launcher2/SmoothPagedView.java +++ b/src/com/android/launcher2/SmoothPagedView.java @@ -35,11 +35,11 @@ public abstract class SmoothPagedView extends PagedView { private Interpolator mScrollInterpolator; - private static class WorkspaceOvershootInterpolator implements Interpolator { + public static class OvershootInterpolator implements Interpolator { private static final float DEFAULT_TENSION = 1.3f; private float mTension; - public WorkspaceOvershootInterpolator() { + public OvershootInterpolator() { mTension = DEFAULT_TENSION; } @@ -101,7 +101,7 @@ public abstract class SmoothPagedView extends PagedView { if (mScrollMode == DEFAULT_MODE) { mBaseLineFlingVelocity = 2500.0f; mFlingVelocityInfluence = 0.4f; - mScrollInterpolator = new WorkspaceOvershootInterpolator(); + mScrollInterpolator = new OvershootInterpolator(); mScroller = new Scroller(getContext(), mScrollInterpolator); } } @@ -139,9 +139,9 @@ public abstract class SmoothPagedView extends PagedView { } if (settle) { - ((WorkspaceOvershootInterpolator) mScrollInterpolator).setDistance(screenDelta); + ((OvershootInterpolator) mScrollInterpolator).setDistance(screenDelta); } else { - ((WorkspaceOvershootInterpolator) mScrollInterpolator).disableSettle(); + ((OvershootInterpolator) mScrollInterpolator).disableSettle(); } velocity = Math.abs(velocity); diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 8f11612ef..cec47c71e 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -183,6 +183,7 @@ public class Workspace extends SmoothPagedView WallpaperOffsetInterpolator mWallpaperOffset; boolean mUpdateWallpaperOffsetImmediately = false; private Runnable mDelayedResizeRunnable; + private Runnable mDelayedSnapToPageRunnable; private int mDisplayWidth; private int mDisplayHeight; private boolean mIsStaticWallpaper; @@ -765,6 +766,11 @@ public class Workspace extends SmoothPagedView mDelayedResizeRunnable.run(); mDelayedResizeRunnable = null; } + + if (mDelayedSnapToPageRunnable != null) { + mDelayedSnapToPageRunnable.run(); + mDelayedSnapToPageRunnable = null; + } } @Override @@ -906,6 +912,20 @@ public class Workspace extends SmoothPagedView computeWallpaperScrollRatio(whichPage); } + @Override + protected void snapToPage(int whichPage, int duration) { + super.snapToPage(whichPage, duration); + computeWallpaperScrollRatio(whichPage); + } + + protected void snapToPage(int whichPage, Runnable r) { + if (mDelayedSnapToPageRunnable != null) { + mDelayedSnapToPageRunnable.run(); + } + mDelayedSnapToPageRunnable = r; + snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION); + } + private void computeWallpaperScrollRatio(int page) { // Here, we determine what the desired scroll would be with and without a layout scale, // and compute a ratio between the two. This allows us to adjust the wallpaper offset |