M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
@@ -0,0 +1,319 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.app.Service;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Checkable;
import android.widget.TextView;
/**
* A {@link Preference} that provides checkbox widget
* functionality.
* <p>
* This preference will store a boolean into the SharedPreferences.
*
* @attr ref android.R.styleable#CheckBoxPreference_summaryOff
* @attr ref android.R.styleable#CheckBoxPreference_summaryOn
* @attr ref android.R.styleable#CheckBoxPreference_disableDependentsState
*/
public class CheckBoxPreference extends Preference {
private CharSequence mSummaryOn;
private CharSequence mSummaryOff;
private boolean mChecked;
private boolean mSendAccessibilityEventViewClickedType;
private AccessibilityManager mAccessibilityManager;
private boolean mDisableDependentsState;
public CheckBoxPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.CheckBoxPreference, defStyle, 0);
mSummaryOn = a.getString(com.android.internal.R.styleable.CheckBoxPreference_summaryOn);
mSummaryOff = a.getString(com.android.internal.R.styleable.CheckBoxPreference_summaryOff);
mDisableDependentsState = a.getBoolean(
com.android.internal.R.styleable.CheckBoxPreference_disableDependentsState, false);
a.recycle();
mAccessibilityManager =
(AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
}
public CheckBoxPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.checkBoxPreferenceStyle);
}
public CheckBoxPreference(Context context) {
this(context, null);
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
View checkboxView = view.findViewById(com.android.internal.R.id.checkbox);
if (checkboxView != null && checkboxView instanceof Checkable) {
((Checkable) checkboxView).setChecked(mChecked);
// send an event to announce the value change of the CheckBox and is done here
// because clicking a preference does not immediately change the checked state
// for example when enabling the WiFi
if (mSendAccessibilityEventViewClickedType &&
mAccessibilityManager.isEnabled() &&
checkboxView.isEnabled()) {
mSendAccessibilityEventViewClickedType = false;
int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED;
checkboxView.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
}
}
// Sync the summary view
TextView summaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
if (summaryView != null) {
boolean useDefaultSummary = true;
if (mChecked && mSummaryOn != null) {
summaryView.setText(mSummaryOn);
useDefaultSummary = false;
} else if (!mChecked && mSummaryOff != null) {
summaryView.setText(mSummaryOff);
useDefaultSummary = false;
}
if (useDefaultSummary) {
final CharSequence summary = getSummary();
if (summary != null) {
summaryView.setText(summary);
useDefaultSummary = false;
}
}
int newVisibility = View.GONE;
if (!useDefaultSummary) {
// Someone has written to it
newVisibility = View.VISIBLE;
}
if (newVisibility != summaryView.getVisibility()) {
summaryView.setVisibility(newVisibility);
}
}
}
@Override
protected void onClick() {
super.onClick();
boolean newValue = !isChecked();
// in onBindView() an AccessibilityEventViewClickedType is sent to announce the change
// not sending
mSendAccessibilityEventViewClickedType = true;
if (!callChangeListener(newValue)) {
return;
}
setChecked(newValue);
}
/**
* Sets the checked state and saves it to the {@link SharedPreferences}.
*
* @param checked The checked state.
*/
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
persistBoolean(checked);
notifyDependencyChange(shouldDisableDependents());
notifyChanged();
}
}
/**
* Returns the checked state.
*
* @return The checked state.
*/
public boolean isChecked() {
return mChecked;
}
@Override
public boolean shouldDisableDependents() {
boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
return shouldDisable || super.shouldDisableDependents();
}
/**
* Sets the summary to be shown when checked.
*
* @param summary The summary to be shown when checked.
*/
public void setSummaryOn(CharSequence summary) {
mSummaryOn = summary;
if (isChecked()) {
notifyChanged();
}
}
/**
* @see #setSummaryOn(CharSequence)
* @param summaryResId The summary as a resource.
*/
public void setSummaryOn(int summaryResId) {
setSummaryOn(getContext().getString(summaryResId));
}
/**
* Returns the summary to be shown when checked.
* @return The summary.
*/
public CharSequence getSummaryOn() {
return mSummaryOn;
}
/**
* Sets the summary to be shown when unchecked.
*
* @param summary The summary to be shown when unchecked.
*/
public void setSummaryOff(CharSequence summary) {
mSummaryOff = summary;
if (!isChecked()) {
notifyChanged();
}
}
/**
* @see #setSummaryOff(CharSequence)
* @param summaryResId The summary as a resource.
*/
public void setSummaryOff(int summaryResId) {
setSummaryOff(getContext().getString(summaryResId));
}
/**
* Returns the summary to be shown when unchecked.
* @return The summary.
*/
public CharSequence getSummaryOff() {
return mSummaryOff;
}
/**
* Returns whether dependents are disabled when this preference is on ({@code true})
* or when this preference is off ({@code false}).
*
* @return Whether dependents are disabled when this preference is on ({@code true})
* or when this preference is off ({@code false}).
*/
public boolean getDisableDependentsState() {
return mDisableDependentsState;
}
/**
* Sets whether dependents are disabled when this preference is on ({@code true})
* or when this preference is off ({@code false}).
*
* @param disableDependentsState The preference state that should disable dependents.
*/
public void setDisableDependentsState(boolean disableDependentsState) {
mDisableDependentsState = disableDependentsState;
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getBoolean(index, false);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setChecked(restoreValue ? getPersistedBoolean(mChecked)
: (Boolean) defaultValue);
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
final SavedState myState = new SavedState(superState);
myState.checked = isChecked();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
setChecked(myState.checked);
}
private static class SavedState extends BaseSavedState {
boolean checked;
public SavedState(Parcel source) {
super(source);
checked = source.readInt() == 1;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(checked ? 1 : 0);
}
public SavedState(Parcelable superState) {
super(superState);
}
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];
}
};
}
}
@@ -0,0 +1,478 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
/**
* A base class for {@link Preference} objects that are
* dialog-based. These preferences will, when clicked, open a dialog showing the
* actual preference controls.
*
* @attr ref android.R.styleable#DialogPreference_dialogTitle
* @attr ref android.R.styleable#DialogPreference_dialogMessage
* @attr ref android.R.styleable#DialogPreference_dialogIcon
* @attr ref android.R.styleable#DialogPreference_dialogLayout
* @attr ref android.R.styleable#DialogPreference_positiveButtonText
* @attr ref android.R.styleable#DialogPreference_negativeButtonText
*/
public abstract class DialogPreference extends Preference implements
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
PreferenceManager.OnActivityDestroyListener {
private AlertDialog.Builder mBuilder;
private CharSequence mDialogTitle;
private CharSequence mDialogMessage;
private Drawable mDialogIcon;
private CharSequence mPositiveButtonText;
private CharSequence mNegativeButtonText;
private int mDialogLayoutResId;
/** The dialog, if it is showing. */
private Dialog mDialog;
/** Which button was clicked. */
private int mWhichButtonClicked;
public DialogPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.DialogPreference, defStyle, 0);
mDialogTitle = a.getString(com.android.internal.R.styleable.DialogPreference_dialogTitle);
if (mDialogTitle == null) {
// Fallback on the regular title of the preference
// (the one that is seen in the list)
mDialogTitle = getTitle();
}
mDialogMessage = a.getString(com.android.internal.R.styleable.DialogPreference_dialogMessage);
mDialogIcon = a.getDrawable(com.android.internal.R.styleable.DialogPreference_dialogIcon);
mPositiveButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_positiveButtonText);
mNegativeButtonText = a.getString(com.android.internal.R.styleable.DialogPreference_negativeButtonText);
mDialogLayoutResId = a.getResourceId(com.android.internal.R.styleable.DialogPreference_dialogLayout,
mDialogLayoutResId);
a.recycle();
}
public DialogPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.dialogPreferenceStyle);
}
/**
* Sets the title of the dialog. This will be shown on subsequent dialogs.
*
* @param dialogTitle The title.
*/
public void setDialogTitle(CharSequence dialogTitle) {
mDialogTitle = dialogTitle;
}
/**
* @see #setDialogTitle(CharSequence)
* @param dialogTitleResId The dialog title as a resource.
*/
public void setDialogTitle(int dialogTitleResId) {
setDialogTitle(getContext().getString(dialogTitleResId));
}
/**
* Returns the title to be shown on subsequent dialogs.
* @return The title.
*/
public CharSequence getDialogTitle() {
return mDialogTitle;
}
/**
* Sets the message of the dialog. This will be shown on subsequent dialogs.
* <p>
* This message forms the content View of the dialog and conflicts with
* list-based dialogs, for example. If setting a custom View on a dialog via
* {@link #setDialogLayoutResource(int)}, include a text View with ID
* {@link android.R.id#message} and it will be populated with this message.
*
* @param dialogMessage The message.
*/
public void setDialogMessage(CharSequence dialogMessage) {
mDialogMessage = dialogMessage;
}
/**
* @see #setDialogMessage(CharSequence)
* @param dialogMessageResId The dialog message as a resource.
*/
public void setDialogMessage(int dialogMessageResId) {
setDialogMessage(getContext().getString(dialogMessageResId));
}
/**
* Returns the message to be shown on subsequent dialogs.
* @return The message.
*/
public CharSequence getDialogMessage() {
return mDialogMessage;
}
/**
* Sets the icon of the dialog. This will be shown on subsequent dialogs.
*
* @param dialogIcon The icon, as a {@link Drawable}.
*/
public void setDialogIcon(Drawable dialogIcon) {
mDialogIcon = dialogIcon;
}
/**
* Sets the icon (resource ID) of the dialog. This will be shown on
* subsequent dialogs.
*
* @param dialogIconRes The icon, as a resource ID.
*/
public void setDialogIcon(int dialogIconRes) {
mDialogIcon = getContext().getResources().getDrawable(dialogIconRes);
}
/**
* Returns the icon to be shown on subsequent dialogs.
* @return The icon, as a {@link Drawable}.
*/
public Drawable getDialogIcon() {
return mDialogIcon;
}
/**
* Sets the text of the positive button of the dialog. This will be shown on
* subsequent dialogs.
*
* @param positiveButtonText The text of the positive button.
*/
public void setPositiveButtonText(CharSequence positiveButtonText) {
mPositiveButtonText = positiveButtonText;
}
/**
* @see #setPositiveButtonText(CharSequence)
* @param positiveButtonTextResId The positive button text as a resource.
*/
public void setPositiveButtonText(int positiveButtonTextResId) {
setPositiveButtonText(getContext().getString(positiveButtonTextResId));
}
/**
* Returns the text of the positive button to be shown on subsequent
* dialogs.
*
* @return The text of the positive button.
*/
public CharSequence getPositiveButtonText() {
return mPositiveButtonText;
}
/**
* Sets the text of the negative button of the dialog. This will be shown on
* subsequent dialogs.
*
* @param negativeButtonText The text of the negative button.
*/
public void setNegativeButtonText(CharSequence negativeButtonText) {
mNegativeButtonText = negativeButtonText;
}
/**
* @see #setNegativeButtonText(CharSequence)
* @param negativeButtonTextResId The negative button text as a resource.
*/
public void setNegativeButtonText(int negativeButtonTextResId) {
setNegativeButtonText(getContext().getString(negativeButtonTextResId));
}
/**
* Returns the text of the negative button to be shown on subsequent
* dialogs.
*
* @return The text of the negative button.
*/
public CharSequence getNegativeButtonText() {
return mNegativeButtonText;
}
/**
* Sets the layout resource that is inflated as the {@link View} to be shown
* as the content View of subsequent dialogs.
*
* @param dialogLayoutResId The layout resource ID to be inflated.
* @see #setDialogMessage(CharSequence)
*/
public void setDialogLayoutResource(int dialogLayoutResId) {
mDialogLayoutResId = dialogLayoutResId;
}
/**
* Returns the layout resource that is used as the content View for
* subsequent dialogs.
*
* @return The layout resource.
*/
public int getDialogLayoutResource() {
return mDialogLayoutResId;
}
/**
* Prepares the dialog builder to be shown when the preference is clicked.
* Use this to set custom properties on the dialog.
* <p>
* Do not {@link AlertDialog.Builder#create()} or
* {@link AlertDialog.Builder#show()}.
*/
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
}
@Override
protected void onClick() {
showDialog(null);
}
/**
* Shows the dialog associated with this Preference. This is normally initiated
* automatically on clicking on the preference. Call this method if you need to
* show the dialog on some other event.
*
* @param state Optional instance state to restore on the dialog
*/
protected void showDialog(Bundle state) {
Context context = getContext();
mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
mBuilder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
View contentView = onCreateDialogView();
if (contentView != null) {
onBindDialogView(contentView);
mBuilder.setView(contentView);
} else {
mBuilder.setMessage(mDialogMessage);
}
onPrepareDialogBuilder(mBuilder);
getPreferenceManager().registerOnActivityDestroyListener(this);
// Create the dialog
final Dialog dialog = mDialog = mBuilder.create();
if (state != null) {
dialog.onRestoreInstanceState(state);
}
if (needInputMethod()) {
requestInputMethod(dialog);
}
dialog.setOnDismissListener(this);
dialog.show();
}
/**
* Returns whether the preference needs to display a soft input method when the dialog
* is displayed. Default is false. Subclasses should override this method if they need
* the soft input method brought up automatically.
* @hide
*/
protected boolean needInputMethod() {
return false;
}
/**
* Sets the required flags on the dialog window to enable input method window to show up.
*/
private void requestInputMethod(Dialog dialog) {
Window window = dialog.getWindow();
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE |
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
/**
* Creates the content view for the dialog (if a custom content view is
* required). By default, it inflates the dialog layout resource if it is
* set.
*
* @return The content View for the dialog.
* @see #setLayoutResource(int)
*/
protected View onCreateDialogView() {
if (mDialogLayoutResId == 0) {
return null;
}
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
return inflater.inflate(mDialogLayoutResId, null);
}
/**
* Binds views in the content View of the dialog to data.
* <p>
* Make sure to call through to the superclass implementation.
*
* @param view The content View of the dialog, if it is custom.
*/
protected void onBindDialogView(View view) {
View dialogMessageView = view.findViewById(com.android.internal.R.id.message);
if (dialogMessageView != null) {
final CharSequence message = getDialogMessage();
int newVisibility = View.GONE;
if (!TextUtils.isEmpty(message)) {
if (dialogMessageView instanceof TextView) {
((TextView) dialogMessageView).setText(message);
}
newVisibility = View.VISIBLE;
}
if (dialogMessageView.getVisibility() != newVisibility) {
dialogMessageView.setVisibility(newVisibility);
}
}
}
public void onClick(DialogInterface dialog, int which) {
mWhichButtonClicked = which;
}
public void onDismiss(DialogInterface dialog) {
getPreferenceManager().unregisterOnActivityDestroyListener(this);
mDialog = null;
onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
}
/**
* Called when the dialog is dismissed and should be used to save data to
* the {@link SharedPreferences}.
*
* @param positiveResult Whether the positive button was clicked (true), or
* the negative button was clicked or the dialog was canceled (false).
*/
protected void onDialogClosed(boolean positiveResult) {
}
/**
* Gets the dialog that is shown by this preference.
*
* @return The dialog, or null if a dialog is not being shown.
*/
public Dialog getDialog() {
return mDialog;
}
/**
* {@inheritDoc}
*/
public void onActivityDestroy() {
if (mDialog == null || !mDialog.isShowing()) {
return;
}
mDialog.dismiss();
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (mDialog == null || !mDialog.isShowing()) {
return superState;
}
final SavedState myState = new SavedState(superState);
myState.isDialogShowing = true;
myState.dialogBundle = mDialog.onSaveInstanceState();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
if (myState.isDialogShowing) {
showDialog(myState.dialogBundle);
}
}
private static class SavedState extends BaseSavedState {
boolean isDialogShowing;
Bundle dialogBundle;
public SavedState(Parcel source) {
super(source);
isDialogShowing = source.readInt() == 1;
dialogBundle = source.readBundle();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(isDialogShowing ? 1 : 0);
dest.writeBundle(dialogBundle);
}
public SavedState(Parcelable superState) {
super(superState);
}
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];
}
};
}
}
@@ -0,0 +1,234 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.EditText;
/**
* A {@link Preference} that allows for string
* input.
* <p>
* It is a subclass of {@link DialogPreference} and shows the {@link EditText}
* in a dialog. This {@link EditText} can be modified either programmatically
* via {@link #getEditText()}, or through XML by setting any EditText
* attributes on the EditTextPreference.
* <p>
* This preference will store a string into the SharedPreferences.
* <p>
* See {@link android.R.styleable#EditText EditText Attributes}.
*/
public class EditTextPreference extends DialogPreference {
/**
* The edit text shown in the dialog.
*/
private EditText mEditText;
private String mText;
public EditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mEditText = new EditText(context, attrs);
// Give it an ID so it can be saved/restored
mEditText.setId(com.android.internal.R.id.edit);
/*
* The preference framework and view framework both have an 'enabled'
* attribute. Most likely, the 'enabled' specified in this XML is for
* the preference framework, but it was also given to the view framework.
* We reset the enabled state.
*/
mEditText.setEnabled(true);
}
public EditTextPreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.editTextPreferenceStyle);
}
public EditTextPreference(Context context) {
this(context, null);
}
/**
* Saves the text to the {@link SharedPreferences}.
*
* @param text The text to save
*/
public void setText(String text) {
final boolean wasBlocking = shouldDisableDependents();
mText = text;
persistString(text);
final boolean isBlocking = shouldDisableDependents();
if (isBlocking != wasBlocking) {
notifyDependencyChange(isBlocking);
}
}
/**
* Gets the text from the {@link SharedPreferences}.
*
* @return The current preference value.
*/
public String getText() {
return mText;
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
EditText editText = mEditText;
editText.setText(getText());
ViewParent oldParent = editText.getParent();
if (oldParent != view) {
if (oldParent != null) {
((ViewGroup) oldParent).removeView(editText);
}
onAddEditTextToDialogView(view, editText);
}
}
/**
* Adds the EditText widget of this preference to the dialog's view.
*
* @param dialogView The dialog view.
*/
protected void onAddEditTextToDialogView(View dialogView, EditText editText) {
ViewGroup container = (ViewGroup) dialogView
.findViewById(com.android.internal.R.id.edittext_container);
if (container != null) {
container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
String value = mEditText.getText().toString();
if (callChangeListener(value)) {
setText(value);
}
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setText(restoreValue ? getPersistedString(mText) : (String) defaultValue);
}
@Override
public boolean shouldDisableDependents() {
return TextUtils.isEmpty(mText) || super.shouldDisableDependents();
}
/**
* Returns the {@link EditText} widget that will be shown in the dialog.
*
* @return The {@link EditText} widget that will be shown in the dialog.
*/
public EditText getEditText() {
return mEditText;
}
/** @hide */
@Override
protected boolean needInputMethod() {
// We want the input method to show, if possible, when dialog is displayed
return true;
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
final SavedState myState = new SavedState(superState);
myState.text = getText();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
setText(myState.text);
}
private static class SavedState extends BaseSavedState {
String text;
public SavedState(Parcel source) {
super(source);
text = source.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(text);
}
public SavedState(Parcelable superState) {
super(superState);
}
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];
}
};
}
}
@@ -0,0 +1,520 @@
/*
* Copyright (C) 2007 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 android.preference;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.ContextThemeWrapper;
import android.view.InflateException;
import android.view.LayoutInflater;
// TODO: fix generics
/**
* Generic XML inflater. This has been adapted from {@link LayoutInflater} and
* quickly passed over to use generics.
*
* @hide
* @param T The type of the items to inflate
* @param P The type of parents (that is those items that contain other items).
* Must implement {@link GenericInflater.Parent}
*/
abstract class GenericInflater<T, P extends GenericInflater.Parent> {
private final boolean DEBUG = false;
protected final Context mContext;
// these are optional, set by the caller
private boolean mFactorySet;
private Factory<T> mFactory;
private final Object[] mConstructorArgs = new Object[2];
private static final Class[] mConstructorSignature = new Class[] {
Context.class, AttributeSet.class};
private static final HashMap sConstructorMap = new HashMap();
private String mDefaultPackage;
public interface Parent<T> {
public void addItemFromInflater(T child);
}
public interface Factory<T> {
/**
* Hook you can supply that is called when inflating from a
* inflater. You can use this to customize the tag
* names available in your XML files.
* <p>
* Note that it is good practice to prefix these custom names with your
* package (i.e., com.coolcompany.apps) to avoid conflicts with system
* names.
*
* @param name Tag name to be inflated.
* @param context The context the item is being created in.
* @param attrs Inflation attributes as specified in XML file.
* @return Newly created item. Return null for the default behavior.
*/
public T onCreateItem(String name, Context context, AttributeSet attrs);
}
private static class FactoryMerger<T> implements Factory<T> {
private final Factory<T> mF1, mF2;
FactoryMerger(Factory<T> f1, Factory<T> f2) {
mF1 = f1;
mF2 = f2;
}
public T onCreateItem(String name, Context context, AttributeSet attrs) {
T v = mF1.onCreateItem(name, context, attrs);
if (v != null) return v;
return mF2.onCreateItem(name, context, attrs);
}
}
/**
* Create a new inflater instance associated with a
* particular Context.
*
* @param context The Context in which this inflater will
* create its items; most importantly, this supplies the theme
* from which the default values for their attributes are
* retrieved.
*/
protected GenericInflater(Context context) {
mContext = context;
}
/**
* Create a new inflater instance that is a copy of an
* existing inflater, optionally with its Context
* changed. For use in implementing {@link #cloneInContext}.
*
* @param original The original inflater to copy.
* @param newContext The new Context to use.
*/
protected GenericInflater(GenericInflater<T,P> original, Context newContext) {
mContext = newContext;
mFactory = original.mFactory;
}
/**
* Create a copy of the existing inflater object, with the copy
* pointing to a different Context than the original. This is used by
* {@link ContextThemeWrapper} to create a new inflater to go along
* with the new Context theme.
*
* @param newContext The new Context to associate with the new inflater.
* May be the same as the original Context if desired.
*
* @return Returns a brand spanking new inflater object associated with
* the given Context.
*/
public abstract GenericInflater cloneInContext(Context newContext);
/**
* Sets the default package that will be searched for classes to construct
* for tag names that have no explicit package.
*
* @param defaultPackage The default package. This will be prepended to the
* tag name, so it should end with a period.
*/
public void setDefaultPackage(String defaultPackage) {
mDefaultPackage = defaultPackage;
}
/**
* Returns the default package, or null if it is not set.
*
* @see #setDefaultPackage(String)
* @return The default package.
*/
public String getDefaultPackage() {
return mDefaultPackage;
}
/**
* Return the context we are running in, for access to resources, class
* loader, etc.
*/
public Context getContext() {
return mContext;
}
/**
* Return the current factory (or null). This is called on each element
* name. If the factory returns an item, add that to the hierarchy. If it
* returns null, proceed to call onCreateItem(name).
*/
public final Factory<T> getFactory() {
return mFactory;
}
/**
* Attach a custom Factory interface for creating items while using this
* inflater. This must not be null, and can only be set
* once; after setting, you can not change the factory. This is called on
* each element name as the XML is parsed. If the factory returns an item,
* that is added to the hierarchy. If it returns null, the next factory
* default {@link #onCreateItem} method is called.
* <p>
* If you have an existing inflater and want to add your
* own factory to it, use {@link #cloneInContext} to clone the existing
* instance and then you can use this function (once) on the returned new
* instance. This will merge your own factory with whatever factory the
* original instance is using.
*/
public void setFactory(Factory<T> factory) {
if (mFactorySet) {
throw new IllegalStateException("" +
"A factory has already been set on this inflater");
}
if (factory == null) {
throw new NullPointerException("Given factory can not be null");
}
mFactorySet = true;
if (mFactory == null) {
mFactory = factory;
} else {
mFactory = new FactoryMerger<T>(factory, mFactory);
}
}
/**
* Inflate a new item hierarchy from the specified xml resource. Throws
* InflaterException if there is an error.
*
* @param resource ID for an XML resource to load (e.g.,
* <code>R.layout.main_page</code>)
* @param root Optional parent of the generated hierarchy.
* @return The root of the inflated hierarchy. If root was supplied,
* this is the root item; otherwise it is the root of the inflated
* XML file.
*/
public T inflate(int resource, P root) {
return inflate(resource, root, root != null);
}
/**
* Inflate a new hierarchy from the specified xml node. Throws
* InflaterException if there is an error. *
* <p>
* <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
* reasons, inflation relies heavily on pre-processing of XML files
* that is done at build time. Therefore, it is not currently possible to
* use inflater with an XmlPullParser over a plain XML file at runtime.
*
* @param parser XML dom node containing the description of the
* hierarchy.
* @param root Optional parent of the generated hierarchy.
* @return The root of the inflated hierarchy. If root was supplied,
* this is the that; otherwise it is the root of the inflated
* XML file.
*/
public T inflate(XmlPullParser parser, P root) {
return inflate(parser, root, root != null);
}
/**
* Inflate a new hierarchy from the specified xml resource. Throws
* InflaterException if there is an error.
*
* @param resource ID for an XML resource to load (e.g.,
* <code>R.layout.main_page</code>)
* @param root Optional root to be the parent of the generated hierarchy (if
* <em>attachToRoot</em> is true), or else simply an object that
* provides a set of values for root of the returned
* hierarchy (if <em>attachToRoot</em> is false.)
* @param attachToRoot Whether the inflated hierarchy should be attached to
* the root parameter?
* @return The root of the inflated hierarchy. If root was supplied and
* attachToRoot is true, this is root; otherwise it is the root of
* the inflated XML file.
*/
public T inflate(int resource, P root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getXml(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
/**
* Inflate a new hierarchy from the specified XML node. Throws
* InflaterException if there is an error.
* <p>
* <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
* reasons, inflation relies heavily on pre-processing of XML files
* that is done at build time. Therefore, it is not currently possible to
* use inflater with an XmlPullParser over a plain XML file at runtime.
*
* @param parser XML dom node containing the description of the
* hierarchy.
* @param root Optional to be the parent of the generated hierarchy (if
* <em>attachToRoot</em> is true), or else simply an object that
* provides a set of values for root of the returned
* hierarchy (if <em>attachToRoot</em> is false.)
* @param attachToRoot Whether the inflated hierarchy should be attached to
* the root parameter?
* @return The root of the inflated hierarchy. If root was supplied and
* attachToRoot is true, this is root; otherwise it is the root of
* the inflated XML file.
*/
public T inflate(XmlPullParser parser, P root,
boolean attachToRoot) {
synchronized (mConstructorArgs) {
final AttributeSet attrs = Xml.asAttributeSet(parser);
mConstructorArgs[0] = mContext;
T result = (T) root;
try {
// Look for the root node.
int type;
while ((type = parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}
if (type != parser.START_TAG) {
throw new InflateException(parser.getPositionDescription()
+ ": No start tag found!");
}
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root: "
+ parser.getName());
System.out.println("**************************");
}
// Temp is the root that was found in the xml
T xmlRoot = createItemFromTag(parser, parser.getName(),
attrs);
result = (T) onMergeRoots(root, attachToRoot, (P) xmlRoot);
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp
rInflate(parser, result, attrs);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
} catch (InflateException e) {
throw e;
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
}
return result;
}
}
/**
* Low-level function for instantiating by name. This attempts to
* instantiate class of the given <var>name</var> found in this
* inflater's ClassLoader.
*
* <p>
* There are two things that can happen in an error case: either the
* exception describing the error will be thrown, or a null will be
* returned. You must deal with both possibilities -- the former will happen
* the first time createItem() is called for a class of a particular name,
* the latter every time there-after for that class name.
*
* @param name The full name of the class to be instantiated.
* @param attrs The XML attributes supplied for this instance.
*
* @return The newly instantied item, or null.
*/
public final T createItem(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor constructor = (Constructor) sConstructorMap.get(name);
try {
if (null == constructor) {
// Class not found in the cache, see if it's real,
// and try to add it
Class clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name);
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
}
Object[] args = mConstructorArgs;
args[1] = attrs;
return (T) constructor.newInstance(args);
} catch (NoSuchMethodException e) {
InflateException ie = new InflateException(attrs
.getPositionDescription()
+ ": Error inflating class "
+ (prefix != null ? (prefix + name) : name));
ie.initCause(e);
throw ie;
} catch (ClassNotFoundException e) {
// If loadClass fails, we should propagate the exception.
throw e;
} catch (Exception e) {
InflateException ie = new InflateException(attrs
.getPositionDescription()
+ ": Error inflating class "
+ constructor.getClass().getName());
ie.initCause(e);
throw ie;
}
}
/**
* This routine is responsible for creating the correct subclass of item
* given the xml element name. Override it to handle custom item objects. If
* you override this in your subclass be sure to call through to
* super.onCreateItem(name) for names you do not recognize.
*
* @param name The fully qualified class name of the item to be create.
* @param attrs An AttributeSet of attributes to apply to the item.
* @return The item created.
*/
protected T onCreateItem(String name, AttributeSet attrs) throws ClassNotFoundException {
return createItem(name, mDefaultPackage, attrs);
}
private final T createItemFromTag(XmlPullParser parser, String name, AttributeSet attrs) {
if (DEBUG) System.out.println("******** Creating item: " + name);
try {
T item = (mFactory == null) ? null : mFactory.onCreateItem(name, mContext, attrs);
if (item == null) {
if (-1 == name.indexOf('.')) {
item = onCreateItem(name, attrs);
} else {
item = createItem(name, null, attrs);
}
}
if (DEBUG) System.out.println("Created item is: " + item);
return item;
} catch (InflateException e) {
throw e;
} catch (ClassNotFoundException e) {
InflateException ie = new InflateException(attrs
.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie;
} catch (Exception e) {
InflateException ie = new InflateException(attrs
.getPositionDescription()
+ ": Error inflating class " + name);
ie.initCause(e);
throw ie;
}
}
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* items, instantiate their children, and then call onFinishInflate().
*/
private void rInflate(XmlPullParser parser, T parent, final AttributeSet attrs)
throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != parser.END_TAG ||
parser.getDepth() > depth) && type != parser.END_DOCUMENT) {
if (type != parser.START_TAG) {
continue;
}
if (onCreateCustomFromTag(parser, parent, attrs)) {
continue;
}
if (DEBUG) {
System.out.println("Now inflating tag: " + parser.getName());
}
String name = parser.getName();
T item = createItemFromTag(parser, name, attrs);
if (DEBUG) {
System.out
.println("Creating params from parent: " + parent);
}
((P) parent).addItemFromInflater(item);
if (DEBUG) {
System.out.println("-----> start inflating children");
}
rInflate(parser, item, attrs);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
}
}
/**
* Before this inflater tries to create an item from the tag, this method
* will be called. The parser will be pointing to the start of a tag, you
* must stop parsing and return when you reach the end of this element!
*
* @param parser XML dom node containing the description of the hierarchy.
* @param parent The item that should be the parent of whatever you create.
* @param attrs An AttributeSet of attributes to apply to the item.
* @return Whether you created a custom object (true), or whether this
* inflater should proceed to create an item.
*/
protected boolean onCreateCustomFromTag(XmlPullParser parser, T parent,
final AttributeSet attrs) throws XmlPullParserException {
return false;
}
protected P onMergeRoots(P givenRoot, boolean attachToGivenRoot, P xmlRoot) {
return xmlRoot;
}
}
@@ -0,0 +1,291 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
/**
* A {@link Preference} that displays a list of entries as
* a dialog.
* <p>
* This preference will store a string into the SharedPreferences. This string will be the value
* from the {@link #setEntryValues(CharSequence[])} array.
*
* @attr ref android.R.styleable#ListPreference_entries
* @attr ref android.R.styleable#ListPreference_entryValues
*/
public class ListPreference extends DialogPreference {
private CharSequence[] mEntries;
private CharSequence[] mEntryValues;
private String mValue;
private int mClickedDialogEntryIndex;
public ListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.ListPreference, 0, 0);
mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries);
mEntryValues = a.getTextArray(com.android.internal.R.styleable.ListPreference_entryValues);
a.recycle();
}
public ListPreference(Context context) {
this(context, null);
}
/**
* Sets the human-readable entries to be shown in the list. This will be
* shown in subsequent dialogs.
* <p>
* Each entry must have a corresponding index in
* {@link #setEntryValues(CharSequence[])}.
*
* @param entries The entries.
* @see #setEntryValues(CharSequence[])
*/
public void setEntries(CharSequence[] entries) {
mEntries = entries;
}
/**
* @see #setEntries(CharSequence[])
* @param entriesResId The entries array as a resource.
*/
public void setEntries(int entriesResId) {
setEntries(getContext().getResources().getTextArray(entriesResId));
}
/**
* The list of entries to be shown in the list in subsequent dialogs.
*
* @return The list as an array.
*/
public CharSequence[] getEntries() {
return mEntries;
}
/**
* The array to find the value to save for a preference when an entry from
* entries is selected. If a user clicks on the second item in entries, the
* second item in this array will be saved to the preference.
*
* @param entryValues The array to be used as values to save for the preference.
*/
public void setEntryValues(CharSequence[] entryValues) {
mEntryValues = entryValues;
}
/**
* @see #setEntryValues(CharSequence[])
* @param entryValuesResId The entry values array as a resource.
*/
public void setEntryValues(int entryValuesResId) {
setEntryValues(getContext().getResources().getTextArray(entryValuesResId));
}
/**
* Returns the array of values to be saved for the preference.
*
* @return The array of values.
*/
public CharSequence[] getEntryValues() {
return mEntryValues;
}
/**
* Sets the value of the key. This should be one of the entries in
* {@link #getEntryValues()}.
*
* @param value The value to set for the key.
*/
public void setValue(String value) {
mValue = value;
persistString(value);
}
/**
* Sets the value to the given index from the entry values.
*
* @param index The index of the value to set.
*/
public void setValueIndex(int index) {
if (mEntryValues != null) {
setValue(mEntryValues[index].toString());
}
}
/**
* Returns the value of the key. This should be one of the entries in
* {@link #getEntryValues()}.
*
* @return The value of the key.
*/
public String getValue() {
return mValue;
}
/**
* Returns the entry corresponding to the current value.
*
* @return The entry corresponding to the current value, or null.
*/
public CharSequence getEntry() {
int index = getValueIndex();
return index >= 0 && mEntries != null ? mEntries[index] : null;
}
/**
* Returns the index of the given value (in the entry values array).
*
* @param value The value whose index should be returned.
* @return The index of the value, or -1 if not found.
*/
public int findIndexOfValue(String value) {
if (value != null && mEntryValues != null) {
for (int i = mEntryValues.length - 1; i >= 0; i--) {
if (mEntryValues[i].equals(value)) {
return i;
}
}
}
return -1;
}
private int getValueIndex() {
return findIndexOfValue(mValue);
}
@Override
protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
if (mEntries == null || mEntryValues == null) {
throw new IllegalStateException(
"ListPreference requires an entries array and an entryValues array.");
}
mClickedDialogEntryIndex = getValueIndex();
builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mClickedDialogEntryIndex = which;
/*
* Clicking on an item simulates the positive button
* click, and dismisses the dialog.
*/
ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
dialog.dismiss();
}
});
/*
* 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(null, null);
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
String value = mEntryValues[mClickedDialogEntryIndex].toString();
if (callChangeListener(value)) {
setValue(value);
}
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue);
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
final SavedState myState = new SavedState(superState);
myState.value = getValue();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
setValue(myState.value);
}
private static class SavedState extends BaseSavedState {
String value;
public SavedState(Parcel source) {
super(source);
value = source.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(value);
}
public SavedState(Parcelable superState) {
super(superState);
}
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];
}
};
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2007 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 android.preference;
/**
* Interface definition for a callback to be invoked when this
* {@link Preference} changes with respect to enabling/disabling
* dependents.
*/
interface OnDependencyChangeListener {
/**
* Called when this preference has changed in a way that dependents should
* care to change their state.
*
* @param disablesDependent Whether the dependent should be disabled.
*/
void onDependencyChanged(Preference dependency, boolean disablesDependent);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,296 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
/**
* Shows a hierarchy of {@link Preference} objects as
* lists, possibly spanning multiple screens. These preferences will
* automatically save to {@link SharedPreferences} as the user interacts with
* them. To retrieve an instance of {@link SharedPreferences} that the
* preference hierarchy in this activity will use, call
* {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
* with a context in the same package as this activity.
* <p>
* Furthermore, the preferences shown will follow the visual style of system
* preferences. It is easy to create a hierarchy of preferences (that can be
* shown on multiple screens) via XML. For these reasons, it is recommended to
* use this activity (as a superclass) to deal with preferences in applications.
* <p>
* A {@link PreferenceScreen} object should be at the top of the preference
* hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy
* denote a screen break--that is the preferences contained within subsequent
* {@link PreferenceScreen} should be shown on another screen. The preference
* framework handles showing these other screens from the preference hierarchy.
* <p>
* The preference hierarchy can be formed in multiple ways:
* <li> From an XML file specifying the hierarchy
* <li> From different {@link Activity Activities} that each specify its own
* preferences in an XML file via {@link Activity} meta-data
* <li> From an object hierarchy rooted with {@link PreferenceScreen}
* <p>
* To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
* root element should be a {@link PreferenceScreen}. Subsequent elements can point
* to actual {@link Preference} subclasses. As mentioned above, subsequent
* {@link PreferenceScreen} in the hierarchy will result in the screen break.
* <p>
* To specify an {@link Intent} to query {@link Activity Activities} that each
* have preferences, use {@link #addPreferencesFromIntent}. Each
* {@link Activity} can specify meta-data in the manifest (via the key
* {@link PreferenceManager#METADATA_KEY_PREFERENCES}) that points to an XML
* resource. These XML resources will be inflated into a single preference
* hierarchy and shown by this activity.
* <p>
* To specify an object hierarchy rooted with {@link PreferenceScreen}, use
* {@link #setPreferenceScreen(PreferenceScreen)}.
* <p>
* As a convenience, this activity implements a click listener for any
* preference in the current hierarchy, see
* {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}.
*
* @see Preference
* @see PreferenceScreen
*/
public abstract class PreferenceActivity extends ListActivity implements
PreferenceManager.OnPreferenceTreeClickListener {
private static final String PREFERENCES_TAG = "android:preferences";
private PreferenceManager mPreferenceManager;
private Bundle mSavedInstanceState;
/**
* The starting request code given out to preference framework.
*/
private static final int FIRST_REQUEST_CODE = 100;
private static final int MSG_BIND_PREFERENCES = 0;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BIND_PREFERENCES:
bindPreferences();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(com.android.internal.R.layout.preference_list_content);
mPreferenceManager = onCreatePreferenceManager();
getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
}
@Override
protected void onStop() {
super.onStop();
mPreferenceManager.dispatchActivityStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
mPreferenceManager.dispatchActivityDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
Bundle container = new Bundle();
preferenceScreen.saveHierarchyState(container);
outState.putBundle(PREFERENCES_TAG, container);
}
}
@Override
protected void onRestoreInstanceState(Bundle state) {
Bundle container = state.getBundle(PREFERENCES_TAG);
if (container != null) {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
preferenceScreen.restoreHierarchyState(container);
mSavedInstanceState = state;
return;
}
}
// Only call this if we didn't save the instance state for later.
// If we did save it, it will be restored when we bind the adapter.
super.onRestoreInstanceState(state);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
}
@Override
public void onContentChanged() {
super.onContentChanged();
postBindPreferences();
}
/**
* Posts a message to bind the preferences to the list view.
* <p>
* Binding late is preferred as any custom preference types created in
* {@link #onCreate(Bundle)} are able to have their views recycled.
*/
private void postBindPreferences() {
if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}
private void bindPreferences() {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
if (preferenceScreen != null) {
preferenceScreen.bind(getListView());
if (mSavedInstanceState != null) {
super.onRestoreInstanceState(mSavedInstanceState);
mSavedInstanceState = null;
}
}
}
/**
* Creates the {@link PreferenceManager}.
*
* @return The {@link PreferenceManager} used by this activity.
*/
private PreferenceManager onCreatePreferenceManager() {
PreferenceManager preferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
preferenceManager.setOnPreferenceTreeClickListener(this);
return preferenceManager;
}
/**
* Returns the {@link PreferenceManager} used by this activity.
* @return The {@link PreferenceManager}.
*/
public PreferenceManager getPreferenceManager() {
return mPreferenceManager;
}
private void requirePreferenceManager() {
if (mPreferenceManager == null) {
throw new RuntimeException("This should be called after super.onCreate.");
}
}
/**
* Sets the root of the preference hierarchy that this activity is showing.
*
* @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
*/
public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
postBindPreferences();
CharSequence title = getPreferenceScreen().getTitle();
// Set the title of the activity
if (title != null) {
setTitle(title);
}
}
}
/**
* Gets the root of the preference hierarchy that this activity is showing.
*
* @return The {@link PreferenceScreen} that is the root of the preference
* hierarchy.
*/
public PreferenceScreen getPreferenceScreen() {
return mPreferenceManager.getPreferenceScreen();
}
/**
* Adds preferences from activities that match the given {@link Intent}.
*
* @param intent The {@link Intent} to query activities.
*/
public void addPreferencesFromIntent(Intent intent) {
requirePreferenceManager();
setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
}
/**
* Inflates the given XML resource and adds the preference hierarchy to the current
* preference hierarchy.
*
* @param preferencesResId The XML resource ID to inflate.
*/
public void addPreferencesFromResource(int preferencesResId) {
requirePreferenceManager();
setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId,
getPreferenceScreen()));
}
/**
* {@inheritDoc}
*/
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
return false;
}
/**
* Finds a {@link Preference} based on its key.
*
* @param key The key of the preference to retrieve.
* @return The {@link Preference} with the key, or null.
* @see PreferenceGroup#findPreference(CharSequence)
*/
public Preference findPreference(CharSequence key) {
if (mPreferenceManager == null) {
return null;
}
return mPreferenceManager.findPreference(key);
}
@Override
protected void onNewIntent(Intent intent) {
if (mPreferenceManager != null) {
mPreferenceManager.dispatchNewIntent(intent);
}
}
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2007 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 android.preference;
import java.util.Map;
import android.content.Context;
import android.util.AttributeSet;
/**
* Used to group {@link Preference} objects
* and provide a disabled title above the group.
*/
public class PreferenceCategory extends PreferenceGroup {
private static final String TAG = "PreferenceCategory";
public PreferenceCategory(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public PreferenceCategory(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.preferenceCategoryStyle);
}
public PreferenceCategory(Context context) {
this(context, null);
}
@Override
protected boolean onPrepareAddPreference(Preference preference) {
if (preference instanceof PreferenceCategory) {
throw new IllegalArgumentException(
"Cannot add a " + TAG + " directly to a " + TAG);
}
return super.onPrepareAddPreference(preference);
}
@Override
public boolean isEnabled() {
return false;
}
}
@@ -0,0 +1,324 @@
/*
* Copyright (C) 2007 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 android.preference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
/**
* A container for multiple
* {@link Preference} objects. It is a base class for Preference objects that are
* parents, such as {@link PreferenceCategory} and {@link PreferenceScreen}.
*
* @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
*/
public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {
/**
* The container for child {@link Preference}s. This is sorted based on the
* ordering, please use {@link #addPreference(Preference)} instead of adding
* to this directly.
*/
private List<Preference> mPreferenceList;
private boolean mOrderingAsAdded = true;
private int mCurrentPreferenceOrder = 0;
private boolean mAttachedToActivity = false;
public PreferenceGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mPreferenceList = new ArrayList<Preference>();
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.PreferenceGroup, defStyle, 0);
mOrderingAsAdded = a.getBoolean(com.android.internal.R.styleable.PreferenceGroup_orderingFromXml,
mOrderingAsAdded);
a.recycle();
}
public PreferenceGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Whether to order the {@link Preference} children of this group as they
* are added. If this is false, the ordering will follow each Preference
* order and default to alphabetic for those without an order.
* <p>
* If this is called after preferences are added, they will not be
* re-ordered in the order they were added, hence call this method early on.
*
* @param orderingAsAdded Whether to order according to the order added.
* @see Preference#setOrder(int)
*/
public void setOrderingAsAdded(boolean orderingAsAdded) {
mOrderingAsAdded = orderingAsAdded;
}
/**
* Whether this group is ordering preferences in the order they are added.
*
* @return Whether this group orders based on the order the children are added.
* @see #setOrderingAsAdded(boolean)
*/
public boolean isOrderingAsAdded() {
return mOrderingAsAdded;
}
/**
* Called by the inflater to add an item to this group.
*/
public void addItemFromInflater(Preference preference) {
addPreference(preference);
}
/**
* Returns the number of children {@link Preference}s.
* @return The number of preference children in this group.
*/
public int getPreferenceCount() {
return mPreferenceList.size();
}
/**
* Returns the {@link Preference} at a particular index.
*
* @param index The index of the {@link Preference} to retrieve.
* @return The {@link Preference}.
*/
public Preference getPreference(int index) {
return mPreferenceList.get(index);
}
/**
* Adds a {@link Preference} at the correct position based on the
* preference's order.
*
* @param preference The preference to add.
* @return Whether the preference is now in this group.
*/
public boolean addPreference(Preference preference) {
if (mPreferenceList.contains(preference)) {
// Exists
return true;
}
if (preference.getOrder() == Preference.DEFAULT_ORDER) {
if (mOrderingAsAdded) {
preference.setOrder(mCurrentPreferenceOrder++);
}
if (preference instanceof PreferenceGroup) {
// TODO: fix (method is called tail recursively when inflating,
// so we won't end up properly passing this flag down to children
((PreferenceGroup)preference).setOrderingAsAdded(mOrderingAsAdded);
}
}
int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
if (insertionIndex < 0) {
insertionIndex = insertionIndex * -1 - 1;
}
if (!onPrepareAddPreference(preference)) {
return false;
}
synchronized(this) {
mPreferenceList.add(insertionIndex, preference);
}
preference.onAttachedToHierarchy(getPreferenceManager());
if (mAttachedToActivity) {
preference.onAttachedToActivity();
}
notifyHierarchyChanged();
return true;
}
/**
* Removes a {@link Preference} from this group.
*
* @param preference The preference to remove.
* @return Whether the preference was found and removed.
*/
public boolean removePreference(Preference preference) {
final boolean returnValue = removePreferenceInt(preference);
notifyHierarchyChanged();
return returnValue;
}
private boolean removePreferenceInt(Preference preference) {
synchronized(this) {
preference.onPrepareForRemoval();
return mPreferenceList.remove(preference);
}
}
/**
* Removes all {@link Preference Preferences} from this group.
*/
public void removeAll() {
synchronized(this) {
List<Preference> preferenceList = mPreferenceList;
for (int i = preferenceList.size() - 1; i >= 0; i--) {
removePreferenceInt(preferenceList.get(0));
}
}
notifyHierarchyChanged();
}
/**
* Prepares a {@link Preference} to be added to the group.
*
* @param preference The preference to add.
* @return Whether to allow adding the preference (true), or not (false).
*/
protected boolean onPrepareAddPreference(Preference preference) {
if (!super.isEnabled()) {
preference.setEnabled(false);
}
return true;
}
/**
* Finds a {@link Preference} based on its key. If two {@link Preference}
* share the same key (not recommended), the first to appear will be
* returned (to retrieve the other preference with the same key, call this
* method on the first preference). If this preference has the key, it will
* not be returned.
* <p>
* This will recursively search for the preference into children that are
* also {@link PreferenceGroup PreferenceGroups}.
*
* @param key The key of the preference to retrieve.
* @return The {@link Preference} with the key, or null.
*/
public Preference findPreference(CharSequence key) {
if (TextUtils.equals(getKey(), key)) {
return this;
}
final int preferenceCount = getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
final Preference preference = getPreference(i);
final String curKey = preference.getKey();
if (curKey != null && curKey.equals(key)) {
return preference;
}
if (preference instanceof PreferenceGroup) {
final Preference returnedPreference = ((PreferenceGroup)preference)
.findPreference(key);
if (returnedPreference != null) {
return returnedPreference;
}
}
}
return null;
}
/**
* Whether this preference group should be shown on the same screen as its
* contained preferences.
*
* @return True if the contained preferences should be shown on the same
* screen as this preference.
*/
protected boolean isOnSameScreenAsChildren() {
return true;
}
@Override
protected void onAttachedToActivity() {
super.onAttachedToActivity();
// Mark as attached so if a preference is later added to this group, we
// can tell it we are already attached
mAttachedToActivity = true;
// Dispatch to all contained preferences
final int preferenceCount = getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
getPreference(i).onAttachedToActivity();
}
}
@Override
protected void onPrepareForRemoval() {
super.onPrepareForRemoval();
// We won't be attached to the activity anymore
mAttachedToActivity = false;
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
// Dispatch to all contained preferences
final int preferenceCount = getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
getPreference(i).setEnabled(enabled);
}
}
void sortPreferences() {
synchronized (this) {
Collections.sort(mPreferenceList);
}
}
@Override
protected void dispatchSaveInstanceState(Bundle container) {
super.dispatchSaveInstanceState(container);
// Dispatch to all contained preferences
final int preferenceCount = getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
getPreference(i).dispatchSaveInstanceState(container);
}
}
@Override
protected void dispatchRestoreInstanceState(Bundle container) {
super.dispatchRestoreInstanceState(container);
// Dispatch to all contained preferences
final int preferenceCount = getPreferenceCount();
for (int i = 0; i < preferenceCount; i++) {
getPreference(i).dispatchRestoreInstanceState(container);
}
}
}
@@ -0,0 +1,283 @@
/*
* Copyright (C) 2007 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 android.preference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.os.Handler;
import android.preference.Preference.OnPreferenceChangeInternalListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.ListView;
/**
* An adapter that returns the {@link Preference} contained in this group.
* In most cases, this adapter should be the base class for any custom
* adapters from {@link Preference#getAdapter()}.
* <p>
* This adapter obeys the
* {@link Preference}'s adapter rule (the
* {@link Adapter#getView(int, View, ViewGroup)} should be used instead of
* {@link Preference#getView(ViewGroup)} if a {@link Preference} has an
* adapter via {@link Preference#getAdapter()}).
* <p>
* This adapter also propagates data change/invalidated notifications upward.
* <p>
* This adapter does not include this {@link PreferenceGroup} in the returned
* adapter, use {@link PreferenceCategoryAdapter} instead.
*
* @see PreferenceCategoryAdapter
*/
class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeInternalListener {
private static final String TAG = "PreferenceGroupAdapter";
/**
* The group that we are providing data from.
*/
private PreferenceGroup mPreferenceGroup;
/**
* Maps a position into this adapter -> {@link Preference}. These
* {@link Preference}s don't have to be direct children of this
* {@link PreferenceGroup}, they can be grand children or younger)
*/
private List<Preference> mPreferenceList;
/**
* List of unique Preference and its subclasses' names. This is used to find
* out how many types of views this adapter can return. Once the count is
* returned, this cannot be modified (since the ListView only checks the
* count once--when the adapter is being set). We will not recycle views for
* Preference subclasses seen after the count has been returned.
*/
private ArrayList<PreferenceLayout> mPreferenceLayouts;
private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();
/**
* Blocks the mPreferenceClassNames from being changed anymore.
*/
private boolean mHasReturnedViewTypeCount = false;
private volatile boolean mIsSyncing = false;
private Handler mHandler = new Handler();
private Runnable mSyncRunnable = new Runnable() {
public void run() {
syncMyPreferences();
}
};
private static class PreferenceLayout implements Comparable<PreferenceLayout> {
private int resId;
private int widgetResId;
private String name;
public int compareTo(PreferenceLayout other) {
int compareNames = name.compareTo(other.name);
if (compareNames == 0) {
if (resId == other.resId) {
if (widgetResId == other.widgetResId) {
return 0;
} else {
return widgetResId - other.widgetResId;
}
} else {
return resId - other.resId;
}
} else {
return compareNames;
}
}
}
public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
mPreferenceGroup = preferenceGroup;
// If this group gets or loses any children, let us know
mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
mPreferenceList = new ArrayList<Preference>();
mPreferenceLayouts = new ArrayList<PreferenceLayout>();
syncMyPreferences();
}
private void syncMyPreferences() {
synchronized(this) {
if (mIsSyncing) {
return;
}
mIsSyncing = true;
}
List<Preference> newPreferenceList = new ArrayList<Preference>(mPreferenceList.size());
flattenPreferenceGroup(newPreferenceList, mPreferenceGroup);
mPreferenceList = newPreferenceList;
notifyDataSetChanged();
synchronized(this) {
mIsSyncing = false;
notifyAll();
}
}
private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {
// TODO: shouldn't always?
group.sortPreferences();
final int groupSize = group.getPreferenceCount();
for (int i = 0; i < groupSize; i++) {
final Preference preference = group.getPreference(i);
preferences.add(preference);
if (!mHasReturnedViewTypeCount && !preference.hasSpecifiedLayout()) {
addPreferenceClassName(preference);
}
if (preference instanceof PreferenceGroup) {
final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;
if (preferenceAsGroup.isOnSameScreenAsChildren()) {
flattenPreferenceGroup(preferences, preferenceAsGroup);
}
}
preference.setOnPreferenceChangeInternalListener(this);
}
}
/**
* Creates a string that includes the preference name, layout id and widget layout id.
* If a particular preference type uses 2 different resources, they will be treated as
* different view types.
*/
private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {
PreferenceLayout pl = in != null? in : new PreferenceLayout();
pl.name = preference.getClass().getName();
pl.resId = preference.getLayoutResource();
pl.widgetResId = preference.getWidgetLayoutResource();
return pl;
}
private void addPreferenceClassName(Preference preference) {
final PreferenceLayout pl = createPreferenceLayout(preference, null);
int insertPos = Collections.binarySearch(mPreferenceLayouts, pl);
// Only insert if it doesn't exist (when it is negative).
if (insertPos < 0) {
// Convert to insert index
insertPos = insertPos * -1 - 1;
mPreferenceLayouts.add(insertPos, pl);
}
}
public int getCount() {
return mPreferenceList.size();
}
public Preference getItem(int position) {
if (position < 0 || position >= getCount()) return null;
return mPreferenceList.get(position);
}
public long getItemId(int position) {
if (position < 0 || position >= getCount()) return ListView.INVALID_ROW_ID;
return this.getItem(position).getId();
}
public View getView(int position, View convertView, ViewGroup parent) {
final Preference preference = this.getItem(position);
// Build a PreferenceLayout to compare with known ones that are cacheable.
mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
// If it's not one of the cached ones, set the convertView to null so that
// the layout gets re-created by the Preference.
if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0) {
convertView = null;
}
return preference.getView(convertView, parent);
}
@Override
public boolean isEnabled(int position) {
if (position < 0 || position >= getCount()) return true;
return this.getItem(position).isSelectable();
}
@Override
public boolean areAllItemsEnabled() {
// There should always be a preference group, and these groups are always
// disabled
return false;
}
public void onPreferenceChange(Preference preference) {
notifyDataSetChanged();
}
public void onPreferenceHierarchyChange(Preference preference) {
mHandler.removeCallbacks(mSyncRunnable);
mHandler.post(mSyncRunnable);
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public int getItemViewType(int position) {
if (!mHasReturnedViewTypeCount) {
mHasReturnedViewTypeCount = true;
}
final Preference preference = this.getItem(position);
if (preference.hasSpecifiedLayout()) {
return IGNORE_ITEM_VIEW_TYPE;
}
mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
int viewType = Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout);
if (viewType < 0) {
// This is a class that was seen after we returned the count, so
// don't recycle it.
return IGNORE_ITEM_VIEW_TYPE;
} else {
return viewType;
}
}
@Override
public int getViewTypeCount() {
if (!mHasReturnedViewTypeCount) {
mHasReturnedViewTypeCount = true;
}
return Math.max(1, mPreferenceLayouts.size());
}
}
@@ -0,0 +1,103 @@
/*
* Copyright (C) 2007 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 android.preference;
import java.io.IOException;
import java.util.Map;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.app.AliasActivity;
import android.content.Context;
import android.content.Intent;
import android.util.AttributeSet;
import android.util.Log;
/**
* The {@link PreferenceInflater} is used to inflate preference hierarchies from
* XML files.
* <p>
* Do not construct this directly, instead use
* {@link Context#getSystemService(String)} with
* {@link Context#PREFERENCE_INFLATER_SERVICE}.
*/
class PreferenceInflater extends GenericInflater<Preference, PreferenceGroup> {
private static final String TAG = "PreferenceInflater";
private static final String INTENT_TAG_NAME = "intent";
private PreferenceManager mPreferenceManager;
public PreferenceInflater(Context context, PreferenceManager preferenceManager) {
super(context);
init(preferenceManager);
}
PreferenceInflater(GenericInflater<Preference, PreferenceGroup> original, PreferenceManager preferenceManager, Context newContext) {
super(original, newContext);
init(preferenceManager);
}
@Override
public GenericInflater<Preference, PreferenceGroup> cloneInContext(Context newContext) {
return new PreferenceInflater(this, mPreferenceManager, newContext);
}
private void init(PreferenceManager preferenceManager) {
mPreferenceManager = preferenceManager;
setDefaultPackage("android.preference.");
}
@Override
protected boolean onCreateCustomFromTag(XmlPullParser parser, Preference parentPreference,
AttributeSet attrs) throws XmlPullParserException {
final String tag = parser.getName();
if (tag.equals(INTENT_TAG_NAME)) {
Intent intent = null;
try {
intent = Intent.parseIntent(getContext().getResources(), parser, attrs);
} catch (IOException e) {
Log.w(TAG, "Could not parse Intent.");
Log.w(TAG, e);
}
if (intent != null) {
parentPreference.setIntent(intent);
}
return true;
}
return false;
}
@Override
protected PreferenceGroup onMergeRoots(PreferenceGroup givenRoot, boolean attachToGivenRoot,
PreferenceGroup xmlRoot) {
// If we were given a Preferences, use it as the root (ignoring the root
// Preferences from the XML file).
if (givenRoot == null) {
xmlRoot.onAttachedToHierarchy(mPreferenceManager);
return xmlRoot;
} else {
return givenRoot;
}
}
}
@@ -0,0 +1,815 @@
/*
* Copyright (C) 2007 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 android.preference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.util.Log;
/**
* Used to help create {@link Preference} hierarchies
* from activities or XML.
* <p>
* In most cases, clients should use
* {@link PreferenceActivity#addPreferencesFromIntent} or
* {@link PreferenceActivity#addPreferencesFromResource(int)}.
*
* @see PreferenceActivity
*/
public class PreferenceManager {
private static final String TAG = "PreferenceManager";
/**
* The Activity meta-data key for its XML preference hierarchy.
*/
public static final String METADATA_KEY_PREFERENCES = "android.preference";
public static final String KEY_HAS_SET_DEFAULT_VALUES = "_has_set_default_values";
/**
* @see #getActivity()
*/
private Activity mActivity;
/**
* The context to use. This should always be set.
*
* @see #mActivity
*/
private Context mContext;
/**
* The counter for unique IDs.
*/
private long mNextId = 0;
/**
* The counter for unique request codes.
*/
private int mNextRequestCode;
/**
* Cached shared preferences.
*/
private SharedPreferences mSharedPreferences;
/**
* If in no-commit mode, the shared editor to give out (which will be
* committed when exiting no-commit mode).
*/
private SharedPreferences.Editor mEditor;
/**
* Blocks commits from happening on the shared editor. This is used when
* inflating the hierarchy. Do not set this directly, use {@link #setNoCommit(boolean)}
*/
private boolean mNoCommit;
/**
* The SharedPreferences name that will be used for all {@link Preference}s
* managed by this instance.
*/
private String mSharedPreferencesName;
/**
* The SharedPreferences mode that will be used for all {@link Preference}s
* managed by this instance.
*/
private int mSharedPreferencesMode;
/**
* The {@link PreferenceScreen} at the root of the preference hierarchy.
*/
private PreferenceScreen mPreferenceScreen;
/**
* List of activity result listeners.
*/
private List<OnActivityResultListener> mActivityResultListeners;
/**
* List of activity stop listeners.
*/
private List<OnActivityStopListener> mActivityStopListeners;
/**
* List of activity destroy listeners.
*/
private List<OnActivityDestroyListener> mActivityDestroyListeners;
/**
* List of dialogs that should be dismissed when we receive onNewIntent in
* our PreferenceActivity.
*/
private List<DialogInterface> mPreferencesScreens;
private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener;
PreferenceManager(Activity activity, int firstRequestCode) {
mActivity = activity;
mNextRequestCode = firstRequestCode;
init(activity);
}
/**
* This constructor should ONLY be used when getting default values from
* an XML preference hierarchy.
* <p>
* The {@link PreferenceManager#PreferenceManager(Activity)}
* should be used ANY time a preference will be displayed, since some preference
* types need an Activity for managed queries.
*/
private PreferenceManager(Context context) {
init(context);
}
private void init(Context context) {
mContext = context;
setSharedPreferencesName(getDefaultSharedPreferencesName(context));
}
/**
* Returns a list of {@link Activity} (indirectly) that match a given
* {@link Intent}.
*
* @param queryIntent The Intent to match.
* @return The list of {@link ResolveInfo} that point to the matched
* activities.
*/
private List<ResolveInfo> queryIntentActivities(Intent queryIntent) {
return mContext.getPackageManager().queryIntentActivities(queryIntent,
PackageManager.GET_META_DATA);
}
/**
* Inflates a preference hierarchy from the preference hierarchies of
* {@link Activity Activities} that match the given {@link Intent}. An
* {@link Activity} defines its preference hierarchy with meta-data using
* the {@link #METADATA_KEY_PREFERENCES} key.
* <p>
* If a preference hierarchy is given, the new preference hierarchies will
* be merged in.
*
* @param queryIntent The intent to match activities.
* @param rootPreferences Optional existing hierarchy to merge the new
* hierarchies into.
* @return The root hierarchy (if one was not provided, the new hierarchy's
* root).
*/
PreferenceScreen inflateFromIntent(Intent queryIntent, PreferenceScreen rootPreferences) {
final List<ResolveInfo> activities = queryIntentActivities(queryIntent);
final HashSet<String> inflatedRes = new HashSet<String>();
for (int i = activities.size() - 1; i >= 0; i--) {
final ActivityInfo activityInfo = activities.get(i).activityInfo;
final Bundle metaData = activityInfo.metaData;
if ((metaData == null) || !metaData.containsKey(METADATA_KEY_PREFERENCES)) {
continue;
}
// Need to concat the package with res ID since the same res ID
// can be re-used across contexts
final String uniqueResId = activityInfo.packageName + ":"
+ activityInfo.metaData.getInt(METADATA_KEY_PREFERENCES);
if (!inflatedRes.contains(uniqueResId)) {
inflatedRes.add(uniqueResId);
final Context context;
try {
context = mContext.createPackageContext(activityInfo.packageName, 0);
} catch (NameNotFoundException e) {
Log.w(TAG, "Could not create context for " + activityInfo.packageName + ": "
+ Log.getStackTraceString(e));
continue;
}
final PreferenceInflater inflater = new PreferenceInflater(context, this);
final XmlResourceParser parser = activityInfo.loadXmlMetaData(context
.getPackageManager(), METADATA_KEY_PREFERENCES);
rootPreferences = (PreferenceScreen) inflater
.inflate(parser, rootPreferences, true);
parser.close();
}
}
rootPreferences.onAttachedToHierarchy(this);
return rootPreferences;
}
/**
* Inflates a preference hierarchy from XML. If a preference hierarchy is
* given, the new preference hierarchies will be merged in.
*
* @param context The context of the resource.
* @param resId The resource ID of the XML to inflate.
* @param rootPreferences Optional existing hierarchy to merge the new
* hierarchies into.
* @return The root hierarchy (if one was not provided, the new hierarchy's
* root).
* @hide
*/
public PreferenceScreen inflateFromResource(Context context, int resId,
PreferenceScreen rootPreferences) {
// Block commits
setNoCommit(true);
final PreferenceInflater inflater = new PreferenceInflater(context, this);
rootPreferences = (PreferenceScreen) inflater.inflate(resId, rootPreferences, true);
rootPreferences.onAttachedToHierarchy(this);
// Unblock commits
setNoCommit(false);
return rootPreferences;
}
public PreferenceScreen createPreferenceScreen(Context context) {
final PreferenceScreen preferenceScreen = new PreferenceScreen(context, null);
preferenceScreen.onAttachedToHierarchy(this);
return preferenceScreen;
}
/**
* Called by a preference to get a unique ID in its hierarchy.
*
* @return A unique ID.
*/
long getNextId() {
synchronized (this) {
return mNextId++;
}
}
/**
* Returns the current name of the SharedPreferences file that preferences managed by
* this will use.
*
* @return The name that can be passed to {@link Context#getSharedPreferences(String, int)}.
* @see Context#getSharedPreferences(String, int)
*/
public String getSharedPreferencesName() {
return mSharedPreferencesName;
}
/**
* Sets the name of the SharedPreferences file that preferences managed by this
* will use.
*
* @param sharedPreferencesName The name of the SharedPreferences file.
* @see Context#getSharedPreferences(String, int)
*/
public void setSharedPreferencesName(String sharedPreferencesName) {
mSharedPreferencesName = sharedPreferencesName;
mSharedPreferences = null;
}
/**
* Returns the current mode of the SharedPreferences file that preferences managed by
* this will use.
*
* @return The mode that can be passed to {@link Context#getSharedPreferences(String, int)}.
* @see Context#getSharedPreferences(String, int)
*/
public int getSharedPreferencesMode() {
return mSharedPreferencesMode;
}
/**
* Sets the mode of the SharedPreferences file that preferences managed by this
* will use.
*
* @param sharedPreferencesMode The mode of the SharedPreferences file.
* @see Context#getSharedPreferences(String, int)
*/
public void setSharedPreferencesMode(int sharedPreferencesMode) {
mSharedPreferencesMode = sharedPreferencesMode;
mSharedPreferences = null;
}
/**
* Gets a SharedPreferences instance that preferences managed by this will
* use.
*
* @return A SharedPreferences instance pointing to the file that contains
* the values of preferences that are managed by this.
*/
public SharedPreferences getSharedPreferences() {
if (mSharedPreferences == null) {
mSharedPreferences = mContext.getSharedPreferences(mSharedPreferencesName,
mSharedPreferencesMode);
}
return mSharedPreferences;
}
/**
* Gets a SharedPreferences instance that points to the default file that is
* used by the preference framework in the given context.
*
* @param context The context of the preferences whose values are wanted.
* @return A SharedPreferences instance that can be used to retrieve and
* listen to values of the preferences.
*/
public static SharedPreferences getDefaultSharedPreferences(Context context) {
return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
getDefaultSharedPreferencesMode());
}
private static String getDefaultSharedPreferencesName(Context context) {
return context.getPackageName() + "_preferences";
}
private static int getDefaultSharedPreferencesMode() {
return Context.MODE_PRIVATE;
}
/**
* Returns the root of the preference hierarchy managed by this class.
*
* @return The {@link PreferenceScreen} object that is at the root of the hierarchy.
*/
PreferenceScreen getPreferenceScreen() {
return mPreferenceScreen;
}
/**
* Sets the root of the preference hierarchy.
*
* @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
* @return Whether the {@link PreferenceScreen} given is different than the previous.
*/
boolean setPreferences(PreferenceScreen preferenceScreen) {
if (preferenceScreen != mPreferenceScreen) {
mPreferenceScreen = preferenceScreen;
return true;
}
return false;
}
/**
* Finds a {@link Preference} based on its key.
*
* @param key The key of the preference to retrieve.
* @return The {@link Preference} with the key, or null.
* @see PreferenceGroup#findPreference(CharSequence)
*/
public Preference findPreference(CharSequence key) {
if (mPreferenceScreen == null) {
return null;
}
return mPreferenceScreen.findPreference(key);
}
/**
* Sets the default values from a preference hierarchy in XML. This should
* be called by the application's main activity.
* <p>
* If {@code readAgain} is false, this will only set the default values if this
* method has never been called in the past (or the
* {@link #KEY_HAS_SET_DEFAULT_VALUES} in the default value shared
* preferences file is false). To attempt to set the default values again
* bypassing this check, set {@code readAgain} to true.
*
* @param context The context of the shared preferences.
* @param resId The resource ID of the preference hierarchy XML file.
* @param readAgain Whether to re-read the default values.
* <p>
* Note: this will NOT reset preferences back to their default
* values. For that functionality, use
* {@link PreferenceManager#getDefaultSharedPreferences(Context)}
* and clear it followed by a call to this method with this
* parameter set to true.
*/
public static void setDefaultValues(Context context, int resId, boolean readAgain) {
// Use the default shared preferences name and mode
setDefaultValues(context, getDefaultSharedPreferencesName(context),
getDefaultSharedPreferencesMode(), resId, readAgain);
}
/**
* Similar to {@link #setDefaultValues(Context, int, boolean)} but allows
* the client to provide the filename and mode of the shared preferences
* file.
*
* @see #setDefaultValues(Context, int, boolean)
* @see #setSharedPreferencesName(String)
* @see #setSharedPreferencesMode(int)
*/
public static void setDefaultValues(Context context, String sharedPreferencesName,
int sharedPreferencesMode, int resId, boolean readAgain) {
final SharedPreferences defaultValueSp = context.getSharedPreferences(
KEY_HAS_SET_DEFAULT_VALUES, Context.MODE_PRIVATE);
if (readAgain || !defaultValueSp.getBoolean(KEY_HAS_SET_DEFAULT_VALUES, false)) {
final PreferenceManager pm = new PreferenceManager(context);
pm.setSharedPreferencesName(sharedPreferencesName);
pm.setSharedPreferencesMode(sharedPreferencesMode);
pm.inflateFromResource(context, resId, null);
SharedPreferences.Editor editor =
defaultValueSp.edit().putBoolean(KEY_HAS_SET_DEFAULT_VALUES, true);
try {
editor.apply();
} catch (AbstractMethodError unused) {
// The app injected its own pre-Gingerbread
// SharedPreferences.Editor implementation without
// an apply method.
editor.commit();
}
}
}
/**
* Returns an editor to use when modifying the shared preferences.
* <p>
* Do NOT commit unless {@link #shouldCommit()} returns true.
*
* @return An editor to use to write to shared preferences.
* @see #shouldCommit()
*/
SharedPreferences.Editor getEditor() {
if (mNoCommit) {
if (mEditor == null) {
mEditor = getSharedPreferences().edit();
}
return mEditor;
} else {
return getSharedPreferences().edit();
}
}
/**
* Whether it is the client's responsibility to commit on the
* {@link #getEditor()}. This will return false in cases where the writes
* should be batched, for example when inflating preferences from XML.
*
* @return Whether the client should commit.
*/
boolean shouldCommit() {
return !mNoCommit;
}
private void setNoCommit(boolean noCommit) {
if (!noCommit && mEditor != null) {
try {
mEditor.apply();
} catch (AbstractMethodError unused) {
// The app injected its own pre-Gingerbread
// SharedPreferences.Editor implementation without
// an apply method.
mEditor.commit();
}
}
mNoCommit = noCommit;
}
/**
* Returns the activity that shows the preferences. This is useful for doing
* managed queries, but in most cases the use of {@link #getContext()} is
* preferred.
* <p>
* This will return null if this class was instantiated with a Context
* instead of Activity. For example, when setting the default values.
*
* @return The activity that shows the preferences.
* @see #mContext
*/
Activity getActivity() {
return mActivity;
}
/**
* Returns the context. This is preferred over {@link #getActivity()} when
* possible.
*
* @return The context.
*/
Context getContext() {
return mContext;
}
/**
* Registers a listener.
*
* @see OnActivityResultListener
*/
void registerOnActivityResultListener(OnActivityResultListener listener) {
synchronized (this) {
if (mActivityResultListeners == null) {
mActivityResultListeners = new ArrayList<OnActivityResultListener>();
}
if (!mActivityResultListeners.contains(listener)) {
mActivityResultListeners.add(listener);
}
}
}
/**
* Unregisters a listener.
*
* @see OnActivityResultListener
*/
void unregisterOnActivityResultListener(OnActivityResultListener listener) {
synchronized (this) {
if (mActivityResultListeners != null) {
mActivityResultListeners.remove(listener);
}
}
}
/**
* Called by the {@link PreferenceManager} to dispatch a subactivity result.
*/
void dispatchActivityResult(int requestCode, int resultCode, Intent data) {
List<OnActivityResultListener> list;
synchronized (this) {
if (mActivityResultListeners == null) return;
list = new ArrayList<OnActivityResultListener>(mActivityResultListeners);
}
final int N = list.size();
for (int i = 0; i < N; i++) {
if (list.get(i).onActivityResult(requestCode, resultCode, data)) {
break;
}
}
}
/**
* Registers a listener.
*
* @see OnActivityStopListener
*/
void registerOnActivityStopListener(OnActivityStopListener listener) {
synchronized (this) {
if (mActivityStopListeners == null) {
mActivityStopListeners = new ArrayList<OnActivityStopListener>();
}
if (!mActivityStopListeners.contains(listener)) {
mActivityStopListeners.add(listener);
}
}
}
/**
* Unregisters a listener.
*
* @see OnActivityStopListener
*/
void unregisterOnActivityStopListener(OnActivityStopListener listener) {
synchronized (this) {
if (mActivityStopListeners != null) {
mActivityStopListeners.remove(listener);
}
}
}
/**
* Called by the {@link PreferenceManager} to dispatch the activity stop
* event.
*/
void dispatchActivityStop() {
List<OnActivityStopListener> list;
synchronized (this) {
if (mActivityStopListeners == null) return;
list = new ArrayList<OnActivityStopListener>(mActivityStopListeners);
}
final int N = list.size();
for (int i = 0; i < N; i++) {
list.get(i).onActivityStop();
}
}
/**
* Registers a listener.
*
* @see OnActivityDestroyListener
*/
void registerOnActivityDestroyListener(OnActivityDestroyListener listener) {
synchronized (this) {
if (mActivityDestroyListeners == null) {
mActivityDestroyListeners = new ArrayList<OnActivityDestroyListener>();
}
if (!mActivityDestroyListeners.contains(listener)) {
mActivityDestroyListeners.add(listener);
}
}
}
/**
* Unregisters a listener.
*
* @see OnActivityDestroyListener
*/
void unregisterOnActivityDestroyListener(OnActivityDestroyListener listener) {
synchronized (this) {
if (mActivityDestroyListeners != null) {
mActivityDestroyListeners.remove(listener);
}
}
}
/**
* Called by the {@link PreferenceManager} to dispatch the activity destroy
* event.
*/
void dispatchActivityDestroy() {
List<OnActivityDestroyListener> list = null;
synchronized (this) {
if (mActivityDestroyListeners != null) {
list = new ArrayList<OnActivityDestroyListener>(mActivityDestroyListeners);
}
}
if (list != null) {
final int N = list.size();
for (int i = 0; i < N; i++) {
list.get(i).onActivityDestroy();
}
}
// Dismiss any PreferenceScreens still showing
dismissAllScreens();
}
/**
* Returns a request code that is unique for the activity. Each subsequent
* call to this method should return another unique request code.
*
* @return A unique request code that will never be used by anyone other
* than the caller of this method.
*/
int getNextRequestCode() {
synchronized (this) {
return mNextRequestCode++;
}
}
void addPreferencesScreen(DialogInterface screen) {
synchronized (this) {
if (mPreferencesScreens == null) {
mPreferencesScreens = new ArrayList<DialogInterface>();
}
mPreferencesScreens.add(screen);
}
}
void removePreferencesScreen(DialogInterface screen) {
synchronized (this) {
if (mPreferencesScreens == null) {
return;
}
mPreferencesScreens.remove(screen);
}
}
/**
* Called by {@link PreferenceActivity} to dispatch the new Intent event.
*
* @param intent The new Intent.
*/
void dispatchNewIntent(Intent intent) {
dismissAllScreens();
}
private void dismissAllScreens() {
// Remove any of the previously shown preferences screens
ArrayList<DialogInterface> screensToDismiss;
synchronized (this) {
if (mPreferencesScreens == null) {
return;
}
screensToDismiss = new ArrayList<DialogInterface>(mPreferencesScreens);
mPreferencesScreens.clear();
}
for (int i = screensToDismiss.size() - 1; i >= 0; i--) {
screensToDismiss.get(i).dismiss();
}
}
/**
* Sets the callback to be invoked when a {@link Preference} in the
* hierarchy rooted at this {@link PreferenceManager} is clicked.
*
* @param listener The callback to be invoked.
*/
void setOnPreferenceTreeClickListener(OnPreferenceTreeClickListener listener) {
mOnPreferenceTreeClickListener = listener;
}
OnPreferenceTreeClickListener getOnPreferenceTreeClickListener() {
return mOnPreferenceTreeClickListener;
}
/**
* Interface definition for a callback to be invoked when a
* {@link Preference} in the hierarchy rooted at this {@link PreferenceScreen} is
* clicked.
*/
interface OnPreferenceTreeClickListener {
/**
* Called when a preference in the tree rooted at this
* {@link PreferenceScreen} has been clicked.
*
* @param preferenceScreen The {@link PreferenceScreen} that the
* preference is located in.
* @param preference The preference that was clicked.
* @return Whether the click was handled.
*/
boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference);
}
/**
* Interface definition for a class that will be called when the container's activity
* receives an activity result.
*/
public interface OnActivityResultListener {
/**
* See Activity's onActivityResult.
*
* @return Whether the request code was handled (in which case
* subsequent listeners will not be called.
*/
boolean onActivityResult(int requestCode, int resultCode, Intent data);
}
/**
* Interface definition for a class that will be called when the container's activity
* is stopped.
*/
public interface OnActivityStopListener {
/**
* See Activity's onStop.
*/
void onActivityStop();
}
/**
* Interface definition for a class that will be called when the container's activity
* is destroyed.
*/
public interface OnActivityDestroyListener {
/**
* See Activity's onDestroy.
*/
void onActivityDestroy();
}
}
@@ -0,0 +1,260 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
/**
* Represents a top-level {@link Preference} that
* is the root of a Preference hierarchy. A {@link PreferenceActivity}
* points to an instance of this class to show the preferences. To instantiate
* this class, use {@link PreferenceManager#createPreferenceScreen(Context)}.
* <ul>
* This class can appear in two places:
* <li> When a {@link PreferenceActivity} points to this, it is used as the root
* and is not shown (only the contained preferences are shown).
* <li> When it appears inside another preference hierarchy, it is shown and
* serves as the gateway to another screen of preferences (either by showing
* another screen of preferences as a {@link Dialog} or via a
* {@link Context#startActivity(android.content.Intent)} from the
* {@link Preference#getIntent()}). The children of this {@link PreferenceScreen}
* are NOT shown in the screen that this {@link PreferenceScreen} is shown in.
* Instead, a separate screen will be shown when this preference is clicked.
* </ul>
* <p>Here's an example XML layout of a PreferenceScreen:</p>
* <pre>
&lt;PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="first_preferencescreen"&gt;
&lt;CheckBoxPreference
android:key="wifi enabled"
android:title="WiFi" /&gt;
&lt;PreferenceScreen
android:key="second_preferencescreen"
android:title="WiFi settings"&gt;
&lt;CheckBoxPreference
android:key="prefer wifi"
android:title="Prefer WiFi" /&gt;
... other preferences here ...
&lt;/PreferenceScreen&gt;
&lt;/PreferenceScreen&gt; </pre>
* <p>
* In this example, the "first_preferencescreen" will be used as the root of the
* hierarchy and given to a {@link PreferenceActivity}. The first screen will
* show preferences "WiFi" (which can be used to quickly enable/disable WiFi)
* and "WiFi settings". The "WiFi settings" is the "second_preferencescreen" and when
* clicked will show another screen of preferences such as "Prefer WiFi" (and
* the other preferences that are children of the "second_preferencescreen" tag).
*
* @see PreferenceCategory
*/
public final class PreferenceScreen extends PreferenceGroup implements AdapterView.OnItemClickListener,
DialogInterface.OnDismissListener {
private ListAdapter mRootAdapter;
private Dialog mDialog;
/**
* Do NOT use this constructor, use {@link PreferenceManager#createPreferenceScreen(Context)}.
* @hide-
*/
public PreferenceScreen(Context context, AttributeSet attrs) {
super(context, attrs, com.android.internal.R.attr.preferenceScreenStyle);
}
/**
* Returns an adapter that can be attached to a {@link PreferenceActivity}
* to show the preferences contained in this {@link PreferenceScreen}.
* <p>
* This {@link PreferenceScreen} will NOT appear in the returned adapter, instead
* it appears in the hierarchy above this {@link PreferenceScreen}.
* <p>
* This adapter's {@link Adapter#getItem(int)} should always return a
* subclass of {@link Preference}.
*
* @return An adapter that provides the {@link Preference} contained in this
* {@link PreferenceScreen}.
*/
public ListAdapter getRootAdapter() {
if (mRootAdapter == null) {
mRootAdapter = onCreateRootAdapter();
}
return mRootAdapter;
}
/**
* Creates the root adapter.
*
* @return An adapter that contains the preferences contained in this {@link PreferenceScreen}.
* @see #getRootAdapter()
*/
protected ListAdapter onCreateRootAdapter() {
return new PreferenceGroupAdapter(this);
}
/**
* Binds a {@link ListView} to the preferences contained in this {@link PreferenceScreen} via
* {@link #getRootAdapter()}. It also handles passing list item clicks to the corresponding
* {@link Preference} contained by this {@link PreferenceScreen}.
*
* @param listView The list view to attach to.
*/
public void bind(ListView listView) {
listView.setOnItemClickListener(this);
listView.setAdapter(getRootAdapter());
onAttachedToActivity();
}
@Override
protected void onClick() {
if (getIntent() != null || getPreferenceCount() == 0) {
return;
}
showDialog(null);
}
private void showDialog(Bundle state) {
Context context = getContext();
ListView listView = new ListView(context);
bind(listView);
// Set the title bar if title is available, else no title bar
final CharSequence title = getTitle();
Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title)
? com.android.internal.R.style.Theme_NoTitleBar
: com.android.internal.R.style.Theme);
dialog.setContentView(listView);
if (!TextUtils.isEmpty(title)) {
dialog.setTitle(title);
}
dialog.setOnDismissListener(this);
if (state != null) {
dialog.onRestoreInstanceState(state);
}
// Add the screen to the list of preferences screens opened as dialogs
getPreferenceManager().addPreferencesScreen(dialog);
dialog.show();
}
public void onDismiss(DialogInterface dialog) {
mDialog = null;
getPreferenceManager().removePreferencesScreen(dialog);
}
/**
* Used to get a handle to the dialog.
* This is useful for cases where we want to manipulate the dialog
* as we would with any other activity or view.
*/
public Dialog getDialog() {
return mDialog;
}
public void onItemClick(AdapterView parent, View view, int position, long id) {
Object item = getRootAdapter().getItem(position);
if (!(item instanceof Preference)) return;
final Preference preference = (Preference) item;
preference.performClick(this);
}
@Override
protected boolean isOnSameScreenAsChildren() {
return false;
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
final Dialog dialog = mDialog;
if (dialog == null || !dialog.isShowing()) {
return superState;
}
final SavedState myState = new SavedState(superState);
myState.isDialogShowing = true;
myState.dialogBundle = dialog.onSaveInstanceState();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
if (myState.isDialogShowing) {
showDialog(myState.dialogBundle);
}
}
private static class SavedState extends BaseSavedState {
boolean isDialogShowing;
Bundle dialogBundle;
public SavedState(Parcel source) {
super(source);
isDialogShowing = source.readInt() == 1;
dialogBundle = source.readBundle();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(isDialogShowing ? 1 : 0);
dest.writeBundle(dialogBundle);
}
public SavedState(Parcelable superState) {
super(superState);
}
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];
}
};
}
}
@@ -0,0 +1,243 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.media.RingtoneManager;
import android.net.Uri;
import android.provider.Settings.System;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
/**
* A {@link Preference} that allows the user to choose a ringtone from those on the device.
* The chosen ringtone's URI will be persisted as a string.
* <p>
* If the user chooses the "Default" item, the saved string will be one of
* {@link System#DEFAULT_RINGTONE_URI},
* {@link System#DEFAULT_NOTIFICATION_URI}, or
* {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent"
* item, the saved string will be an empty string.
*
* @attr ref android.R.styleable#RingtonePreference_ringtoneType
* @attr ref android.R.styleable#RingtonePreference_showDefault
* @attr ref android.R.styleable#RingtonePreference_showSilent
*/
public class RingtonePreference extends Preference implements
PreferenceManager.OnActivityResultListener {
private static final String TAG = "RingtonePreference";
private int mRingtoneType;
private boolean mShowDefault;
private boolean mShowSilent;
private int mRequestCode;
public RingtonePreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.RingtonePreference, defStyle, 0);
mRingtoneType = a.getInt(com.android.internal.R.styleable.RingtonePreference_ringtoneType,
RingtoneManager.TYPE_RINGTONE);
mShowDefault = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showDefault,
true);
mShowSilent = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showSilent,
true);
a.recycle();
}
public RingtonePreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.ringtonePreferenceStyle);
}
public RingtonePreference(Context context) {
this(context, null);
}
/**
* Returns the sound type(s) that are shown in the picker.
*
* @return The sound type(s) that are shown in the picker.
* @see #setRingtoneType(int)
*/
public int getRingtoneType() {
return mRingtoneType;
}
/**
* Sets the sound type(s) that are shown in the picker.
*
* @param type The sound type(s) that are shown in the picker.
* @see RingtoneManager#EXTRA_RINGTONE_TYPE
*/
public void setRingtoneType(int type) {
mRingtoneType = type;
}
/**
* Returns whether to a show an item for the default sound/ringtone.
*
* @return Whether to show an item for the default sound/ringtone.
*/
public boolean getShowDefault() {
return mShowDefault;
}
/**
* Sets whether to show an item for the default sound/ringtone. The default
* to use will be deduced from the sound type(s) being shown.
*
* @param showDefault Whether to show the default or not.
* @see RingtoneManager#EXTRA_RINGTONE_SHOW_DEFAULT
*/
public void setShowDefault(boolean showDefault) {
mShowDefault = showDefault;
}
/**
* Returns whether to a show an item for 'Silent'.
*
* @return Whether to show an item for 'Silent'.
*/
public boolean getShowSilent() {
return mShowSilent;
}
/**
* Sets whether to show an item for 'Silent'.
*
* @param showSilent Whether to show 'Silent'.
* @see RingtoneManager#EXTRA_RINGTONE_SHOW_SILENT
*/
public void setShowSilent(boolean showSilent) {
mShowSilent = showSilent;
}
@Override
protected void onClick() {
// Launch the ringtone picker
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
onPrepareRingtonePickerIntent(intent);
getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
}
/**
* Prepares the intent to launch the ringtone picker. This can be modified
* to adjust the parameters of the ringtone picker.
*
* @param ringtonePickerIntent The ringtone picker intent that can be
* modified by putting extras.
*/
protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
onRestoreRingtone());
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, mShowDefault);
if (mShowDefault) {
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
RingtoneManager.getDefaultUri(getRingtoneType()));
}
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, mShowSilent);
ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, mRingtoneType);
}
/**
* Called when a ringtone is chosen.
* <p>
* By default, this saves the ringtone URI to the persistent storage as a
* string.
*
* @param ringtoneUri The chosen ringtone's {@link Uri}. Can be null.
*/
protected void onSaveRingtone(Uri ringtoneUri) {
persistString(ringtoneUri != null ? ringtoneUri.toString() : "");
}
/**
* Called when the chooser is about to be shown and the current ringtone
* should be marked. Can return null to not mark any ringtone.
* <p>
* By default, this restores the previous ringtone URI from the persistent
* storage.
*
* @return The ringtone to be marked as the current ringtone.
*/
protected Uri onRestoreRingtone() {
final String uriString = getPersistedString(null);
return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValueObj) {
String defaultValue = (String) defaultValueObj;
/*
* This method is normally to make sure the internal state and UI
* matches either the persisted value or the default value. Since we
* don't show the current value in the UI (until the dialog is opened)
* and we don't keep local state, if we are restoring the persisted
* value we don't need to do anything.
*/
if (restorePersistedValue) {
return;
}
// If we are setting to the default value, we should persist it.
if (!TextUtils.isEmpty(defaultValue)) {
onSaveRingtone(Uri.parse(defaultValue));
}
}
@Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
super.onAttachedToHierarchy(preferenceManager);
preferenceManager.registerOnActivityResultListener(this);
mRequestCode = preferenceManager.getNextRequestCode();
}
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == mRequestCode) {
if (data != null) {
Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
if (callChangeListener(uri != null ? uri.toString() : "")) {
onSaveRingtone(uri);
}
}
return true;
}
return false;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
/**
* @hide
*/
public class SeekBarPreference extends DialogPreference {
private static final String TAG = "SeekBarPreference";
private Drawable mMyIcon;
public SeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setDialogLayoutResource(com.android.internal.R.layout.seekbar_dialog);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
// Steal the XML dialogIcon attribute's value
mMyIcon = getDialogIcon();
setDialogIcon(null);
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
final ImageView iconView = (ImageView) view.findViewById(android.R.id.icon);
if (mMyIcon != null) {
iconView.setImageDrawable(mMyIcon);
} else {
iconView.setVisibility(View.GONE);
}
}
protected static SeekBar getSeekBar(View dialogView) {
return (SeekBar) dialogView.findViewById(com.android.internal.R.id.seekbar);
}
}
@@ -0,0 +1,356 @@
/*
* Copyright (C) 2007 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 android.preference;
import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.Settings;
import android.provider.Settings.System;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
/**
* @hide
*/
public class VolumePreference extends SeekBarPreference implements
PreferenceManager.OnActivityStopListener, View.OnKeyListener {
private static final String TAG = "VolumePreference";
private int mStreamType;
/** May be null if the dialog isn't visible. */
private SeekBarVolumizer mSeekBarVolumizer;
public VolumePreference(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.VolumePreference, 0, 0);
mStreamType = a.getInt(android.R.styleable.VolumePreference_streamType, 0);
a.recycle();
}
public void setStreamType(int streamType) {
mStreamType = streamType;
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType);
getPreferenceManager().registerOnActivityStopListener(this);
// grab focus and key events so that pressing the volume buttons in the
// dialog doesn't also show the normal volume adjust toast.
view.setOnKeyListener(this);
view.setFocusableInTouchMode(true);
view.requestFocus();
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If key arrives immediately after the activity has been cleaned up.
if (mSeekBarVolumizer == null) return true;
boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
if (isdown) {
mSeekBarVolumizer.changeVolumeBy(-1);
}
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
if (isdown) {
mSeekBarVolumizer.changeVolumeBy(1);
}
return true;
default:
return false;
}
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (!positiveResult && mSeekBarVolumizer != null) {
mSeekBarVolumizer.revertVolume();
}
cleanup();
}
public void onActivityStop() {
cleanup();
}
/**
* Do clean up. This can be called multiple times!
*/
private void cleanup() {
getPreferenceManager().unregisterOnActivityStopListener(this);
if (mSeekBarVolumizer != null) {
Dialog dialog = getDialog();
if (dialog != null && dialog.isShowing()) {
View view = dialog.getWindow().getDecorView()
.findViewById(com.android.internal.R.id.seekbar);
if (view != null) view.setOnKeyListener(null);
// Stopped while dialog was showing, revert changes
mSeekBarVolumizer.revertVolume();
}
mSeekBarVolumizer.stop();
mSeekBarVolumizer = null;
}
}
protected void onSampleStarting(SeekBarVolumizer volumizer) {
if (mSeekBarVolumizer != null && volumizer != mSeekBarVolumizer) {
mSeekBarVolumizer.stopSample();
}
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
if (isPersistent()) {
// No need to save instance state since it's persistent
return superState;
}
final SavedState myState = new SavedState(superState);
if (mSeekBarVolumizer != null) {
mSeekBarVolumizer.onSaveInstanceState(myState.getVolumeStore());
}
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
if (mSeekBarVolumizer != null) {
mSeekBarVolumizer.onRestoreInstanceState(myState.getVolumeStore());
}
}
public static class VolumeStore {
public int volume = -1;
public int originalVolume = -1;
}
private static class SavedState extends BaseSavedState {
VolumeStore mVolumeStore = new VolumeStore();
public SavedState(Parcel source) {
super(source);
mVolumeStore.volume = source.readInt();
mVolumeStore.originalVolume = source.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mVolumeStore.volume);
dest.writeInt(mVolumeStore.originalVolume);
}
VolumeStore getVolumeStore() {
return mVolumeStore;
}
public SavedState(Parcelable superState) {
super(superState);
}
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];
}
};
}
/**
* Turns a {@link SeekBar} into a volume control.
*/
public class SeekBarVolumizer implements OnSeekBarChangeListener, Runnable {
private Context mContext;
private Handler mHandler = new Handler();
private AudioManager mAudioManager;
private int mStreamType;
private int mOriginalStreamVolume;
private Ringtone mRingtone;
private int mLastProgress = -1;
private SeekBar mSeekBar;
private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
if (mSeekBar != null) {
int volume = System.getInt(mContext.getContentResolver(),
System.VOLUME_SETTINGS[mStreamType], -1);
// Works around an atomicity problem with volume updates
// TODO: Fix the actual issue, probably in AudioService
if (volume >= 0) {
mSeekBar.setProgress(volume);
}
}
}
};
public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mStreamType = streamType;
mSeekBar = seekBar;
initSeekBar(seekBar);
}
private void initSeekBar(SeekBar seekBar) {
seekBar.setMax(mAudioManager.getStreamMaxVolume(mStreamType));
mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
seekBar.setProgress(mOriginalStreamVolume);
seekBar.setOnSeekBarChangeListener(this);
mContext.getContentResolver().registerContentObserver(
System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
false, mVolumeObserver);
Uri defaultUri = null;
if (mStreamType == AudioManager.STREAM_RING) {
defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
} else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
} else {
defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
}
mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
if (mRingtone != null) {
mRingtone.setStreamType(mStreamType);
}
}
public void stop() {
stopSample();
mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
mSeekBar.setOnSeekBarChangeListener(null);
}
public void revertVolume() {
mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0);
}
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromTouch) {
if (!fromTouch) {
return;
}
postSetVolume(progress);
}
void postSetVolume(int progress) {
// Do the volume changing separately to give responsive UI
mLastProgress = progress;
mHandler.removeCallbacks(this);
mHandler.post(this);
}
public void onStartTrackingTouch(SeekBar seekBar) {
}
public void onStopTrackingTouch(SeekBar seekBar) {
if (mRingtone != null && !mRingtone.isPlaying()) {
sample();
}
}
public void run() {
mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0);
}
private void sample() {
onSampleStarting(this);
mRingtone.play();
}
public void stopSample() {
if (mRingtone != null) {
mRingtone.stop();
}
}
public SeekBar getSeekBar() {
return mSeekBar;
}
public void changeVolumeBy(int amount) {
mSeekBar.incrementProgressBy(amount);
if (mRingtone != null && !mRingtone.isPlaying()) {
sample();
}
postSetVolume(mSeekBar.getProgress());
}
public void onSaveInstanceState(VolumeStore volumeStore) {
if (mLastProgress >= 0) {
volumeStore.volume = mLastProgress;
volumeStore.originalVolume = mOriginalStreamVolume;
}
}
public void onRestoreInstanceState(VolumeStore volumeStore) {
if (volumeStore.volume != -1) {
mOriginalStreamVolume = volumeStore.originalVolume;
mLastProgress = volumeStore.volume;
postSetVolume(mLastProgress);
}
}
}
}
@@ -0,0 +1,23 @@
<HTML>
<BODY>
Provides classes that manage application preferences and implement the preferences UI.
Using these ensures that all the preferences within each application are maintained
in the same manner and the user experience is consistent with that of the system and
other applications.
<p>
The preferences portion of an application
should be ran as a separate {@link android.app.Activity} that extends
the {@link android.preference.PreferenceActivity} class. In the PreferenceActivity, a
{@link android.preference.PreferenceScreen} object should be the root element of the layout.
The PreferenceScreen contains {@link android.preference.Preference} elements such as a
{@link android.preference.CheckBoxPreference}, {@link android.preference.EditTextPreference},
{@link android.preference.ListPreference}, {@link android.preference.PreferenceCategory},
or {@link android.preference.RingtonePreference}. </p>
<p>
All settings made for a given {@link android.preference.Preference} will be automatically saved
to the application's instance of {@link android.content.SharedPreferences}. Access to the
SharedPreferences is simple with {@link android.preference.Preference#getSharedPreferences()}.</p>
<p>
Note that saved preferences are accessible only to the application that created them.</p>
</BODY>
</HTML>