Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-dev
diff --git a/res/layout/accessibility_text_reading_reset_button.xml b/res/layout/accessibility_text_reading_reset_button.xml
index f3691e1..a0bdcba 100644
--- a/res/layout/accessibility_text_reading_reset_button.xml
+++ b/res/layout/accessibility_text_reading_reset_button.xml
@@ -27,14 +27,11 @@
         android:id="@+id/reset_button"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center|end"
+        android:layout_gravity="center_vertical|start"
         android:paddingVertical="14dp"
-        android:background="@null"
         android:drawableStart="@drawable/ic_history"
         android:drawablePadding="9dp"
-        android:drawableTint="?android:attr/colorAccent"
-        android:textColor="?android:attr/colorAccent"
-        android:textSize="16sp"
-        android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
-        android:text="@string/accessibility_text_reading_reset_button_title"/>
+        android:drawableTint="@color/settingslib_btn_colored_text_material"
+        android:text="@string/accessibility_text_reading_reset_button_title"
+        style="@style/ActionPrimaryButton"/>
 </FrameLayout>
diff --git a/res/layout/confirm_lock_password_base.xml b/res/layout/confirm_lock_password_base.xml
index 7c2fc5b..a3e5659 100644
--- a/res/layout/confirm_lock_password_base.xml
+++ b/res/layout/confirm_lock_password_base.xml
@@ -59,7 +59,7 @@
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_horizontal"
                 android:inputType="textPassword"
-                android:imeOptions="actionNext|flagForceAscii"
+                android:imeOptions="actionNext|flagForceAscii|flagNoExtractUi"
                 android:gravity="center"
                 style="@style/TextAppearance.PasswordEntry"/>
 
diff --git a/res/layout/confirm_lock_password_normal.xml b/res/layout/confirm_lock_password_normal.xml
index f2b8d1f..645c444 100644
--- a/res/layout/confirm_lock_password_normal.xml
+++ b/res/layout/confirm_lock_password_normal.xml
@@ -52,7 +52,7 @@
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_horizontal"
                 android:inputType="textPassword"
-                android:imeOptions="actionNext|flagForceAscii"
+                android:imeOptions="actionNext|flagForceAscii|flagNoExtractUi"
                 android:gravity="center"
                 style="@style/TextAppearance.PasswordEntry"/>
 
@@ -85,4 +85,4 @@
             android:text="@string/cancel" />
 
     </com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient>
-</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
+</com.google.android.setupdesign.GlifLayout>
diff --git a/res/layout/face_enroll_introduction.xml b/res/layout/face_enroll_introduction.xml
index 5e7a6a1..c497015 100644
--- a/res/layout/face_enroll_introduction.xml
+++ b/res/layout/face_enroll_introduction.xml
@@ -30,9 +30,7 @@
         android:layout_height="wrap_content"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:orientation="vertical"
-        android:layout_marginStart="?attr/sudMarginStart"
-        android:layout_marginEnd="?attr/sudMarginEnd">
+        android:orientation="vertical">
 
         <com.google.android.setupdesign.view.RichTextView
             android:id="@+id/error_text"
diff --git a/res/layout/fingerprint_enroll_introduction.xml b/res/layout/fingerprint_enroll_introduction.xml
index aaed5be..5b4560e 100644
--- a/res/layout/fingerprint_enroll_introduction.xml
+++ b/res/layout/fingerprint_enroll_introduction.xml
@@ -30,9 +30,7 @@
         android:layout_height="wrap_content"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:orientation="vertical"
-        android:layout_marginStart="?attr/sudMarginStart"
-        android:layout_marginEnd="?attr/sudMarginEnd">
+        android:orientation="vertical">
 
         <com.google.android.setupdesign.view.RichTextView
             android:id="@+id/error_text"
diff --git a/res/layout/icon_discrete_slider.xml b/res/layout/icon_discrete_slider.xml
index b1d960f..47be5ef 100644
--- a/res/layout/icon_discrete_slider.xml
+++ b/res/layout/icon_discrete_slider.xml
@@ -19,6 +19,7 @@
     android:id="@+id/seekbar_frame"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:clipChildren="false"
     android:background="?android:colorBackground"
     android:gravity="center_vertical">
 
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 37e71e2..5248da6 100755
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -18,8 +18,6 @@
     <dimen name="appwidget_width">421dip</dimen>
     <dimen name="appwidget_height">68dip</dimen>
 
-    <dimen name="captioning_preview_height">100dp</dimen>
-
     <!-- ActionBar height -->
     <dimen name="actionbar_size">46dip</dimen>
 
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
index 4e4a1d3..015a6af 100755
--- a/res/values-sw600dp-land/dimens.xml
+++ b/res/values-sw600dp-land/dimens.xml
@@ -19,8 +19,6 @@
     <dimen name="screen_margin_top">72dip</dimen>
     <dimen name="screen_margin_bottom">48dip</dimen>
 
-    <dimen name="captioning_preview_height">150dp</dimen>
-
     <dimen name="confirm_credentials_top_padding">20dp</dimen>
     <dimen name="confirm_credentials_top_margin">24dp</dimen>
 
diff --git a/res/values/config.xml b/res/values/config.xml
index 6b42baa..c7ef595 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -71,9 +71,6 @@
     <!-- If the support features are enabled. -->
     <bool name="config_support_enabled">false</bool>
 
