diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java index a441449ba55..e6be684196d 100644 --- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java +++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarController.java @@ -20,6 +20,8 @@ import android.content.Context; import android.os.PowerManager; import android.provider.Settings; import android.provider.Settings.Global; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; import androidx.preference.Preference; import androidx.preference.Preference.OnPreferenceChangeListener; @@ -42,22 +44,27 @@ import com.android.settings.widget.SeekBarPreference; * See {@link Settings.Global#AUTOMATIC_POWER_SAVE_MODE} for more details. */ public class BatterySaverScheduleSeekBarController implements - OnPreferenceChangeListener { + OnPreferenceChangeListener, OnSeekBarChangeListener { public static final int MAX_SEEKBAR_VALUE = 15; public static final int MIN_SEEKBAR_VALUE = 2; public static final String KEY_BATTERY_SAVER_SEEK_BAR = "battery_saver_seek_bar"; + private static final int LEVEL_UNIT_SCALE = 5; @VisibleForTesting public SeekBarPreference mSeekBarPreference; private Context mContext; + @VisibleForTesting + int mPercentage; + public BatterySaverScheduleSeekBarController(Context context) { mContext = context; mSeekBarPreference = new SeekBarPreference(context); mSeekBarPreference.setLayoutResource(R.layout.preference_widget_seekbar_settings); mSeekBarPreference.setIconSpaceReserved(false); mSeekBarPreference.setOnPreferenceChangeListener(this); + mSeekBarPreference.setOnSeekBarChangeListener(this); mSeekBarPreference.setContinuousUpdates(true); mSeekBarPreference.setMax(MAX_SEEKBAR_VALUE); mSeekBarPreference.setMin(MIN_SEEKBAR_VALUE); @@ -68,16 +75,28 @@ public class BatterySaverScheduleSeekBarController implements @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - // The nits are in intervals of 5% - final int percentage = ((Integer) newValue) * 5; - Settings.Global.putInt(mContext.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, - percentage); - final CharSequence stateDescription = formatStateDescription(percentage); + mPercentage = ((Integer) newValue) * LEVEL_UNIT_SCALE; + final CharSequence stateDescription = formatStateDescription(mPercentage); preference.setTitle(stateDescription); mSeekBarPreference.overrideSeekBarStateDescription(stateDescription); return true; } + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {} + + @Override + public void onStartTrackingTouch(SeekBar seekBar) {} + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + if (mPercentage > 0) { + Settings.Global.putInt(mContext.getContentResolver(), + Global.LOW_POWER_MODE_TRIGGER_LEVEL, + mPercentage); + } + } + public void updateSeekBar() { final ContentResolver resolver = mContext.getContentResolver(); // Note: this can also be obtained via PowerManager.getPowerSaveModeTrigger() diff --git a/src/com/android/settings/widget/SeekBarPreference.java b/src/com/android/settings/widget/SeekBarPreference.java index e4a37e3493f..30884eebada 100644 --- a/src/com/android/settings/widget/SeekBarPreference.java +++ b/src/com/android/settings/widget/SeekBarPreference.java @@ -64,6 +64,7 @@ public class SeekBarPreference extends RestrictedPreference private CharSequence mOverrideSeekBarStateDescription; private CharSequence mSeekBarContentDescription; private CharSequence mSeekBarStateDescription; + private OnSeekBarChangeListener mOnSeekBarChangeListener; public SeekBarPreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { @@ -101,6 +102,14 @@ public class SeekBarPreference extends RestrictedPreference this(context, null); } + /** + * A callback that notifies clients when the seekbar progress level has been + * changed. See {@link OnSeekBarChangeListener} for more info. + */ + public void setOnSeekBarChangeListener(OnSeekBarChangeListener listener) { + mOnSeekBarChangeListener = listener; + } + public void setShouldBlink(boolean shouldBlink) { mShouldBlink = shouldBlink; notifyChanged(); @@ -301,6 +310,9 @@ public class SeekBarPreference extends RestrictedPreference if (fromUser && (mContinuousUpdates || !mTrackingTouch)) { syncProgress(seekBar); } + if (mOnSeekBarChangeListener != null) { + mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser); + } } @Override @@ -309,6 +321,9 @@ public class SeekBarPreference extends RestrictedPreference mJankMonitor.begin(InteractionJankMonitor.Configuration.Builder .withView(CUJ_SETTINGS_SLIDER, seekBar) .setTag(getKey())); + if (mOnSeekBarChangeListener != null) { + mOnSeekBarChangeListener.onStartTrackingTouch(seekBar); + } } @Override @@ -317,6 +332,9 @@ public class SeekBarPreference extends RestrictedPreference if (seekBar.getProgress() != mProgress) { syncProgress(seekBar); } + if (mOnSeekBarChangeListener != null) { + mOnSeekBarChangeListener.onStopTrackingTouch(seekBar); + } mJankMonitor.end(CUJ_SETTINGS_SLIDER); } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java index b05d06d9d8e..9de3ada629a 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/batterysaver/BatterySaverScheduleSeekBarControllerTest.java @@ -12,6 +12,7 @@ import android.content.Context; import android.os.PowerManager; import android.provider.Settings; import android.provider.Settings.Global; +import android.widget.SeekBar; import androidx.preference.PreferenceScreen; @@ -46,22 +47,47 @@ public class BatterySaverScheduleSeekBarControllerTest { } @Test - public void onPreferenceChange_updatesSettingsGlobal() { + public void onPreferenceChange_withoutOnStopTrackingTouch_updatesTitleAndDescriptionOnly() { final CharSequence expectedTitle = "50%"; - Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 5); - mController.onPreferenceChange(mController.mSeekBarPreference, 10); - assertThat(Settings.Global.getInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1)) - .isEqualTo(50); + setTriggerLevel(5); + mController.onPreferenceChange(mController.mSeekBarPreference, 10); + + assertThat(getTriggerLevel()).isEqualTo(5); assertThat(mController.mSeekBarPreference.getTitle()).isEqualTo(expectedTitle); verify(mController.mSeekBarPreference).overrideSeekBarStateDescription(expectedTitle); } + @Test + public void onPreferenceChange_withOnStopTrackingTouch_updatesSettingsGlobal() { + final CharSequence expectedTitle = "50%"; + setTriggerLevel(5); + + mController.onPreferenceChange(mController.mSeekBarPreference, 10); + mController.onStopTrackingTouch(new SeekBar(mContext)); + + assertThat(getTriggerLevel()).isEqualTo(50); + assertThat(mController.mSeekBarPreference.getTitle()).isEqualTo(expectedTitle); + verify(mController.mSeekBarPreference).overrideSeekBarStateDescription(expectedTitle); + } + + @Test + public void onStopTrackingTouch_invalidValue_noUpdates() { + setTriggerLevel(5); + + mController.mPercentage = 0; + mController.onStopTrackingTouch(new SeekBar(mContext)); + + assertThat(getTriggerLevel()).isEqualTo(5); + } + @Test public void updateSeekBar_routineMode_hasCorrectProperties() { Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC); + mController.updateSeekBar(); + assertThat(mController.mSeekBarPreference.isVisible()).isFalse(); verify(mController.mSeekBarPreference, never()).overrideSeekBarStateDescription(any()); } @@ -71,7 +97,8 @@ public class BatterySaverScheduleSeekBarControllerTest { final CharSequence expectedTitle = "10%"; Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); - Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 10); + setTriggerLevel(10); + mController.updateSeekBar(); assertThat(mController.mSeekBarPreference.isVisible()).isTrue(); @@ -83,8 +110,10 @@ public class BatterySaverScheduleSeekBarControllerTest { public void updateSeekBar_noneMode_hasCorrectProperties() { Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); - Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0); + setTriggerLevel(0); + mController.updateSeekBar(); + assertThat(mController.mSeekBarPreference.isVisible()).isFalse(); verify(mController.mSeekBarPreference, never()).overrideSeekBarStateDescription(any()); } @@ -93,9 +122,18 @@ public class BatterySaverScheduleSeekBarControllerTest { public void addToScreen_addsToEnd() { Settings.Global.putInt(mResolver, Global.AUTOMATIC_POWER_SAVE_MODE, PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE); - Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15); + setTriggerLevel(15); + mController.addToScreen(mScreen); + assertThat(mController.mSeekBarPreference.getOrder()).isEqualTo(100); } + private void setTriggerLevel(int level) { + Settings.Global.putInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, level); + } + + private int getTriggerLevel() { + return Settings.Global.getInt(mResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1); + } } diff --git a/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java index 8b527fd5cff..8c5f428357d 100644 --- a/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/widget/SeekBarPreferenceTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -40,6 +41,7 @@ import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal; 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; @@ -59,6 +61,9 @@ public class SeekBarPreferenceTest { private SeekBarPreference mSeekBarPreference; private SeekBar mSeekBar; + @Mock + SeekBar.OnSeekBarChangeListener mMockOnSeekBarChangeListener; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -179,6 +184,54 @@ public class SeekBarPreferenceTest { assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK); } + @Test + public void onProgressChanged_hasSeekBarChangeListener_receiveCallBack() { + mSeekBarPreference.setOnSeekBarChangeListener(mMockOnSeekBarChangeListener); + + mSeekBarPreference.onProgressChanged(mSeekBar, PROGRESS, true); + + verify(mMockOnSeekBarChangeListener).onProgressChanged(mSeekBar, PROGRESS, true); + } + + @Test + public void onProgressChanged_noSeekBarChangeListener_noAction() { + mSeekBarPreference.onProgressChanged(mSeekBar, PROGRESS, true); + + verifyZeroInteractions(mMockOnSeekBarChangeListener); + } + + @Test + public void onStartTrackingTouch_hasSeekBarChangeListener_receiveCallBack() { + mSeekBarPreference.setOnSeekBarChangeListener(mMockOnSeekBarChangeListener); + + mSeekBarPreference.onStartTrackingTouch(mSeekBar); + + verify(mMockOnSeekBarChangeListener).onStartTrackingTouch(mSeekBar); + } + + @Test + public void onStartTrackingTouch_noSeekBarChangeListener_noAction() { + mSeekBarPreference.onStartTrackingTouch(mSeekBar); + + verifyZeroInteractions(mMockOnSeekBarChangeListener); + } + + @Test + public void onStopTrackingTouch_hasSeekBarChangeListener_receiveCallBack() { + mSeekBarPreference.setOnSeekBarChangeListener(mMockOnSeekBarChangeListener); + + mSeekBarPreference.onStopTrackingTouch(mSeekBar); + + verify(mMockOnSeekBarChangeListener).onStopTrackingTouch(mSeekBar); + } + + @Test + public void onStopTrackingTouch_noSeekBarChangeListener_noAction() { + mSeekBarPreference.onStopTrackingTouch(mSeekBar); + + verifyZeroInteractions(mMockOnSeekBarChangeListener); + } + public static class TestFragment extends PreferenceFragmentCompat { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {