Merge "Add special column for All Apps button in FocusLogic's sparse matrix." into ub-launcher3-burnaby-polish
diff --git a/WallpaperPicker/res/values-sw720dp-v19/styles.xml b/WallpaperPicker/res/values-sw720dp-v19/styles.xml
deleted file mode 100644
index d8dab22..0000000
--- a/WallpaperPicker/res/values-sw720dp-v19/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-    <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
-        <item name="android:windowActionModeOverlay">true</item>
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
-    </style>
-</resources>
diff --git a/WallpaperPicker/res/values-sw720dp/styles.xml b/WallpaperPicker/res/values-sw720dp/styles.xml
index 12f8884..0058f7e 100644
--- a/WallpaperPicker/res/values-sw720dp/styles.xml
+++ b/WallpaperPicker/res/values-sw720dp/styles.xml
@@ -18,7 +18,11 @@
 -->
 
 <resources>
-    <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
+    <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionModeOverlay">true</item>
     </style>
 </resources>
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
index 70220ed..de4b2f2 100644
--- a/WallpaperPicker/res/values-v21/styles.xml
+++ b/WallpaperPicker/res/values-v21/styles.xml
@@ -34,8 +34,8 @@
     </style>
 
     <style name="Theme" parent="@style/BaseWallpaperTheme">
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
+        <item name="android:statusBarColor">#00000000</item>
+        <item name="android:navigationBarColor">#00000000</item>
         <item name="android:colorControlActivated">@color/launcher_accent_color</item>
         <item name="android:colorAccent">@color/launcher_accent_color</item>
         <item name="android:colorPrimary">@color/launcher_accent_color</item>
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 2d496a5..6baac6a 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -159,6 +159,7 @@
         public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
         private State mState = State.NOT_LOADED;
 
+        /** Returns whether loading was successful. */
         public boolean loadInBackground(InBitmapProvider bitmapProvider) {
             ExifInterface ei = new ExifInterface();
             if (readExif(ei)) {
@@ -193,7 +194,7 @@
                         try {
                             mPreview = loadPreviewBitmap(opts);
                         } catch (IllegalArgumentException e) {
-                            Log.d(TAG, "Unable to reusage bitmap", e);
+                            Log.d(TAG, "Unable to reuse bitmap", e);
                             opts.inBitmap = null;
                             mPreview = null;
                         }
@@ -202,6 +203,10 @@
                 if (mPreview == null) {
                     mPreview = loadPreviewBitmap(opts);
                 }
+                if (mPreview == null) {
+                    mState = State.ERROR_LOADING;
+                    return false;
+                }
 
                 // Verify that the bitmap can be used on GL surface
                 try {
@@ -212,7 +217,7 @@
                     Log.d(TAG, "Image cannot be rendered on a GL surface", e);
                     mState = State.ERROR_LOADING;
                 }
-                return true;
+                return mState == State.LOADED;
             }
         }
 
@@ -310,7 +315,7 @@
                 Bitmap b = BitmapFactory.decodeStream(is, null, options);
                 Utils.closeSilently(is);
                 return b;
-            } catch (FileNotFoundException e) {
+            } catch (FileNotFoundException | OutOfMemoryError e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return null;
             }
@@ -412,7 +417,8 @@
                         "Failed to create preview of apropriate size! "
                         + " in: %dx%d, out: %dx%d",
                         mWidth, mHeight,
-                        preview.getWidth(), preview.getHeight()));
+                        preview == null ? -1 : preview.getWidth(),
+                        preview == null ? -1 : preview.getHeight()));
             }
         }
     }
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index c95d558..ede6c71 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -51,11 +51,6 @@
      */
     boolean usingLowResIcon;
 
-    /**
-     * The time at which the app was first installed.
-     */
-    long firstInstallTime;
-
     public ComponentName componentName;
 
     static final int DOWNLOADED_FLAG = 1;
@@ -84,7 +79,6 @@
         this.container = ItemInfo.NO_ID;
 
         flags = initFlags(info);
-        firstInstallTime = info.getFirstInstallTime();
         iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
         intent = makeLaunchIntent(context, info, user);
         this.user = user;
@@ -109,7 +103,6 @@
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
         flags = info.flags;
-        firstInstallTime = info.firstInstallTime;
         iconBitmap = info.iconBitmap;
     }
 
@@ -129,7 +122,6 @@
         Log.d(tag, label + " size=" + list.size());
         for (AppInfo info: list) {
             Log.d(tag, "   title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap 
-                    + " firstInstallTime=" + info.firstInstallTime
                     + " componentName=" + info.componentName.getPackageName());
         }
     }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 71a1df3..0a2a017 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -36,7 +36,6 @@
 import android.graphics.drawable.TransitionDrawable;
 import android.os.Build;
 import android.os.Parcelable;
