Merge "New launcher2 icon migration algorithm." into jb-ub-now-kermit
diff --git a/res/values/config.xml b/res/values/config.xml
index 2a08216..b512ffe 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -94,4 +94,7 @@
          filter the activities shown in the launcher. Can be empty. -->
     <string name="app_filter_class" translatable="false"></string>
 
+    <!-- Name of a subclass of com.android.launcher3.BuildInfo used to
+         get build information. Can be empty. -->
+    <string name="build_info_class" translatable="false"></string>
 </resources>
diff --git a/src/com/android/launcher3/Alarm.java b/src/com/android/launcher3/Alarm.java
index 91f9bd0..e9f1fd9 100644
--- a/src/com/android/launcher3/Alarm.java
+++ b/src/com/android/launcher3/Alarm.java
@@ -78,7 +78,3 @@
         return mAlarmPending;
     }
 }
-
-interface OnAlarmListener {
-    public void onAlarm(Alarm alarm);
-}
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 2865bc5..37cdb9e 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -199,8 +199,6 @@
     private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
     private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
 
-    public static boolean DISABLE_ALL_APPS = false;
-
     // Previews & outlines
     ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
     private static final int sPageSleepDelay = 200;
@@ -427,7 +425,7 @@
         int width = MeasureSpec.getSize(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
         if (!isDataReady()) {
-            if ((DISABLE_ALL_APPS || !mApps.isEmpty()) && !mWidgets.isEmpty()) {
+            if ((LauncherAppState.isDisableAllApps() || !mApps.isEmpty()) && !mWidgets.isEmpty()) {
                 setDataIsReady();
                 setMeasuredDimension(width, height);
                 onDataReady(width, height);
@@ -1558,7 +1556,7 @@
     }
 
     public void setApps(ArrayList<AppInfo> list) {
-        if (!DISABLE_ALL_APPS) {
+        if (!LauncherAppState.isDisableAllApps()) {
             mApps = list;
             Collections.sort(mApps, LauncherModel.getAppNameComparator());
             updatePageCountsAndInvalidateData();
@@ -1576,7 +1574,7 @@
         }
     }
     public void addApps(ArrayList<AppInfo> list) {
-        if (!DISABLE_ALL_APPS) {
+        if (!LauncherAppState.isDisableAllApps()) {
             addAppsWithoutInvalidate(list);
             updatePageCountsAndInvalidateData();
         }
@@ -1604,7 +1602,7 @@
         }
     }
     public void removeApps(ArrayList<AppInfo> appInfos) {
-        if (!DISABLE_ALL_APPS) {
+        if (!LauncherAppState.isDisableAllApps()) {
             removeAppsWithoutInvalidate(appInfos);
             updatePageCountsAndInvalidateData();
         }
@@ -1613,7 +1611,7 @@
         // We remove and re-add the updated applications list because it's properties may have
         // changed (ie. the title), and this will ensure that the items will be in their proper
         // place in the list.
-        if (!DISABLE_ALL_APPS) {
+        if (!LauncherAppState.isDisableAllApps()) {
             removeAppsWithoutInvalidate(list);
             addAppsWithoutInvalidate(list);
             updatePageCountsAndInvalidateData();
@@ -1727,4 +1725,4 @@
 
         return String.format(getContext().getString(stringId), page + 1, count);
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BuildInfo.java b/src/com/android/launcher3/BuildInfo.java
new file mode 100644
index 0000000..b49ee0d
--- /dev/null
+++ b/src/com/android/launcher3/BuildInfo.java
@@ -0,0 +1,32 @@
+package com.android.launcher3;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+public class BuildInfo {
+    private static final boolean DBG = false;
+    private static final String TAG = "BuildInfo";
+
+    public boolean isDogfoodBuild() {
+        return false;
+    }
+
+    public static BuildInfo loadByName(String className) {
+        if (TextUtils.isEmpty(className)) return new BuildInfo();
+
+        if (DBG) Log.d(TAG, "Loading BuildInfo: " + className);
+        try {
+            Class<?> cls = Class.forName(className);
+            return (BuildInfo) cls.newInstance();
+        } catch (ClassNotFoundException e) {
+            Log.e(TAG, "Bad BuildInfo class", e);
+        } catch (InstantiationException e) {
+            Log.e(TAG, "Bad BuildInfo class", e);
+        } catch (IllegalAccessException e) {
+            Log.e(TAG, "Bad BuildInfo class", e);
+        } catch (ClassCastException e) {
+            Log.e(TAG, "Bad BuildInfo class", e);
+        }
+        return new BuildInfo();
+    }
+}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index c76425a..75d906b 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -146,12 +146,12 @@
                 return true;
             }
 
-            if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
+            if (!LauncherAppState.isDisableAllApps() &&
                     item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
                 return true;
             }
 
-            if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
+            if (!LauncherAppState.isDisableAllApps() &&
                     item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
                     item instanceof AppInfo) {
                 AppInfo appInfo = (AppInfo) info;
@@ -160,7 +160,7 @@
 
             if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
                 item instanceof ShortcutInfo) {
-                if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+                if (LauncherAppState.isDisableAllApps()) {
                     ShortcutInfo shortcutInfo = (ShortcutInfo) info;
                     return (shortcutInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0;
                 } else {
@@ -174,7 +174,7 @@
     @Override
     public void onDragStart(DragSource source, Object info, int dragAction) {
         boolean isVisible = true;
-        boolean useUninstallLabel = !AppsCustomizePagedView.DISABLE_ALL_APPS &&
+        boolean useUninstallLabel = !LauncherAppState.isDisableAllApps() &&
                 isAllAppsApplication(source, info);
         boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget();
 
@@ -264,7 +264,7 @@
     }
 
     private boolean isUninstallFromWorkspace(DragObject d) {
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS && isWorkspaceOrFolderApplication(d)) {
+        if (LauncherAppState.isDisableAllApps() && isWorkspaceOrFolderApplication(d)) {
             ShortcutInfo shortcut = (ShortcutInfo) d.dragInfo;
             // Only allow manifest shortcuts to initiate an un-install.
             return !InstallShortcutReceiver.isValidShortcutLaunchIntent(shortcut.intent);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 67b0933..a236b84 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -126,7 +126,7 @@
     DeviceProfile(String n, float w, float h, float r, float c,
                   float is, float its, float hs, float his) {
         // Ensure that we have an odd number of hotseat items (since we need to place all apps)
-        if (!AppsCustomizePagedView.DISABLE_ALL_APPS && hs % 2 == 0) {
+        if (!LauncherAppState.isDisableAllApps() && hs % 2 == 0) {
             throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
         }
 
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 22928cc..3aced1f 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -56,7 +56,7 @@
         DisplayMetrics dm = resources.getDisplayMetrics();
         ArrayList<DeviceProfile> deviceProfiles =
                 new ArrayList<DeviceProfile>();
-        boolean hasAA = !AppsCustomizePagedView.DISABLE_ALL_APPS;
+        boolean hasAA = !LauncherAppState.isDisableAllApps();
         DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm);
         // Our phone profiles include the bar sizes in each orientation
         deviceProfiles.add(new DeviceProfile("Super Short Stubby",
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index ca82bd1..a98d121 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -146,7 +146,7 @@
         Resources res = getResources();
         mMaxCountX = (int) grid.numColumns;
         // Allow scrolling folders when DISABLE_ALL_APPS is true.
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (LauncherAppState.isDisableAllApps()) {
             mMaxCountY = mMaxNumItems = Integer.MAX_VALUE;
         } else {
             mMaxCountY = (int) grid.numRows;
@@ -773,7 +773,11 @@
                 }
                 completeDragExit();
             }
-        } else {
+        }
+
+        // This is kind of hacky, but in general, dropping on the workspace handles removing
+        // the extra screen, but dropping elsewhere (back to self, or onto delete) doesn't.
+        if (target != mLauncher.getWorkspace()) {
             mLauncher.getWorkspace().removeExtraEmptyScreen(true, null);
         }
 
@@ -1014,7 +1018,7 @@
         int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(getContentAreaHeight(),
                 MeasureSpec.EXACTLY);
 
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (LauncherAppState.isDisableAllApps()) {
             // Don't cap the height of the content to allow scrolling.
             mContent.setFixedSize(getContentAreaWidth(), mContent.getDesiredHeight());
         } else {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 094e188..59d60e3 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -95,7 +95,7 @@
         return hasVerticalHotseat() ? (mContent.getCountY() - (rank + 1)) : 0;
     }
     public boolean isAllAppsButtonRank(int rank) {
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (LauncherAppState.isDisableAllApps()) {
             return false;
         } else {
             return rank == mAllAppsButtonRank;
@@ -142,7 +142,7 @@
     void resetLayout() {
         mContent.removeAllViewsInLayout();
 
-        if (!AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (!LauncherAppState.isDisableAllApps()) {
             // Add the Apps button
             Context context = getContext();
 
@@ -189,7 +189,7 @@
     void addAllAppsFolder(IconCache iconCache,
             ArrayList<AppInfo> allApps, ArrayList<ComponentName> onWorkspace,
             Launcher launcher, Workspace workspace) {
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (LauncherAppState.isDisableAllApps()) {
             FolderInfo fi = new FolderInfo();
 
             fi.cellX = getCellXFromOrder(mAllAppsButtonRank);
@@ -219,7 +219,7 @@
     }
 
     void addAppsToAllAppsFolder(ArrayList<AppInfo> apps) {
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (LauncherAppState.isDisableAllApps()) {
             View v = mContent.getChildAt(getCellXFromOrder(mAllAppsButtonRank), getCellYFromOrder(mAllAppsButtonRank));
             FolderIcon fi = null;
 
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 1ff9472..7ab4e04 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -272,7 +272,7 @@
                 final Intent intent = pendingInfo.launchIntent;
                 final String name = pendingInfo.name;
 
-                if (AppsCustomizePagedView.DISABLE_ALL_APPS && !isValidShortcutLaunchIntent(intent)) {
+                if (LauncherAppState.isDisableAllApps() && !isValidShortcutLaunchIntent(intent)) {
                     if (DBG) Log.d(TAG, "Ignoring shortcut with launchIntent:" + intent);
                     continue;
                 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ceb31b0..db36030 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -157,6 +157,7 @@
     // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
     static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate";
     static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
+    static final String DISABLE_ALL_APPS_PROPERTY = "launcher_noallapps";
 
     // The Intent extra that defines whether to ignore the launch animation
     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
@@ -189,6 +190,9 @@
     // Type: int[]
     private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
 
+
+    static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
+
     private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
     private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME =
             "com.android.launcher.toolbar_search_icon";
@@ -367,7 +371,7 @@
 
     private Stats mStats;
 
-    private static boolean isPropertyEnabled(String propertyName) {
+    static boolean isPropertyEnabled(String propertyName) {
         return Log.isLoggable(propertyName, Log.VERBOSE);
     }
 
@@ -478,6 +482,7 @@
         // On large interfaces, we want the screen to auto-rotate based on the current orientation
         unlockScreenOrientation(true);
 
+        showFirstRunActivity();
         showFirstRunCling();
     }
 
@@ -500,6 +505,21 @@
     }
 
     /**
+     * To be overridden by subclasses to indicate that there is an activity to launch
+     * before showing the standard launcher experience.
+     */
+    protected boolean hasFirstRunActivity() {
+        return false;
+    }
+
+    /**
+     * To be overridden by subclasses to launch any first run activity
+     */
+    protected Intent getFirstRunActivity() {
+        return null;
+    }
+
+    /**
      * Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
      * ensure the custom content page is added or removed if necessary.
      */
@@ -2921,7 +2941,7 @@
         // Shrink workspaces away if going to AppsCustomize from workspace
         Animator workspaceAnim =
                 mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
-        if (!AppsCustomizePagedView.DISABLE_ALL_APPS
+        if (!LauncherAppState.isDisableAllApps()
                 || contentType == AppsCustomizePagedView.ContentType.Widgets) {
             // Set the content type for the all apps/widgets space
             mAppsCustomizeTabHost.setContentTypeImmediate(contentType);
@@ -3792,7 +3812,7 @@
         // Remove the extra empty screen
         mWorkspace.removeExtraEmptyScreen(false, null);
 
-        if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
+        if (!LauncherAppState.isDisableAllApps() &&
                 addedApps != null && mAppsCustomizeContent != null) {
             mAppsCustomizeContent.addApps(addedApps);
         }
@@ -4059,7 +4079,7 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindAllApplications(final ArrayList<AppInfo> apps) {
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (LauncherAppState.isDisableAllApps()) {
             if (mIntentsOnWorkspaceFromUpgradePath != null) {
                 if (LauncherModel.UPGRADE_USE_MORE_APPS_FOLDER) {
                     getHotseat().addAllAppsFolder(mIconCache, apps,
@@ -4099,7 +4119,7 @@
             mWorkspace.updateShortcuts(apps);
         }
 
-        if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
+        if (!LauncherAppState.isDisableAllApps() &&
                 mAppsCustomizeContent != null) {
             mAppsCustomizeContent.updateApps(apps);
         }
@@ -4136,7 +4156,7 @@
         mDragController.onAppsRemoved(packageNames, appInfos);
 
         // Update AllApps
-        if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
+        if (!LauncherAppState.isDisableAllApps() &&
                 mAppsCustomizeContent != null) {
             mAppsCustomizeContent.removeApps(appInfos);
         }
@@ -4221,6 +4241,10 @@
         }
     }
 
+    private boolean shouldRunFirstRunActivity() {
+        return !ActivityManager.isRunningInTestHarness();
+    }
+
     /* Cling related */
     private boolean isClingsEnabled() {
         if (DISABLE_CLINGS) {
@@ -4289,15 +4313,9 @@
             final Runnable cleanUpClingCb = new Runnable() {
                 public void run() {
                     cling.cleanup();
-                    // We should update the shared preferences on a background thread
-                    new AsyncTask<Void, Void, Void>() {
-                        public Void doInBackground(Void ... args) {
-                            SharedPreferences.Editor editor = mSharedPrefs.edit();
-                            editor.putBoolean(flag, true);
-                            editor.commit();
-                            return null;
-                        }
-                    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+                    SharedPreferences.Editor editor = mSharedPrefs.edit();
+                    editor.putBoolean(flag, true);
+                    editor.apply();
                     if (postAnimationCb != null) {
                         postAnimationCb.run();
                     }
@@ -4394,10 +4412,29 @@
         }
     }
 
+    public void showFirstRunActivity() {
+        if (shouldRunFirstRunActivity() && hasFirstRunActivity()
+                && !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false)) {
+            Intent firstRunIntent = getFirstRunActivity();
+            if (firstRunIntent != null) {
+                startActivity(firstRunIntent);
+                markFirstRunActivityShown();
+            }
+        }
+    }
+
+    private void markFirstRunActivityShown() {
+        SharedPreferences.Editor editor = mSharedPrefs.edit();
+        editor.putBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, true);
+        editor.apply();
+    }
+
     public void showFirstRunCling() {
         if (isClingsEnabled() &&
                 !mSharedPrefs.getBoolean(Cling.FIRST_RUN_CLING_DISMISSED_KEY, false) &&
                 !skipCustomClingIfNoAccounts() ) {
+
+
             // If we're not using the default workspace layout, replace workspace cling
             // with a custom workspace cling (usually specified in an overlay)
             // For now, only do this on tablets
@@ -4686,4 +4723,4 @@
 interface DebugIntents {
     static final String DELETE_DATABASE = "com.android.launcher3.action.DELETE_DATABASE";
     static final String MIGRATE_DATABASE = "com.android.launcher3.action.MIGRATE_DATABASE";
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 84a1d04..5e41fca 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -30,9 +30,10 @@
     private static final String TAG = "LauncherAppState";
     private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
 
+    private final AppFilter mAppFilter;
+    private final BuildInfo mBuildInfo;
     private LauncherModel mModel;
     private IconCache mIconCache;
-    private AppFilter mAppFilter;
     private WidgetPreviewLoader.CacheDb mWidgetPreviewCacheDb;
     private boolean mIsScreenLarge;
     private float mScreenDensity;
@@ -87,6 +88,7 @@
         mIconCache = new IconCache(sContext);
 
         mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
+        mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
         mModel = new LauncherModel(this, mIconCache, mAppFilter);
 
         // Register intent receivers
@@ -230,4 +232,10 @@
     public void onAvailableSizeChanged(DeviceProfile grid) {
         Utilities.setIconSize(grid.iconSizePx);
     }
+
+    public static boolean isDisableAllApps() {
+        // Returns false on non-dogfood builds.
+        return getInstance().mBuildInfo.isDogfoodBuild() &&
+                Launcher.isPropertyEnabled(Launcher.DISABLE_ALL_APPS_PROPERTY);
+    }
 }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 2a5ed69..a081c21 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -297,7 +297,7 @@
                 Key key = getKey(Key.FAVORITE, id);
                 keys.add(key);
                 currentIds.add(keyToBackupKey(key));
-                if (updateTime > in.t) {
+                if (updateTime >= in.t) {
                     byte[] blob = packFavorite(cursor);
                     writeRowToBackup(key, blob, out, data);
                 }
@@ -365,7 +365,7 @@
                 Key key = getKey(Key.SCREEN, id);
                 keys.add(key);
                 currentIds.add(keyToBackupKey(key));
-                if (updateTime > in.t) {
+                if (updateTime >= in.t) {
                     byte[] blob = packScreen(cursor);
                     writeRowToBackup(key, blob, out, data);
                 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 460ab5d..2559d3d 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1476,7 +1476,7 @@
                 sBgDbIconCache.clear();
             }
 
-            if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+            if (LauncherAppState.isDisableAllApps()) {
                 // Ensure that all the applications that are in the system are
                 // represented on the home screen.
                 if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
@@ -2586,7 +2586,7 @@
             if (added != null) {
                 // Ensure that we add all the workspace applications to the db
                 Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
-                if (!AppsCustomizePagedView.DISABLE_ALL_APPS) {
+                if (!LauncherAppState.isDisableAllApps()) {
                     addAndBindAddedApps(context, new ArrayList<ItemInfo>(), cb, added);
                 } else {
                     final ArrayList<ItemInfo> addedInfos = new ArrayList<ItemInfo>(added);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 3cacd6c..7adbade 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -298,7 +298,7 @@
     }
 
     private static int getDefaultWorkspaceResourceId() {
-        if (AppsCustomizePagedView.DISABLE_ALL_APPS) {
+        if (LauncherAppState.isDisableAllApps()) {
             return R.xml.default_workspace_no_all_apps;
         } else {
             return R.xml.default_workspace;
diff --git a/src/com/android/launcher3/OnAlarmListener.java b/src/com/android/launcher3/OnAlarmListener.java
new file mode 100644
index 0000000..b5ef83e
--- /dev/null
+++ b/src/com/android/launcher3/OnAlarmListener.java
@@ -0,0 +1,5 @@
+package com.android.launcher3;
+
+public interface OnAlarmListener {
+    public void onAlarm(Alarm alarm);
+}
diff --git a/util/com/android/launcher3/DecoderRing.java b/util/com/android/launcher3/DecoderRing.java
index 7098e18..86431d9 100644
--- a/util/com/android/launcher3/DecoderRing.java
+++ b/util/com/android/launcher3/DecoderRing.java
@@ -86,6 +86,7 @@
         boolean fromLogs = false;
         int skip = 0;
         List<File> files = new LinkedList<File>();
+        boolean verbose = false;
 
         for (int i = 0; i < args.length; i++) {
             if ("-k".equals(args[i])) {
@@ -108,6 +109,8 @@
                 }
             } else if ("-x".equals(args[i])) {
                 extractImages = true;
+            } else if ("-v".equals(args[i])) {
+                verbose = true;
             } else if ("-L".equals(args[i])) {
                 fromLogs = true;
             } else if (args[i] != null && !args[i].startsWith("-")) {
@@ -123,7 +126,7 @@
             usage(args);
         }
 
-        if (files.size() > 1) {
+        if (files.size() > 1 && defaultType != null) {
             System.err.println("Explicit type ignored for multiple files.");
             defaultType = null;
         }
@@ -136,8 +139,13 @@
             Class type = null;
             if (defaultType == null) {
                 Key key = decodeKey(source.getName().getBytes(), fromLogs);
-                type = TYPES[key.type];
-                System.err.println("This is a " + type.getSimpleName() + " backup");
+                if (key != null) {
+                    type = TYPES[key.type];
+                    if (verbose) {
+                        System.err.println(source.getName() + " is a " + type.getSimpleName());
+                        System.out.println(key.toString());
+                    }
+                }
             } else {
                 type = defaultType;
             }
@@ -178,12 +186,14 @@
             byte[] payload = byteStream.toByteArray();
             if (type == Key.class) {
                 proto = decodeKey(payload, fromLogs);
-            } else {
+            } else if (type != null) {
                 proto = decodeBackupData(payload, type, fromLogs);
             }
 
             // Generic string output
-            System.out.println(proto.toString());
+            if (proto != null) {
+                System.out.println(proto.toString());
+            }
 
             if (extractImages) {
                 String prefix = "stdin";
@@ -262,7 +272,7 @@
                 byte[] rawKey = DatatypeConverter.parseBase64Binary(encodedKey);
                 if (rawKey[0] != 'L' || rawKey[1] != ':') {
                     System.err.println(encodedKey + " is not a launcher backup key.");
-                    System.exit(1);
+                    return null;
                 }
                 encodedKey = new String(rawKey, 2, rawKey.length - 2);
             }
@@ -270,16 +280,16 @@
             key = Key.parseFrom(keyProtoData);
         } catch (InvalidProtocolBufferNanoException protoException) {
             System.err.println("failed to extract key from filename: " + protoException);
-            System.exit(1);
+            return null;
         } catch (IllegalArgumentException base64Exception) {
             System.err.println("failed to extract key from filename: " + base64Exception);
-            System.exit(1);
+            return null;
         }
 
         // keys are self-checked
         if (key.checksum != checkKey(key)) {
             System.err.println("key ckecksum failed");
-            System.exit(1);
+            return null;
         }
         return key;
     }
@@ -323,6 +333,7 @@
         System.err.println("\t-w\tdecode a widget");
         System.err.println("\t-S b\tskip b bytes");
         System.err.println("\t-x\textract image data to files");
+        System.err.println("\t-v\tprint key type data, as well as payload");
         System.err.println("\t-l\texpect data from logcat, instead of the local transport");
         System.err.println("\tfilename\tread from filename, not stdin");
         System.exit(1);