gecko/mobile/android/base/GeckoPreferences.java

400 lines
17 KiB
Java

/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.ArrayList;
public class GeckoPreferences
extends PreferenceActivity
implements OnPreferenceChangeListener, GeckoEventListener
{
private static final String LOGTAG = "GeckoPreferences";
private ArrayList<String> mPreferencesList;
private PreferenceScreen mPreferenceScreen;
private static boolean sIsCharEncodingEnabled = false;
private static final String NON_PREF_PREFIX = "android.not_a_preference.";
private static final String FONT_SIZE_PREF_KEY = "font.size.inflation.minTwips";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
GeckoAppShell.registerGeckoEventListener("Preferences:Data", this);
GeckoAppShell.registerGeckoEventListener("Sanitize:Finished", this);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (!hasFocus)
return;
mPreferencesList = new ArrayList<String>();
mPreferenceScreen = getPreferenceScreen();
initGroups(mPreferenceScreen);
initValues();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
final FontSizePreference fontSizePref =
(FontSizePreference) mPreferenceScreen.findPreference(FONT_SIZE_PREF_KEY);
fontSizePref.onConfigurationChanged(newConfig);
}
@Override
protected void onDestroy() {
super.onDestroy();
GeckoAppShell.unregisterGeckoEventListener("Preferences:Data", this);
GeckoAppShell.unregisterGeckoEventListener("Sanitize:Finished", this);
}
public void handleMessage(String event, JSONObject message) {
try {
if (event.equals("Preferences:Data")) {
JSONArray jsonPrefs = message.getJSONArray("preferences");
refresh(jsonPrefs);
} else if (event.equals("Sanitize:Finished")) {
boolean success = message.getBoolean("success");
final int stringRes = success ? R.string.private_data_success : R.string.private_data_fail;
final Context context = this;
GeckoAppShell.getMainHandler().post(new Runnable () {
public void run() {
Toast.makeText(context, stringRes, Toast.LENGTH_SHORT).show();
}
});
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
}
}
// Initialize preferences by sending the "Preferences:Get" command to Gecko
private void initValues() {
JSONArray jsonPrefs = new JSONArray(mPreferencesList);
GeckoEvent event = GeckoEvent.createBroadcastEvent("Preferences:Get", jsonPrefs.toString());
GeckoAppShell.sendEventToGecko(event);
}
private void initGroups(PreferenceGroup preferences) {
final int count = preferences.getPreferenceCount();
for (int i = 0; i < count; i++) {
Preference pref = preferences.getPreference(i);
if (pref instanceof PreferenceGroup)
initGroups((PreferenceGroup)pref);
else {
pref.setOnPreferenceChangeListener(this);
// Some Preference UI elements are not actually preferences,
// but they require a key to work correctly. For example,
// "Clear private data" requires a key for its state to be
// saved when the orientation changes. It uses the
// "android.not_a_preference.privacy.clear" key - which doesn't
// exist in Gecko - to satisfy this requirement.
String key = pref.getKey();
if (key != null && !key.startsWith(NON_PREF_PREFIX)) {
mPreferencesList.add(pref.getKey());
}
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
final private int DIALOG_CREATE_MASTER_PASSWORD = 0;
final private int DIALOG_REMOVE_MASTER_PASSWORD = 1;
public static void setCharEncodingState(boolean enabled) {
sIsCharEncodingEnabled = enabled;
}
public static boolean getCharEncodingState() {
return sIsCharEncodingEnabled;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String prefName = preference.getKey();
if (prefName != null && prefName.equals("privacy.masterpassword.enabled")) {
showDialog((Boolean)newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD);
return false;
} else if (prefName != null && prefName.equals("browser.menu.showCharacterEncoding")) {
setCharEncodingState(((String) newValue).equals("true"));
}
setPreference(prefName, newValue);
if (preference instanceof ListPreference) {
// We need to find the entry for the new value
int newIndex = ((ListPreference)preference).findIndexOfValue((String) newValue);
CharSequence newEntry = ((ListPreference)preference).getEntries()[newIndex];
((ListPreference)preference).setSummary(newEntry);
} else if (preference instanceof LinkPreference) {
finish();
} else if (preference instanceof FontSizePreference) {
final FontSizePreference fontSizePref = (FontSizePreference) preference;
fontSizePref.setSummary(fontSizePref.getSavedFontSizeName());
}
return true;
}
private EditText getTextBox(int aHintText) {
EditText input = new EditText(GeckoApp.mAppContext);
int inputtype = InputType.TYPE_CLASS_TEXT;
inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
input.setInputType(inputtype);
String hint = getResources().getString(aHintText);
input.setHint(aHintText);
return input;
}
private class PasswordTextWatcher implements TextWatcher {
EditText input1 = null;
EditText input2 = null;
AlertDialog dialog = null;
PasswordTextWatcher(EditText aInput1, EditText aInput2, AlertDialog aDialog) {
input1 = aInput1;
input2 = aInput2;
dialog = aDialog;
}
public void afterTextChanged(Editable s) {
if (dialog == null)
return;
String text1 = input1.getText().toString();
String text2 = input2.getText().toString();
boolean disabled = TextUtils.isEmpty(text1) || TextUtils.isEmpty(text2) || !text1.equals(text2);
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(!disabled);
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
public void onTextChanged(CharSequence s, int start, int before, int count) { }
}
protected Dialog onCreateDialog(int id) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);
AlertDialog dialog = null;
switch(id) {
case DIALOG_CREATE_MASTER_PASSWORD:
final EditText input1 = getTextBox(R.string.masterpassword_password);
final EditText input2 = getTextBox(R.string.masterpassword_confirm);
linearLayout.addView(input1);
linearLayout.addView(input2);
builder.setTitle(R.string.masterpassword_create_title)
.setView((View)linearLayout)
.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
JSONObject jsonPref = new JSONObject();
try {
jsonPref.put("name", "privacy.masterpassword.enabled");
jsonPref.put("type", "string");
jsonPref.put("value", input1.getText().toString());
GeckoEvent event = GeckoEvent.createBroadcastEvent("Preferences:Set", jsonPref.toString());
GeckoAppShell.sendEventToGecko(event);
} catch(Exception ex) {
Log.e(LOGTAG, "Error setting masterpassword", ex);
}
return;
}
})
.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
}
});
dialog = builder.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
public void onShow(DialogInterface dialog) {
input1.setText("");
input2.setText("");
input1.requestFocus();
}
});
PasswordTextWatcher watcher = new PasswordTextWatcher(input1, input2, dialog);
input1.addTextChangedListener((TextWatcher)watcher);
input2.addTextChangedListener((TextWatcher)watcher);
break;
case DIALOG_REMOVE_MASTER_PASSWORD:
final EditText input = getTextBox(R.string.masterpassword_password);
linearLayout.addView(input);
builder.setTitle(R.string.masterpassword_remove_title)
.setView((View)linearLayout)
.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
try {
JSONObject jsonPref = new JSONObject();
jsonPref.put("name", "privacy.masterpassword.enabled");
jsonPref.put("type", "string");
jsonPref.put("value", input.getText().toString());
GeckoEvent event = GeckoEvent.createBroadcastEvent("Preferences:Set", jsonPref.toString());
GeckoAppShell.sendEventToGecko(event);
} catch(Exception ex) {
Log.e(LOGTAG, "Error setting masterpassword", ex);
}
return;
}
})
.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
return;
}
});
dialog = builder.create();
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
input.setText("");
}
});
break;
default:
return null;
}
return dialog;
}
private void refresh(JSONArray jsonPrefs) {
// enable all preferences once we have them from gecko
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
mPreferenceScreen.setEnabled(true);
}
});
try {
if (mPreferenceScreen == null)
return;
final int length = jsonPrefs.length();
for (int i = 0; i < length; i++) {
JSONObject jPref = jsonPrefs.getJSONObject(i);
final String prefName = jPref.getString("name");
final String prefType = jPref.getString("type");
final Preference pref = mPreferenceScreen.findPreference(prefName);
if (pref instanceof CheckBoxPreference && "bool".equals(prefType)) {
final boolean value = jPref.getBoolean("value");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
if (((CheckBoxPreference)pref).isChecked() != value)
((CheckBoxPreference)pref).setChecked(value);
}
});
} else if (pref instanceof EditTextPreference && "string".equals(prefType)) {
final String value = jPref.getString("value");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
((EditTextPreference)pref).setText(value);
}
});
} else if (pref instanceof ListPreference && "string".equals(prefType)) {
final String value = jPref.getString("value");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
((ListPreference)pref).setValue(value);
// Set the summary string to the current entry
CharSequence selectedEntry = ((ListPreference)pref).getEntry();
((ListPreference)pref).setSummary(selectedEntry);
}
});
} else if (pref instanceof FontSizePreference) {
final FontSizePreference fontSizePref = (FontSizePreference) pref;
final String twipValue = jPref.getString("value");
fontSizePref.setSavedFontSize(twipValue);
final String fontSizeName = fontSizePref.getSavedFontSizeName();
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
fontSizePref.setSummary(fontSizeName); // Ex: "Small".
}
});
}
}
} catch (JSONException e) {
Log.e(LOGTAG, "Problem parsing preferences response: ", e);
}
}
// send the Preferences:Set message to Gecko
public static void setPreference(String pref, Object value) {
if (pref == null || pref.length() == 0)
return;
try {
JSONObject jsonPref = new JSONObject();
jsonPref.put("name", pref);
if (value instanceof Boolean) {
jsonPref.put("type", "bool");
jsonPref.put("value", ((Boolean)value).booleanValue());
}
else if (value instanceof Integer) {
jsonPref.put("type", "int");
jsonPref.put("value", ((Integer)value).intValue());
}
else {
jsonPref.put("type", "string");
jsonPref.put("value", String.valueOf(value));
}
GeckoEvent event = GeckoEvent.createBroadcastEvent("Preferences:Set", jsonPref.toString());
GeckoAppShell.sendEventToGecko(event);
} catch (JSONException e) {
Log.e(LOGTAG, "JSON exception: ", e);
}
}
}