-import android.os.PowerManager;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -899,9 +898,14 @@
 
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() -
-                (mCountX * mCellWidth);
-        int left = getPaddingLeft() + (int) Math.ceil(offset / 2f);
+        boolean isFullscreen = mShortcutsAndWidgets.getChildCount() > 0 &&
+                ((LayoutParams) mShortcutsAndWidgets.getChildAt(0).getLayoutParams()).isFullscreen;
+        int left = getPaddingLeft();
+        if (!isFullscreen) {
+            int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() -
+                    (mCountX * mCellWidth);
+            left += (int) Math.ceil(offset / 2f);
+        }
         int top = getPaddingTop();
 
         mTouchFeedbackView.layout(left, top,
@@ -2178,10 +2182,7 @@
             // Animations are disabled in power save mode, causing the repeated animation to jump
             // spastically between beginning and end states. Since this looks bad, we don't repeat
             // the animation in power save mode.
-            PowerManager powerManager = (PowerManager) getContext()
-                    .getSystemService(Context.POWER_SERVICE);
-            boolean powerSaverOn = Utilities.ATLEAST_LOLLIPOP && powerManager.isPowerSaveMode();
-            if (!powerSaverOn) {
+            if (!Utilities.isPowerSaverOn(getContext())) {
                 va.setRepeatMode(ValueAnimator.REVERSE);
                 va.setRepeatCount(ValueAnimator.INFINITE);
             }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c0ad516..ccbfba1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -245,7 +245,9 @@
         hotseatCellHeightPx = iconSizePx;
 
         // Folder
-        folderCellWidthPx = Math.min(cellWidthPx + 6 * edgeMarginPx,
+        int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
+        // Don't let the folder get too close to the edges of the screen.
+        folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding,
                 (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
         folderCellHeightPx = cellHeightPx + edgeMarginPx;
         folderBackgroundOffset = -edgeMarginPx;
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 3d12aa3..4709488 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -174,7 +174,7 @@
     }
 
     /**
-     * Handles key events in the workspace hot seat (bottom of the screen).
+     * Handles key events in the workspace hotseat (bottom of the screen).
      * <p>Currently we don't special case for the phone UI in different orientations, even though
      * the hotseat is on the side in landscape mode. This is to ensure that accessibility
      * consistency is maintained across rotations.
@@ -261,12 +261,38 @@
                 countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources()));
 
         View newIcon = null;
-        if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
-            parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
-            newIcon = parent.getChildAt(0);
-            // TODO(hyunyoungs): handle cases where the child is not an icon but
-            // a folder or a widget.
-            workspace.snapToPage(pageIndex + 1);
+        switch (newIconIndex) {
+            case FocusLogic.NEXT_PAGE_FIRST_ITEM:
+                parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
+                newIcon = parent.getChildAt(0);
+                // TODO(hyunyoungs): handle cases where the child is not an icon but
+                // a folder or a widget.
+                workspace.snapToPage(pageIndex + 1);
+                break;
+            case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
+                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+                newIcon = parent.getChildAt(0);
+                // TODO(hyunyoungs): handle cases where the child is not an icon but
+                // a folder or a widget.
+                workspace.snapToPage(pageIndex - 1);
+                break;
+            case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
+                parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+                newIcon = parent.getChildAt(parent.getChildCount() - 1);
+                // TODO(hyunyoungs): handle cases where the child is not an icon but
+                // a folder or a widget.
+                workspace.snapToPage(pageIndex - 1);
+                break;
+            case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
+            case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
+                // Go to the previous page but keep the focus on the same hotseat icon.
+                workspace.snapToPage(pageIndex - 1);
+                break;
+            case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
+            case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
+                // Go to the next page but keep the focus on the same hotseat icon.
+                workspace.snapToPage(pageIndex + 1);
+                break;
         }
         if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
             newIconIndex -= iconParent.getChildCount();
@@ -366,7 +392,7 @@
                 if (parent != null) {
                     iconLayout = (CellLayout) parent.getParent();
                     matrix = FocusLogic.createSparseMatrix(iconLayout,
-                        iconLayout.getCountX(), row);
+                            iconLayout.getCountX(), row);
                     newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
                             matrix, FocusLogic.PIVOT, newPageIndex, pageCount,
                             Utilities.isRtl(v.getResources()));
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 9377bad..da895c6 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -318,9 +318,10 @@
             sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                     String.format(getContext().getString(R.string.folder_renamed), newTitle));
         }
-        // In order to clear the focus from the text field, we set the focus on ourself. This
-        // ensures that every time the field is clicked, focus is gained, giving reliable behavior.
-        requestFocus();
+
+        // This ensures that focus is gained every time the field is clicked, which selects all
+        // the text and brings up the soft keyboard if necessary.
+        mFolderName.clearFocus();
 
         Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
         mIsEditingName = false;
@@ -451,6 +452,11 @@
             mContent.snapToPageImmediately(0);
         }
 
+        // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+        // leads to an consistent state if you drag out of the folder and drag back in without
+        // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
+        mDeleteFolderOnDropCompleted = false;
+
         Animator openFolderAnim = null;
         final Runnable onCompleteRunnable;
         if (!Utilities.ATLEAST_LOLLIPOP) {
@@ -640,7 +646,7 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 setLayerType(LAYER_TYPE_NONE, null);
-                close();
+                close(true);
             }
             @Override
             public void onAnimationStart(Animator animation) {
@@ -654,7 +660,7 @@
         oa.start();
     }
 
-    public void close() {
+    public void close(boolean wasAnimated) {
         // TODO: Clear all active animations.
         DragLayer parent = (DragLayer) getParent();
         if (parent != null) {
@@ -662,7 +668,9 @@
         }
         mDragController.removeDropTarget(this);
         clearFocus();
-        mFolderIcon.requestFocus();
+        if (wasAnimated) {
+            mFolderIcon.requestFocus();
+        }
 
         if (mRearrangeOnClose) {
             rearrangeChildren();
@@ -1144,10 +1152,10 @@
                         // addInScreenFromBind() to ensure that hotseat items are placed correctly.
                         mLauncher.getWorkspace().addInScreenFromBind(newIcon, mInfo.container,
                                 mInfo.screenId, mInfo.cellX, mInfo.cellY, mInfo.spanX, mInfo.spanY);
-                    }
 
-                    // Focus the newly created child
-                    newIcon.requestFocus();
+                        // Focus the newly created child
+                        newIcon.requestFocus();
+                    }
                 }
             }
         };
@@ -1164,15 +1172,37 @@
         return mDestroyed;
     }
 
-    // This method keeps track of the last item in the folder for the purposes
+    // This method keeps track of the first and last item in the folder for the purposes
     // of keyboard focus
     public void updateTextViewFocus() {
-        View lastChild = mContent.getLastItem();
-        if (lastChild != null) {
+        final View firstChild = mContent.getFirstItem();
+        final View lastChild = mContent.getLastItem();
+        if (firstChild != null && lastChild != null) {
             mFolderName.setNextFocusDownId(lastChild.getId());
             mFolderName.setNextFocusRightId(lastChild.getId());
             mFolderName.setNextFocusLeftId(lastChild.getId());
             mFolderName.setNextFocusUpId(lastChild.getId());
+            // Hitting TAB from the folder name wraps around to the first item on the current
+            // folder page, and hitting SHIFT+TAB from that item wraps back to the folder name.
+            mFolderName.setNextFocusForwardId(firstChild.getId());
+            // When clicking off the folder when editing the name, this Folder gains focus. When
+            // pressing an arrow key from that state, give the focus to the first item.
+            this.setNextFocusDownId(firstChild.getId());
+            this.setNextFocusRightId(firstChild.getId());
+            this.setNextFocusLeftId(firstChild.getId());
+            this.setNextFocusUpId(firstChild.getId());
+            // When pressing shift+tab in the above state, give the focus to the last item.
+            setOnKeyListener(new OnKeyListener() {
+                @Override
+                public boolean onKey(View v, int keyCode, KeyEvent event) {
+                    boolean isShiftPlusTab = keyCode == KeyEvent.KEYCODE_TAB &&
+                            event.hasModifiers(KeyEvent.META_SHIFT_ON);
+                    if (isShiftPlusTab && Folder.this.isFocused()) {
+                        return lastChild.requestFocus();
+                    }
+                    return false;
+                }
+            });
         }
     }
 
@@ -1293,7 +1323,11 @@
             rearrangeChildren();
         }
         if (getItemCount() <= 1) {
-            replaceFolderWithFinalItem();
+            if (mInfo.opened) {
+                mLauncher.closeFolder(this, true);
+            } else {
+                replaceFolderWithFinalItem();
+            }
         }
     }
 
@@ -1337,6 +1371,8 @@
     public void onFocusChange(View v, boolean hasFocus) {
         if (v == mFolderName && hasFocus) {
             startEditingFolderName();
+        } else if (v == mFolderName && !hasFocus) {
+            dismissEditingName();
         }
     }
 
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index cc9c573..d503d2c 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -402,16 +402,28 @@
         return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
     }
 
+    public View getFirstItem() {
+        if (getChildCount() < 1) {
+            return null;
+        }
+        ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+        if (mGridCountX > 0) {
+            return currContainer.getChildAt(0, 0);
+        } else {
+            return currContainer.getChildAt(0);
+        }
+    }
+
     public View getLastItem() {
         if (getChildCount() < 1) {
             return null;
         }
-        ShortcutAndWidgetContainer lastContainer = getCurrentCellLayout().getShortcutsAndWidgets();
-        int lastRank = lastContainer.getChildCount() - 1;
+        ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+        int lastRank = currContainer.getChildCount() - 1;
         if (mGridCountX > 0) {
-            return lastContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
+            return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
         } else {
-            return lastContainer.getChildAt(lastRank);
+            return currContainer.getChildAt(lastRank);
         }
     }
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 59ab839..a77332f 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -179,15 +179,7 @@
 
     private Bitmap makeDefaultIcon(UserHandleCompat user) {
         Drawable unbadged = getFullResDefaultActivityIcon();
-        Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
-        Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
-                Math.max(d.getIntrinsicHeight(), 1),
-                Bitmap.Config.ARGB_8888);
-        Canvas c = new Canvas(b);
-        d.setBounds(0, 0, b.getWidth(), b.getHeight());
-        d.draw(c);
-        c.setBitmap(null);
-        return b;
+        return Utilities.createBadgedIconBitmap(unbadged, user, mContext);
     }
 
     /**
@@ -380,7 +372,8 @@
         }
         if (entry == null) {
             entry = new CacheEntry();
-            entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext);
+            entry.icon = Utilities.createBadgedIconBitmap(
+                    app.getIcon(mIconDpi), app.getUser(), mContext);
         }
         entry.title = app.getLabel();
         entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
@@ -542,7 +535,8 @@
             // Check the DB first.
             if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
                 if (info != null) {
-                    entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext);
+                    entry.icon = Utilities.createBadgedIconBitmap(
+                            info.getIcon(mIconDpi), info.getUser(), mContext);
                 } else {
                     if (usePackageIcon) {
                         CacheEntry packageEntry = getEntryForPackageLocked(
@@ -623,9 +617,8 @@
                     if (appInfo == null) {
                         throw new NameNotFoundException("ApplicationInfo is null");
                     }
-                    Drawable drawable = mUserManager.getBadgedDrawableForUser(
-                            appInfo.loadIcon(mPackageManager), user);
-                    entry.icon = Utilities.createIconBitmap(drawable, mContext);
+                    entry.icon = Utilities.createBadgedIconBitmap(
+                            appInfo.loadIcon(mPackageManager), user, mContext);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
                     entry.isLowResIcon = false;
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 9a75193..571d99a 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -171,7 +171,6 @@
 
     private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
         // Queue the item up for adding if launcher has not loaded properly yet
-        LauncherAppState.setApplicationContext(context.getApplicationContext());
         LauncherAppState app = LauncherAppState.getInstance();
         boolean launcherNotLoaded = app.getModel().getCallback() == null;
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index e983eb1..0d183db 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.annotation.TargetApi;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Point;
 import android.util.DisplayMetrics;
@@ -84,9 +83,6 @@
     DeviceProfile landscapeProfile;
     DeviceProfile portraitProfile;
 
-    // On Marshmallow the status bar is no longer opaque, when drawn on the right.
-    public boolean isRightInsetOpaque;
-
     InvariantDeviceProfile() {
     }
 
@@ -170,9 +166,6 @@
                 largeSide, smallSide, true /* isLandscape */);
         portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
                 smallSide, largeSide, false /* isLandscape */);
-
-        isRightInsetOpaque = !Utilities.ATLEAST_MARSHMALLOW ||
-                context.getSystemService(ActivityManager.class).isLowRamDevice();
     }
 
     ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 9824e3e..a379acc 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,7 +85,6 @@
 import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.view.ViewTreeObserver;
