mirror of
https://github.com/BillyOutlast/aps3e.git
synced 2026-07-01 13:20:07 -04:00
480 lines
17 KiB
Java
480 lines
17 KiB
Java
// SPDX-License-Identifier: WTFPL
|
|
package aenu.preference;
|
|
|
|
import android.content.Context;
|
|
import android.content.DialogInterface;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Color;
|
|
import android.os.Bundle;
|
|
import android.os.Parcel;
|
|
import android.os.Parcelable;
|
|
import android.util.AttributeSet;
|
|
import android.util.Log;
|
|
import android.view.KeyEvent;
|
|
import android.view.View;
|
|
import android.widget.EditText;
|
|
import android.widget.SeekBar;
|
|
import android.widget.TextView;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.appcompat.app.AlertDialog;
|
|
import androidx.preference.DialogPreference;
|
|
import androidx.preference.PreferenceDialogFragmentCompat;
|
|
import androidx.preference.PreferenceViewHolder;
|
|
|
|
import androidx.preference.R;
|
|
|
|
//modify from https://github.com/androidx/androidx/blob/androidx-main/preference/preference/src/main/java/androidx/preference/SeekBarPreference.java
|
|
public class SeekBarPreference extends DialogPreference {
|
|
|
|
|
|
public static class SeekBarPreferenceFragmentCompat extends PreferenceDialogFragmentCompat {
|
|
|
|
private static final String SAVE_STATE_VALUE = "SeekBarPreferenceFragmentCompat.value";
|
|
private static final String SAVE_STATE_MIN = "SeekBarPreferenceFragmentCompat.min";
|
|
private static final String SAVE_STATE_MAX =
|
|
"SeekBarPreferenceFragmentCompat.max";
|
|
|
|
int m_value;
|
|
int m_min;
|
|
int m_max;
|
|
|
|
EditText mEditText;
|
|
|
|
@NonNull
|
|
public static SeekBarPreference.SeekBarPreferenceFragmentCompat newInstance(String key) {
|
|
final SeekBarPreference.SeekBarPreferenceFragmentCompat fragment =
|
|
new SeekBarPreference.SeekBarPreferenceFragmentCompat();
|
|
final Bundle b = new Bundle(1);
|
|
b.putString(ARG_KEY, key);
|
|
fragment.setArguments(b);
|
|
return fragment;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
if (savedInstanceState == null) {
|
|
final SeekBarPreference preference = getSeekBarPreference();
|
|
m_value = preference.getValue();
|
|
m_min = preference.getMin();
|
|
m_max = preference.getMax();
|
|
} else {
|
|
m_value = savedInstanceState.getInt(SAVE_STATE_VALUE);
|
|
m_min = savedInstanceState.getInt(SAVE_STATE_MIN);
|
|
m_max = savedInstanceState.getInt(SAVE_STATE_MAX);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
|
super.onSaveInstanceState(outState);
|
|
outState.putInt(SAVE_STATE_VALUE, m_value);
|
|
outState.putInt(SAVE_STATE_MIN, m_min);
|
|
outState.putInt(SAVE_STATE_MAX, m_max);
|
|
}
|
|
|
|
private SeekBarPreference getSeekBarPreference() {
|
|
return (SeekBarPreference) getPreference();
|
|
}
|
|
|
|
|
|
@Override
|
|
protected void onBindDialogView(@NonNull View view) {
|
|
super.onBindDialogView(view);
|
|
mEditText = (EditText) view.findViewById(android.R.id.edit);
|
|
if(mEditText!=null){
|
|
mEditText.setHint(Integer.toString(m_min)+" -> "+Integer.toString(m_max));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onPrepareDialogBuilder(@NonNull AlertDialog.Builder builder) {
|
|
super.onPrepareDialogBuilder(builder);
|
|
|
|
// The typical interaction for list-based dialogs is to have click-on-an-item dismiss the
|
|
// dialog instead of the user having to press 'Ok'.
|
|
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
if(mEditText!=null){
|
|
try{
|
|
int value = Integer.parseInt(mEditText.getText().toString());
|
|
if (value >= m_min && value <= m_max){
|
|
final SeekBarPreference preference = getSeekBarPreference();
|
|
if(preference.callChangeListener(value))
|
|
preference.setValue(value);
|
|
}
|
|
}catch(Exception e){
|
|
Log.e("SeekbarPreference",e.toString());
|
|
}
|
|
}
|
|
dialog.dismiss();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onDialogClosed(boolean positiveResult) {
|
|
if (positiveResult) {
|
|
try{
|
|
int value = Integer.parseInt(mEditText.getText().toString());
|
|
if (value >= m_min && value <= m_max){
|
|
final SeekBarPreference preference = getSeekBarPreference();
|
|
if(preference.callChangeListener(value))
|
|
preference.setValue(value);
|
|
}
|
|
}catch(Exception e){
|
|
Log.e("SeekbarPreference",e.toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
int mSeekBarValue;
|
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
|
int mMin;
|
|
private int mMax;
|
|
private int mSeekBarIncrement;
|
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
|
boolean mTrackingTouch;
|
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
|
SeekBar mSeekBar;
|
|
private TextView mSeekBarValueTextView;
|
|
// Whether the SeekBar should respond to the left/right keys
|
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
|
boolean mAdjustable;
|
|
// Whether to show the SeekBar value TextView next to the bar
|
|
private boolean mShowSeekBarValue;
|
|
// Whether the SeekBarPreference should continuously save the Seekbar value while it is being
|
|
// dragged.
|
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
|
boolean mUpdatesContinuously;
|
|
|
|
private final SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
|
|
@Override
|
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
|
if (fromUser && (mUpdatesContinuously || !mTrackingTouch)) {
|
|
syncValueInternal(seekBar);
|
|
} else {
|
|
// We always want to update the text while the seekbar is being dragged
|
|
updateLabelValue(progress + mMin);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
|
mTrackingTouch = true;
|
|
}
|
|
|
|
@Override
|
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
|
mTrackingTouch = false;
|
|
if (seekBar.getProgress() + mMin != mSeekBarValue) {
|
|
syncValueInternal(seekBar);
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Listener reacting to the user pressing DPAD left/right keys if {@code
|
|
* adjustable} attribute is set to true; it transfers the key presses to the {@link SeekBar}
|
|
* to be handled accordingly.
|
|
*/
|
|
private final View.OnKeyListener mSeekBarKeyListener = new View.OnKeyListener() {
|
|
@Override
|
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
if (event.getAction() != KeyEvent.ACTION_DOWN) {
|
|
return false;
|
|
}
|
|
|
|
if (!mAdjustable && (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
|
|
|| keyCode == KeyEvent.KEYCODE_DPAD_RIGHT)) {
|
|
// Right or left keys are pressed when in non-adjustable mode; Skip the keys.
|
|
return false;
|
|
}
|
|
|
|
// We don't want to propagate the click keys down to the SeekBar view since it will
|
|
// create the ripple effect for the thumb.
|
|
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
|
|
return false;
|
|
}
|
|
|
|
if (mSeekBar == null) {
|
|
return false;
|
|
}
|
|
return mSeekBar.onKeyDown(keyCode, event);
|
|
}
|
|
};
|
|
|
|
public SeekBarPreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
super(context, attrs, defStyleAttr, defStyleRes);
|
|
|
|
setDialogLayoutResource(aenu.aps3e.R.layout.edit_seek_bar);
|
|
TypedArray a = context.obtainStyledAttributes(
|
|
attrs, R.styleable.SeekBarPreference, defStyleAttr, defStyleRes);
|
|
|
|
// The ordering of these two statements are important. If we want to set max first, we need
|
|
// to perform the same steps by changing min/max to max/min as following:
|
|
// mMax = a.getInt(...) and setMin(...).
|
|
mMin = a.getInt(R.styleable.SeekBarPreference_min, 0);
|
|
setMax(a.getInt(R.styleable.SeekBarPreference_android_max, 100));
|
|
setSeekBarIncrement(a.getInt(R.styleable.SeekBarPreference_seekBarIncrement, 0));
|
|
mAdjustable = a.getBoolean(R.styleable.SeekBarPreference_adjustable, true);
|
|
mShowSeekBarValue = a.getBoolean(R.styleable.SeekBarPreference_showSeekBarValue, false);
|
|
mUpdatesContinuously = a.getBoolean(R.styleable.SeekBarPreference_updatesContinuously,
|
|
false);
|
|
|
|
a.recycle();
|
|
|
|
a = context.obtainStyledAttributes(attrs,
|
|
new int[]{android.R.attr.textColorPrimary}, defStyleAttr, defStyleRes);
|
|
title_color=a.getColor(0, Color.RED);
|
|
|
|
a.recycle();
|
|
}
|
|
|
|
public SeekBarPreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
|
|
|
this(context, attrs, defStyleAttr, 0);
|
|
}
|
|
|
|
|
|
public SeekBarPreference(@NonNull Context context, @Nullable AttributeSet attrs) {
|
|
this(context, attrs, R.attr.seekBarPreferenceStyle);
|
|
}
|
|
|
|
public SeekBarPreference(@NonNull Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
TextView title_view;
|
|
int title_color;
|
|
boolean is_modify_color=false;
|
|
int modify_color= Color.MAGENTA;
|
|
|
|
public int get_modify_color(){
|
|
return modify_color;
|
|
}
|
|
|
|
public void set_modify_color(int color){
|
|
modify_color=color;
|
|
}
|
|
|
|
public void set_is_modify_color(boolean is_modify_color){
|
|
this.is_modify_color=is_modify_color;
|
|
}
|
|
|
|
public boolean get_is_modify_color(){
|
|
return is_modify_color;
|
|
}
|
|
|
|
@Override
|
|
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
|
|
super.onBindViewHolder(holder);
|
|
|
|
TextView title_v=(TextView) holder.itemView.findViewById(android.R.id.title);
|
|
title_view=title_v;
|
|
|
|
if(is_modify_color){
|
|
if(title_v!=null)
|
|
title_v.setTextColor(modify_color);
|
|
}
|
|
else{
|
|
if(title_v!=null)
|
|
title_v.setTextColor(title_v.isEnabled()?title_color:Color.GRAY);
|
|
}
|
|
|
|
holder.itemView.setOnKeyListener(mSeekBarKeyListener);
|
|
mSeekBar = (SeekBar) holder.findViewById(R.id.seekbar);
|
|
mSeekBarValueTextView = (TextView) holder.findViewById(R.id.seekbar_value);
|
|
if (mShowSeekBarValue) {
|
|
mSeekBarValueTextView.setVisibility(View.VISIBLE);
|
|
mSeekBarValueTextView.setTextColor(is_modify_color?modify_color:title_color);
|
|
} else {
|
|
mSeekBarValueTextView.setVisibility(View.GONE);
|
|
mSeekBarValueTextView = null;
|
|
}
|
|
|
|
if (mSeekBar == null) {
|
|
//Log.e(TAG, "SeekBar view is null in onBindViewHolder.");
|
|
return;
|
|
}
|
|
mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
|
|
mSeekBar.setMax(mMax - mMin);
|
|
// If the increment is not zero, use that. Otherwise, use the default mKeyProgressIncrement
|
|
// in AbsSeekBar when it's zero. This default increment value is set by AbsSeekBar
|
|
// after calling setMax. That's why it's important to call setKeyProgressIncrement after
|
|
// calling setMax() since setMax() can change the increment value.
|
|
if (mSeekBarIncrement != 0) {
|
|
mSeekBar.setKeyProgressIncrement(mSeekBarIncrement);
|
|
} else {
|
|
mSeekBarIncrement = mSeekBar.getKeyProgressIncrement();
|
|
}
|
|
|
|
mSeekBar.setProgress(mSeekBarValue - mMin);
|
|
updateLabelValue(mSeekBarValue);
|
|
mSeekBar.setEnabled(isEnabled());
|
|
}
|
|
|
|
@Override
|
|
protected void onSetInitialValue(Object defaultValue) {
|
|
if (defaultValue == null) {
|
|
defaultValue = 0;
|
|
}
|
|
setValue(getPersistedInt((Integer) defaultValue));
|
|
}
|
|
|
|
@Override
|
|
protected @Nullable Object onGetDefaultValue(@NonNull TypedArray a, int index) {
|
|
return a.getInt(index, 0);
|
|
}
|
|
|
|
public int getMin() {
|
|
return mMin;
|
|
}
|
|
|
|
/**
|
|
* Sets the lower bound on the {@link SeekBar}.
|
|
*
|
|
* @param min The lower bound to set
|
|
*/
|
|
public void setMin(int min) {
|
|
if (min > mMax) {
|
|
min = mMax;
|
|
}
|
|
if (min != mMin) {
|
|
mMin = min;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
public int getValue() {
|
|
return mSeekBarValue;
|
|
}
|
|
|
|
/**
|
|
* Sets the current progress of the {@link SeekBar}.
|
|
*
|
|
* @param seekBarValue The current progress of the {@link SeekBar}
|
|
*/
|
|
public void setValue(int seekBarValue) {
|
|
setValueInternal(seekBarValue, true);
|
|
}
|
|
public final void setSeekBarIncrement(int seekBarIncrement) {
|
|
if (seekBarIncrement != mSeekBarIncrement) {
|
|
mSeekBarIncrement = Math.min(mMax - mMin, Math.abs(seekBarIncrement));
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
private void setValueInternal(int seekBarValue, boolean notifyChanged) {
|
|
if (seekBarValue < mMin) {
|
|
seekBarValue = mMin;
|
|
}
|
|
if (seekBarValue > mMax) {
|
|
seekBarValue = mMax;
|
|
}
|
|
|
|
if (seekBarValue != mSeekBarValue) {
|
|
mSeekBarValue = seekBarValue;
|
|
updateLabelValue(mSeekBarValue);
|
|
persistInt(seekBarValue);
|
|
if (notifyChanged) {
|
|
notifyChanged();
|
|
}
|
|
}
|
|
}
|
|
public int getMax() {
|
|
return mMax;
|
|
}
|
|
|
|
/**
|
|
* Sets the upper bound on the {@link SeekBar}.
|
|
*
|
|
* @param max The upper bound to set
|
|
*/
|
|
public final void setMax(int max) {
|
|
if (max < mMin) {
|
|
max = mMin;
|
|
}
|
|
if (max != mMax) {
|
|
mMax = max;
|
|
notifyChanged();
|
|
}
|
|
}
|
|
|
|
void syncValueInternal(@NonNull SeekBar seekBar) {
|
|
int seekBarValue = mMin + seekBar.getProgress();
|
|
if (seekBarValue != mSeekBarValue) {
|
|
if (callChangeListener(seekBarValue)) {
|
|
setValueInternal(seekBarValue, false);
|
|
} else {
|
|
seekBar.setProgress(mSeekBarValue - mMin);
|
|
updateLabelValue(mSeekBarValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to update the TextView label that displays the current value.
|
|
*
|
|
* @param value the value to display next to the {@link SeekBar}
|
|
*/
|
|
@SuppressWarnings("WeakerAccess") /* synthetic access */
|
|
void updateLabelValue(int value) {
|
|
if (mSeekBarValueTextView != null) {
|
|
mSeekBarValueTextView.setTextColor(is_modify_color?modify_color:title_color);
|
|
mSeekBarValueTextView.setText(String.valueOf(value));
|
|
}
|
|
|
|
if(title_view!=null){
|
|
if(title_view.isEnabled())
|
|
title_view.setTextColor(is_modify_color?modify_color:title_color);
|
|
else
|
|
title_view.setTextColor(Color.GRAY);
|
|
}
|
|
}
|
|
|
|
private static class SavedState extends BaseSavedState {
|
|
public static final Parcelable.Creator<SavedState> CREATOR =
|
|
new Parcelable.Creator<SavedState>() {
|
|
@Override
|
|
public SavedState createFromParcel(Parcel in) {
|
|
return new SavedState(in);
|
|
}
|
|
|
|
@Override
|
|
public SavedState[] newArray(int size) {
|
|
return new SavedState[size];
|
|
}
|
|
};
|
|
|
|
int mSeekBarValue;
|
|
int mMin;
|
|
int mMax;
|
|
|
|
SavedState(Parcel source) {
|
|
super(source);
|
|
|
|
// Restore the click counter
|
|
mSeekBarValue = source.readInt();
|
|
mMin = source.readInt();
|
|
mMax = source.readInt();
|
|
}
|
|
|
|
SavedState(Parcelable superState) {
|
|
super(superState);
|
|
}
|
|
|
|
@Override
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
super.writeToParcel(dest, flags);
|
|
|
|
// Save the click counter
|
|
dest.writeInt(mSeekBarValue);
|
|
dest.writeInt(mMin);
|
|
dest.writeInt(mMax);
|
|
}
|
|
}
|
|
}
|