-    <!-- Whether to enable "show operator name in the status bar" setting -->
-    <bool name="config_showOperatorNameInStatusBar">false</bool>
-
     <!-- List containing the component names of pre-installed screen reader services. -->
     <string-array name="config_preinstalled_screen_reader_services" translatable="false">
         <!--
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b67bb22..c80e42c 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -49,7 +49,7 @@
     <dimen name="pager_tabs_title_padding">16dp</dimen>
     <dimen name="pager_tabs_selected_indicator_height">3dp</dimen>
 
-    <dimen name="captioning_preview_height">200dp</dimen>
+    <dimen name="captioning_preview_height">300dp</dimen>
 
     <dimen name="autoclick_preview_height">200dp</dimen>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cfcb62e..f87b489 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -98,10 +98,10 @@
     <string name="font_size_make_larger_desc">Make larger</string>
 
     <!-- Auto rotate switchbar title. [CHAR_LIMIT=NONE] -->
-    <string name="auto_rotate_settings_primary_switch_title">Use Auto-rotate</string>
+    <string name="auto_rotate_settings_primary_switch_title">Use auto-rotate</string>
 
     <!-- Disclaimer for camera based rotate [CHAR_LIMIT=NONE] -->
-    <string name="smart_rotate_text_headline">Face Detection uses the front-facing camera to improve Auto-rotate accuracy. Images are never stored or sent to Google.</string>
+    <string name="smart_rotate_text_headline">Face Detection uses the front-facing camera to improve auto-rotate accuracy. Images are never stored or sent to Google.</string>
     <string name="font_size_preview_text_headline">Sample text</string>
     <string name="font_size_preview_text_title"
             translation_description="Title text in sample text used to illustrate how the currently selected font size will appear to the user. NOTE: Translate manually. No not adopt any copyrighted material for translation.">
@@ -2926,7 +2926,13 @@
     <!-- Sound & display settings screen, face-based rotation check box label [CHAR LIMIT=30] -->
     <string name="auto_rotate_option_face_based">On - Face-based</string>
     <!-- SmartAutoRotatePreferenceFragment settings screen, face-based rotation switch label [CHAR LIMIT=30] -->
-    <string name="auto_rotate_switch_face_based">Enable Face Detection</string>
+    <string name="auto_rotate_switch_face_based">Face Detection</string>
+    <!-- Preference summary to enable auto rotate[CHAR_LIMIT=NONE]-->
+    <string name="auto_rotate_screen_summary">Automatically adjust the screen orientation when you move your phone between portrait and landscape</string>
+    <!-- Accessibility description for auto rotate learn more link [CHAR LIMIT=NONE] -->
+    <string name="auto_rotate_link_a11y">Learn more about auto-rotate</string>
+    <!-- Accessibility summary text for auto rotate [CHAR LIMIT=60] -->
+    <string name="auto_rotate_summary_a11y">When you move your phone between portrait and landscape</string>
 
     <!-- Display settings screen, screen resolution settings title [CHAR LIMIT=30] -->
     <string name="screen_resolution_title">Screen resolution</string>
@@ -5302,6 +5308,8 @@
     <string name="accessibility_text_reading_preview_mail_content">Good morning!\n\nI just wanted to check how the designs are coming. Will they be ready before we start building the new balloons?</string>
     <!-- Title for the reset button of the accessibility text reading page to reset all preferences state. [CHAR LIMIT=25] -->
     <string name="accessibility_text_reading_reset_button_title">Reset settings</string>
+    <!-- Message for the toast after clicking the reset button of the dialog of the accessibility text reading page. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_text_reading_reset_message">Display size and text settings have been reset</string>
     <!-- Title for the confirm dialog of reset settings. [CHAR LIMIT=NONE] -->
     <string name="accessibility_text_reading_confirm_dialog_title">Reset display size and text?</string>
     <!-- Message for the confirm dialog of reset settings. [CHAR LIMIT=NONE] -->
@@ -12453,6 +12461,9 @@
     <!-- Help URI, battery saver page [DO NOT TRANSLATE] -->
     <string name="help_url_battery_saver_settings" translatable="false"></string>
 
+    <!-- Help URI, auto rotate page [DO NOT TRANSLATE] -->
+    <string name="help_url_auto_rotate_settings" translatable="false"></string>
+
     <!-- Help URI, app usage page [DO NOT TRANSLATE] -->
     <string name="help_url_app_usage_settings" translatable="false"></string>
 
diff --git a/res/values/themes_suw.xml b/res/values/themes_suw.xml
index f0be252..79adbd8 100644
--- a/res/values/themes_suw.xml
+++ b/res/values/themes_suw.xml
@@ -229,8 +229,6 @@
         <item name="android:clipToPadding">true</item>
         <item name="android:clipChildren">true</item>
         <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item>
-        <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item>
-        <item name="android:listPreferredItemPaddingEnd">16dp</item>
         <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item>
         <item name="android:switchStyle">@style/Switch.SettingsLib</item>
         <item name="preferenceFragmentCompatStyle">@style/SetupWizardPreferenceFragmentStyle</item>
diff --git a/res/xml/accessibility_text_reading_options.xml b/res/xml/accessibility_text_reading_options.xml
index ad742c9..7fd5ff9 100644
--- a/res/xml/accessibility_text_reading_options.xml
+++ b/res/xml/accessibility_text_reading_options.xml
@@ -27,7 +27,7 @@
 
     <com.android.settings.widget.LabeledSeekBarPreference
         android:key="font_size"
-        android:selectable="false"
+        android:selectable="true"
         android:summary="@string/short_summary_font_size"
         android:title="@string/title_font_size"
         settings:iconEnd="@drawable/ic_add_24dp"