-import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
@@ -418,7 +417,6 @@
 
         super.onCreate(savedInstanceState);
 
-        LauncherAppState.setApplicationContext(getApplicationContext());
         LauncherAppState app = LauncherAppState.getInstance();
 
         // Load configuration-specific DeviceProfile
@@ -1623,18 +1621,28 @@
                 // The AppWidgetHostView has already been inflated and instantiated
                 launcherInfo.hostView = hostView;
             }
-            launcherInfo.hostView.setTag(launcherInfo);
             launcherInfo.hostView.setVisibility(View.VISIBLE);
-            launcherInfo.notifyWidgetSizeChanged(this);
-
-            mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, info.cellX,
-                    info.cellY, launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
-
-            addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
+            addAppWidgetToWorkspace(launcherInfo, appWidgetInfo, isWorkspaceLocked());
         }
         resetAddInfo();
     }
 
+    private void addAppWidgetToWorkspace(LauncherAppWidgetInfo item,
+            LauncherAppWidgetProviderInfo appWidgetInfo, boolean insert) {
+        item.hostView.setTag(item);
+        item.onBindAppWidget(this);
+
+        item.hostView.setFocusable(true);
+        item.hostView.setOnFocusChangeListener(mFocusHandler);
+
+        mWorkspace.addInScreen(item.hostView, item.container, item.screenId,
+                item.cellX, item.cellY, item.spanX, item.spanY, insert);
+
+        if (!item.isCustomWidget()) {
+            addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
+        }
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -1684,31 +1692,10 @@
         }
         registerReceiver(mReceiver, filter);
         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
