Merge "Remove invisible object in A11y Settings screens' menu" into nyc-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 64d4834..51c54c9 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>
@@ -7473,4 +7473,24 @@
     <!-- [CHAR LIMIT=NONE] The preference summary for enabling cross-profile remote contact search -->
     <string name="managed_profile_contact_search_summary">Allow contact searches by your organization to identify callers and contacts</string>
 
+    <!-- Message for telling the user the kind of BT device being displayed in list. -->
+    <string name="bluetooth_talkback_computer">Computer</string>
+
+    <!-- Message for telling the user the kind of BT device being displayed in list. -->
+    <string name="bluetooth_talkback_headset">Headset</string>
+
+    <!-- Message for telling the user the kind of BT device being displayed in list. -->
+    <string name="bluetooth_talkback_phone">Phone</string>
+
+    <!-- Message for telling the user the kind of BT device being displayed in list. -->
+    <string name="bluetooth_talkback_imaging">Imaging</string>
+
+    <!-- Message for telling the user the kind of BT device being displayed in list. -->
+    <string name="bluetooth_talkback_headphone">Headphone</string>
+
+    <!-- Message for telling the user the kind of BT device being displayed in list. -->
+    <string name="bluetooth_talkback_input_peripheral">Input Peripheral</string>
+
+    <!-- Message for telling the user the kind of BT device being displayed in list. -->
+    <string name="bluetooth_talkback_bluetooth">Bluetooth</string>
 </resources>
diff --git a/src/com/android/settings/ManagedProfileSetup.java b/src/com/android/settings/ManagedProfileSetup.java
index 677bdf1..ac012b0 100644
--- a/src/com/android/settings/ManagedProfileSetup.java
+++ b/src/com/android/settings/ManagedProfileSetup.java
@@ -32,6 +32,7 @@
 import static android.content.pm.PackageManager.GET_ACTIVITIES;
 import static android.content.pm.PackageManager.GET_META_DATA;
 import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 
 /**
  * Listens to {@link Intent.ACTION_BOOT_COMPLETED} and {@link Intent.ACTION_PRE_BOOT_COMPLETED}
@@ -63,7 +64,7 @@
 
         // Resolves activities for the managed profile (which we're running as)
         List<ResolveInfo> resolvedIntents = pm.queryIntentActivities(intent,
-                GET_ACTIVITIES | GET_META_DATA | GET_RESOLVED_FILTER);
+                GET_ACTIVITIES | GET_META_DATA | GET_RESOLVED_FILTER | MATCH_DISABLED_COMPONENTS);
         final int count = resolvedIntents.size();
         for (int i = 0; i < count; i++) {
             ResolveInfo info = resolvedIntents.get(i);
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 6d95351..a45e7b4 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -21,12 +21,14 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.res.Resources;
 import android.os.UserManager;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceViewHolder;
 import android.text.Html;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -59,6 +61,19 @@
 
     private AlertDialog mDisconnectDialog;
 
+    private String contentDescription = null;
+
+    /* Talk-back descriptions for various BT icons */
+    Resources r = getContext().getResources();
+    public final String COMPUTER =  r.getString(R.string.bluetooth_talkback_computer);
+    public final String INPUT_PERIPHERAL = r.getString(
+        R.string.bluetooth_talkback_input_peripheral);
+    public final String HEADSET = r.getString(R.string.bluetooth_talkback_headset);
+    public final String PHONE = r.getString(R.string.bluetooth_talkback_phone);
+    public final String IMAGING = r.getString(R.string.bluetooth_talkback_imaging);
+    public final String HEADPHONE = r.getString(R.string.bluetooth_talkback_headphone);
+    public final String BLUETOOTH = r.getString(R.string.bluetooth_talkback_bluetooth);
+
     public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
         super(context);
 
@@ -121,9 +136,11 @@
             setSummary(null);   // empty summary for unpaired devices
         }
 
-        int iconResId = getBtClassDrawable();
-        if (iconResId != 0) {
-            setIcon(iconResId);
+
+        Pair<Integer, String> pair = getBtClassDrawableWithDescription();
+        if (pair.first != 0) {
+            setIcon(pair.first);
+            contentDescription = pair.second;
         }
 
         // Used to gray out the item
@@ -148,7 +165,10 @@
                 deviceDetails.setTag(mCachedDevice);
             }
         }
-
+        final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
+        if (imageView != null) {
+            imageView.setContentDescription(contentDescription);
+        }
         super.onBindViewHolder(view);
     }
 
@@ -234,21 +254,22 @@
         }
     }
 
