Perform haptic feedback when the seekbar snaps.
- add haptic feedback mode to perform haptic feedback as the seekbar's progress value is updated. - add haptic feedback mode to perform haptic feedback as the seekbar's progress value is equal to the min/max value Bug: 172900709 Test: make -j42 RunSettingsRoboTests ROBOTEST_FILTER=SeekBarPreferenceTest Change-Id: I9378313d366f09bb8e7069d9240fde24af318c53
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.android.settings.widget;
|
package com.android.settings.widget;
|
||||||
|
|
||||||
|
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
@@ -39,12 +41,17 @@ import com.android.settingslib.RestrictedPreference;
|
|||||||
public class SeekBarPreference extends RestrictedPreference
|
public class SeekBarPreference extends RestrictedPreference
|
||||||
implements OnSeekBarChangeListener, View.OnKeyListener {
|
implements OnSeekBarChangeListener, View.OnKeyListener {
|
||||||
|
|
||||||
|
public static final int HAPTIC_FEEDBACK_MODE_NONE = 0;
|
||||||
|
public static final int HAPTIC_FEEDBACK_MODE_ON_TICKS = 1;
|
||||||
|
public static final int HAPTIC_FEEDBACK_MODE_ON_ENDS = 2;
|
||||||
|
|
||||||
private int mProgress;
|
private int mProgress;
|
||||||
private int mMax;
|
private int mMax;
|
||||||
private int mMin;
|
private int mMin;
|
||||||
private boolean mTrackingTouch;
|
private boolean mTrackingTouch;
|
||||||
|
|
||||||
private boolean mContinuousUpdates;
|
private boolean mContinuousUpdates;
|
||||||
|
private int mHapticFeedbackMode = HAPTIC_FEEDBACK_MODE_NONE;
|
||||||
private int mDefaultProgress = -1;
|
private int mDefaultProgress = -1;
|
||||||
|
|
||||||
private SeekBar mSeekBar;
|
private SeekBar mSeekBar;
|
||||||
@@ -235,6 +242,17 @@ public class SeekBarPreference extends RestrictedPreference
|
|||||||
mContinuousUpdates = continuousUpdates;
|
mContinuousUpdates = continuousUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the haptic feedback mode. HAPTIC_FEEDBACK_MODE_ON_TICKS means to perform haptic feedback
|
||||||
|
* as the SeekBar's progress is updated; HAPTIC_FEEDBACK_MODE_ON_ENDS means to perform haptic
|
||||||
|
* feedback as the SeekBar's progress value is equal to the min/max value.
|
||||||
|
*
|
||||||
|
* @param hapticFeedbackMode the haptic feedback mode.
|
||||||
|
*/
|
||||||
|
public void setHapticFeedbackMode(int hapticFeedbackMode) {
|
||||||
|
mHapticFeedbackMode = hapticFeedbackMode;
|
||||||
|
}
|
||||||
|
|
||||||
private void setProgress(int progress, boolean notifyChanged) {
|
private void setProgress(int progress, boolean notifyChanged) {
|
||||||
if (progress > mMax) {
|
if (progress > mMax) {
|
||||||
progress = mMax;
|
progress = mMax;
|
||||||
@@ -264,6 +282,16 @@ public class SeekBarPreference extends RestrictedPreference
|
|||||||
if (progress != mProgress) {
|
if (progress != mProgress) {
|
||||||
if (callChangeListener(progress)) {
|
if (callChangeListener(progress)) {
|
||||||
setProgress(progress, false);
|
setProgress(progress, false);
|
||||||
|
switch (mHapticFeedbackMode) {
|
||||||
|
case HAPTIC_FEEDBACK_MODE_ON_TICKS:
|
||||||
|
seekBar.performHapticFeedback(CLOCK_TICK);
|
||||||
|
break;
|
||||||
|
case HAPTIC_FEEDBACK_MODE_ON_ENDS:
|
||||||
|
if (progress == mMax || progress == mMin) {
|
||||||
|
seekBar.performHapticFeedback(CLOCK_TICK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
seekBar.setProgress(mProgress);
|
seekBar.setProgress(mProgress);
|
||||||
}
|
}
|
||||||
|
@@ -16,15 +16,21 @@
|
|||||||
|
|
||||||
package com.android.settings.widget;
|
package com.android.settings.widget;
|
||||||
|
|
||||||
|
import static android.view.HapticFeedbackConstants.CLOCK_TICK;
|
||||||
|
import static android.view.HapticFeedbackConstants.CONTEXT_CLICK;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
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.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
|
||||||
import androidx.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
@@ -46,9 +52,11 @@ public class SeekBarPreferenceTest {
|
|||||||
private static final int MAX = 75;
|
private static final int MAX = 75;
|
||||||
private static final int MIN = 5;
|
private static final int MIN = 5;
|
||||||
private static final int PROGRESS = 16;
|
private static final int PROGRESS = 16;
|
||||||
|
private static final int NEW_PROGRESS = 17;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private SeekBarPreference mSeekBarPreference;
|
private SeekBarPreference mSeekBarPreference;
|
||||||
|
private SeekBar mSeekBar;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@@ -60,6 +68,11 @@ public class SeekBarPreferenceTest {
|
|||||||
mSeekBarPreference.setMin(MIN);
|
mSeekBarPreference.setMin(MIN);
|
||||||
mSeekBarPreference.setProgress(PROGRESS);
|
mSeekBarPreference.setProgress(PROGRESS);
|
||||||
mSeekBarPreference.setPersistent(false);
|
mSeekBarPreference.setPersistent(false);
|
||||||
|
mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_NONE);
|
||||||
|
|
||||||
|
mSeekBar = new SeekBar(mContext);
|
||||||
|
mSeekBar.setMax(MAX);
|
||||||
|
mSeekBar.setMin(MIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -118,6 +131,53 @@ public class SeekBarPreferenceTest {
|
|||||||
verify(mSeekBarPreference).setSeekBarStateDescription("test");
|
verify(mSeekBarPreference).setSeekBarStateDescription("test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onProgressChanged_hapticFeedbackModeNone_clockTickFeedbackNotPerformed() {
|
||||||
|
mSeekBar.setProgress(NEW_PROGRESS);
|
||||||
|
when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true);
|
||||||
|
mSeekBar.performHapticFeedback(CONTEXT_CLICK);
|
||||||
|
|
||||||
|
mSeekBarPreference.onProgressChanged(mSeekBar, NEW_PROGRESS, true);
|
||||||
|
|
||||||
|
assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isNotEqualTo(CLOCK_TICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onProgressChanged_hapticFeedbackModeOnTicks_clockTickFeedbackPerformed() {
|
||||||
|
mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_TICKS);
|
||||||
|
mSeekBar.setProgress(NEW_PROGRESS);
|
||||||
|
when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true);
|
||||||
|
mSeekBar.performHapticFeedback(CONTEXT_CLICK);
|
||||||
|
|
||||||
|
mSeekBarPreference.onProgressChanged(mSeekBar, NEW_PROGRESS, true);
|
||||||
|
|
||||||
|
assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onProgressChanged_hapticFeedbackModeOnEnds_clockTickFeedbackNotPerformed() {
|
||||||
|
mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
|
||||||
|
mSeekBar.setProgress(NEW_PROGRESS);
|
||||||
|
when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true);
|
||||||
|
mSeekBar.performHapticFeedback(CONTEXT_CLICK);
|
||||||
|
|
||||||
|
mSeekBarPreference.onProgressChanged(mSeekBar, NEW_PROGRESS, true);
|
||||||
|
|
||||||
|
assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isNotEqualTo(CLOCK_TICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onProgressChanged_hapticFeedbackModeOnEndsAndMinValue_clockTickFeedbackPerformed() {
|
||||||
|
mSeekBarPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
|
||||||
|
mSeekBar.setProgress(MIN);
|
||||||
|
when(mSeekBarPreference.callChangeListener(anyInt())).thenReturn(true);
|
||||||
|
mSeekBar.performHapticFeedback(CONTEXT_CLICK);
|
||||||
|
|
||||||
|
mSeekBarPreference.onProgressChanged(mSeekBar, MIN, true);
|
||||||
|
|
||||||
|
assertThat(shadowOf(mSeekBar).lastHapticFeedbackPerformed()).isEqualTo(CLOCK_TICK);
|
||||||
|
}
|
||||||
|
|
||||||
public static class TestFragment extends PreferenceFragmentCompat {
|
public static class TestFragment extends PreferenceFragmentCompat {
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
|
Reference in New Issue
Block a user