-        setupTransparentSystemBarsForLollipop();
         mAttached = true;
         mVisible = true;
     }
 
-    /**
-     * Sets up transparent navigation and status bars in Lollipop.
-     * This method is a no-op for other platform versions.
-     */
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    private void setupTransparentSystemBarsForLollipop() {
-        if (Utilities.ATLEAST_LOLLIPOP) {
-            Window window = getWindow();
-            window.getAttributes().systemUiVisibility |=
-                    (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
-                    | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
-            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
-            window.setStatusBarColor(Color.TRANSPARENT);
-            window.setNavigationBarColor(Color.TRANSPARENT);
-        }
-    }
-
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
@@ -3125,6 +3112,10 @@
         }
         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
         oa.start();
+        if (Utilities.isPowerSaverOn(this)) {
+            // Animations are disabled in battery saver mode, so just skip to the end state.
+            oa.end();
+        }
     }
 
     private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
@@ -3226,7 +3217,7 @@
         if (animate) {
             folder.animateClosed();
         } else {
-            folder.close();
+            folder.close(false);
         }
 
         // Notify the accessibility manager that this folder "window" has disappeared and no
@@ -4124,15 +4115,7 @@
             item.hostView.setOnClickListener(this);
         }
 
-        item.hostView.setTag(item);
-        item.onBindAppWidget(this);
-
-        workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
-                item.cellY, item.spanX, item.spanY, false);
-        if (!item.isCustomWidget()) {
-            addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
-        }
-
+        addAppWidgetToWorkspace(item, appWidgetInfo, false);
         workspace.requestLayout();
 
         if (DEBUG_WIDGETS) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index cf461a5..c49d43f 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -19,6 +19,8 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.graphics.Rect;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -28,6 +30,8 @@
 
 import com.android.launcher3.DragLayer.TouchCompleteListener;
 