-    private int getBtClassDrawable() {
+    private Pair<Integer, String> getBtClassDrawableWithDescription() {
         BluetoothClass btClass = mCachedDevice.getBtClass();
         if (btClass != null) {
             switch (btClass.getMajorDeviceClass()) {
                 case BluetoothClass.Device.Major.COMPUTER:
-                    return R.drawable.ic_bt_laptop;
+                    return new Pair<Integer, String>(R.drawable.ic_bt_laptop, COMPUTER);
 
                 case BluetoothClass.Device.Major.PHONE:
-                    return R.drawable.ic_bt_cellphone;
+                    return new Pair<Integer, String>(R.drawable.ic_bt_cellphone, PHONE);
 
                 case BluetoothClass.Device.Major.PERIPHERAL:
-                    return HidProfile.getHidClassDrawable(btClass);
+                    return new Pair<Integer, String>(HidProfile.getHidClassDrawable(btClass),
+                                                     INPUT_PERIPHERAL);
 
                 case BluetoothClass.Device.Major.IMAGING:
-                    return R.drawable.ic_bt_imaging;
+                    return new Pair<Integer, String>(R.drawable.ic_bt_imaging, IMAGING);
 
                 default:
                     // unrecognized device class; continue
@@ -261,18 +282,17 @@
         for (LocalBluetoothProfile profile : profiles) {
             int resId = profile.getDrawableResource(btClass);
             if (resId != 0) {
-                return resId;
+                return new Pair<Integer, String>(resId, null);
             }
         }
         if (btClass != null) {
             if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
-                return R.drawable.ic_bt_headphones_a2dp;
-
+                return new Pair<Integer, String>(R.drawable.ic_bt_headphones_a2dp, HEADPHONE);
             }
             if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
-                return R.drawable.ic_bt_headset_hfp;
+                return new Pair<Integer, String>(R.drawable.ic_bt_headset_hfp, HEADSET);
             }
         }
-        return R.drawable.ic_settings_bluetooth;
+        return new Pair<Integer, String>(R.drawable.ic_settings_bluetooth, BLUETOOTH);
     }
 }
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index 0e44a9d..7e9be3f 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -40,8 +40,6 @@
 import android.util.ArraySet;
 import android.view.View;
 import android.widget.AdapterView;
-
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.settings.AppHeader;
 import com.android.settings.R;
@@ -52,7 +50,6 @@
 import com.android.settingslib.net.ChartDataLoader;
 import com.android.settingslib.net.UidDetailProvider;
 
-import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 
 public class AppDataUsage extends DataUsageBase implements Preference.OnPreferenceChangeListener,
@@ -209,7 +206,9 @@
     @Override
     public void onResume() {
         super.onResume();
-        mDataSaverBackend.addListener(this);
+        if (mDataSaverBackend != null) {
+            mDataSaverBackend.addListener(this);
+        }
         mPolicy = services.mPolicyEditor.getPolicy(mTemplate);
         getLoaderManager().restartLoader(LOADER_CHART_DATA,
                 ChartDataLoader.buildArgs(mTemplate, mAppItem), mChartDataCallbacks);
@@ -219,7 +218,9 @@
     @Override
     public void onPause() {
         super.onPause();
-        mDataSaverBackend.remListener(this);
+        if (mDataSaverBackend != null) {
+            mDataSaverBackend.remListener(this);
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/datausage/BillingCycleSettings.java b/src/com/android/settings/datausage/BillingCycleSettings.java
index be05af7..a17c8fc 100644
--- a/src/com/android/settings/datausage/BillingCycleSettings.java
+++ b/src/com/android/settings/datausage/BillingCycleSettings.java
@@ -173,11 +173,13 @@
 
 
             final LayoutInflater dialogInflater = LayoutInflater.from(context);
+            final boolean isLimit = getArguments().getBoolean(EXTRA_LIMIT);
             mView = dialogInflater.inflate(R.layout.data_usage_bytes_editor, null, false);
             setupPicker((EditText) mView.findViewById(R.id.bytes),
                     (Spinner) mView.findViewById(R.id.size_spinner));
             return new AlertDialog.Builder(context)
-                    .setTitle(R.string.data_usage_warning_editor_title)
+                    .setTitle(isLimit ? R.string.data_usage_limit_editor_title
+                            : R.string.data_usage_warning_editor_title)
                     .setView(mView)
                     .setPositiveButton(R.string.data_usage_cycle_editor_positive, this)
                     .create();
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;
+            }
+        }
+    }
+
 }