Merge "Deduping shortcuts to app-shortcuts if they have a valid intent" into ub-launcher3-burnaby
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 0db22a4..5422de9 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -22,6 +22,7 @@
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.text.TextUtils;
@@ -147,6 +148,7 @@
 
         if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0));
         PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
+        info = convertToLauncherActivityIfPossible(info);
 
         queuePendingShortcutInfo(info, context);
     }
@@ -373,6 +375,10 @@
             }
             return packageName;
         }
+
+        public boolean isLuncherActivity() {
+            return activityInfo != null;
+        }
     }
 
     private static PendingInstallShortcutInfo decode(String encoded, Context context) {
@@ -420,4 +426,40 @@
         }
         return null;
     }
+
+    /**
+     * Tries to create a new PendingInstallShortcutInfo which represents the same target,
+     * but is an app target and not a shortcut.
+     * @return the newly created info or the original one.
+     */
+    private static PendingInstallShortcutInfo convertToLauncherActivityIfPossible(
+            PendingInstallShortcutInfo original) {
+        if (original.isLuncherActivity()) {
+            // Already an activity target
+            return original;
+        }
+        if (isValidShortcutLaunchIntent(original.launchIntent)
+                || !original.user.equals(UserHandleCompat.myUserHandle())) {
+            // We can only convert shortcuts which point to a main activity in the current user.
+            return original;
+        }
+
+        PackageManager pm = original.mContext.getPackageManager();
+        ResolveInfo info = pm.resolveActivity(original.launchIntent, 0);
+
+        if (info == null) {
+            return original;
+        }
+
+        // Ignore any conflicts in the label name, as that can change based on locale.
+        LauncherActivityInfoCompat launcherInfo = LauncherActivityInfoCompat
+                .fromResolveInfo(info, original.mContext);
+        return new PendingInstallShortcutInfo(launcherInfo, original.mContext);
+    }
+
+    public static boolean isLauncherActivity(Intent intent, Context context) {
+        Intent data = new Intent().putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
+        PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
+        return convertToLauncherActivityIfPossible(info).isLuncherActivity();
+    }
 }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 1f59533..d75ef73 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -37,6 +37,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
+import android.database.sqlite.SQLiteStatement;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -63,7 +64,7 @@
     private static final String TAG = "Launcher.LauncherProvider";
     private static final boolean LOGD = false;
 
-    private static final int DATABASE_VERSION = 23;
+    private static final int DATABASE_VERSION = 24;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -617,7 +618,9 @@
                         break;
                     }
                 }
-                case 23: {
+                case 23:
+                    convertShortcutsToLauncherActivities(db);
+                case 24: {
                     // DB Upgraded successfully
                     return;
                 }
@@ -636,7 +639,6 @@
             createEmptyDB(db);
         }
 
-
         /**
          * Clears all the data for a fresh start.
          */
@@ -647,6 +649,63 @@
         }
 
         /**
+         * Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid
+         * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}.
+         */
+        private void convertShortcutsToLauncherActivities(SQLiteDatabase db) {
+            db.beginTransaction();
+            Cursor c = null;
+            SQLiteStatement updateStmt = null;
+
+            try {
+                // Only consider the primary user as other users can't have a shortcut.
+                long userSerial = UserManagerCompat.getInstance(mContext)
+                        .getSerialNumberForUser(UserHandleCompat.myUserHandle());
+                c = db.query(TABLE_FAVORITES, new String[] {
+                        Favorites._ID,
+                        Favorites.INTENT,
+                    }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial,
+                    null, null, null, null);
+
+                updateStmt = db.compileStatement("UPDATE favorites SET itemType="
+                        + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?");
+
+                final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
+                final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
+
+                while (c.moveToNext()) {
+                    String intentDescription = c.getString(intentIndex);
+                    Intent intent;
+                    try {
+                        intent = Intent.parseUri(intentDescription, 0);
+                    } catch (URISyntaxException e) {
+                        Log.e(TAG, "Unable to parse intent", e);
+                        continue;
+                    }
+
+                    if (!InstallShortcutReceiver.isLauncherActivity(intent, mContext)) {
+                        continue;
+                    }
+
+                    long id = c.getLong(idIndex);
+                    updateStmt.bindLong(1, id);
+                    updateStmt.execute();
+                }
+                db.setTransactionSuccessful();
+            } catch (SQLException ex) {
+                Log.w(TAG, "Error deduping shortcuts", ex);
+            } finally {
+                db.endTransaction();
+                if (c != null) {
+                    c.close();
+                }
+                if (updateStmt != null) {
+                    updateStmt.close();
+                }
+            }
+        }
+
+        /**
          * Recreates workspace table and migrates data to the new table.
          */
         public boolean recreateWorkspaceTable(SQLiteDatabase db) {
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
index 90a4d1a..07ef0ef 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -17,7 +17,9 @@
 package com.android.launcher3.compat;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 
 public abstract class LauncherActivityInfoCompat {
@@ -32,4 +34,11 @@
     public abstract ApplicationInfo getApplicationInfo();
     public abstract long getFirstInstallTime();
     public abstract Drawable getBadgedIcon(int density);
+
+    /**
+     * Creates a LauncherActivityInfoCompat for the primary user.
+     */
+    public static LauncherActivityInfoCompat fromResolveInfo(ResolveInfo info, Context context) {
+        return new LauncherActivityInfoCompatV16(context, info);
+    }
 }