+import java.util.ArrayList;
+
 /**
  * {@inheritDoc}
  */
@@ -43,6 +47,8 @@
 
     private float mSlop;
 
+    private boolean mChildrenFocused;
+
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mContext = context;
@@ -175,6 +181,90 @@
 
     @Override
     public int getDescendantFocusability() {
-        return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+        return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
+                : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
+                && event.getAction() == KeyEvent.ACTION_UP) {
+            mChildrenFocused = false;
+            requestFocus();
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+            event.startTracking();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (event.isTracking()) {
+            if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+                mChildrenFocused = true;
+                ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
+                focusableChildren.remove(this);
+                int childrenCount = focusableChildren.size();
+                switch (childrenCount) {
+                    case 0:
+                        mChildrenFocused = false;
+                        break;
+                    case 1: {
+                        if (getTag() instanceof ItemInfo) {
+                            ItemInfo item = (ItemInfo) getTag();
+                            if (item.spanX == 1 && item.spanY == 1) {
+                                focusableChildren.get(0).performClick();
+                                mChildrenFocused = false;
+                                return true;
+                            }
+                        }
+                        // continue;
+                    }
+                    default:
+                        focusableChildren.get(0).requestFocus();
+                        return true;
+                }
+            }
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        if (gainFocus) {
+            mChildrenFocused = false;
+        }
+        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+    }
+
+    @Override
+    public void requestChildFocus(View child, View focused) {
+        super.requestChildFocus(child, focused);
+        dispatchChildFocus(focused != null);
+    }
+
+    @Override
+    public void clearChildFocus(View child) {
+        super.clearChildFocus(child);
+        dispatchChildFocus(false);
+    }
+
+    @Override
+    public boolean dispatchUnhandledMove(View focused, int direction) {
+        return mChildrenFocused;
+    }
+
+    private void dispatchChildFocus(boolean focused) {
+        if (getOnFocusChangeListener() != null) {
+            getOnFocusChangeListener().onFocusChange(this, focused || isFocused());
+        }
     }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 882f7e2..55edf45 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -123,17 +123,11 @@
      */
     void onBindAppWidget(Launcher launcher) {
         if (!mHasNotifiedInitialWidgetSizeChanged) {
-            notifyWidgetSizeChanged(launcher);
+            AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
+            mHasNotifiedInitialWidgetSizeChanged = true;
         }
     }
 
-    /**
-     * Trigger an update callback to the widget to notify it that its size has changed.
-     */
-    void notifyWidgetSizeChanged(Launcher launcher) {
-        AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
-        mHasNotifiedInitialWidgetSizeChanged = true;
-    }
 
     @Override
     public String toString() {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index ef79cf8..f095a05 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -84,6 +84,10 @@
     @Override
     public boolean onCreate() {
         final Context context = getContext();
+        // The content provider exists for the entire duration of the launcher main process and
+        // is the first component to get created. Initializing application context here ensures
+        // that LauncherAppState always exists in the main process.
+        LauncherAppState.setApplicationContext(context.getApplicationContext());
         StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
         mOpenHelper = new DatabaseHelper(context);
         StrictMode.setThreadPolicy(oldPolicy);
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 1c6ca87..71ccd85 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,17 +1,22 @@
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.view.View;
 
 public class LauncherRootView extends InsettableFrameLayout {
 
     private final Paint mOpaquePaint;
     private boolean mDrawRightInsetBar;
 
+    private View mAlignedView;
+
     public LauncherRootView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -21,10 +26,32 @@
     }
 
     @Override
+    protected void onFinishInflate() {
+        if (getChildCount() > 0) {
+            // LauncherRootView contains only one child, which should be aligned
+            // based on the horizontal insets.
+            mAlignedView = getChildAt(0);
+        }
+        super.onFinishInflate();
+    }
+
+    @TargetApi(23)
+    @Override
     protected boolean fitSystemWindows(Rect insets) {
-        setInsets(insets);
-        mDrawRightInsetBar = mInsets.right > 0 && LauncherAppState
-                .getInstance().getInvariantDeviceProfile().isRightInsetOpaque;
+        mDrawRightInsetBar = insets.right > 0 &&
+                (!Utilities.ATLEAST_MARSHMALLOW ||
+                getContext().getSystemService(ActivityManager.class).isLowRamDevice());
+        setInsets(mDrawRightInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets);
+
+        if (mAlignedView != null) {
+            // Apply margins on aligned view to handle left/right insets.
+            MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams();
+            if (lp.leftMargin != insets.left || lp.rightMargin != insets.right) {
+                lp.leftMargin = insets.left;
+                lp.rightMargin = insets.right;
+                mAlignedView.setLayoutParams(lp);
+            }
+        }
 
         return true; // I'll take it from here
     }
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 3391d06..83b12a9 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -641,11 +641,11 @@
                     }
                     animation.play(reveal);
                 }