@@ -38,7 +38,7 @@
 
     <com.android.settings.widget.LabeledSeekBarPreference
         android:key="display_size"
-        android:selectable="false"
+        android:selectable="true"
         android:summary="@string/screen_zoom_short_summary"
         android:title="@string/screen_zoom_title"
         settings:iconEnd="@drawable/ic_add_24dp"
diff --git a/res/xml/auto_rotate_settings.xml b/res/xml/auto_rotate_settings.xml
index 0c120d6..38aa5f0 100644
--- a/res/xml/auto_rotate_settings.xml
+++ b/res/xml/auto_rotate_settings.xml
@@ -20,6 +20,14 @@
     xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:title="@string/accelerometer_title" >
 
+    <com.android.settingslib.widget.TopIntroPreference
+        android:title="@string/auto_rotate_screen_summary"/>
+
+    <com.android.settingslib.widget.MainSwitchPreference
+        android:key="auto_rotate_main_switch"
+        android:title="@string/auto_rotate_settings_primary_switch_title"
+        settings:controller="com.android.settings.display.AutoRotateSwitchBarController"/>
+
     <SwitchPreference
         android:key="auto_rotate_switch"
         android:title="@string/auto_rotate_settings_primary_switch_title"
@@ -48,8 +56,9 @@
         settings:controller="com.android.settings.display.SmartAutoRotateController" />
 
     <com.android.settingslib.widget.FooterPreference
-        android:icon="@drawable/ic_privacy_shield_24dp"
+        android:key="auto_rotate_footer_preference"
+        android:title="@string/smart_rotate_text_headline"
         android:selectable="false"
-        settings:searchable="false" />
+        settings:searchable="false"/>
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/CaptionAppearanceFragment.java b/src/com/android/settings/accessibility/CaptionAppearanceFragment.java
index 13055be..53c1dd0 100644
--- a/src/com/android/settings/accessibility/CaptionAppearanceFragment.java
+++ b/src/com/android/settings/accessibility/CaptionAppearanceFragment.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.accessibility;
 
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
 import android.app.settings.SettingsEnums;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -400,6 +402,7 @@
         }
 
         refreshPreviewText();
+        enableCaptioningManager();
     }
 
     @Override
@@ -409,16 +412,26 @@
             Settings.Secure.putString(
                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE, (String) value);
             refreshPreviewText();
+            enableCaptioningManager();
         } else if (mFontSize == preference) {
             Settings.Secure.putFloat(
                     cr, Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
                     Float.parseFloat((String) value));
             refreshPreviewText();
+            enableCaptioningManager();
         }
 
         return true;
     }
 
+    private void enableCaptioningManager() {
+        if (mCaptioningManager.isEnabled()) {
+            return;
+        }
+        Settings.Secure.putInt(getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, ON);
+    }
+
     @Override
     public int getHelpResource() {
         return R.string.help_url_caption;
diff --git a/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java b/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java
index 1148584..a4ee961c 100644
--- a/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/TextReadingPreferenceFragment.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.widget.Toast;
 
 import androidx.appcompat.app.AlertDialog;
 
@@ -178,6 +179,9 @@
         } else {
             mResetStateListeners.forEach(ResetStateListener::resetState);
         }
