InputSettings: Define order of items in Physical keyboards screen.

Bug: 28158120
Change-Id: Ia7cec64edb9da53a2048865e317b2b9d4b2059b0
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 64d4834..ea4da30 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3669,7 +3669,7 @@
     <!--
         Format string for a physical device in the form: InputMethodSubtype - InputMethodEditor.
         e.g. English (US) - X Keyboard -->
-    <string name="physical_device_title"><xliff:g id="input_method_subtype" example="English (US)">%1$s</xliff:g> - <xliff:g id="input_method_editor" example="X Keyboard">%2$s</xliff:g></string>
+    <string name="physical_device_title" translatable="false"><xliff:g id="input_method_editor" example="X Keyboard">%1$s</xliff:g> - <xliff:g id="input_method_subtype" example="English (US)">%2$s</xliff:g></string>
 
     <!-- Summary text for keyboards when no layout has been selected. [CHAR LIMIT=35] -->
     <string name="default_keyboard_layout">Default</string>
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
index c0d67bb..0d4e173 100644
--- a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
+++ b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
@@ -34,6 +34,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.inputmethod.PhysicalKeyboardFragment.KeyboardInfoPreference;
 
 import java.util.Arrays;
 import java.util.HashMap;
@@ -153,7 +154,7 @@
             mPreferenceMap.put(pref, layout);
         }
 
-        root.setTitle(PhysicalKeyboardFragment.getDisplayName(getContext(), mImi, mSubtype));
+        root.setTitle(KeyboardInfoPreference.getDisplayName(getContext(), mImi, mSubtype));
         return root;
     }
 }
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index 4c5a731..de6c699 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -49,12 +49,13 @@
 import com.android.settings.Settings;
 import com.android.settings.SettingsPreferenceFragment;
 
-import libcore.util.Objects;
-
+import java.text.Collator;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 
 public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
         implements InputManager.InputDeviceListener {
@@ -67,6 +68,8 @@
 
     @NonNull
     private final List<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
+    @NonNull
+    private final List<KeyboardInfoPreference> mTempKeyboardInfoList = new ArrayList<>();
 
     @NonNull
     private final HashSet<Integer> mLoaderIDs = new HashSet<>();
@@ -136,6 +139,7 @@
             return;
         }
 
+        Collections.sort(keyboardsList);
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
         preferenceScreen.removeAll();
         for (Keyboards keyboards : keyboardsList) {
@@ -144,27 +148,26 @@
             category.setOrder(0);
             preferenceScreen.addPreference(category);
             for (Keyboards.KeyboardInfo info : keyboards.mKeyboardInfoList) {
-                Preference pref = new Preference(getPrefContext(), null);
+                mTempKeyboardInfoList.clear();
                 final InputMethodInfo imi = info.mImi;
                 final InputMethodSubtype imSubtype = info.mImSubtype;
                 if (imi != null) {
-                    pref.setTitle(getDisplayName(getContext(), imi, imSubtype));
-                    KeyboardLayout layout = info.mLayout;
-                    if (layout != null) {
-                        pref.setSummary(layout.getLabel());
-                    } else {
-                        pref.setSummary(
-                                getPrefContext().getString(R.string.default_keyboard_layout));
-                    }
+                    KeyboardInfoPreference pref =
+                            new KeyboardInfoPreference(getPrefContext(), info);
                     pref.setOnPreferenceClickListener(preference -> {
                         showKeyboardLayoutScreen(
                                 keyboards.mDeviceInfo.mDeviceIdentifier, imi, imSubtype);
                         return true;
                     });
+                    mTempKeyboardInfoList.add(pref);
+                    Collections.sort(mTempKeyboardInfoList);
+                }
+                for (KeyboardInfoPreference pref : mTempKeyboardInfoList) {
                     category.addPreference(pref);
                 }
             }
         }
+        mTempKeyboardInfoList.clear();
         mKeyboardAssistanceCategory.setOrder(1);
         preferenceScreen.addPreference(mKeyboardAssistanceCategory);
         updateShowVirtualKeyboardSwitch();