-
-                dispatchOnLauncherTransitionPrepare(fromView, animated, true);
-                dispatchOnLauncherTransitionPrepare(toView, animated, true);
             }
 
+            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+            dispatchOnLauncherTransitionPrepare(toView, animated, true);
+
             animation.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9258360..b6d3cc4 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -202,9 +202,6 @@
     protected final Rect mInsets = new Rect();
     protected final boolean mIsRtl;
 
-    // When set to true, full screen content and overscroll effect is shited inside by right inset.
-    protected boolean mIgnoreRightInset;
-
     // Edge effect
     private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect();
     private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect();
@@ -822,8 +819,7 @@
                     childWidthMode = MeasureSpec.EXACTLY;
                     childHeightMode = MeasureSpec.EXACTLY;
 
-                    childWidth = getViewportWidth() - mInsets.left
-                            - (mIgnoreRightInset ? mInsets.right : 0);
+                    childWidth = getViewportWidth() - mInsets.left - mInsets.right;
                     childHeight = getViewportHeight();
                 }
                 if (referenceChildWidth == 0) {
@@ -1182,9 +1178,8 @@
 
                 getEdgeVerticalPostion(sTmpIntPoint);
 
-                int width = mIgnoreRightInset ? (display.width() - mInsets.right) : display.width();
-                canvas.translate(sTmpIntPoint[0] - display.top, -width);
-                mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], width);
+                canvas.translate(sTmpIntPoint[0] - display.top, -display.width());
+                mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
                 if (mEdgeGlowRight.draw(canvas)) {
                     postInvalidateOnAnimation();
                 }
@@ -1225,7 +1220,17 @@
 
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
-        // XXX-RTL: This will be fixed in a future CL
+        if (super.dispatchUnhandledMove(focused, direction)) {
+            return true;
+        }
+
+        if (mIsRtl) {
+            if (direction == View.FOCUS_LEFT) {
+                direction = View.FOCUS_RIGHT;
+            } else if (direction == View.FOCUS_RIGHT) {
+                direction = View.FOCUS_LEFT;
+            }
+        }
         if (direction == View.FOCUS_LEFT) {
             if (getCurrentPage() > 0) {
                 snapToPage(getCurrentPage() - 1);
@@ -1237,7 +1242,7 @@
                 return true;
             }
         }
-        return super.dispatchUnhandledMove(focused, direction);
+        return false;
     }
 
     @Override
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 157b48a..56282fe 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -23,7 +23,6 @@
 import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
 
 public class ShortcutAndWidgetContainer extends ViewGroup {
     static final String TAG = "CellLayoutChildren";
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 5766cf2..6bdcb4b 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -52,7 +52,7 @@
     public static final int FLAG_AUTOINTALL_ICON = 2; //0B10;
 
     /**
-     * The icon is being installed. If {@link FLAG_RESTORED_ICON} or {@link FLAG_AUTOINTALL_ICON}
+     * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINTALL_ICON}
      * is set, then the icon is either being installed or is in a broken state.
      */
     public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100;
@@ -126,19 +126,14 @@
     private int mInstallProgress;
 
     /**
-     * Refer {@link AppInfo#firstInstallTime}.
-     */
-    public long firstInstallTime;
-
-    /**
-     * TODO move this to {@link status}
+     * TODO move this to {@link #status}
      */
     int flags = 0;
 
     /**
      * If this shortcut is a placeholder, then intent will be a market intent for the package, and
      * this will hold the original intent from the database.  Otherwise, null.
-     * Refer {@link #FLAG_RESTORE_PENDING}, {@link #FLAG_INSTALL_PENDING}
+     * Refer {@link #FLAG_RESTORED_ICON}, {@link #FLAG_AUTOINTALL_ICON}
      */
     Intent promisedIntent;
 
@@ -172,7 +167,6 @@
         mIcon = info.mIcon; // TODO: should make a copy here.  maybe we don't need this ctor at all
         customIcon = info.customIcon;
         flags = info.flags;
-        firstInstallTime = info.firstInstallTime;
         user = info.user;
         status = info.status;
     }
