Merge "Import translations. DO NOT MERGE" into nyc-dev
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 94d7df8..0a072ae 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -130,4 +130,9 @@
     <declare-styleable name="WorkPreference">
         <attr name="forWork" format="boolean" />
     </declare-styleable>
+
+    <declare-styleable name="DividerPreference">
+        <attr name="allowDividerAbove" format="boolean" />
+        <attr name="allowDividerBelow" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/res/xml/battery_saver_settings.xml b/res/xml/battery_saver_settings.xml
index 832da58..630016f 100644
--- a/res/xml/battery_saver_settings.xml
+++ b/res/xml/battery_saver_settings.xml
@@ -15,6 +15,7 @@
 -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:settings="http://schemas.android.com/apk/res-auto"
         android:title="@string/battery_saver"
         android:key="battery_saver">
 
@@ -29,6 +30,7 @@
             android:key="description"
             android:summary="@*android:string/battery_saver_description"
             android:persistent="false"
-            android:selectable="false" />
+            android:selectable="false"
+            settings:allowDividerAbove="true" />
 
 </PreferenceScreen>
diff --git a/res/xml/data_usage.xml b/res/xml/data_usage.xml
index 0626da9..95e16ce 100644
--- a/res/xml/data_usage.xml
+++ b/res/xml/data_usage.xml
@@ -15,6 +15,7 @@
 -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:title="@string/data_usage_summary_title">
 
     <PreferenceCategory
@@ -25,9 +26,10 @@
             android:key="status_header"
             android:selectable="false" />
 
-        <Preference
+        <com.android.settings.DividerPreference
             android:key="limit_summary"
-            android:selectable="false" />
+            android:selectable="false"
+            settings:allowDividerBelow="true" />
 
         <com.android.settings.datausage.DataSaverPreference
             android:key="restrict_background"
diff --git a/res/xml/security_settings_encryption_interstitial.xml b/res/xml/security_settings_encryption_interstitial.xml
index 37ac815..576cbf0 100644
--- a/res/xml/security_settings_encryption_interstitial.xml
+++ b/res/xml/security_settings_encryption_interstitial.xml
@@ -19,7 +19,7 @@
     android:title="@string/lock_settings_picker_title"
     android:key="lock_settings_picker">
 
-    <Preference
+    <com.android.settings.MultiLinePreference
         android:icon="@drawable/ic_lock_list_icon"
         android:key="encrypt_require_password"
         android:persistent="false"/>
diff --git a/src/com/android/settings/DividedCategory.java b/src/com/android/settings/DividedCategory.java
new file mode 100644
index 0000000..d1c40c8
--- /dev/null
+++ b/src/com/android/settings/DividedCategory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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.settings;
+
+import android.content.Context;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+
+/**
+ * PreferenceCategory that allows a divider above it.
+ */
+public class DividedCategory extends PreferenceCategory {
+
+    public DividedCategory(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        holder.setDividerAllowedAbove(true);
+    }
+}
diff --git a/src/com/android/settings/DividerPreference.java b/src/com/android/settings/DividerPreference.java
new file mode 100644
index 0000000..e9ff7a7
--- /dev/null
+++ b/src/com/android/settings/DividerPreference.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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.settings;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+
+public class DividerPreference extends Preference {
+
+    private Boolean mAllowAbove;
+    private Boolean mAllowBelow;
+
+    public DividerPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DividerPreference, 0, 0);
+        if (a.hasValue(R.styleable.DividerPreference_allowDividerAbove)) {
+            mAllowAbove = a.getBoolean(R.styleable.DividerPreference_allowDividerAbove, false);
+        }
+        if (a.hasValue(R.styleable.DividerPreference_allowDividerBelow)) {
+            mAllowBelow = a.getBoolean(R.styleable.DividerPreference_allowDividerBelow, false);
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+        if (mAllowAbove != null) {
+            holder.setDividerAllowedAbove(mAllowAbove);
+        }
+        if (mAllowBelow != null) {
+            holder.setDividerAllowedBelow(mAllowBelow);
+        }
+    }
+}
diff --git a/src/com/android/settings/MultiLinePreference.java b/src/com/android/settings/MultiLinePreference.java
new file mode 100644
index 0000000..fff6b24
--- /dev/null
+++ b/src/com/android/settings/MultiLinePreference.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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.settings;
+
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+public class MultiLinePreference extends Preference {
+
+    public MultiLinePreference(Context ctx, AttributeSet attrs, int defStyle) {
+        super(ctx, attrs, defStyle);
+    }
+
+    public MultiLinePreference(Context ctx, AttributeSet attrs) {
+        super(ctx, attrs);
+    }
+
+    public MultiLinePreference(Context ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder view) {
+        super.onBindViewHolder(view);
+
+        TextView textView = (TextView) view.findViewById(android.R.id.title);
+        if (textView != null) {
+            textView.setSingleLine(false);
+        }
+    }
+}
diff --git a/src/com/android/settings/RingtonePreference.java b/src/com/android/settings/RingtonePreference.java
index 454ae0b..41318f0 100644
--- a/src/com/android/settings/RingtonePreference.java
+++ b/src/com/android/settings/RingtonePreference.java
@@ -49,8 +49,6 @@
 
     private static final String TAG = "RingtonePreference";
 