@@ -205,7 +208,7 @@
 
     private void updateHardKeyboards() {
         final ArrayList<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards();
-        if (!Objects.equal(newHardKeyboards, mLastHardKeyboards)) {
+        if (!Objects.equals(newHardKeyboards, mLastHardKeyboards)) {
             clearLoader();
             mLastHardKeyboards.clear();
             mLastHardKeyboards.addAll(newHardKeyboards);
@@ -274,20 +277,6 @@
         }
     };
 
-    @NonNull
-    static CharSequence getDisplayName(
-            @NonNull Context context, @NonNull InputMethodInfo imi,
-            @Nullable InputMethodSubtype imSubtype) {
-        final CharSequence imeName = imi.loadLabel(context.getPackageManager());
-        if (imSubtype == null) {
-            return imeName;
-        }
-        final CharSequence imSubtypeName = imSubtype.getDisplayName(
-                context, imi.getPackageName(), imi.getServiceInfo().applicationInfo);
-        return String.format(
-                context.getString(R.string.physical_device_title), imSubtypeName, imeName);
-    }
-
     private static final class Callbacks implements LoaderManager.LoaderCallbacks<List<Keyboards>> {
         @NonNull
         final Context mContext;
@@ -424,11 +413,13 @@
         }
     }
 
-    public static final class Keyboards {
+    public static final class Keyboards implements Comparable<Keyboards> {
         @NonNull
         public final HardKeyboardDeviceInfo mDeviceInfo;
         @NonNull
         public final ArrayList<KeyboardInfo> mKeyboardInfoList;
+        @NonNull
+        public final Collator mCollator = Collator.getInstance();
 
         public Keyboards(
                 @NonNull final HardKeyboardDeviceInfo deviceInfo,
@@ -437,6 +428,11 @@
             mKeyboardInfoList = keyboardInfoList;
         }
 
+        @Override
+        public int compareTo(@NonNull Keyboards another) {
+            return mCollator.compare(mDeviceInfo.mDeviceName, another.mDeviceInfo.mDeviceName);
+        }
+
         public static final class KeyboardInfo {
             @NonNull
             public final InputMethodInfo mImi;
@@ -456,4 +452,80 @@
         }
     }
 
+    static final class KeyboardInfoPreference extends Preference {
+
+        @NonNull
+        private final CharSequence mImeName;
+        @Nullable
+        private final CharSequence mImSubtypeName;
+        @NonNull
+        private final Collator collator = Collator.getInstance();
+
+        private KeyboardInfoPreference(
+                @NonNull Context context, @NonNull Keyboards.KeyboardInfo info) {
+            super(context);
+            mImeName = info.mImi.loadLabel(context.getPackageManager());
+            mImSubtypeName = getImSubtypeName(context, info.mImi, info.mImSubtype);
+            setTitle(formatDisplayName(context, mImeName, mImSubtypeName));
+            if (info.mLayout != null) {
+                setSummary(info.mLayout.getLabel());
+            }
+        }
+
+        @NonNull
+        static CharSequence getDisplayName(
+                @NonNull Context context, @NonNull InputMethodInfo imi,
+                @Nullable InputMethodSubtype imSubtype) {
+            final CharSequence imeName = imi.loadLabel(context.getPackageManager());
+            final CharSequence imSubtypeName = getImSubtypeName(context, imi, imSubtype);
+            return formatDisplayName(context, imeName, imSubtypeName);
+        }
+
+        private static CharSequence formatDisplayName(
+                @NonNull Context context,
+                @NonNull CharSequence imeName, @Nullable CharSequence imSubtypeName) {
+            if (imSubtypeName == null) {
+                return imeName;
+            }
+            return String.format(
+                    context.getString(R.string.physical_device_title), imeName, imSubtypeName);
+        }
+
+        @Nullable
+        private static CharSequence getImSubtypeName(
+                @NonNull Context context, @NonNull InputMethodInfo imi,
+                @Nullable InputMethodSubtype imSubtype) {
+            if (imSubtype != null) {
+                return imSubtype.getDisplayName(
+                        context, imi.getPackageName(), imi.getServiceInfo().applicationInfo);
+            }
+            return null;
+        }
+
+        @Override
+        public int compareTo(@NonNull Preference object) {
+            if (!(object instanceof KeyboardInfoPreference)) {
+                return super.compareTo(object);
+            }
+            KeyboardInfoPreference another = (KeyboardInfoPreference) object;
+            int result = compare(mImeName, another.mImeName);
+            if (result == 0) {
+                result = compare(mImSubtypeName, another.mImSubtypeName);
+            }
+            return result;
+        }
+
+        private int compare(@Nullable CharSequence lhs, @Nullable CharSequence rhs) {
+            if (!TextUtils.isEmpty(lhs) && !TextUtils.isEmpty(rhs)) {
+                return collator.compare(lhs.toString(), rhs.toString());
+            } else if (TextUtils.isEmpty(lhs) && TextUtils.isEmpty(rhs)) {
+                return 0;
+            } else if (!TextUtils.isEmpty(lhs)) {
+                return -1;
+            } else {
+                return 1;
+            }
+        }
+    }
+
 }