@@ -184,7 +178,6 @@
         intent = new Intent(info.intent);
         customIcon = false;
         flags = info.flags;
-        firstInstallTime = info.firstInstallTime;
     }
 
     public void setIcon(Bitmap b) {
@@ -293,7 +286,6 @@
         shortcut.intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
         shortcut.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
         shortcut.flags = AppInfo.initFlags(info);
-        shortcut.firstInstallTime = info.getFirstInstallTime();
         return shortcut;
     }
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d14d056..54d050f 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -46,6 +46,7 @@
 import android.graphics.drawable.PaintDrawable;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.PowerManager;
 import android.os.Process;
 import android.text.Spannable;
 import android.text.SpannableString;
@@ -59,6 +60,8 @@
 import android.view.View;
 import android.widget.Toast;
 
+import com.android.launcher3.compat.UserHandleCompat;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -194,6 +197,28 @@
     }
 
     /**
+     * Returns a bitmap suitable for the all apps view. The icon is badged for {@param user}
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public static Bitmap createBadgedIconBitmap(
+            Drawable icon, UserHandleCompat user, Context context) {
+        Bitmap bitmap = createIconBitmap(icon, context);
+        if (Utilities.ATLEAST_LOLLIPOP && user != null
+                && !UserHandleCompat.myUserHandle().equals(user)) {
+            BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap);
+            Drawable badged = context.getPackageManager().getUserBadgedIcon(
+                    drawable, user.getUser());
+            if (badged instanceof BitmapDrawable) {
+                return ((BitmapDrawable) badged).getBitmap();
+            } else {
+                return createIconBitmap(badged, context);
+            }
+        } else {
+            return bitmap;
+        }
+    }
+
+    /**
      * Returns a bitmap suitable for the all apps view.
      */
     public static Bitmap createIconBitmap(Drawable icon, Context context) {
@@ -759,4 +784,10 @@
         return context.getSharedPreferences(
                 LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
     }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public static boolean isPowerSaverOn(Context context) {
+        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        return ATLEAST_LOLLIPOP && powerManager.isPowerSaveMode();
+    }
 }
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
index 2d5612f..c24fbff 100644
--- a/src/com/android/launcher3/WallpaperChangedReceiver.java
+++ b/src/com/android/launcher3/WallpaperChangedReceiver.java
@@ -22,8 +22,6 @@
 
 public class WallpaperChangedReceiver extends BroadcastReceiver {
     public void onReceive(Context context, Intent data) {
-        LauncherAppState.setApplicationContext(context.getApplicationContext());
-        LauncherAppState appState = LauncherAppState.getInstance();
-        appState.onWallpaperChanged();
+        LauncherAppState.getInstance().onWallpaperChanged();
     }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f046fbd..5073902 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -455,7 +455,6 @@
         setWallpaperDimension();
 
         setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
-        mIgnoreRightInset = app.getInvariantDeviceProfile().isRightInsetOpaque;
     }
 
     private void setupLayoutTransition() {
@@ -1544,6 +1543,13 @@
     }
 
     @Override
