Merge "Bug fix: QSB sometimes gets stuck to transparent." into ub-launcher3-master
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 0380923..c45ff7b 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -355,10 +355,11 @@
 
     public void snapToWidget(boolean animate) {
         final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
-        int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding
-                - mWidgetPadding.left - mWidgetPadding.right;
-        int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding
-                - mWidgetPadding.top - mWidgetPadding.bottom;
+        DeviceProfile profile = mLauncher.getDeviceProfile();
+        int newWidth = (int) (mWidgetView.getWidth() * profile.appWidgetScale.x)
+                + 2 * mBackgroundPadding - mWidgetPadding.left - mWidgetPadding.right;
+        int newHeight = (int) (mWidgetView.getHeight() * profile.appWidgetScale.y)
+                + 2 * mBackgroundPadding - mWidgetPadding.top - mWidgetPadding.bottom;
 
         mTmpPt[0] = mWidgetView.getLeft();
         mTmpPt[1] = mWidgetView.getTop();
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 3564cec..9eaef90 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2690,6 +2690,18 @@
         }
 
         public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount) {
+            setup(cellWidth, cellHeight, invertHorizontally, colCount, 1.0f, 1.0f);
+        }
+
+        /**
+         * Use this method, as opposed to {@link #setup(int, int, boolean, int)}, if the view needs
+         * to be scaled.
+         *
+         * ie. In multi-window mode, we setup widgets so that they are measured and laid out
+         * using their full/invariant device profile sizes.
+         */
+        public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+                float cellScaleX, float cellScaleY) {
             if (isLockedToGrid) {
                 final int myCellHSpan = cellHSpan;
                 final int myCellVSpan = cellVSpan;
@@ -2700,8 +2712,8 @@
                     myCellX = colCount - myCellX - cellHSpan;
                 }
 
-                width = myCellHSpan * cellWidth - leftMargin - rightMargin;
-                height = myCellVSpan * cellHeight - topMargin - bottomMargin;
+                width = (int) (myCellHSpan * cellWidth / cellScaleX - leftMargin - rightMargin);
+                height = (int) (myCellVSpan * cellHeight / cellScaleY - topMargin - bottomMargin);
                 x = (myCellX * cellWidth + leftMargin);
                 y = (myCellY * cellHeight + topMargin);
             }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 59ec56a..eb1db1c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.Gravity;
@@ -123,6 +124,9 @@
     public int allAppsIconDrawablePaddingPx;
     public float allAppsIconTextSizePx;
 
+    // Widgets
+    public final PointF appWidgetScale = new PointF(1.0f, 1.0f);
+
     // Drop Target
     public int dropTargetBarSizePx;
 
@@ -220,6 +224,12 @@
         // The nav bar is black so we add bottom padding to visually center hotseat icons.
         profile.hotseatBarBottomPaddingPx = profile.hotseatBarTopPaddingPx;
 
+        // We use these scales to measure and layout the widgets using their full invariant profile
+        // sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
+        float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
+        float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y;
+        profile.appWidgetScale.set(appWidgetScaleX, appWidgetScaleY);
+
         return profile;
     }
 
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 056facb..d2f25a4 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -33,6 +33,8 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.Thunk;
 
 import org.json.JSONException;
@@ -44,6 +46,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 
 public class InstallShortcutReceiver extends BroadcastReceiver {
@@ -76,11 +79,7 @@
             String encoded = info.encodeToString();
             if (encoded != null) {
                 Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
-                if (strings == null) {
-                    strings = new HashSet<String>(1);
-                } else {
-                    strings = new HashSet<String>(strings);
-                }
+                strings = (strings != null) ? new HashSet<>(strings) : new HashSet<String>(1);
                 strings.add(encoded);
                 sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
             }
@@ -115,16 +114,15 @@
         }
     }
 
