- when we adjust the sound volme in Sound settings, we only re-post the stop sample message when we receive the onSampleStarting callback. However, if we change the volume while a sample is still playing, onSampleStarting will not be called as it's already started. This results in shortened sample duration, which in extreme case, the new sample will not be played at all if the new volume change is made almost towards the end of the previous sample period. So, everytime user change the volume, we should re-post the stop sample message, so that the sample playing duration would be extended properly. - also removed the original calls to the onStreamValueChanged() during init, as the original implementation is empty, and during init, we do not need any handling to start/stop the sample. Change-Id: I9f35ddfb6d809eeb83b1a732a09362286ff6ed77 Fixes: 77514234 Test: make RunSettingsRoboTests
341 lines
10 KiB
Java
341 lines
10 KiB
Java
/*
|
|
* Copyright (C) 2011 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.widget;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.TypedArray;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.support.v4.content.res.TypedArrayUtils;
|
|
import android.support.v7.preference.PreferenceViewHolder;
|
|
import android.text.TextUtils;
|
|
import android.util.AttributeSet;
|
|
import android.view.KeyEvent;
|
|
import android.view.View;
|
|
import android.widget.SeekBar;
|
|
import android.widget.SeekBar.OnSeekBarChangeListener;
|
|
|
|
import com.android.settings.widget.DefaultIndicatorSeekBar;
|
|
import com.android.settingslib.RestrictedPreference;
|
|
|
|
/**
|
|
* Based on android.preference.SeekBarPreference, but uses support preference as base.
|
|
*/
|
|
public class SeekBarPreference extends RestrictedPreference
|
|
implements OnSeekBarChangeListener, View.OnKeyListener {
|
|
|
|
private int mProgress;
|
|
private int mMax;
|
|
private int mMin;
|
|
private boolean mTrackingTouch;
|
|
|
|
private boolean mContinuousUpdates;
|
|
private int mDefaultProgress = -1;
|
|
|
|
private SeekBar mSeekBar;
|
|
private boolean mShouldBlink;
|
|
|
|
public SeekBarPreference(
|
|
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
|
|
TypedArray a = context.obtainStyledAttributes(
|
|
attrs, com.android.internal.R.styleable.ProgressBar, defStyleAttr, defStyleRes);
|
|
setMax(a.getInt(com.android.internal.R.styleable.ProgressBar_max, mMax));
|
|
setMin(a.getInt(com.android.internal.R.styleable.ProgressBar_min, mMin));
|
|
a.recycle();
|
|
|
|
a = context.obtainStyledAttributes(attrs,
|
|
com.android.internal.R.styleable.SeekBarPreference, defStyleAttr, defStyleRes);
|
|
final int layoutResId = a.getResourceId(
|
|
com.android.internal.R.styleable.SeekBarPreference_layout,
|
|
com.android.internal.R.layout.preference_widget_seekbar);
|
|
a.recycle();
|
|
|
|
setLayoutResource(layoutResId);
|
|
}
|
|
|
|
public SeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
this(context, attrs, defStyleAttr, 0);
|
|
}
|
|
|
|
public SeekBarPreference(Context context, AttributeSet attrs) {
|
|
this(context, attrs, TypedArrayUtils.getAttr(context,
|
|
android.support.v7.preference.R.attr.seekBarPreferenceStyle,
|
|
com.android.internal.R.attr.seekBarPreferenceStyle));
|
|
}
|
|
|
|
public SeekBarPreference(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public void setShouldBlink(boolean shouldBlink) {
|
|
mShouldBlink = shouldBlink;
|
|
notifyChanged();
|
|
}
|
|
|
|
@Override
|
|
public void onBindViewHolder(PreferenceViewHolder view) {
|
|
super.onBindViewHolder(view);
|
|
view.itemView.setOnKeyListener(this);
|
|
mSeekBar = (SeekBar) view.findViewById(
|
|
com.android.internal.R.id.seekbar);
|
|
mSeekBar.setOnSeekBarChangeListener(this);
|
|
mSeekBar.setMax(mMax);
|
|
mSeekBar.setMin(mMin);
|
|
mSeekBar.setProgress(mProgress);
|
|
mSeekBar.setEnabled(isEnabled());
|
|
final CharSequence title = getTitle();
|
|
if (!TextUtils.isEmpty(title)) {
|
|
mSeekBar.setContentDescription(title);
|
|
}
|
|
if (mSeekBar instanceof DefaultIndicatorSeekBar) {
|
|
((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress);
|
|
}
|
|
if (mShouldBlink) {
|
|
View v = view.itemView;
|
|
v.post(() -> {
|
|
if (v.getBackground() != null) {
|
|
final int centerX = v.getWidth() / 2;
|
|
final int centerY = v.getHeight() / 2;
|
|
v.getBackground().setHotspot(centerX, centerY);
|
|
}
|
|
v.setPressed(true);
|
|
v.setPressed(false);
|
|
mShouldBlink = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public CharSequence getSummary() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
|
|
setProgress(restoreValue ? getPersistedInt(mProgress)
|
|
: (Integer) defaultValue);
|
|
}
|
|
|
|
@Override
|
|
protected Object onGetDefaultValue(TypedArray a, int index) {
|
|
return a.getInt(index, 0);
|
|
}
|
|
|
|
@Override
|
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
if (event.getAction() != KeyEvent.ACTION_DOWN) {
|
|
return false;
|
|
}
|
|
|
|
SeekBar seekBar = (SeekBar) v.findViewById(com.android.internal.R.id.seekbar);
|
|
if (seekBar == null) {
|
|
return false;
|
|
}
|
|
return seekBar.onKeyDown(keyCode, event);
|
|
}
|
|
|
|
public void setMax(int max) {
|
|
if (max != mMax) {
|
|
mMax = max;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
public void setMin(int min) {
|
|
if (min != mMin) {
|
|
mMin = min;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
public int getMax() {
|
|
return mMax;
|
|
}
|
|
|
|
public int getMin() {
|
|
return mMin;
|
|
}
|
|
|
|
public void setProgress(int progress) {
|
|
setProgress(progress, true);
|
|
}
|
|
|
|
/**
|
|
* Sets the progress point to draw a single tick mark representing a default value.
|
|
*/
|
|
public void setDefaultProgress(int defaultProgress) {
|
|
if (mDefaultProgress != defaultProgress) {
|
|
mDefaultProgress = defaultProgress;
|
|
if (mSeekBar instanceof DefaultIndicatorSeekBar) {
|
|
((DefaultIndicatorSeekBar) mSeekBar).setDefaultProgress(mDefaultProgress);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When {@code continuousUpdates} is true, update the persisted setting immediately as the thumb
|
|
* is dragged along the SeekBar. Otherwise, only update the value of the setting when the thumb
|
|
* is dropped.
|
|
*/
|
|
public void setContinuousUpdates(boolean continuousUpdates) {
|
|
mContinuousUpdates = continuousUpdates;
|
|
}
|
|
|
|
private void setProgress(int progress, boolean notifyChanged) {
|
|
if (progress > mMax) {
|
|
progress = mMax;
|
|
}
|
|
if (progress < mMin) {
|
|
progress = mMin;
|
|
}
|
|
if (progress != mProgress) {
|
|
mProgress = progress;
|
|
persistInt(progress);
|
|
if (notifyChanged) {
|
|
notifyChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public int getProgress() {
|
|
return mProgress;
|
|
}
|
|
|
|
/**
|
|
* Persist the seekBar's progress value if callChangeListener
|
|
* returns true, otherwise set the seekBar's progress to the stored value
|
|
*/
|
|
void syncProgress(SeekBar seekBar) {
|
|
int progress = seekBar.getProgress();
|
|
if (progress != mProgress) {
|
|
if (callChangeListener(progress)) {
|
|
setProgress(progress, false);
|
|
} else {
|
|
seekBar.setProgress(mProgress);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
if (fromUser && (mContinuousUpdates || !mTrackingTouch)) {
|
|
syncProgress(seekBar);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
|
mTrackingTouch = true;
|
|
}
|
|
|
|
@Override
|
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
mTrackingTouch = false;
|
|
if (seekBar.getProgress() != mProgress) {
|
|
syncProgress(seekBar);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected Parcelable onSaveInstanceState() {
|
|
/*
|
|
* Suppose a client uses this preference type without persisting. We
|
|
* must save the instance state so it is able to, for example, survive
|
|
* orientation changes.
|
|
*/
|
|
|
|
final Parcelable superState = super.onSaveInstanceState();
|
|
if (isPersistent()) {
|
|
// No need to save instance state since it's persistent
|
|
return superState;
|
|
}
|
|
|
|
// Save the instance state
|
|
final SavedState myState = new SavedState(superState);
|
|
myState.progress = mProgress;
|
|
myState.max = mMax;
|
|
myState.min = mMin;
|
|
return myState;
|
|
}
|
|
|
|
@Override
|
|
protected void onRestoreInstanceState(Parcelable state) {
|
|
if (!state.getClass().equals(SavedState.class)) {
|
|
// Didn't save state for us in onSaveInstanceState
|
|
super.onRestoreInstanceState(state);
|
|
return;
|
|
}
|
|
|
|
// Restore the instance state
|
|
SavedState myState = (SavedState) state;
|
|
super.onRestoreInstanceState(myState.getSuperState());
|
|
mProgress = myState.progress;
|
|
mMax = myState.max;
|
|
mMin = myState.min;
|
|
notifyChanged();
|
|
}
|
|
|
|
/**
|
|
* SavedState, a subclass of {@link BaseSavedState}, will store the state
|
|
* of MyPreference, a subclass of Preference.
|
|
* <p>
|
|
* It is important to always call through to super methods.
|
|
*/
|
|
private static class SavedState extends BaseSavedState {
|
|
int progress;
|
|
int max;
|
|
int min;
|
|
|
|
public SavedState(Parcel source) {
|
|
super(source);
|
|
|
|
// Restore the click counter
|
|
progress = source.readInt();
|
|
max = source.readInt();
|
|
min = source.readInt();
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
super.writeToParcel(dest, flags);
|
|
|
|
// Save the click counter
|
|
dest.writeInt(progress);
|
|
dest.writeInt(max);
|
|
dest.writeInt(min);
|
|
}
|
|
|
|
public SavedState(Parcelable superState) {
|
|
super(superState);
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
public static final Parcelable.Creator<SavedState> CREATOR =
|
|
new Parcelable.Creator<SavedState>() {
|
|
public SavedState createFromParcel(Parcel in) {
|
|
return new SavedState(in);
|
|
}
|
|
|
|
public SavedState[] newArray(int size) {
|
|
return new SavedState[size];
|
|
}
|
|
};
|
|
}
|
|
}
|