-    private static int sRequestCode = 100;
-
     private int mRingtoneType;
     private boolean mShowDefault;
     private boolean mShowSilent;
@@ -213,24 +211,18 @@
     }
     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
         super.onAttachedToHierarchy(preferenceManager);
-
-        mRequestCode = sRequestCode++;
     }
 
     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == mRequestCode) {
-            if (data != null) {
-                Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+        if (data != null) {
+            Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
 
-                if (callChangeListener(uri != null ? uri.toString() : "")) {
-                    onSaveRingtone(uri);
-                }
+            if (callChangeListener(uri != null ? uri.toString() : "")) {
+                onSaveRingtone(uri);
             }
-
-            return true;
         }
 
-        return false;
+        return true;
     }
 
 }
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 5a29cb7..4f7e15a 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -350,7 +350,7 @@
         pinnedHeader.addView(mSpinnerHeader, 0);
 
         mFilterAdapter.enableFilter(getDefaultFilter());
-        if (mListType == LIST_TYPE_MAIN || mListType == LIST_TYPE_NOTIFICATION) {
+        if (mListType == LIST_TYPE_MAIN) {
             if (UserManager.get(getActivity()).getUserProfiles().size() > 1) {
                 mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
                 mFilterAdapter.enableFilter(FILTER_APPS_WORK);
diff --git a/src/com/android/settings/datausage/TemplatePreferenceCategory.java b/src/com/android/settings/datausage/TemplatePreferenceCategory.java
index 0be5c73..1ce5f1c 100644
--- a/src/com/android/settings/datausage/TemplatePreferenceCategory.java
+++ b/src/com/android/settings/datausage/TemplatePreferenceCategory.java
@@ -17,10 +17,10 @@
 import android.content.Context;
 import android.net.NetworkTemplate;
 import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceCategory;
 import android.util.AttributeSet;
+import com.android.settings.DividedCategory;
 
-public class TemplatePreferenceCategory extends PreferenceCategory implements TemplatePreference {
+public class TemplatePreferenceCategory extends DividedCategory implements TemplatePreference {
 
     private NetworkTemplate mTemplate;
     private int mSubId;
diff --git a/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
index 7a805cb..5be9865 100644
--- a/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/fingerprint/FingerprintEnrollIntroduction.java
@@ -16,25 +16,21 @@
 
 package com.android.settings.fingerprint;
 
-import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
-import android.hardware.fingerprint.FingerprintManager;
 import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.text.Annotation;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
 import android.text.TextPaint;
-import android.text.method.LinkMovementMethod;
 import android.text.style.URLSpan;
 import android.util.Log;
 import android.view.View;
-import android.widget.AdapterView;
 
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.settings.ChooseLockGeneric;
diff --git a/src/com/android/settings/fuelgauge/WallOfTextPreference.java b/src/com/android/settings/fuelgauge/WallOfTextPreference.java
index 096f98a..6b29742 100644
--- a/src/com/android/settings/fuelgauge/WallOfTextPreference.java
+++ b/src/com/android/settings/fuelgauge/WallOfTextPreference.java
@@ -21,26 +21,14 @@
 import android.support.v7.preference.PreferenceViewHolder;
 import android.util.AttributeSet;
 import android.widget.TextView;
+import com.android.settings.DividerPreference;
 
-public class WallOfTextPreference extends Preference {
-
-    public WallOfTextPreference(Context context) {
-        super(context);
-    }
+public class WallOfTextPreference extends DividerPreference {
 
     public WallOfTextPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public WallOfTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    public WallOfTextPreference(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
     @Override
     public void onBindViewHolder(PreferenceViewHolder view) {
         super.onBindViewHolder(view);
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index 80870b4..7f590a1 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.inputmethod;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.LoaderManager;
 import android.content.AsyncTaskLoader;
@@ -33,11 +35,13 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
 import android.support.v7.preference.PreferenceCategory;
-import android.util.Pair;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
 import android.view.InputDevice;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
+
 import com.android.internal.inputmethod.InputMethodUtils;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
@@ -45,27 +49,33 @@
 import com.android.settings.Settings;
 import com.android.settings.SettingsPreferenceFragment;
 
+import libcore.util.Objects;
+
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.HashSet;
 
 public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
-        implements LoaderManager.LoaderCallbacks<PhysicalKeyboardFragment.Keyboards>,
-        InputManager.InputDeviceListener {
+        implements InputManager.InputDeviceListener {
 
     private static final int USER_SYSTEM = 0;
     private static final String KEYBOARD_ASSISTANCE_CATEGORY = "keyboard_assistance_category";
     private static final String SHOW_VIRTUAL_KEYBOARD_SWITCH = "show_virtual_keyboard_switch";
     private static final String IM_SUBTYPE_MODE_KEYBOARD = "keyboard";
 
-    private final HashMap<Integer, Pair<InputDeviceIdentifier, PreferenceCategory>> mLoaderReference
-            = new HashMap<>();
-    private final Map<InputMethodInfo, List<InputMethodSubtype>> mImiSubtypes = new HashMap<>();
+    @NonNull
+    private final ArrayList<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
+
+    @NonNull
+    private final HashSet<Integer> mLoaderIDs = new HashSet<>();
+    private int mNextLoaderId = 0;
+
     private InputManager mIm;
-    private InputMethodManager mImm;
+    @NonNull
     private PreferenceCategory mKeyboardAssistanceCategory;
+    @NonNull
     private SwitchPreference mShowVirtualKeyboardSwitch;
+    @NonNull
     private InputMethodUtils.InputMethodSettings mSettings;
 
     @Override
@@ -73,12 +83,11 @@
         Activity activity = Preconditions.checkNotNull(getActivity());
         addPreferencesFromResource(R.xml.physical_keyboard_settings);
         mIm = Preconditions.checkNotNull(activity.getSystemService(InputManager.class));
-        mImm = Preconditions.checkNotNull(activity.getSystemService(InputMethodManager.class));
         mSettings = new InputMethodUtils.InputMethodSettings(
                 activity.getResources(),
                 getContentResolver(),
-                new HashMap<String, InputMethodInfo>(),
-                new ArrayList<InputMethodInfo>(),
+                new HashMap<>(),
+                new ArrayList<>(),
                 USER_SYSTEM,
                 false /* copyOnWrite */);
         mKeyboardAssistanceCategory = Preconditions.checkNotNull(
@@ -91,6 +100,8 @@
     @Override
     public void onResume() {
         super.onResume();
+        clearLoader();
+        mLastHardKeyboards.clear();
         updateHardKeyboards();
         mIm.registerInputDeviceListener(this, null);
         mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(
@@ -101,26 +112,23 @@
     @Override
     public void onPause() {
         super.onPause();
-        clearHardKeyboardsData();
+        clearLoader();
+        mLastHardKeyboards.clear();
         mIm.unregisterInputDeviceListener(this);
         mShowVirtualKeyboardSwitch.setOnPreferenceChangeListener(null);
         unregisterShowVirtualKeyboardSettingsObserver();
     }
 
-    @Override
-    public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
-        final InputDeviceIdentifier deviceId = mLoaderReference.get(id).first;
-        return new KeyboardLayoutLoader(
-                getActivity().getBaseContext(), mIm, mImiSubtypes, deviceId);
-    }
+    public void onLoadFinishedInternal(final int loaderId, @NonNull final Keyboards data,
+            @NonNull final PreferenceCategory preferenceCategory) {
+        if (!mLoaderIDs.remove(loaderId)) {
+            // Already destroyed loader.  Ignore.
+            return;
+        }
 
-    @Override
-    public void onLoadFinished(Loader<Keyboards> loader, Keyboards data) {
-        // TODO: Investigate why this is being called twice.
-        final InputDeviceIdentifier deviceId = mLoaderReference.get(loader.getId()).first;
-        final PreferenceCategory category = mLoaderReference.get(loader.getId()).second;
-        category.removeAll();
-        for (Keyboards.KeyboardInfo info : data.mInfos) {
+        final InputDeviceIdentifier deviceId = data.mInputDeviceIdentifier;
+        preferenceCategory.removeAll();
+        for (Keyboards.KeyboardInfo info : data.mKeyboardInfoList) {
             Preference pref = new Preference(getPrefContext(), null);
             final InputMethodInfo imi = info.mImi;
             final InputMethodSubtype imSubtype = info.mImSubtype;
@@ -130,22 +138,16 @@
                 if (layout != null) {
                     pref.setSummary(layout.getLabel());
                 }
-                pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
-                    @Override
-                    public boolean onPreferenceClick(Preference preference) {
-                        showKeyboardLayoutScreen(deviceId, imi, imSubtype);
-                        return true;
-                    }
+                pref.setOnPreferenceClickListener(preference -> {
+                    showKeyboardLayoutScreen(deviceId, imi, imSubtype);
+                    return true;
                 });
-                category.addPreference(pref);
+                preferenceCategory.addPreference(pref);
             }
         }
     }
 
     @Override
-    public void onLoaderReset(Loader<Keyboards> loader) {}
-
-    @Override
     public void onInputDeviceAdded(int deviceId) {
         updateHardKeyboards();
     }
@@ -165,27 +167,42 @@
         return MetricsEvent.PHYSICAL_KEYBOARDS;
     }
 
-    private void updateHardKeyboards() {
-        clearHardKeyboardsData();
-        loadInputMethodInfoSubtypes();
-        final int[] devices = InputDevice.getDeviceIds();
-        for (int deviceIndex = 0; deviceIndex < devices.length; deviceIndex++) {
-            InputDevice device = InputDevice.getDevice(devices[deviceIndex]);
-            if (device != null
-                    && !device.isVirtual()
-                    && device.isFullKeyboard()) {
-                final PreferenceCategory category = new PreferenceCategory(getPrefContext(), null);
-                category.setTitle(device.getName());
-                category.setOrder(0);
-                mLoaderReference.put(deviceIndex, new Pair(device.getIdentifier(), category));
-                getPreferenceScreen().addPreference(category);
+    @NonNull
+    private static ArrayList<HardKeyboardDeviceInfo> getHardKeyboards() {
+        final ArrayList<HardKeyboardDeviceInfo> keyboards = new ArrayList<>();
+        final int[] devicesIds = InputDevice.getDeviceIds();
+        for (int deviceId : devicesIds) {
+            final InputDevice device = InputDevice.getDevice(deviceId);
+            if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
+                keyboards.add(new HardKeyboardDeviceInfo(device.getName(), device.getIdentifier()));
             }
         }
-        mKeyboardAssistanceCategory.setOrder(1);
-        getPreferenceScreen().addPreference(mKeyboardAssistanceCategory);
+        return keyboards;
+    }
 
-        for (int deviceIndex : mLoaderReference.keySet()) {
-            getLoaderManager().initLoader(deviceIndex, null, this);
+    private void updateHardKeyboards() {
+        final ArrayList<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards();
+        if (!Objects.equal(newHardKeyboards, mLastHardKeyboards)) {
+            clearLoader();
+            final PreferenceScreen preferenceScreen = getPreferenceScreen();
+            preferenceScreen.removeAll();
+            mLastHardKeyboards.clear();
+            mLastHardKeyboards.addAll(newHardKeyboards);
+            final int N = newHardKeyboards.size();
+            for (int i = 0; i < N; ++i) {
+                final HardKeyboardDeviceInfo deviceInfo = newHardKeyboards.get(i);
+                final PreferenceCategory category = new PreferenceCategory(getPrefContext(), null);
+                category.setTitle(deviceInfo.mDeviceName);
+                category.setOrder(0);
+                getLoaderManager().initLoader(mNextLoaderId, null,
+                        new Callbacks(getContext(), this, deviceInfo.mDeviceIdentifier, category));
+                mLoaderIDs.add(mNextLoaderId);
+                ++mNextLoaderId;
+                preferenceScreen.addPreference(category);
+            }
+            mKeyboardAssistanceCategory.setOrder(1);
+            preferenceScreen.addPreference(mKeyboardAssistanceCategory);
+
         }
         updateShowVirtualKeyboardSwitch();
     }
@@ -203,27 +220,11 @@
         startActivity(intent);
     }
 
-    private void clearHardKeyboardsData() {
-        getPreferenceScreen().removeAll();
-        for (int index = 0; index < mLoaderReference.size(); index++) {
-            getLoaderManager().destroyLoader(index);
+    private void clearLoader() {
+        for (final int loaderId : mLoaderIDs) {
+            getLoaderManager().destroyLoader(loaderId);
         }
-        mLoaderReference.clear();
-    }
-
-    private void loadInputMethodInfoSubtypes() {
-        mImiSubtypes.clear();
-        final List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
-        for (InputMethodInfo imi : imis) {
-            final List<InputMethodSubtype> subtypes = new ArrayList<>();
-            for (InputMethodSubtype subtype : mImm.getEnabledInputMethodSubtypeList(
-                    imi, true /* allowsImplicitlySelectedSubtypes */)) {
-                if (IM_SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
-                    subtypes.add(subtype);
-                }
-            }
-            mImiSubtypes.put(imi, subtypes);
-        }
+        mLoaderIDs.clear();
     }
 
     private void registerShowVirtualKeyboardSettingsObserver() {
@@ -260,44 +261,84 @@
         }
     };
 
+    @NonNull
     static String getDisplayName(
-            Context context, InputMethodInfo imi, InputMethodSubtype imSubtype) {
-        CharSequence imSubtypeName =  imSubtype.getDisplayName(
-                context, imi.getPackageName(),
-                imi.getServiceInfo().applicationInfo);
-        CharSequence imeName = imi.loadLabel(context.getPackageManager());
+            @NonNull Context context, @NonNull InputMethodInfo imi,
+            @NonNull InputMethodSubtype imSubtype) {
+        final CharSequence imSubtypeName = imSubtype.getDisplayName(
+                context, imi.getPackageName(), imi.getServiceInfo().applicationInfo);
+        final CharSequence imeName = imi.loadLabel(context.getPackageManager());
         return String.format(
                 context.getString(R.string.physical_device_title), imSubtypeName, imeName);
     }
 
-    private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
+    private static final class Callbacks
+            implements LoaderManager.LoaderCallbacks<PhysicalKeyboardFragment.Keyboards> {
+        @NonNull
+        final Context mContext;
+        @NonNull
+        final PhysicalKeyboardFragment mPhysicalKeyboardFragment;
+        @NonNull
+        final InputDeviceIdentifier mInputDeviceIdentifier;
+        @NonNull
+        final PreferenceCategory mPreferenceCategory;
+        public Callbacks(
+                @NonNull Context context,
+                @NonNull PhysicalKeyboardFragment physicalKeyboardFragment,
+                @NonNull InputDeviceIdentifier inputDeviceIdentifier,
+                @NonNull PreferenceCategory preferenceCategory) {
+            mContext = context;
+            mPhysicalKeyboardFragment = physicalKeyboardFragment;
+            mInputDeviceIdentifier = inputDeviceIdentifier;
+            mPreferenceCategory = preferenceCategory;
+        }
 
-        private final Map<InputMethodInfo, List<InputMethodSubtype>> mImiSubtypes;
+        @Override
+        public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
+            return new KeyboardLayoutLoader(mContext, mInputDeviceIdentifier);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<Keyboards> loader, Keyboards data) {
+            mPhysicalKeyboardFragment.onLoadFinishedInternal(loader.getId(), data,
+                    mPreferenceCategory);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<Keyboards> loader) {
+        }
+    }
+
+    private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
+        @NonNull
         private final InputDeviceIdentifier mInputDeviceIdentifier;
-        private final InputManager mIm;
 
         public KeyboardLayoutLoader(
-                Context context,
-                InputManager im,
-                Map<InputMethodInfo, List<InputMethodSubtype>> imiSubtypes,
-                InputDeviceIdentifier inputDeviceIdentifier) {
+                @NonNull Context context,
+                @NonNull InputDeviceIdentifier inputDeviceIdentifier) {
             super(context);
-            mIm = Preconditions.checkNotNull(im);
             mInputDeviceIdentifier = Preconditions.checkNotNull(inputDeviceIdentifier);
-            mImiSubtypes = new HashMap<>(imiSubtypes);
         }
 
         @Override
         public Keyboards loadInBackground() {
-            final Keyboards keyboards = new Keyboards();
-            for (InputMethodInfo imi : mImiSubtypes.keySet()) {
-                for (InputMethodSubtype subtype : mImiSubtypes.get(imi)) {
-                    final KeyboardLayout layout = mIm.getKeyboardLayoutForInputDevice(
-                            mInputDeviceIdentifier, imi, subtype);
-                    keyboards.mInfos.add(new Keyboards.KeyboardInfo(imi, subtype, layout));
+            final ArrayList<Keyboards.KeyboardInfo> keyboardInfoList = new ArrayList<>();
+            final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
+            final InputManager im = getContext().getSystemService(InputManager.class);
+            if (imm != null && im != null) {
+                for (InputMethodInfo imi : imm.getEnabledInputMethodList()) {
+                    for (InputMethodSubtype subtype : imm.getEnabledInputMethodSubtypeList(
+                            imi, true /* allowsImplicitlySelectedSubtypes */)) {
+                        if (!IM_SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
+                            continue;
+                        }
+                        final KeyboardLayout layout = im.getKeyboardLayoutForInputDevice(
+                                mInputDeviceIdentifier, imi, subtype);
+                        keyboardInfoList.add(new Keyboards.KeyboardInfo(imi, subtype, layout));
+                    }
                 }
             }
-            return keyboards;
+            return new Keyboards(mInputDeviceIdentifier, keyboardInfoList);
         }
 
         @Override
@@ -313,18 +354,70 @@
         }
     }
 
-    public static final class Keyboards {
+    public static final class HardKeyboardDeviceInfo {
+        @NonNull
+        public final String mDeviceName;
+        @NonNull
+        public final InputDeviceIdentifier mDeviceIdentifier;
 
-        public final ArrayList<KeyboardInfo> mInfos = new ArrayList<>();
+        public HardKeyboardDeviceInfo(
+                @Nullable final String deviceName,
+                @NonNull final InputDeviceIdentifier deviceIdentifier) {
+            mDeviceName = deviceName != null ? deviceName : "";
+            mDeviceIdentifier = deviceIdentifier;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) return true;
+            if (o == null) return false;
+
+            if (!(o instanceof HardKeyboardDeviceInfo)) return false;
+
+            final HardKeyboardDeviceInfo that = (HardKeyboardDeviceInfo) o;
+            if (!TextUtils.equals(mDeviceName, that.mDeviceName)) {
+                return false;
+            }
+            if (mDeviceIdentifier.getVendorId() != that.mDeviceIdentifier.getVendorId()) {
+                return false;
+            }
+            if (mDeviceIdentifier.getProductId() != that.mDeviceIdentifier.getProductId()) {
+                return false;
+            }
+            if (!TextUtils.equals(mDeviceIdentifier.getDescriptor(),
+                    that.mDeviceIdentifier.getDescriptor())) {
+                return false;
+            }
+
+            return true;
+        }
+    }
+
+    public static final class Keyboards {
+        @NonNull
+        public final InputDeviceIdentifier mInputDeviceIdentifier;
+        @NonNull
+        public final ArrayList<KeyboardInfo> mKeyboardInfoList;
+
+        public Keyboards(
+                @NonNull final InputDeviceIdentifier inputDeviceIdentifier,
+                @NonNull final ArrayList<KeyboardInfo> keyboardInfoList) {
+            mInputDeviceIdentifier = inputDeviceIdentifier;
+            mKeyboardInfoList = keyboardInfoList;
+        }
 
         public static final class KeyboardInfo {
-
+            @NonNull
             public final InputMethodInfo mImi;
+            @NonNull
             public final InputMethodSubtype mImSubtype;
+            @NonNull
             public final KeyboardLayout mLayout;
 
             public KeyboardInfo(
-                    InputMethodInfo imi, InputMethodSubtype imSubtype, KeyboardLayout layout) {
+                    @NonNull final InputMethodInfo imi,
+                    @NonNull final InputMethodSubtype imSubtype,
+                    @NonNull final KeyboardLayout layout) {
                 mImi = imi;
                 mImSubtype = imSubtype;
                 mLayout = layout;
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index 4a44a1d..566bb9a 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -48,7 +48,9 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
 import android.support.v7.preference.TwoStatePreference;
+import android.text.TextUtils;
 import android.util.Log;
+
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.RingtonePreference;
@@ -83,6 +85,9 @@
     private static final String KEY_ZEN_MODE = "zen_mode";
     private static final String KEY_CELL_BROADCAST_SETTINGS = "cell_broadcast_settings";
 
+    private static final String SELECTED_PREFERENCE_KEY = "selected_preference";
+    private static final int REQUEST_CODE = 200;
+
     private static final String[] RESTRICTED_KEYS = {
         KEY_MEDIA_VOLUME,
         KEY_ALARM_VOLUME,
@@ -175,6 +180,13 @@
         initVibrateWhenRinging();
         updateRingerMode();
         updateEffectsSuppressor();
+
+        if (savedInstanceState != null) {
+            String selectedPreference = savedInstanceState.getString(SELECTED_PREFERENCE_KEY, null);
+            if (!TextUtils.isEmpty(selectedPreference)) {
+                mRequestPreference = (RingtonePreference) findPreference(selectedPreference);
+            }
+        }
     }
 
     @Override
@@ -226,7 +238,7 @@
         if (preference instanceof RingtonePreference) {
             mRequestPreference = (RingtonePreference) preference;
             mRequestPreference.onPrepareRingtonePickerIntent(mRequestPreference.getIntent());
-            startActivityForResult(preference.getIntent(), mRequestPreference.getRequestCode());
+            startActivityForResult(preference.getIntent(), REQUEST_CODE);
             return true;
         }
         return super.onPreferenceTreeClick(preference);
@@ -240,6 +252,14 @@
         }
     }
 
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mRequestPreference != null) {
+            outState.putString(SELECTED_PREFERENCE_KEY, mRequestPreference.getKey());
+        }
+    }
+
     // === Volumes ===
 
     private VolumeSeekBarPreference initVolumePreference(String key, int stream, int muteIcon) {
diff --git a/src/com/android/settings/widget/LinkTextView.java b/src/com/android/settings/widget/LinkTextView.java
index ab72fcf..9142e39 100644
--- a/src/com/android/settings/widget/LinkTextView.java
+++ b/src/com/android/settings/widget/LinkTextView.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.support.annotation.NonNull;
+import android.text.Spanned;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.widget.TextView;
@@ -40,6 +43,18 @@
     }
 
     @Override
+    public void setText(CharSequence text, BufferType type) {
+        super.setText(text, type);
+        if (text instanceof Spanned) {
+            final ClickableSpan[] spans =
+                    ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+            if (spans.length > 0) {
+                setMovementMethod(LinkMovementMethod.getInstance());
+            }
+        }
+    }
+
+    @Override
     protected boolean dispatchHoverEvent(@NonNull MotionEvent event) {
         if (mAccessibilityHelper.dispatchHoverEvent(event)) {
             return true;