-    private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue(
-            SharedPreferences sharedPrefs, Context context) {
+    private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue(Context context) {
+        SharedPreferences sharedPrefs = Utilities.getPrefs(context);
         synchronized(sLock) {
+            ArrayList<PendingInstallShortcutInfo> infos = new ArrayList<>();
             Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
             if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
             if (strings == null) {
-                return new ArrayList<PendingInstallShortcutInfo>();
+                return infos;
             }
-            ArrayList<PendingInstallShortcutInfo> infos =
-                new ArrayList<PendingInstallShortcutInfo>();
             for (String encoded : strings) {
                 PendingInstallShortcutInfo info = decode(encoded, context);
                 if (info != null) {
@@ -212,36 +210,12 @@
         mUseInstallQueue = false;
         flushInstallQueue(context);
     }
+
     static void flushInstallQueue(Context context) {
-        SharedPreferences sp = Utilities.getPrefs(context);
-        ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp, context);
-        if (!installQueue.isEmpty()) {
-            Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator();
-            ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>();
-            while (iter.hasNext()) {
-                final PendingInstallShortcutInfo pendingInfo = iter.next();
-
-                // If the intent specifies a package, make sure the package exists
-                String packageName = pendingInfo.getTargetPackage();
-                if (!TextUtils.isEmpty(packageName)) {
-                    UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
-                    if (!LauncherAppsCompat.getInstance(context)
-                            .isPackageEnabledForProfile(packageName, myUserHandle)) {
-                        if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
-                                + pendingInfo.launchIntent);
-                        continue;
-                    }
-                }
-
-                // Generate a shortcut info to add into the model
-                addShortcuts.add(pendingInfo.getShortcutInfo());
-            }
-
-            // Add the new apps to the model and bind them
-            if (!addShortcuts.isEmpty()) {
-                LauncherAppState app = LauncherAppState.getInstance();
-                app.getModel().addAndBindAddedWorkspaceItems(addShortcuts);
-            }
+        ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context);
+        if (!items.isEmpty()) {
+            LauncherAppState.getInstance().getModel().addAndBindAddedWorkspaceItems(
+                    new LazyShortcutsProvider(context.getApplicationContext(), items));
         }
     }
 
@@ -445,4 +419,40 @@
         // Ignore any conflicts in the label name, as that can change based on locale.
         return new PendingInstallShortcutInfo(info, original.mContext);
     }
+
+    private static class LazyShortcutsProvider extends Provider<List<ItemInfo>> {
+
+        private final Context mContext;
+        private final ArrayList<PendingInstallShortcutInfo> mPendingItems;
+
+        public LazyShortcutsProvider(Context context, ArrayList<PendingInstallShortcutInfo> items) {
+            mContext = context;
+            mPendingItems = items;
+        }
+
+        /**
+         * This must be called on the background thread as this requires multiple calls to
+         * packageManager and icon cache.
+         */
+        @Override
+        public ArrayList<ItemInfo> get() {
+            Preconditions.assertNonUiThread();
+            ArrayList<ItemInfo> installQueue = new ArrayList<>();
+            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
+            for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
+                // If the intent specifies a package, make sure the package exists
+                String packageName = pendingInfo.getTargetPackage();
+                if (!TextUtils.isEmpty(packageName) && !launcherApps.isPackageEnabledForProfile(
+                        packageName, pendingInfo.user)) {
+                    if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
+                            + pendingInfo.launchIntent);
+                    continue;
+                }
+
+                // Generate a shortcut info to add into the model
+                installQueue.add(pendingInfo.getShortcutInfo());
+            }
+            return installQueue;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index cfb28f9..c7bb188 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -85,6 +85,7 @@
 import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 
@@ -260,9 +261,16 @@
     /**
      * Adds the provided items to the workspace.
      */
+    public void addAndBindAddedWorkspaceItems(List<ItemInfo> workspaceApps) {
+        addAndBindAddedWorkspaceItems(Provider.of(workspaceApps));
+    }
+
+    /**
+     * Adds the provided items to the workspace.
+     */
     public void addAndBindAddedWorkspaceItems(
-            final ArrayList<? extends ItemInfo> workspaceApps) {
-        enqueueModelUpdateTask(new AddWorkspaceItemsTask(workspaceApps));
+            Provider<List<ItemInfo>> appsProvider) {
+        enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 349f094..b30c3f3 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -650,7 +650,7 @@
             if (mWidgetHostResetHandler != null) {
                 new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID).deleteHost();
                 mWidgetHostResetHandler.sendEmptyMessage(
-                        ChangeListenerWrapper.MSG_EXTRACTED_COLORS_CHANGED);
+                        ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET);
             }
 
             // Set the flag for empty DB
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 5f89af6..342479f 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,6 +18,7 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.support.annotation.IntDef;
 import android.view.View;
