Migrate Spinner on Battery Usage to settingsLib Spinner widget.

Bug: 359429437
Test: visual
Test: atest BatteryUsageBreakdownControllerTest
Flag: EXEMPT bug fix
Change-Id: I71bd1f31db302d407603c71d1a1163ed22fafed8
diff --git a/res/layout/preference_spinner.xml b/res/layout/preference_spinner.xml
deleted file mode 100644
index 4129303..0000000
--- a/res/layout/preference_spinner.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
-  ~ Copyright (C) 2023 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.
-  -->
-
-<Spinner
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/spinner"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_marginStart="24dp"
-    android:layout_marginTop="8dp"
-    android:theme="@style/Widget.PopupWindow.Settings" />
diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml
index 816e197..f2c3d87 100644
--- a/res/xml/power_usage_advanced.xml
+++ b/res/xml/power_usage_advanced.xml
@@ -57,7 +57,7 @@
             "com.android.settings.fuelgauge.batteryusage.BatteryUsageBreakdownController"
         settings:isPreferenceVisible="false">
 
-        <com.android.settings.fuelgauge.batteryusage.SpinnerPreference
+        <com.android.settingslib.widget.SettingsSpinnerPreference
             android:key="battery_usage_spinner"
             settings:isPreferenceVisible="false" />
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index f451f0a..09940b3 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.text.TextUtils;
@@ -46,9 +47,13 @@
 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.OnCreate;
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
 import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
 import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
+import com.android.settingslib.widget.SettingsSpinnerPreference;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -58,7 +63,7 @@
 
 /** Controller for battery usage breakdown preference group. */
 public class BatteryUsageBreakdownController extends BasePreferenceController