+    protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
+        if (!isSwitchingState()) {
+            super.determineScrollingStart(ev, touchSlopScale);
+        }
+    }
+
+    @Override
     public void announceForAccessibility(CharSequence text) {
         // Don't announce if apps is on top of us.
         if (!mLauncher.isAppsViewVisible()) {
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
index 07ef0ef..0bc9588 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -33,7 +33,6 @@
     public abstract Drawable getIcon(int density);
     public abstract ApplicationInfo getApplicationInfo();
     public abstract long getFirstInstallTime();
-    public abstract Drawable getBadgedIcon(int density);
 
     /**
      * Creates a LauncherActivityInfoCompat for the primary user.
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
index ea51aac..fee0376 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
@@ -93,8 +93,4 @@
     public String getName() {
         return mActivityInfo.name;
     }
-
-    public Drawable getBadgedIcon(int density) {
-        return getIcon(density);
-    }
 }
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
index 4448758..67c5c27 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
@@ -55,8 +55,4 @@
     public long getFirstInstallTime() {
         return mLauncherActivityInfo.getFirstInstallTime();
     }
-
-    public Drawable getBadgedIcon(int density) {
-        return mLauncherActivityInfo.getBadgedIcon(density);
-    }
 }
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 6424e03..4aa667e 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -43,7 +43,6 @@
 
     PackageInstallerCompatVL(Context context) {
         mInstaller = context.getPackageManager().getPackageInstaller();
-        LauncherAppState.setApplicationContext(context.getApplicationContext());
         mCache = LauncherAppState.getInstance().getIconCache();
         mWorker = new Handler(LauncherModel.getWorkerLooper());
 
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
index 567022b..9479908 100644
--- a/src/com/android/launcher3/compat/UserHandleCompat.java
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -49,7 +49,7 @@
         }
     }
 
-    UserHandle getUser() {
+    public UserHandle getUser() {
         return mUser;
     }
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index f708004..6b7cba8 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -17,8 +17,6 @@
 package com.android.launcher3.compat;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
 
 import com.android.launcher3.Utilities;
 
@@ -54,7 +52,6 @@
     public abstract List<UserHandleCompat> getUserProfiles();
     public abstract long getSerialNumberForUser(UserHandleCompat user);
     public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
-    public abstract Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user);
     public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user);
     public abstract long getUserCreationTime(UserHandleCompat user);
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index 85aee57..fcd7555 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.compat;
 
-import android.graphics.drawable.Drawable;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -36,11 +34,6 @@
         return UserHandleCompat.myUserHandle();
     }
 
-    public Drawable getBadgedDrawableForUser(Drawable unbadged,
-            UserHandleCompat user) {
-        return unbadged;
-    }
-
     public long getSerialNumberForUser(UserHandleCompat user) {
         return 0;
     }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index 98d5eca..c53d702 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -21,7 +21,6 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.UserHandle;
 
@@ -86,11 +85,6 @@
     }
 
     @Override
-    public Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user) {
-        return mPm.getUserBadgedIcon(unbadged, user.getUser());
-    }
-
-    @Override
     public CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user) {
         if (user == null) {
             return label;
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index 849b05c..fb9bbb2 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -42,6 +42,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -85,6 +86,7 @@
 
     private ArrayList<ShortcutInfo> mHomescreenApps;
     private ArrayList<ShortcutInfo> mWorkFolderApps;
+    private HashMap<ShortcutInfo, Long> mShortcutToInstallTimeMap;
 
     private ManagedProfileHeuristic(Context context, UserHandleCompat user) {
         mContext = context;
@@ -100,32 +102,29 @@
                 Context.MODE_PRIVATE);
     }
 
+    private void initVars() {
+        mHomescreenApps = new ArrayList<>();
+        mWorkFolderApps = new ArrayList<>();
+        mShortcutToInstallTimeMap = new HashMap<>();
+    }
+
     /**
      * Checks the list of user apps and adds icons for newly installed apps on the homescreen or
      * workfolder.
      */
     public void processUserApps(List<LauncherActivityInfoCompat> apps) {
-        mHomescreenApps = new ArrayList<>();
-        mWorkFolderApps = new ArrayList<>();
+        initVars();
 
         HashSet<String> packageSet = new HashSet<>();
         final boolean userAppsExisted = getUserApps(packageSet);
 
         boolean newPackageAdded = false;
-
         for (LauncherActivityInfoCompat info : apps) {
             String packageName = info.getComponentName().getPackageName();
             if (!packageSet.contains(packageName)) {
                 packageSet.add(packageName);
                 newPackageAdded = true;
-
-                try {
-                    PackageInfo pkgInfo = mContext.getPackageManager()
-                            .getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
-                    markForAddition(info, pkgInfo.firstInstallTime);
-                } catch (NameNotFoundException e) {
-                    Log.e(TAG, "Unknown package " + packageName, e);
-                }
+                markForAddition(info, info.getFirstInstallTime());
             }
         }
 
@@ -142,7 +141,22 @@
         ArrayList<ShortcutInfo> targetList =
                 (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
                         mWorkFolderApps : mHomescreenApps;
-        targetList.add(ShortcutInfo.fromActivityInfo(info, mContext));
+        ShortcutInfo si = ShortcutInfo.fromActivityInfo(info, mContext);
+        mShortcutToInstallTimeMap.put(si, installTime);
+        targetList.add(si);
+    }
+
+    private void sortList(ArrayList<ShortcutInfo> infos) {
+        Collections.sort(infos, new Comparator<ShortcutInfo>() {
+
+            @Override
+            public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
+                Long lhsTime = mShortcutToInstallTimeMap.get(lhs);
+                Long rhsTime = mShortcutToInstallTimeMap.get(rhs);
+                return Utilities.longCompare(lhsTime == null ? 0 : lhsTime,
+                        rhsTime == null ? 0 : rhsTime);
+            }
+        });
     }
 
     /**
@@ -152,13 +166,7 @@
         if (mWorkFolderApps.isEmpty()) {
             return;
         }
-        Collections.sort(mWorkFolderApps, new Comparator<ShortcutInfo>() {
-
-            @Override
-            public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
-                return Utilities.longCompare(lhs.firstInstallTime, rhs.firstInstallTime);
-            }
-        });
+        sortList(mWorkFolderApps);
 
         // Try to get a work folder.
         String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
@@ -222,6 +230,7 @@
         finalizeWorkFolder();
 
         if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) {
+            sortList(mHomescreenApps);
             mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps);
         }
     }
@@ -230,9 +239,7 @@
      * Updates the list of installed apps and adds any new icons on homescreen or work folder.
      */
     public void processPackageAdd(String[] packages) {
-        mHomescreenApps = new ArrayList<>();
-        mWorkFolderApps = new ArrayList<>();
-
+        initVars();
         HashSet<String> packageSet = new HashSet<>();
         final boolean userAppsExisted = getUserApps(packageSet);