@@ -120,20 +121,20 @@
     }
 
     public void measureChild(View child) {
-        final DeviceProfile grid = mLauncher.getDeviceProfile();
-        final int cellWidth = mCellWidth;
-        final int cellHeight = mCellHeight;
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
         if (!lp.isFullscreen) {
-            lp.setup(cellWidth, cellHeight, invertLayoutHorizontally(), mCountX);
+            final DeviceProfile profile = mLauncher.getDeviceProfile();
 
             if (child instanceof LauncherAppWidgetHostView) {
-                // Widgets have their own padding, so skip
+                lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
+                        profile.appWidgetScale.x, profile.appWidgetScale.y);
+                // Widgets have their own padding
             } else {
-                // Otherwise, center the icon/folder
+                lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+                // Center the icon/folder
                 int cHeight = getCellContentHeight();
                 int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
-                int cellPaddingX = (int) (grid.edgeMarginPx / 2f);
+                int cellPaddingX = (int) (profile.edgeMarginPx / 2f);
                 child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
             }
         } else {
@@ -158,6 +159,21 @@
             final View child = getChildAt(i);
             if (child.getVisibility() != GONE) {
                 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+
+                if (child instanceof LauncherAppWidgetHostView) {
+                    // Scale and center the widget to fit within its cells.
+                    DeviceProfile profile = mLauncher.getDeviceProfile();
+                    float scaleX = profile.appWidgetScale.x;
+                    float scaleY = profile.appWidgetScale.y;
+
+                    float scale = Math.min(scaleX, scaleY);
+                    child.setScaleX(scale);
+                    child.setScaleY(scale);
+
+                    child.setTranslationX(-(lp.width - (lp.width * scaleX)) / 2.0f);
+                    child.setTranslationY(-(lp.height - (lp.height * scaleY)) / 2.0f);
+                }
+
                 int childLeft = lp.x;
                 int childTop = lp.y;
                 child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 986e163..97335cb 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -33,26 +33,29 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.Provider;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Task to add auto-created workspace items.
  */
 public class AddWorkspaceItemsTask extends ExtendedModelTask {
 
-    private final ArrayList<? extends ItemInfo> mWorkspaceApps;
+    private final Provider<List<ItemInfo>> mAppsProvider;
 
     /**
-     * @param workspaceApps items to add on the workspace
+     * @param appsProvider items to add on the workspace
      */
-    public AddWorkspaceItemsTask(ArrayList<? extends ItemInfo> workspaceApps) {
-        mWorkspaceApps = workspaceApps;
+    public AddWorkspaceItemsTask(Provider<List<ItemInfo>> appsProvider) {
+        mAppsProvider = appsProvider;
     }
 
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
-        if (mWorkspaceApps.isEmpty()) {
+        List<ItemInfo> workspaceApps = mAppsProvider.get();
+        if (workspaceApps.isEmpty()) {
             return;
         }
         Context context = app.getContext();
@@ -65,7 +68,7 @@
         // called.
         ArrayList<Long> workspaceScreens = LauncherModel.loadWorkspaceScreensDb(context);
         synchronized(dataModel) {
-            for (ItemInfo item : mWorkspaceApps) {
+            for (ItemInfo item : workspaceApps) {
                 if (item instanceof ShortcutInfo) {
                     // Short-circuit this logic if the icon exists somewhere on the workspace
                     if (shortcutExists(dataModel, item.getIntent(), item.user)) {
@@ -258,5 +261,4 @@
         }
         return occupied.findVacantCell(xy, spanX, spanY);
     }
-
 }
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index 78b7a3e..817a38a 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -121,7 +121,7 @@
             // getting filled with the managed user apps, when it start with a fresh DB (or after
             // a very long time).
             if (userAppsExisted && !homescreenApps.isEmpty()) {
-                mModel.addAndBindAddedWorkspaceItems(homescreenApps);
+                mModel.addAndBindAddedWorkspaceItems(new ArrayList<ItemInfo>(homescreenApps));
             }
         }
 
@@ -173,7 +173,7 @@
                 }
 
                 // Add the item to home screen and DB. This also generates an item id synchronously.
-                ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1);
+                ArrayList<ItemInfo> itemList = new ArrayList<>(1);
                 itemList.add(workFolder);
                 mModel.addAndBindAddedWorkspaceItems(itemList);
                 mPrefs.edit().putLong(folderIdKey, workFolder.id).apply();
diff --git a/src/com/android/launcher3/util/Provider.java b/src/com/android/launcher3/util/Provider.java
new file mode 100644
index 0000000..1cdd8d6
--- /dev/null
+++ b/src/com/android/launcher3/util/Provider.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+package com.android.launcher3.util;
+
+/**
+ * Utility class to allow lazy initialization of objects.
+ */
+public abstract class Provider<T> {
+
+    /**
+     * Initializes and returns the object. This may contain expensive operations not suitable
+     * to UI thread.
+     */
+    public abstract T get();
+
+    public static <T> Provider<T> of (final T value) {
+        return new Provider<T>() {
+            @Override
+            public T get() {
+                return value;
+            }
+        };
+    }
+}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index ecb3782..b2f0cbb 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -15,6 +15,7 @@
 import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.Provider;
 
 import org.mockito.ArgumentCaptor;
 
@@ -48,8 +49,8 @@
         idp.numRows = 5;
     }
 
-    private <T extends ItemInfo> AddWorkspaceItemsTask newTask(T... items) {
-        return new AddWorkspaceItemsTask(new ArrayList<>(Arrays.asList(items))) {
+    private AddWorkspaceItemsTask newTask(ItemInfo... items) {
+        return new AddWorkspaceItemsTask(Provider.of(Arrays.asList(items))) {
 
             @Override
             protected void addItemToDatabase(Context context, ItemInfo item,