Merge "[Settings] Code refactor for async Lifecycle listening" 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/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/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 a398c38..f87b489 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2931,6 +2931,8 @@
     <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>
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/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/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/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/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 1b4e998..36d3006 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
@@ -64,7 +64,6 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        use(SmartAutoRotateController.class).init(getLifecycle());
         DeviceStateAutoRotationHelper.initControllers(
                 getLifecycle(),
                 useAll(DeviceStateAutoRotateSettingController.class)
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/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;
+        }
+    }
+}