+
+        Toast.makeText(getPrefContext(), R.string.accessibility_text_reading_reset_message,
+                Toast.LENGTH_SHORT).show();
     }
 
     private List<ResetStateListener> getResetStateListeners() {
diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
index 71f47cc..06f8276 100644
--- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
@@ -51,6 +51,11 @@
 
     @Override
     protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
+        final boolean isEnabled = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;
+        if (enabled == isEnabled) {
+            return;
+        }
+
         if (enabled) {
             showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
         }
diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
index ac4408f..208d5c1 100644
--- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
@@ -166,6 +166,11 @@
 
     @Override
     protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
+        final boolean isEnabled = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON;
+        if (enabled == isEnabled) {
+            return;
+        }
+
         if (enabled) {
             showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
         }
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index f0809ad..b7f8909 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -479,7 +479,7 @@
     @Override
     protected void refresh() {
         for (LocalBluetoothProfile profile : getProfiles()) {
-            if (!profile.isProfileReady()) {
+            if (profile != null && !profile.isProfileReady()) {
                 continue;
             }
             SwitchPreference pref = mProfilesContainer.findPreference(
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
index 8eb71df..96acd43 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogFragment.java
@@ -22,6 +22,7 @@
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AlertDialog;
@@ -75,7 +76,12 @@
         mRootView = LayoutInflater.from(builder.getContext())
                 .inflate(R.layout.dialog_sim_status, null /* parent */);
         mController.initialize();
-        return builder.setView(mRootView).create();
+
+        Dialog dlg = builder.setView(mRootView).create();
+        dlg.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
+                WindowManager.LayoutParams.FLAG_SECURE);
+
+        return dlg;
     }
 
     @Override
@@ -94,7 +100,7 @@
     /**
      * View ID(s) which is digit format (instead of decimal number) text.
      **/
-    private static final int [] sViewIdsInDigitFormat = IntStream
+    private static final int[] sViewIdsInDigitFormat = IntStream
             .of(SimStatusDialogController.ICCID_INFO_VALUE_ID,
                     SimStatusDialogController.PHONE_NUMBER_VALUE_ID,
                     SimStatusDialogController.EID_INFO_VALUE_ID)
@@ -107,8 +113,7 @@
         }
         if (TextUtils.isEmpty(text)) {
             text = getResources().getString(R.string.device_info_default);
-        }
-        else if (Arrays.binarySearch(sViewIdsInDigitFormat, viewId) >= 0) {
+        } else if (Arrays.binarySearch(sViewIdsInDigitFormat, viewId) >= 0) {
             text = PhoneNumberUtil.expandByTts(text);
         }
         textView.setText(text);
diff --git a/src/com/android/settings/deviceinfo/storage/ManageStoragePreferenceController.java b/src/com/android/settings/deviceinfo/storage/ManageStoragePreferenceController.java
index 1a60cb8..9613b55 100644
--- a/src/com/android/settings/deviceinfo/storage/ManageStoragePreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/ManageStoragePreferenceController.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
+import android.text.TextUtils;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
@@ -65,6 +66,10 @@
 
     @Override
     public boolean handlePreferenceTreeClick(Preference preference) {
+        if (!TextUtils.equals(getPreferenceKey(), preference.getKey())) {
+            return super.handlePreferenceTreeClick(preference);
+        }
+
         final MetricsFeatureProvider metricsFeatureProvider =
                 FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
         metricsFeatureProvider.action(mContext, SettingsEnums.STORAGE_FREE_UP_SPACE_NOW);
diff --git a/src/com/android/settings/display/AutoRotateSwitchBarController.java b/src/com/android/settings/display/AutoRotateSwitchBarController.java
index 48dedfd..d76104a 100644
--- a/src/com/android/settings/display/AutoRotateSwitchBarController.java
+++ b/src/com/android/settings/display/AutoRotateSwitchBarController.java
@@ -18,86 +18,78 @@
 
 import android.app.settings.SettingsEnums;
 import android.content.Context;
-import android.widget.Switch;
 
 import com.android.internal.view.RotationPolicy;
+import com.android.settings.R;
 import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settings.widget.SettingsMainSwitchPreferenceController;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
-import com.android.settingslib.widget.OnMainSwitchChangeListener;
 
 /**
- * The switch controller for auto-rotate.
+ * The main switch controller for auto-rotate.
  */
-public class AutoRotateSwitchBarController implements OnMainSwitchChangeListener,
+public class AutoRotateSwitchBarController extends SettingsMainSwitchPreferenceController implements
         LifecycleObserver, OnStart, OnStop {
 
-    private final SettingsMainSwitchBar mSwitchBar;
-    private final Context mContext;
-    private boolean mValidListener;
     private final MetricsFeatureProvider mMetricsFeatureProvider;
+    private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
 
-    public AutoRotateSwitchBarController(Context context, SettingsMainSwitchBar switchBar,
-            Lifecycle lifecycle) {
-        mSwitchBar = switchBar;
-        mContext = context;
+    public AutoRotateSwitchBarController(Context context, String key) {
+        super(context, key);
         mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
-        if (lifecycle != null) {
-            lifecycle.addObserver(this);
-        }
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return RotationPolicy.isRotationLockToggleVisible(mContext)
+                && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
+                ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
     }
 
     @Override
     public void onStart() {
-        if (!mValidListener) {
-            mSwitchBar.addOnSwitchChangeListener(this);
-            mValidListener = true;
+        if (mRotationPolicyListener == null) {
+            mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() {
+                @Override
+                public void onChange() {
+                    if (mSwitchPreference != null) {
+                        updateState(mSwitchPreference);
+                    }
+                }
+            };
         }
-        onChange();
+        RotationPolicy.registerRotationPolicyListener(mContext,
+                mRotationPolicyListener);
     }
 
     @Override
     public void onStop() {
-        if (mValidListener) {
-            mSwitchBar.removeOnSwitchChangeListener(this);
-            mValidListener = false;
+        if (mRotationPolicyListener != null) {
+            RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
         }
     }
 
-    /**
-     * Listens to the state change of the rotation primary switch.
-     */
     @Override
-    public void onSwitchChanged(Switch switchView, boolean isChecked) {
-        setRotationLock(isChecked);
+    public boolean isChecked() {
+        return !RotationPolicy.isRotationLocked(mContext);
     }
 
-
-    protected void onChange() {
-        final boolean isEnabled = !RotationPolicy.isRotationLocked(mContext);
-        if (isEnabled != mSwitchBar.isChecked()) {
-            // set listener to null so that that code below doesn't trigger onCheckedChanged()
-            if (mValidListener) {
-                mSwitchBar.removeOnSwitchChangeListener(this);
-            }
-            mSwitchBar.setChecked(isEnabled);
-            if (mValidListener) {
-                mSwitchBar.addOnSwitchChangeListener(this);
-            }
-        }
-    }
-
-    private boolean setRotationLock(boolean isChecked) {
+    @Override
+    public boolean setChecked(boolean isChecked) {
         final boolean isLocked = !isChecked;
         mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATE_ROTATE_MASTER_TOGGLE,
-                isChecked);
+                isLocked);
         RotationPolicy.setRotationLock(mContext, isLocked);
         return true;
     }
 
+    @Override
+    public int getSliceHighlightMenuRes() {
+        return R.string.menu_key_display;
+    }
+
 }
 
diff --git a/src/com/android/settings/display/SmartAutoRotateController.java b/src/com/android/settings/display/SmartAutoRotateController.java
index d29a64e..093c845 100644
--- a/src/com/android/settings/display/SmartAutoRotateController.java
+++ b/src/com/android/settings/display/SmartAutoRotateController.java
@@ -35,7 +35,6 @@
 import android.service.rotationresolver.RotationResolverService;
 import android.text.TextUtils;
 
-import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.OnLifecycleEvent;
 import androidx.preference.Preference;
@@ -82,10 +81,6 @@
                 context);
     }
 
-    public void init(Lifecycle lifecycle) {
-        lifecycle.addObserver(this);
-    }
-
     @Override
     public int getAvailabilityStatus() {
         if (!isRotationResolverServiceAvailable(mContext)) {
diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
index 9fda03c..36d3006 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
@@ -20,7 +20,7 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Bundle;
-import android.text.Html;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -28,12 +28,11 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 
-import com.android.internal.view.RotationPolicy;
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.HelpUtils;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.Indexable;
 import com.android.settingslib.search.SearchIndexable;
@@ -51,9 +50,11 @@
 
     private static final String TAG = "SmartAutoRotatePreferenceFragment";
 
-    private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
-    private AutoRotateSwitchBarController mSwitchBarController;
-    @VisibleForTesting static final String AUTO_ROTATE_SWITCH_PREFERENCE_ID = "auto_rotate_switch";
+    @VisibleForTesting
+    static final String AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY = "auto_rotate_main_switch";
+    @VisibleForTesting
+    static final String AUTO_ROTATE_SWITCH_PREFERENCE_KEY = "auto_rotate_switch";
+    private static final String KEY_FOOTER_PREFERENCE = "auto_rotate_footer_preference";
 
     @Override
     protected int getPreferenceScreenResId() {
@@ -63,7 +64,6 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        use(SmartAutoRotateController.class).init(getLifecycle());
         DeviceStateAutoRotationHelper.initControllers(
                 getLifecycle(),
                 useAll(DeviceStateAutoRotateSettingController.class)
@@ -81,11 +81,10 @@
         final View view = super.onCreateView(inflater, container, savedInstanceState);
         final SettingsActivity activity = (SettingsActivity) getActivity();
         createHeader(activity);
-        final Preference footerPreference = findPreference(FooterPreference.KEY_FOOTER);
+        final Preference footerPreference = findPreference(KEY_FOOTER_PREFERENCE);
         if (footerPreference != null) {
-            footerPreference.setTitle(Html.fromHtml(getString(R.string.smart_rotate_text_headline),
-                    Html.FROM_HTML_MODE_COMPACT));
             footerPreference.setVisible(isRotationResolverServiceAvailable(activity));
+            setupFooter();
         }
         return view;
     }
@@ -95,39 +94,9 @@
         boolean deviceStateRotationEnabled =
                 DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(activity);
         if (isRotationResolverServiceAvailable(activity) && !deviceStateRotationEnabled) {
-            final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
-            switchBar.setTitle(
-                    getContext().getString(R.string.auto_rotate_settings_primary_switch_title));
-            switchBar.show();
-            mSwitchBarController = new AutoRotateSwitchBarController(activity, switchBar,
-                    getSettingsLifecycle());
-            findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID).setVisible(false);
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mRotationPolicyListener == null) {
-            mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() {
-                @Override
-                public void onChange() {
-                    if (mSwitchBarController != null) {
-                        mSwitchBarController.onChange();
-                    }
-                }
-            };
-        }
-        RotationPolicy.registerRotationPolicyListener(getPrefContext(),
-                mRotationPolicyListener);
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        if (mRotationPolicyListener != null) {
-            RotationPolicy.unregisterRotationPolicyListener(getPrefContext(),
-                    mRotationPolicyListener);
+            findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_KEY).setVisible(false);
+        } else {
+            findPreference(AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY).setVisible(false);
         }
     }
 
@@ -141,6 +110,34 @@
         return TAG;
     }
 
+    @Override
+    public int getHelpResource() {
+        return R.string.help_url_auto_rotate_settings;
+    }
+
+    // Updates the footer for this page.
+    @VisibleForTesting
+    void setupFooter() {
+        final String mHelpUri = getString(getHelpResource());
+        if (!TextUtils.isEmpty(mHelpUri)) {
+            addHelpLink();
+        }
+    }
+
+    // Changes the text to include a learn more link if the link is defined.
+    @VisibleForTesting
+    void addHelpLink() {
+        final FooterPreference pref = findPreference(KEY_FOOTER_PREFERENCE);
+        if (pref != null) {
+            pref.setLearnMoreAction(v -> {
+                startActivityForResult(HelpUtils.getHelpIntent(getContext(),
+                        getString(getHelpResource()),
+                        /*backupContext=*/ ""), /*requestCode=*/ 0);
+            });
+            pref.setLearnMoreContentDescription(getString(R.string.auto_rotate_link_a11y));
+        }
+    }
+
     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
             new BaseSearchIndexProvider(R.xml.auto_rotate_settings) {
 
diff --git a/src/com/android/settings/network/helper/LifecycleCallbackAdapter.java b/src/com/android/settings/network/helper/LifecycleCallbackAdapter.java
new file mode 100644
index 0000000..548eae5
--- /dev/null
+++ b/src/com/android/settings/network/helper/LifecycleCallbackAdapter.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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.network.helper;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A {@link androidx.lifecycle.LifecycleObserver} implementation of adapter over callback.
+ *
+ * Which including:
+ * 1. Request to active callback when Lifecycle.State.STARTED
+ * 2. Request to inactive callback when Lifecycle.State.STOPPED
+ * 3. Close (no further resume) when Lifecycle.State.DESTROYED
+ */
+@VisibleForTesting
+abstract class LifecycleCallbackAdapter implements LifecycleEventObserver, AutoCloseable {
+    private static final String TAG = "LifecycleCallbackAdapter";
+    private AtomicReference<Lifecycle> mLifecycle = new AtomicReference<Lifecycle>();
+
+    /**
+     * Constructor
+     * @param lifecycle {@link Lifecycle} to monitor
+     */
+    @VisibleForTesting
+    protected LifecycleCallbackAdapter(@NonNull Lifecycle lifecycle) {
+        mLifecycle.set(lifecycle);
+        lifecycle.addObserver(this);
+    }
+
+    /**
+     * Get {@link Lifecycle} under monitor.
+     * @return {@link Lifecycle}. Return {@code null} when closed.
+     */
+    @VisibleForTesting
+    public Lifecycle getLifecycle() {
+        return mLifecycle.get();
+    }
+
+    /**
+     * Check current callback status.
+     * @return true when callback is active.
+     */
+    public abstract boolean isCallbackActive();
+
+    /**
+     * Change callback status.
+     * @param isActive true to active callback, otherwise inactive.
+     */
+    public abstract void setCallbackActive(boolean isActive);
+
+    /**
+     * Implementation of LifecycleEventObserver.
+     */
+    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
+        if (mLifecycle.get() == null) {
+            return;
+        }
+
+        Lifecycle.State state = event.getTargetState();
+        boolean expectCallbackActive = state.isAtLeast(Lifecycle.State.STARTED);
+        if (expectCallbackActive != isCallbackActive()) {
+            setCallbackActive(expectCallbackActive);
+        }
+        if (state == Lifecycle.State.DESTROYED) {
+            close();
+        }
+    }
+
+    /**
+     * Implementation of AutoCloseable.
+     */
+    @MainThread
+    public void close() {
+        Lifecycle lifecycle = mLifecycle.getAndSet(null);
+        if (lifecycle != null) {
+            lifecycle.removeObserver(this);
+        }
+    }
+}
diff --git a/src/com/android/settings/network/helper/LifecycleCallbackConverter.java b/src/com/android/settings/network/helper/LifecycleCallbackConverter.java
new file mode 100644
index 0000000..f35b69a
--- /dev/null
+++ b/src/com/android/settings/network/helper/LifecycleCallbackConverter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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.network.helper;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.Lifecycle;
+
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+
+/**
+ * A {@link LifecycleCallbackAdapter} which support carrying a result from any threads back to UI
+ * thread through {@link #postResult(T)}.
+ *
+ * A {@link Consumer<T>} would be invoked from UI thread for further processing on the result.
+ *
+ * Note: Result not in STARTED or RESUMED stage will be discarded silently.
+ *       This is to align with the criteria set within
+ *       {@link LifecycleCallbackAdapter#onStateChanged()}.
+ */
+@VisibleForTesting
+public class LifecycleCallbackConverter<T> extends LifecycleCallbackAdapter {
+    private static final String TAG = "LifecycleCallbackConverter";
+
+    private final Thread mUiThread;
+    private final Consumer<T> mResultCallback;
+
+    /**
+     * A record of number of active status change.
+     * Even numbers (0, 2, 4, 6 ...) are inactive status.
+     * Odd numbers (1, 3, 5, 7 ...) are active status.
+     */
+    private final AtomicLong mNumberOfActiveStatusChange = new AtomicLong();
+
+    /**
+     * Constructor
+     *
+     * @param lifecycle {@link Lifecycle} to monitor
+     * @param resultCallback for further processing the result
+     */
+    @VisibleForTesting
+    @UiThread
+    public LifecycleCallbackConverter(
+            @NonNull Lifecycle lifecycle, @NonNull Consumer<T> resultCallback) {
+        super(lifecycle);
+        mUiThread = Thread.currentThread();
+        mResultCallback = resultCallback;
+    }
+
+    /**
+     * Post a result (from any thread) back to UI thread.
+     *
+     * @param result the object ready to be passed back to {@link Consumer<T>}.
+     */
+    @AnyThread
+    @VisibleForTesting
+    public void postResult(T result) {
+        /**
+         * Since mNumberOfActiveStatusChange only increase, it is a concept of sequence number.
+         * Carry it when sending data in between different threads allow to verify if the data
+         * has arrived on time. And drop the data when expired.
+         */
+        long currentNumberOfChange = mNumberOfActiveStatusChange.get();
+        if (Thread.currentThread() == mUiThread) {
+            dispatchExtResult(currentNumberOfChange, result); // Dispatch directly
+        } else {
+            postResultToUiThread(currentNumberOfChange, result);
+        }
+    }
+
+    @AnyThread
+    protected void postResultToUiThread(long numberOfStatusChange, T result) {
+        ThreadUtils.postOnMainThread(() -> dispatchExtResult(numberOfStatusChange, result));
+    }
+
+    @UiThread
+    protected void dispatchExtResult(long numberOfStatusChange, T result) {
+        /**
+         * For a postResult() sending in between different threads, not only create a latency
+         * but also enqueued into main UI thread for dispatch.
+         *
+         * To align behavior within {@link LifecycleCallbackAdapter#onStateChanged()},
+         * some checking on both numberOfStatusChange and {@link Lifecycle} status are required.
+         */
+        if (isActiveStatus(numberOfStatusChange)
+                && (numberOfStatusChange == mNumberOfActiveStatusChange.get())
+                && getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
+            mResultCallback.accept(result);
+        }
+    }
+
+    private static final boolean isActiveStatus(long numberOfStatusChange) {
+        return ((numberOfStatusChange & 1L) != 0L);
+    }
+
+    /* Implementation of LifecycleCallbackAdapter */
+    @UiThread
+    public boolean isCallbackActive() {
+        return isActiveStatus(mNumberOfActiveStatusChange.get());
+    }
+
+    /* Implementation of LifecycleCallbackAdapter */
+    @UiThread
+    public void setCallbackActive(boolean updatedActiveStatus) {
+        /**
+         * Make sure only increase when active status got changed.
+         * This is to implement the definition of mNumberOfActiveStatusChange.
+         */
+        if (isCallbackActive() != updatedActiveStatus) {
+            mNumberOfActiveStatusChange.getAndIncrement();
+        }
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java
index 1793cc2..b72cdf7 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreferenceFragmentTest.java
@@ -47,6 +47,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowToast;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -104,4 +105,18 @@
         verify(listener1).resetState();
         verify(listener2).resetState();
     }
+
+    @Test
+    public void onDialogPositiveButtonClicked_boldTextEnabled_showToast() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.FONT_WEIGHT_ADJUSTMENT, BOLD_TEXT_ADJUSTMENT);
+        final AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(
+                DialogEnums.DIALOG_RESET_SETTINGS);
+        dialog.show();
+
+        dialog.getButton(DialogInterface.BUTTON_POSITIVE).callOnClick();
+
+        assertThat(ShadowToast.getTextOfLatestToast())
+                .isEqualTo(mContext.getString(R.string.accessibility_text_reading_reset_message));
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
index 942fed6..e537419 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
@@ -16,7 +16,8 @@
 
 package com.android.settings.display;
 
-import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_ID;
+import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY;
+import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_KEY;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -45,7 +46,6 @@
 import com.android.settings.testutils.ResolveInfoBuilder;
 import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
 import com.android.settings.testutils.shadow.ShadowRotationPolicy;
-import com.android.settings.widget.SettingsMainSwitchBar;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
 
@@ -71,8 +71,6 @@
 
     private SmartAutoRotatePreferenceFragment mFragment;
 
-    private SettingsMainSwitchBar mSwitchBar;
-
     @Mock
     private PackageManager mPackageManager;
 
@@ -87,6 +85,9 @@
     private Resources mResources;
     private Context mContext;
 
+    @Mock
+    private Preference mRotateMainSwitchPreference;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -112,21 +113,21 @@
         when(mFragment.getContext()).thenReturn(mContext);
         doReturn(mView).when(mFragment).getView();
 
-        when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID)).thenReturn(
+        when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_KEY)).thenReturn(
                 mRotateSwitchPreference);
 
