Unify sorting between all apps and widget tray

- selected locale names are shown before latin
- case independent sorting
- main app > enterprise app

Future possible refactoring:
- Move all the *ItemInfo data structures to model package
- Rename the comparator based on NOT what data structure it supports
but what functionality it supports (locale? case independent?
main app > enterprise app?)

b/21271658
b/20339403

Change-Id: I8a776467392e21d5014e85cd3f51931a3ef89724
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index eff7b06..82aaeb9 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -7,6 +7,7 @@
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.AppNameComparator;
 
 import java.text.Collator;
 import java.util.ArrayList;
@@ -18,96 +19,6 @@
 import java.util.Map;
 import java.util.TreeMap;
 
-
-/**
- * A private class to manage access to an app name comparator.
- */
-class AppNameComparator {
-    private final UserManagerCompat mUserManager;
-    private final Collator mCollator;
-    private final Comparator<AppInfo> mAppInfoComparator;
-    private final Comparator<String> mSectionNameComparator;
-    private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
-
-    public AppNameComparator(Context context) {
-        mCollator = Collator.getInstance();
-        mUserManager = UserManagerCompat.getInstance(context);
-        mAppInfoComparator = new Comparator<AppInfo>() {
-            public final int compare(AppInfo a, AppInfo b) {
-                // Order by the title in the current locale
-                int result = compareTitles(a.title.toString(), b.title.toString());
-                if (result == 0) {
-                    // If two apps have the same title, then order by the component name
-                    result = a.componentName.compareTo(b.componentName);
-                    if (result == 0) {
-                        // If the two apps are the same component, then prioritize by the order that
-                        // the app user was created (prioritizing the main user's apps)
-                        if (UserHandleCompat.myUserHandle().equals(a.user)) {
-                            return -1;
-                        } else {
-                            Long aUserSerial = getAndCacheUserSerial(a.user);
-                            Long bUserSerial = getAndCacheUserSerial(b.user);
-                            return aUserSerial.compareTo(bUserSerial);
-                        }
-                    }
-                }
-                return result;
-            }
-        };
-        mSectionNameComparator = new Comparator<String>() {
-            @Override
-            public int compare(String o1, String o2) {
-                return compareTitles(o1, o2);
-            }
-        };
-    }
-
-    /**
-     * Returns a locale-aware comparator that will alphabetically order a list of applications.
-     */
-    public Comparator<AppInfo> getAppInfoComparator() {
-        // Clear the user serial cache so that we get serials as needed in the comparator
-        mUserSerialCache.clear();
-        return mAppInfoComparator;
-    }
-
-    /**
-     * Returns a locale-aware comparator that will alphabetically order a list of section names.
-     */
-    public Comparator<String> getSectionNameComparator() {
-        return mSectionNameComparator;
-    }
-
-    /**
-     * Compares two titles with the same return value semantics as Comparator.
-     */
-    private int compareTitles(String titleA, String titleB) {
-        // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit
-        boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0));
-        boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0));
-        if (aStartsWithLetter && !bStartsWithLetter) {
-            return -1;
-        } else if (!aStartsWithLetter && bStartsWithLetter) {
-            return 1;
-        }
-
-        // Order by the title in the current locale
-        return mCollator.compare(titleA, titleB);
-    }
-
-    /**
-     * Returns the user serial for this user, using a cached serial if possible.
-     */
-    private Long getAndCacheUserSerial(UserHandleCompat user) {
-        Long userSerial = mUserSerialCache.get(user);
-        if (userSerial == null) {
-            userSerial = mUserManager.getSerialNumberForUser(user);
-            mUserSerialCache.put(user, userSerial);
-        }
-        return userSerial;
-    }
-}
-
 /**
  * The alphabetically sorted list of applications.
  */
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 0b3049c..58e899a 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -66,7 +66,6 @@
 import java.lang.ref.WeakReference;
 import java.net.URISyntaxException;
 import java.security.InvalidParameterException;
-import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -3665,39 +3664,6 @@
         return folderInfo;
     }
 