-        implements LifecycleObserver, OnResume, OnDestroy {
+        implements LifecycleObserver, OnResume, OnDestroy, OnCreate, OnSaveInstanceState {
     private static final String TAG = "BatteryUsageBreakdownController";
     private static final String ROOT_PREFERENCE_KEY = "battery_usage_breakdown";
     private static final String FOOTER_PREFERENCE_KEY = "battery_usage_footer";
@@ -67,6 +72,7 @@
     private static final String PACKAGE_NAME_NONE = "none";
     private static final String SLOT_TIMESTAMP = "slot_timestamp";
     private static final String ANOMALY_KEY = "anomaly_key";
+    private static final String KEY_SPINNER_POSITION = "spinner_position";
     private static final List<BatteryDiffEntry> EMPTY_ENTRY_LIST = new ArrayList<>();
 
     private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
@@ -78,12 +84,12 @@
 
     @VisibleForTesting final Map<String, Preference> mPreferenceCache = new ArrayMap<>();
 
-    private int mSpinnerPosition;
     private String mSlotInformation;
+    private SettingsSpinnerPreference mSpinnerPreference;
+    private SettingsSpinnerAdapter<CharSequence> mSpinnerAdapter;
 
     @VisibleForTesting Context mPrefContext;
     @VisibleForTesting PreferenceCategory mRootPreference;
-    @VisibleForTesting SpinnerPreference mSpinnerPreference;
     @VisibleForTesting PreferenceGroup mAppListPreferenceGroup;
     @VisibleForTesting FooterPreference mFooterPreference;
     @VisibleForTesting BatteryDiffData mBatteryDiffData;
@@ -92,6 +98,7 @@
     @VisibleForTesting String mPercentLessThanThresholdContentDescription;
     @VisibleForTesting boolean mIsHighlightSlot;
     @VisibleForTesting int mAnomalyKeyNumber;
+    @VisibleForTesting int mSpinnerPosition;
     @VisibleForTesting String mAnomalyEntryKey;
     @VisibleForTesting String mAnomalyHintString;
     @VisibleForTesting String mAnomalyHintPrefKey;
@@ -111,6 +118,15 @@
     }
 
     @Override
+    public void onCreate(Bundle savedInstanceState) {
+        if (savedInstanceState == null) {
+            return;
+        }
+        mSpinnerPosition = savedInstanceState.getInt(KEY_SPINNER_POSITION, mSpinnerPosition);
+        Log.d(TAG, "onCreate() spinnerPosition=" + mSpinnerPosition);
+    }
+
+    @Override
     public void onResume() {
         final int currentUiMode =
                 mContext.getResources().getConfiguration().uiMode
@@ -140,6 +156,15 @@
         return false;
     }
 
+    @Override
+    public void onSaveInstanceState(Bundle savedInstanceState) {
+        if (savedInstanceState == null) {
+            return;
+        }
+        savedInstanceState.putInt(KEY_SPINNER_POSITION, mSpinnerPosition);
+        Log.d(TAG, "onSaveInstanceState() spinnerPosition=" + mSpinnerPosition);
+    }
+
     private boolean isAnomalyBatteryDiffEntry(BatteryDiffEntry entry) {
         return mIsHighlightSlot
                 && mAnomalyEntryKey != null
@@ -218,11 +243,14 @@
                         formatPercentage);
 
         mAppListPreferenceGroup.setOrderingAsAdded(false);
-        mSpinnerPreference.initializeSpinner(
+        mSpinnerAdapter = new SettingsSpinnerAdapter<>(mPrefContext);
+        mSpinnerAdapter.addAll(
                 new String[] {
                     mPrefContext.getString(R.string.battery_usage_spinner_view_by_apps),
                     mPrefContext.getString(R.string.battery_usage_spinner_view_by_systems)
-                },
+                });
+        mSpinnerPreference.setAdapter(mSpinnerAdapter);
+        mSpinnerPreference.setOnItemSelectedListener(
                 new AdapterView.OnItemSelectedListener() {
                     @Override
                     public void onItemSelected(
@@ -244,6 +272,7 @@
                     @Override
                     public void onNothingSelected(AdapterView<?> parent) {}
                 });
+        mSpinnerPreference.setSelection(mSpinnerPosition);
     }
 
     /**
diff --git a/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreference.java b/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreference.java
deleted file mode 100644
index 886d00d..0000000
--- a/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreference.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2023 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.fuelgauge.batteryusage;
-
-import android.content.Context;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.AdapterView;
-import android.widget.Spinner;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settingslib.widget.SettingsSpinnerAdapter;
-
-/** A preference which contains a spinner. */
-public class SpinnerPreference extends Preference {
-    private static final String TAG = "SpinnerPreference";
-
-    private AdapterView.OnItemSelectedListener mOnItemSelectedListener;
-
-    @VisibleForTesting Spinner mSpinner;
-    @VisibleForTesting String[] mItems;
-    @VisibleForTesting int mSavedSpinnerPosition;
-
-    public SpinnerPreference(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setLayoutResource(R.layout.preference_spinner);
-    }
-
-    void initializeSpinner(
-            String[] items, AdapterView.OnItemSelectedListener onItemSelectedListener) {
-        mItems = items;
-        mOnItemSelectedListener = onItemSelectedListener;
-    }
-
-    @Override
-    public void onBindViewHolder(PreferenceViewHolder view) {
-        if (mSpinner != null) {
-            return;
-        }
-
-        mSpinner = (Spinner) view.findViewById(R.id.spinner);
-        mSpinner.setAdapter(new SpinnerAdapter(getContext(), mItems));
-        mSpinner.setSelection(mSavedSpinnerPosition);
-        mSpinner.setLongClickable(false);
-        if (mOnItemSelectedListener != null) {
-            mSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-        }
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        if (mSpinner == null) {
-            return super.onSaveInstanceState();
-        }
-        Log.d(TAG, "onSaveInstanceState() spinnerPosition=" + mSpinner.getSelectedItemPosition());
-        return new SavedState(super.onSaveInstanceState(), mSpinner.getSelectedItemPosition());
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (state == null || state == BaseSavedState.EMPTY_STATE) {
-            super.onRestoreInstanceState(state);
-            return;
-        }
-        if (!(state instanceof SavedState)) {
-            // To avoid the IllegalArgumentException, return the BaseSavedState.EMPTY_STATE.
-            super.onRestoreInstanceState(BaseSavedState.EMPTY_STATE);
-            return;
-        }
-        SavedState savedState = (SavedState) state;
-        super.onRestoreInstanceState(savedState.getSuperState());
-        mSavedSpinnerPosition = savedState.getSpinnerPosition();
-        if (mOnItemSelectedListener != null) {
-            mOnItemSelectedListener.onItemSelected(
-                    /* parent= */ null,
-                    /* view= */ null,
-                    savedState.getSpinnerPosition(),
-                    /* id= */ 0);
-        }
-        Log.d(TAG, "onRestoreInstanceState() spinnerPosition=" + savedState.getSpinnerPosition());
-    }
-
-    @VisibleForTesting
-    static class SavedState extends BaseSavedState {
-        private int mSpinnerPosition;
-
-        SavedState(Parcelable superState, int spinnerPosition) {
-            super(superState);
-            mSpinnerPosition = spinnerPosition;
-        }
-
-        int getSpinnerPosition() {
-            return mSpinnerPosition;
-        }
-    }
-
-    private static class SpinnerAdapter extends SettingsSpinnerAdapter<CharSequence> {
-        private final String[] mItems;
-
-        SpinnerAdapter(Context context, String[] items) {
-            super(context);
-            mItems = items;
-        }
-
-        @Override
-        public int getCount() {
-            return mItems.length;
-        }
-
-        @Override
-        public CharSequence getItem(int position) {
-            return mItems[position];
-        }
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
index 4c64808..85fc6e2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.LocaleList;
 import android.text.format.DateUtils;
 
@@ -57,6 +58,7 @@
     private static final String PREF_KEY = "pref_key";
     private static final String PREF_KEY2 = "pref_key2";
     private static final String PREF_SUMMARY = "fake preference summary";
+    private static final String KEY_SPINNER_POSITION = "spinner_position";
     private static final long TIME_LESS_THAN_HALF_MINUTE  = DateUtils.MINUTE_IN_MILLIS / 2  - 1;
 
     @Mock private InstrumentedPreferenceFragment mFragment;
@@ -149,6 +151,15 @@
     }
 
     @Test
+    public void onSaveInstanceState_returnExpectedResult() {
+        mBatteryUsageBreakdownController.mSpinnerPosition = 1;
+        final Bundle savedInstanceState = new Bundle();
+        mBatteryUsageBreakdownController.onSaveInstanceState(savedInstanceState);
+
+        assertThat(savedInstanceState.getInt(KEY_SPINNER_POSITION)).isEqualTo(1);
+    }
+
+    @Test
     public void addAllPreferences_addAllPreferences() {
         final String appLabel = "fake app label";
         doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreferenceTest.java
deleted file mode 100644
index 8050984..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/SpinnerPreferenceTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.fuelgauge.batteryusage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-
-import android.content.Context;
-import android.widget.Spinner;
-
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public final class SpinnerPreferenceTest {
-
-    private Context mContext;
-    private SpinnerPreference mSpinnerPreference;
-
-    @Mock private Spinner mMockSpinner;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(RuntimeEnvironment.application);
-        mSpinnerPreference = new SpinnerPreference(mContext, /* attrs= */ null);
-    }
-
-    @Test
-    public void constructor_returnExpectedResult() {
-        assertThat(mSpinnerPreference.getLayoutResource()).isEqualTo(R.layout.preference_spinner);
-    }
-
-    @Test
-    public void initializeSpinner_returnExpectedResult() {
-        final String[] items = new String[] {"item1", "item2"};
-        mSpinnerPreference.initializeSpinner(items, null);
-        assertThat(mSpinnerPreference.mItems).isEqualTo(items);
-    }
-
-    @Test
-    public void onSaveInstanceState_returnExpectedResult() {
-        doReturn(1).when(mMockSpinner).getSelectedItemPosition();
-        mSpinnerPreference.mSpinner = mMockSpinner;
-        SpinnerPreference.SavedState savedState =
-                (SpinnerPreference.SavedState) mSpinnerPreference.onSaveInstanceState();
-        assertThat(savedState.getSpinnerPosition()).isEqualTo(1);
-    }
-
-    @Test
-    public void onRestoreInstanceState_returnExpectedResult() {
-        SpinnerPreference.SavedState savedState =
-                new SpinnerPreference.SavedState(Preference.BaseSavedState.EMPTY_STATE, 2);
-        mSpinnerPreference.onRestoreInstanceState(savedState);
-        assertThat(mSpinnerPreference.mSavedSpinnerPosition).isEqualTo(2);
-    }
-}