-        mSwitchBar = spy(new SettingsMainSwitchBar(mContext));
-        when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
-        doReturn(mSwitchBar).when(mView).findViewById(R.id.switch_bar);
-        ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
+
+        when(mFragment.findPreference(AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY)).thenReturn(
+                mRotateMainSwitchPreference);
     }
 
 
     @Test
     public void createHeader_faceDetectionSupported_switchBarIsEnabled() {
+        ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
         mFragment.createHeader(mActivity);
 
-        verify(mSwitchBar, times(1)).show();
+        verify(mRotateMainSwitchPreference, never()).setVisible(false);
         verify(mRotateSwitchPreference, times(1)).setVisible(false);
     }
 
@@ -137,7 +138,7 @@
 
         mFragment.createHeader(mActivity);
 
-        verify(mSwitchBar, never()).show();
+        verify(mRotateMainSwitchPreference, times(1)).setVisible(false);
         verify(mRotateSwitchPreference, never()).setVisible(false);
     }
 
@@ -147,7 +148,7 @@
 
         mFragment.createHeader(mActivity);
 
-        verify(mSwitchBar, never()).show();
+        verify(mRotateMainSwitchPreference, times(1)).setVisible(false);
         verify(mRotateSwitchPreference, never()).setVisible(false);
     }
 