-    public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
-        private final AppWidgetManagerCompat mManager;
-        private final PackageManager mPackageManager;
-        private final HashMap<Object, String> mLabelCache;
-        private final Collator mCollator;
-
-        public WidgetAndShortcutNameComparator(Context context) {
-            mManager = AppWidgetManagerCompat.getInstance(context);
-            mPackageManager = context.getPackageManager();
-            mLabelCache = new HashMap<Object, String>();
-            mCollator = Collator.getInstance();
-        }
-        public final int compare(Object a, Object b) {
-            String labelA, labelB;
-            if (mLabelCache.containsKey(a)) {
-                labelA = mLabelCache.get(a);
-            } else {
-                labelA = (a instanceof LauncherAppWidgetProviderInfo)
-                        ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
-                        : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
-                mLabelCache.put(a, labelA);
-            }
-            if (mLabelCache.containsKey(b)) {
-                labelB = mLabelCache.get(b);
-            } else {
-                labelB = (b instanceof LauncherAppWidgetProviderInfo)
-                        ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
-                        : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
-                mLabelCache.put(b, labelB);
-            }
-            return mCollator.compare(labelA, labelB);
-        }
-    };
 
     static boolean isValidProvider(AppWidgetProviderInfo provider) {
         return (provider != null) && (provider.provider != null)
diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java
new file mode 100644
index 0000000..706f751
--- /dev/null
+++ b/src/com/android/launcher3/model/AppNameComparator.java
@@ -0,0 +1,105 @@
+package com.android.launcher3.model;
+
+import android.content.Context;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.HashMap;
+
+/**
+ * Class to manage access to an app name comparator.
+ * <p>
+ * Used to sort application name in all apps view and widget tray view.
+ */
+public class AppNameComparator {
+    private final UserManagerCompat mUserManager;
+    private final Collator mCollator;
+    private final Comparator<ItemInfo> mAppInfoComparator;
+    private final Comparator<String> mSectionNameComparator;
+    private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
+
+    public AppNameComparator(Context context) {
+        mCollator = Collator.getInstance();
+        mUserManager = UserManagerCompat.getInstance(context);
+        mAppInfoComparator = new Comparator<ItemInfo>() {
+
+            public final int compare(ItemInfo a, ItemInfo b) {
+                // Order by the title in the current locale
+                int result = compareTitles(a.title.toString(), b.title.toString());
+                if (result == 0 && a instanceof AppInfo && b instanceof AppInfo) {
+                    AppInfo aAppInfo = (AppInfo) a;
+                    AppInfo bAppInfo = (AppInfo) b;
+                    // If two apps have the same title, then order by the component name
+                    result = aAppInfo.componentName.compareTo(bAppInfo.componentName);
+                    if (result == 0) {
+                        // If the two apps are the same component, then prioritize by the order that
+                        // the app user was created (prioritizing the main user's apps)
+                        if (UserHandleCompat.myUserHandle().equals(a.user)) {
+                            return -1;
+                        } else {
+                            Long aUserSerial = getAndCacheUserSerial(a.user);
+                            Long bUserSerial = getAndCacheUserSerial(b.user);
+                            return aUserSerial.compareTo(bUserSerial);
+                        }
+                    }
+                }
+                return result;
+            }
+        };
+        mSectionNameComparator = new Comparator<String>() {
+            @Override
+            public int compare(String o1, String o2) {
+                return compareTitles(o1, o2);
+            }
+        };
+    }
+
+    /**
+     * Returns a locale-aware comparator that will alphabetically order a list of applications.
+     */
+    public Comparator<ItemInfo> getAppInfoComparator() {
+        // Clear the user serial cache so that we get serials as needed in the comparator
+        mUserSerialCache.clear();
+        return mAppInfoComparator;
+    }
+
+    /**
+     * Returns a locale-aware comparator that will alphabetically order a list of section names.
+     */
+    public Comparator<String> getSectionNameComparator() {
+        return mSectionNameComparator;
+    }
+
+    /**
+     * Compares two titles with the same return value semantics as Comparator.
+     */
+    private int compareTitles(String titleA, String titleB) {
+        // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit
+        boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0));
+        boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0));
+        if (aStartsWithLetter && !bStartsWithLetter) {
+            return -1;
+        } else if (!aStartsWithLetter && bStartsWithLetter) {
+            return 1;
+        }
+
+        // Order by the title in the current locale
+        return mCollator.compare(titleA, titleB);
+    }
+
+    /**
+     * Returns the user serial for this user, using a cached serial if possible.
+     */
+    private Long getAndCacheUserSerial(UserHandleCompat user) {
+        Long userSerial = mUserSerialCache.get(user);
+        if (userSerial == null) {
+            userSerial = mUserManager.getSerialNumberForUser(user);
+            mUserSerialCache.put(user, userSerial);
+        }
+        return userSerial;
+    }
+}
diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
new file mode 100644
index 0000000..7c4e806
--- /dev/null
+++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
@@ -0,0 +1,63 @@
+package com.android.launcher3.model;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.HashMap;
+
+public class WidgetsAndShortcutNameComparator implements Comparator<Object> {
+    private final AppWidgetManagerCompat mManager;
+    private final PackageManager mPackageManager;
+    private final HashMap<Object, String> mLabelCache;
+    private final Collator mCollator;
+
+    public WidgetsAndShortcutNameComparator(Context context) {
+        mManager = AppWidgetManagerCompat.getInstance(context);
+        mPackageManager = context.getPackageManager();
+        mLabelCache = new HashMap<Object, String>();
+        mCollator = Collator.getInstance();
+    }
+
+    @Override
+    public final int compare(Object a, Object b) {
+        String labelA, labelB;
+        if (mLabelCache.containsKey(a)) {
+            labelA = mLabelCache.get(a);
+        } else {
+            labelA = (a instanceof LauncherAppWidgetProviderInfo)
+                    ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
+                    : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
+            mLabelCache.put(a, labelA);
+        }
+        if (mLabelCache.containsKey(b)) {
+            labelB = mLabelCache.get(b);
+        } else {
+            labelB = (b instanceof LauncherAppWidgetProviderInfo)
+                    ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
+                    : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
+            mLabelCache.put(b, labelB);
+        }
+        int result = mCollator.compare(labelA, labelB);
+        if (result == 0 && a instanceof AppWidgetProviderInfo &&
+                b instanceof AppWidgetProviderInfo) {
+            AppWidgetProviderInfo aInfo = (AppWidgetProviderInfo) a;
+            AppWidgetProviderInfo bInfo = (AppWidgetProviderInfo) b;
+
+            // prioritize main user's widgets against work profile widgets.
+            if (aInfo.getProfile().equals(android.os.Process.myUserHandle())) {
+                return -1;
+            } else if (bInfo.getProfile().equals(android.os.Process.myUserHandle())) {
+                return 1;
+            }
+        }
+        return result;
+    }
+};
diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java
index 71a7b94..5a920e8 100644
--- a/src/com/android/launcher3/widget/WidgetsModel.java
+++ b/src/com/android/launcher3/widget/WidgetsModel.java
@@ -10,8 +10,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel.WidgetAndShortcutNameComparator;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.model.AppNameComparator;
+import com.android.launcher3.model.WidgetsAndShortcutNameComparator;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,12 +41,14 @@
     private RecyclerView.Adapter mAdapter;
 
     private Comparator mWidgetAndShortcutNameComparator;
+    private Comparator mAppNameComparator;
 
     private IconCache mIconCache;
 
     public WidgetsModel(Context context, RecyclerView.Adapter adapter) {
         mAdapter = adapter;
-        mWidgetAndShortcutNameComparator = new WidgetAndShortcutNameComparator(context);
+        mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
+        mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
         mIconCache = LauncherAppState.getInstance().getIconCache();
     }
 
@@ -108,7 +111,7 @@
         }
 
         // sort.
-        sortPackageItemInfos();
+        Collections.sort(mPackageItemInfos, mAppNameComparator);
         for (PackageItemInfo p: mPackageItemInfos) {
             Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
         }
@@ -116,13 +119,4 @@
         // notify.
         mAdapter.notifyDataSetChanged();
     }
-
-    private void sortPackageItemInfos() {
-        Collections.sort(mPackageItemInfos, new Comparator<PackageItemInfo>() {
-            @Override
-            public int compare(PackageItemInfo lhs, PackageItemInfo rhs) {
-                return lhs.title.toString().compareTo(rhs.title.toString());
-            }
-        });
-    }
 }
\ No newline at end of file