blob: 6b9b793a6cb22aa008ee815505520eea91d6f7b0 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package org.mozc.android.inputmethod.japanese.preference;
import org.mozc.android.inputmethod.japanese.resources.R;
import android.app.Dialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
/**
* Preference widget with a dialog containing multi selectable items.
*
*/
public class MozcMultiSelectListPreference extends DialogPreference {
/**
* State of the current dialog.
*/
private static class SavedState extends BaseSavedState {
boolean isDialogShowing;
Bundle dialogBundle;
public SavedState(Parcel source) {
super(source);
isDialogShowing = (source.readInt() != 0);
dialogBundle = source.readBundle();
}
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(isDialogShowing ? 1 : 0);
dest.writeBundle(dialogBundle);
}
@SuppressWarnings({"hiding", "unused"})
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
/** A list of entries shown on the dialog. */
private CharSequence[] entryList;
/** A list of SharedPreferences' keys. */
private CharSequence[] keyList;
/** A list of current values. */
private boolean[] valueList;
/** A copy of values for dialog. This may be just discarded when user clicks "cancel." */
private boolean[] dialogValueList;
public MozcMultiSelectListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(attrs);
}
public MozcMultiSelectListPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize(attrs);
}
private void initialize(AttributeSet attrs) {
TypedArray a =
getContext().obtainStyledAttributes(attrs, R.styleable.MozcMultiSelectListPreference);
entryList = a.getTextArray(R.styleable.MozcMultiSelectListPreference_entries);
keyList = a.getTextArray(R.styleable.MozcMultiSelectListPreference_entryKeys);
a.recycle();
}
public void setKeys(CharSequence[] keyList) {
if (isDialogShowing()) {
throw new IllegalStateException("Keys cannot be set when dialog is showing.");
}
this.keyList = keyList;
}
public void setEntries(CharSequence[] entryList) {
if (isDialogShowing()) {
throw new IllegalStateException("Entries cannot be set when dialog is showing.");
}
this.entryList = entryList;
}
public void setValues(boolean[] valueList) {
if (isDialogShowing()) {
throw new IllegalStateException("Values cannot be set when dialog is showing.");
}
boolean[] oldValueList = this.valueList;
this.valueList = valueList.clone();
persistBooleanArray(keyList, oldValueList, valueList);
}
public boolean[] getValues() {
return valueList.clone();
}
private boolean persistBooleanArray(
CharSequence[] keyList, boolean[] oldValueList, boolean[] valueList) {
if (!shouldPersist()) {
return false;
}
Editor editor = getSharedPreferences().edit();
for (int i = 0; i < keyList.length; ++i) {
if (oldValueList == null || oldValueList[i] != valueList[i]) {
editor.putBoolean(keyList[i].toString(), valueList[i]);
}
}
editor.commit();
return true;
}
@Override
protected boolean shouldPersist() {
// Look at keyList instead of Key.
return getPreferenceManager() != null && isPersistent() && keyList != null;
}
private boolean isDialogShowing() {
Dialog dialog = getDialog();
return dialog != null && dialog.isShowing();
}
@Override
protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
if (entryList == null || keyList == null || valueList == null) {
throw new IllegalStateException();
}
if (entryList.length != keyList.length || entryList.length != valueList.length) {
throw new IllegalStateException(
"All entryList, keyList and valueList must have the same number of elements: "
+ entryList.length + ", " + keyList.length + ", " + valueList.length);
}
// Set multi selectable items and its handler.
dialogValueList = valueList.clone();
builder.setMultiChoiceItems(entryList, dialogValueList, new OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
dialogValueList[which] = isChecked;
}
});
}
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
// If user tap OK, set the value.
setValues(dialogValueList);
}
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return toBooleanArray(a.getTextArray(index));
}
@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
if (!shouldPersist()) {
// restoreValue may be the result of look up for the key, but in this class
// what we need to check is keyList.
setValues((boolean[]) defaultValue);
return;
}
// Use persisted values if exist, otherwise use default value.
boolean[] valueList = ((boolean[]) defaultValue).clone();
SharedPreferences sharedPreferences = getSharedPreferences();
for (int i = 0; i < keyList.length; ++i) {
if (sharedPreferences.contains(keyList[i].toString())) {
valueList[i] = sharedPreferences.getBoolean(keyList[i].toString(), valueList[i]);
}
}
setValues(valueList);
}
/** Parses each text in {@code array} to boolean value, and returns as an array. */
private static boolean[] toBooleanArray(CharSequence[] array) {
if (array == null) {
return null;
}
boolean[] result = new boolean[array.length];
for (int i = 0; i < result.length; ++i) {
if (array[i] != null) {
result[i] = Boolean.parseBoolean(array[i].toString());
}
}
return result;
}
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
if (!isDialogShowing()) {
return superState;
}
// Now the dialog is showing. Keep the current state.
SavedState state = new SavedState(superState);
state.isDialogShowing = true;
state.dialogBundle = getDialog().onSaveInstanceState();
return state;
}
@Override
protected void onRestoreInstanceState(Parcelable parcelable) {
if (!(parcelable instanceof SavedState)) {
super.onRestoreInstanceState(parcelable);
return;
}
SavedState state = SavedState.class.cast(parcelable);
super.onRestoreInstanceState(state.getSuperState());
if (state.isDialogShowing) {
// The dialog was shown when the state was saved. Re-show the dialog.
showDialog(state.dialogBundle);
}
}
}