@@ -176,6 +177,19 @@
                 DeviceStateAutoRotateSettingController.class);
     }
 
+    @Test
+    public void setupFooter_linkAddedWhenAppropriate() {
+        doReturn("").when(mFragment).getText(anyInt());
+        doReturn("").when(mFragment).getString(anyInt());
+        mFragment.setupFooter();
+        verify(mFragment, never()).addHelpLink();
+
+        doReturn("testString").when(mFragment).getText(anyInt());
+        doReturn("testString").when(mFragment).getString(anyInt());
+        mFragment.setupFooter();
+        verify(mFragment, times(1)).addHelpLink();
+    }
+
     private void enableDeviceStateSettableRotationStates(String[] settableStates,
             String[] settableStatesDescriptions) {
         when(mResources.getStringArray(
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/ManageStoragePreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/ManageStoragePreferenceControllerTest.java
new file mode 100644
index 0000000..6806bdd
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/ManageStoragePreferenceControllerTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.deviceinfo.storage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.widget.CardPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class ManageStoragePreferenceControllerTest {
+
+    private ManageStoragePreferenceController mController;
+    private CardPreference mPreference;
+
+    @Before
+    public void setUp() {
+        Context context = ApplicationProvider.getApplicationContext();
+        mPreference = new CardPreference(context);
+        mController = new ManageStoragePreferenceController(context, "free_up_space");
+    }
+
+    @Test
+    public void handPreferenceTreeClick_keyMatched_consumeClickEvent() {
+        mPreference.setKey(mController.getPreferenceKey());
+
+        assertTrue(mController.handlePreferenceTreeClick(mPreference));
+    }
+
+    @Test
+    public void handPreferenceTreeClick_keyNotMatched_notConsumeClickEvent() {
+        mPreference.setKey("not_matched_key");
+
+        assertFalse(mController.handlePreferenceTreeClick(mPreference));
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackAdapterTest.java b/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackAdapterTest.java
new file mode 100644
index 0000000..ddeb3f8
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackAdapterTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 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.network.helper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class LifecycleCallbackAdapterTest implements LifecycleOwner {
+
+    private final LifecycleRegistry mRegistry = LifecycleRegistry.createUnsafe(this);
+
+    private TestObj mTarget;
+
+    @Before
+    public void setUp() {
+        mTarget = new TestObj(getLifecycle());
+    }
+
+    public Lifecycle getLifecycle() {
+        return mRegistry;
+    }
+
+    @Test
+    public void lifecycle_get_lifecycleToMonitor() {
+        assertThat(mTarget.getLifecycle()).isEqualTo(mRegistry);
+    }
+
+    @Test
+    public void lifecycle_stateChangeToStart_callbackActive() {
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+
+        assertThat(mTarget.getCallbackCount()).isEqualTo(0);
+        assertThat(mTarget.isCallbackActive()).isEqualTo(Boolean.FALSE);
+
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+
+        assertThat(mTarget.getCallbackCount()).isEqualTo(1);
+        assertThat(mTarget.isCallbackActive()).isEqualTo(Boolean.TRUE);
+    }
+
+    @Test
+    public void lifecycle_stateChangeToStop_callbackInActive() {
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+        assertThat(mTarget.getCallbackCount()).isEqualTo(2);
+        assertThat(mTarget.isCallbackActive()).isEqualTo(Boolean.FALSE);
+    }
+
+    @Test
+    public void lifecycle_stateChangeToDestroy_noFurtherActive() {
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
+
+        assertThat(mTarget.getCallbackCount()).isEqualTo(2);
+        assertThat(mTarget.isCallbackActive()).isEqualTo(Boolean.FALSE);
+
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+
+        assertThat(mTarget.getCallbackCount()).isEqualTo(2);
+        assertThat(mTarget.isCallbackActive()).isEqualTo(Boolean.FALSE);
+    }
+
+    public static class TestObj extends LifecycleCallbackAdapter {
+        boolean mIsActive;
+        int mNumberOfCallback;
+
+        public TestObj(Lifecycle lifecycle) {
+            super(lifecycle);
+        }
+
+        public boolean isCallbackActive() {
+            return mIsActive;
+        }
+
+        public void setCallbackActive(boolean isActive) {
+            mIsActive = isActive;
+            mNumberOfCallback ++;
+        }
+
+        protected int getCallbackCount() {
+            return mNumberOfCallback;
+        }
+    }
+}
diff --git a/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackConverterTest.java b/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackConverterTest.java
new file mode 100644
index 0000000..149fd8293
--- /dev/null
+++ b/tests/unit/src/com/android/settings/network/helper/LifecycleCallbackConverterTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 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.network.helper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.LifecycleRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Phaser;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+@RunWith(AndroidJUnit4.class)
+public class LifecycleCallbackConverterTest implements LifecycleOwner {
+
+    private final LifecycleRegistry mRegistry = LifecycleRegistry.createUnsafe(this);
+
+    private Object mTestData;
+    private TestConsumer mConsumer;
+    private LifecycleCallbackConverter<Object> mConverter;
+
+    @Before
+    public void setUp() {
+        mTestData = new Object();
+        mConsumer = new TestConsumer();
+        mConverter = new LifecycleCallbackConverter<Object>(getLifecycle(), mConsumer);
+    }
+
+    public Lifecycle getLifecycle() {
+        return mRegistry;
+    }
+
+    @Test
+    public void converter_dropResult_whenInActive() {
+        mConverter.postResult(mTestData);
+
+        assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void converter_callbackResult_whenActive() {
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+
+        mConverter.postResult(mTestData);
+        assertThat(mConsumer.getCallbackCount()).isEqualTo(1);
+        assertThat(mConsumer.getData()).isEqualTo(mTestData);
+    }
+
+    @Test
+    public void converter_dropResult_whenBackToInActive() {
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+
+        mConverter.postResult(mTestData);
+        assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void converter_passResultToUiThread_whenActive() {
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+        mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+
+        final Phaser phaser = new Phaser(1);
+        Thread executionThread = new Thread(() -> {
+            mConverter.postResult(phaser);
+        });
+        executionThread.start();
+        phaser.awaitAdvance(0);
+
+        assertThat(mConsumer.getData()).isEqualTo(phaser);
+        assertThat(mConsumer.getCallbackCount()).isEqualTo(1);
+    }
+
+    public static class TestConsumer implements Consumer<Object> {
+        long mNumberOfCallback;
+        AtomicReference<Object> mLatestData;
+
+        public TestConsumer() {
+            mLatestData = new AtomicReference<Object>();
+        }
+
+        public void accept(Object data) {
+            mLatestData.set(data);
+            mNumberOfCallback ++;
+            if ((data != null) && (data instanceof Phaser)) {
+                ((Phaser)data).arrive();
+            }
+        }
+
+        protected long getCallbackCount() {
+            return mNumberOfCallback;
+        }
+
+        protected Object getData() {
+            return mLatestData.get();
+        }
+    }
+}