Adding support for badging instant app icons.

Change-Id: Idc43a1a83e0a93f70879730a0acefbc124f9c0e2
diff --git a/res/drawable/ic_instant_app_badge.xml b/res/drawable/ic_instant_app_badge.xml
new file mode 100644
index 0000000..cc53230
--- /dev/null
+++ b/res/drawable/ic_instant_app_badge.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/profile_badge_size"
+    android:height="@dimen/profile_badge_size"
+    android:viewportWidth="18"
+    android:viewportHeight="18">
+
+    <path
+        android:fillColor="@android:color/black"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/white"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 9 0 C 13.9705627485 0 18 4.02943725152 18 9 C 18 13.9705627485 13.9705627485 18 9 18 C 4.02943725152 18 0 13.9705627485 0 9 C 0 4.02943725152 4.02943725152 0 9 0 Z" />
+    <path
+        android:fillColor="@android:color/black"
+        android:fillAlpha="0.87"
+        android:fillType="evenOdd"
+        android:strokeWidth="1"
+        android:pathData="M 6 10.4123279 L 8.63934949 10.4123279 L 8.63934949 15.6 L 12.5577168 7.84517705 L 9.94547194 7.84517705 L 9.94547194 2 Z" />
+</vector>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index b41172b..01772f1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -116,6 +116,9 @@
     <!-- Name of a color extraction implementation class. -->
     <string name="color_extraction_impl_class" translatable="false"></string>
 
+    <!-- Name of a subclass of com.android.launcher3.util.InstantAppResolver. Can be empty. -->
+    <string name="instant_app_resolver_class" translatable="false"></string>
+
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
 
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index ecadb18..6f86954 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -47,6 +47,7 @@
 import com.android.launcher3.graphics.LauncherIcons;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Provider;
 import com.android.launcher3.util.SQLiteCacheHelper;
@@ -94,6 +95,7 @@
     private final LauncherAppsCompat mLauncherApps;
     private final HashMap<ComponentKey, CacheEntry> mCache =
             new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
+    private final InstantAppResolver mInstantAppResolver;
     private final int mIconDpi;
     @Thunk final IconDB mIconDb;
 
@@ -106,6 +108,7 @@
         mPackageManager = context.getPackageManager();
         mUserManager = UserManagerCompat.getInstance(mContext);
         mLauncherApps = LauncherAppsCompat.getInstance(mContext);
+        mInstantAppResolver = InstantAppResolver.newInstance(mContext);
         mIconDpi = inv.fillResIconDpi;
         mIconDb = new IconDB(context, inv.iconBitmapSize);
 
@@ -478,12 +481,6 @@
     }
 
     private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
-        if (info instanceof ShortcutInfo
-                && ((ShortcutInfo) info).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
-                && (entry.icon == null || isDefaultIcon(entry.icon, info.user))) {
-            // skip updating shortcut info if no icon and supports web ui
-            return;
-        }
         info.title = Utilities.trim(entry.title);
         info.contentDescription = entry.contentDescription;
         info.iconBitmap = entry.icon == null ? getDefaultIcon(info.user) : entry.icon;
@@ -581,7 +578,6 @@
         // For icon caching, do not go through DB. Just update the in-memory entry.
         if (entry == null) {
             entry = new CacheEntry();
-            mCache.put(cacheKey, entry);
         }
         if (!TextUtils.isEmpty(title)) {
             entry.title = title;
@@ -589,6 +585,9 @@
         if (icon != null) {
             entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
         }
+        if (!TextUtils.isEmpty(title) && entry.icon != null) {
+            mCache.put(cacheKey, entry);
+        }
     }
 
     private static ComponentKey getPackageKey(String packageName, UserHandle user) {
@@ -625,6 +624,10 @@
                     // only keep the low resolution icon instead of the larger full-sized icon
                     Bitmap icon = LauncherIcons.createBadgedIconBitmap(
                             appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
+                    if (mInstantAppResolver.isInstantApp(appInfo)) {
+                        icon = LauncherIcons.badgeWithDrawable(icon,
+                                mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
+                    }
                     Bitmap lowResIcon =  generateLowResIcon(icon);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 7a27741..0139bd9 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -88,7 +88,7 @@
             case OP_CACHE_UPDATE:
                 return true;
             case OP_SESSION_UPDATE:
-                return si.isPromise();
+                return si.hasPromiseIconUi();
             default:
                 return false;
         }
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index ebc6f23..32dfe25 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -16,6 +16,9 @@
 package com.android.launcher3.model;
 
 import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Process;
 
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
@@ -28,6 +31,7 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.util.InstantAppResolver;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -46,6 +50,17 @@
     @Override
     public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
         if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
+            try {
+                // For instant apps we do not get package-add. Use setting events to update
+                // any pinned icons.
+                ApplicationInfo ai = app.getContext()
+                        .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
+                if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
+                    app.getModel().onPackageAdded(ai.packageName, Process.myUserHandle());
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Ignore
+            }
             // Ignore install success events as they are handled by Package add events.
             return;
         }
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 98bffe1..13962a2 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -23,6 +23,7 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.Log;
+
 import com.android.launcher3.AllAppsList;
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.IconCache;
@@ -32,7 +33,6 @@
 import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel.CallbackTask;
 import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.ShortcutInfo;
@@ -46,6 +46,7 @@
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.PackageUserKey;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -198,10 +199,11 @@
                         if (cn != null && matcher.matches(si, cn)) {
                             AppInfo appInfo = addedOrUpdatedApps.get(cn);
 
-                            if (mOp == OP_REMOVE
-                                    && si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) {
+                            if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)) {
                                 removedShortcuts.put(si.id, false);
-                                continue;
+                                if (mOp == OP_REMOVE) {
+                                    continue;
+                                }
                             }
 
                             // For system apps, package manager send OP_UPDATE when an
@@ -220,22 +222,20 @@
                                             appInfo = addedOrUpdatedApps.get(cn);
                                         }
 
-                                        if ((intent == null) || (appInfo == null)) {
+                                        if (intent != null && appInfo != null) {
+                                            si.intent = intent;
+                                            si.status = ShortcutInfo.DEFAULT;
+                                            infoUpdated = true;
+                                        } else if (si.hasPromiseIconUi()) {
                                             removedShortcuts.put(si.id, true);
                                             continue;
                                         }
-                                        si.intent = intent;
                                     }
                                 }
-                                si.status = ShortcutInfo.DEFAULT;
-                                infoUpdated = true;
-                                if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
-                                    iconCache.getTitleAndIcon(si, si.usingLowResIcon);
-                                }
                             }
 
-                            if (appInfo != null && Intent.ACTION_MAIN.equals(si.intent.getAction())
-                                    && si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+                            if ((mOp == OP_ADD || mOp == OP_UPDATE) &&
+                                    si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
                                 iconCache.getTitleAndIcon(si, si.usingLowResIcon);
                                 infoUpdated = true;
                             }
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
new file mode 100644
index 0000000..e60d768
--- /dev/null
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A wrapper class to access instant app related APIs.
+ */
+public class InstantAppResolver {
+
+    public static InstantAppResolver newInstance(Context context) {
+        return Utilities.getOverrideObject(
+                InstantAppResolver.class, context, R.string.instant_app_resolver_class);
+    }
+
+    public boolean isInstantApp(ApplicationInfo info) {
+        return false;
+    }
+
+    public List<ApplicationInfo> getInstantApps() {
+        return Collections.emptyList();
+    }
+}