From eddf9c6c117449012e2aece5776694467e3483f0 Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Sat, 13 Jan 2024 20:59:05 +0100 Subject: Refactor: New namespace for preference classes --- srcs/juloo.common/IntSlideBarPreference.java | 120 -------- srcs/juloo.common/SlideBarPreference.java | 131 --------- srcs/juloo.keyboard2/Autocapitalisation.java | 2 +- srcs/juloo.keyboard2/Config.java | 5 +- .../juloo.keyboard2/CustomExtraKeysPreference.java | 69 ----- srcs/juloo.keyboard2/ExtraKeys.java | 2 +- srcs/juloo.keyboard2/ExtraKeysPreference.java | 176 ------------ srcs/juloo.keyboard2/KeyEventHandler.java | 2 +- srcs/juloo.keyboard2/KeyModifier.java | 2 +- srcs/juloo.keyboard2/KeyValue.java | 2 +- srcs/juloo.keyboard2/Keyboard2.java | 1 + srcs/juloo.keyboard2/KeyboardData.java | 2 +- srcs/juloo.keyboard2/LayoutsPreference.java | 301 -------------------- srcs/juloo.keyboard2/ListGroupPreference.java | 293 -------------------- srcs/juloo.keyboard2/Utils.java | 2 +- .../prefs/CustomExtraKeysPreference.java | 70 +++++ .../juloo.keyboard2/prefs/ExtraKeysPreference.java | 177 ++++++++++++ .../prefs/IntSlideBarPreference.java | 120 ++++++++ srcs/juloo.keyboard2/prefs/LayoutsPreference.java | 302 +++++++++++++++++++++ .../juloo.keyboard2/prefs/ListGroupPreference.java | 294 ++++++++++++++++++++ srcs/juloo.keyboard2/prefs/SlideBarPreference.java | 131 +++++++++ 21 files changed, 1106 insertions(+), 1098 deletions(-) delete mode 100644 srcs/juloo.common/IntSlideBarPreference.java delete mode 100644 srcs/juloo.common/SlideBarPreference.java delete mode 100644 srcs/juloo.keyboard2/CustomExtraKeysPreference.java delete mode 100644 srcs/juloo.keyboard2/ExtraKeysPreference.java delete mode 100644 srcs/juloo.keyboard2/LayoutsPreference.java delete mode 100644 srcs/juloo.keyboard2/ListGroupPreference.java create mode 100644 srcs/juloo.keyboard2/prefs/CustomExtraKeysPreference.java create mode 100644 srcs/juloo.keyboard2/prefs/ExtraKeysPreference.java create mode 100644 srcs/juloo.keyboard2/prefs/IntSlideBarPreference.java create mode 100644 srcs/juloo.keyboard2/prefs/LayoutsPreference.java create mode 100644 srcs/juloo.keyboard2/prefs/ListGroupPreference.java create mode 100644 srcs/juloo.keyboard2/prefs/SlideBarPreference.java (limited to 'srcs') diff --git a/srcs/juloo.common/IntSlideBarPreference.java b/srcs/juloo.common/IntSlideBarPreference.java deleted file mode 100644 index 0bdf2c7..0000000 --- a/srcs/juloo.common/IntSlideBarPreference.java +++ /dev/null @@ -1,120 +0,0 @@ -package juloo.common; - -import android.content.Context; -import android.content.res.TypedArray; -import android.preference.DialogPreference; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.SeekBar; - -/* - ** IntSlideBarPreference - ** - - ** Open a dialog showing a seekbar - ** - - ** xml attrs: - ** android:defaultValue Default value (int) - ** min min value (int) - ** max max value (int) - ** - - ** Summary field allow to show the current value using %s flag - */ -public class IntSlideBarPreference extends DialogPreference - implements SeekBar.OnSeekBarChangeListener -{ - private LinearLayout _layout; - private TextView _textView; - private SeekBar _seekBar; - - private int _min; - - private String _initialSummary; - - public IntSlideBarPreference(Context context, AttributeSet attrs) - { - super(context, attrs); - _initialSummary = getSummary().toString(); - _textView = new TextView(context); - _textView.setPadding(48, 40, 48, 40); - _seekBar = new SeekBar(context); - _seekBar.setOnSeekBarChangeListener(this); - _min = attrs.getAttributeIntValue(null, "min", 0); - int max = attrs.getAttributeIntValue(null, "max", 0); - _seekBar.setMax(max - _min); - _layout = new LinearLayout(getContext()); - _layout.setOrientation(LinearLayout.VERTICAL); - _layout.addView(_textView); - _layout.addView(_seekBar); - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) - { - updateText(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) - { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) - { - } - - @Override - protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) - { - int value; - - if (restorePersistedValue) - { - value = getPersistedInt(_min); - } - else - { - value = (Integer)defaultValue; - persistInt(value); - } - _seekBar.setProgress(value - _min); - updateText(); - } - - @Override - protected Object onGetDefaultValue(TypedArray a, int index) - { - return (a.getInt(index, _min)); - } - - @Override - protected void onDialogClosed(boolean positiveResult) - { - if (positiveResult) - persistInt(_seekBar.getProgress() + _min); - else - _seekBar.setProgress(getPersistedInt(_min) - _min); - - updateText(); - } - - protected View onCreateDialogView() - { - ViewGroup parent = (ViewGroup)_layout.getParent(); - - if (parent != null) - parent.removeView(_layout); - return (_layout); - } - - private void updateText() - { - String f = String.format(_initialSummary, _seekBar.getProgress() + _min); - - _textView.setText(f); - setSummary(f); - } -} diff --git a/srcs/juloo.common/SlideBarPreference.java b/srcs/juloo.common/SlideBarPreference.java deleted file mode 100644 index b41448b..0000000 --- a/srcs/juloo.common/SlideBarPreference.java +++ /dev/null @@ -1,131 +0,0 @@ -package juloo.common; - -import android.content.Context; -import android.content.res.TypedArray; -import android.preference.DialogPreference; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.SeekBar; - -/* - ** SideBarPreference - ** - - ** Open a dialog showing a seekbar - ** - - ** xml attrs: - ** android:defaultValue Default value (float) - ** min min value (float) - ** max max value (float) - ** - - ** Summary field allow to show the current value using %f or %s flag - */ -public class SlideBarPreference extends DialogPreference - implements SeekBar.OnSeekBarChangeListener -{ - private static final int STEPS = 100; - - private LinearLayout _layout; - private TextView _textView; - private SeekBar _seekBar; - - private float _min; - private float _max; - private float _value; - - private String _initialSummary; - - public SlideBarPreference(Context context, AttributeSet attrs) - { - super(context, attrs); - _initialSummary = getSummary().toString(); - _textView = new TextView(context); - _textView.setPadding(48, 40, 48, 40); - _seekBar = new SeekBar(context); - _seekBar.setOnSeekBarChangeListener(this); - _seekBar.setMax(STEPS); - _min = float_of_string(attrs.getAttributeValue(null, "min")); - _value = _min; - _max = Math.max(1f, float_of_string(attrs.getAttributeValue(null, "max"))); - _layout = new LinearLayout(getContext()); - _layout.setOrientation(LinearLayout.VERTICAL); - _layout.addView(_textView); - _layout.addView(_seekBar); - } - - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) - { - _value = Math.round(progress * (_max - _min)) / (float)STEPS + _min; - updateText(); - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) - { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) - { - } - - @Override - protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) - { - if (restorePersistedValue) - { - _value = getPersistedFloat(_min); - } - else - { - _value = (Float)defaultValue; - persistFloat(_value); - } - _seekBar.setProgress((int)((_value - _min) * STEPS / (_max - _min))); - updateText(); - } - - @Override - protected Object onGetDefaultValue(TypedArray a, int index) - { - return (a.getFloat(index, _min)); - } - - @Override - protected void onDialogClosed(boolean positiveResult) - { - if (positiveResult) - persistFloat(_value); - else - _seekBar.setProgress((int)((getPersistedFloat(_min) - _min) * STEPS / (_max - _min))); - - updateText(); - } - - protected View onCreateDialogView() - { - ViewGroup parent = (ViewGroup)_layout.getParent(); - - if (parent != null) - parent.removeView(_layout); - return (_layout); - } - - private void updateText() - { - String f = String.format(_initialSummary, _value); - - _textView.setText(f); - setSummary(f); - } - - private static float float_of_string(String str) - { - if (str == null) - return (0f); - return (Float.parseFloat(str)); - } -} diff --git a/srcs/juloo.keyboard2/Autocapitalisation.java b/srcs/juloo.keyboard2/Autocapitalisation.java index b73f2e3..3baff48 100644 --- a/srcs/juloo.keyboard2/Autocapitalisation.java +++ b/srcs/juloo.keyboard2/Autocapitalisation.java @@ -8,7 +8,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.KeyEvent; -final class Autocapitalisation +public final class Autocapitalisation { boolean _enabled = false; boolean _should_enable_shift = false; diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index cb68e80..f59bce3 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -14,8 +14,11 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import juloo.keyboard2.prefs.CustomExtraKeysPreference; +import juloo.keyboard2.prefs.ExtraKeysPreference; +import juloo.keyboard2.prefs.LayoutsPreference; -final class Config +public final class Config { private final SharedPreferences _prefs; diff --git a/srcs/juloo.keyboard2/CustomExtraKeysPreference.java b/srcs/juloo.keyboard2/CustomExtraKeysPreference.java deleted file mode 100644 index d007bf7..0000000 --- a/srcs/juloo.keyboard2/CustomExtraKeysPreference.java +++ /dev/null @@ -1,69 +0,0 @@ -package juloo.keyboard2; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.preference.Preference; -import android.preference.PreferenceCategory; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.json.JSONArray; -import org.json.JSONException; - -/** Allows to enter custom keys to be added to the keyboard. This shows up at - the top of the "Add keys to the keyboard" option. */ -public class CustomExtraKeysPreference extends ListGroupPreference -{ - /** This pref stores a list of strings encoded as JSON. */ - static final String KEY = "custom_extra_keys"; - static final ListGroupPreference.Serializer SERIALIZER = - new ListGroupPreference.StringSerializer(); - - public CustomExtraKeysPreference(Context context, AttributeSet attrs) - { - super(context, attrs); - setKey(KEY); - } - - public static Map get(SharedPreferences prefs) - { - Map kvs = - new HashMap(); - List key_names = load_from_preferences(KEY, prefs, null, SERIALIZER); - if (key_names != null) - { - for (String key_name : key_names) - kvs.put(KeyValue.makeStringKey(key_name), KeyboardData.PreferredPos.DEFAULT); - } - return kvs; - } - - String label_of_value(String value, int i) { return value; } - - @Override - void select(final SelectionCallback callback) - { - new AlertDialog.Builder(getContext()) - .setView(R.layout.dialog_edit_text) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){ - public void onClick(DialogInterface dialog, int which) - { - EditText input = (EditText)((AlertDialog)dialog).findViewById(R.id.text); - final String k = input.getText().toString(); - if (!k.equals("")) - callback.select(k); - } - }) - .setNegativeButton(android.R.string.cancel, null) - .show(); - } - - @Override - Serializer get_serializer() { return SERIALIZER; } -} diff --git a/srcs/juloo.keyboard2/ExtraKeys.java b/srcs/juloo.keyboard2/ExtraKeys.java index ca9e46a..416e507 100644 --- a/srcs/juloo.keyboard2/ExtraKeys.java +++ b/srcs/juloo.keyboard2/ExtraKeys.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Set; -class ExtraKeys +public final class ExtraKeys { public static final ExtraKeys EMPTY = new ExtraKeys(Collections.EMPTY_LIST); diff --git a/srcs/juloo.keyboard2/ExtraKeysPreference.java b/srcs/juloo.keyboard2/ExtraKeysPreference.java deleted file mode 100644 index 3e8b74b..0000000 --- a/srcs/juloo.keyboard2/ExtraKeysPreference.java +++ /dev/null @@ -1,176 +0,0 @@ -package juloo.keyboard2; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.preference.CheckBoxPreference; -import android.preference.PreferenceCategory; -import android.util.AttributeSet; -import android.view.View; -import android.widget.TextView; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** This class implements the "extra keys" preference but also defines the - possible extra keys. */ -public class ExtraKeysPreference extends PreferenceCategory -{ - /** Array of the keys that can be selected. */ - public static String[] extra_keys = new String[] - { - "alt", - "meta", - "voice_typing", - "accent_aigu", - "accent_grave", - "accent_double_aigu", - "accent_dot_above", - "accent_circonflexe", - "accent_tilde", - "accent_cedille", - "accent_trema", - "accent_ring", - "accent_caron", - "accent_macron", - "accent_ogonek", - "accent_breve", - "accent_slash", - "accent_bar", - "accent_dot_below", - "accent_hook_above", - "accent_horn", - "€", - "ß", - "£", - "§", - "†", - "ª", - "º", - "page_up", - "page_down", - "home", - "end", - "switch_greekmath", - "capslock", - "copy", - "paste", - "cut", - "selectAll", - "shareText", - "pasteAsPlainText", - "undo", - "redo", - "superscript", - "subscript", - }; - - /** Whether an extra key is enabled by default. */ - public static boolean default_checked(String name) - { - switch (name) - { - case "voice_typing": - return true; - default: - return false; - } - } - - /** Text that describe a key. Might be null. */ - static String key_description(Resources res, String name) - { - int id = 0; - switch (name) - { - case "capslock": id = R.string.key_descr_capslock; break; - case "switch_greekmath": id = R.string.key_descr_switch_greekmath; break; - case "voice_typing": id = R.string.key_descr_voice_typing; break; - case "copy": id = R.string.key_descr_copy; break; - case "paste": id = R.string.key_descr_paste; break; - case "cut": id = R.string.key_descr_cut; break; - case "selectAll": id = R.string.key_descr_selectAll; break; - case "shareText": id = R.string.key_descr_shareText; break; - case "pasteAsPlainText": id = R.string.key_descr_pasteAsPlainText; break; - case "undo": id = R.string.key_descr_undo; break; - case "redo": id = R.string.key_descr_redo; break; - case "ª": id = R.string.key_descr_ª; break; - case "º": id = R.string.key_descr_º; break; - case "superscript": id = R.string.key_descr_superscript; break; - case "subscript": id = R.string.key_descr_subscript; break; - case "page_up": id = R.string.key_descr_page_up; break; - case "page_down": id = R.string.key_descr_page_down; break; - case "home": id = R.string.key_descr_home; break; - case "end": id = R.string.key_descr_end; break; - } - if (id == 0) - return null; - return res.getString(id); - } - - /** Get the set of enabled extra keys. */ - public static Map get_extra_keys(SharedPreferences prefs) - { - Map ks = - new HashMap(); - for (String key_name : extra_keys) - { - if (prefs.getBoolean(pref_key_of_key_name(key_name), - default_checked(key_name))) - ks.put(KeyValue.getKeyByName(key_name), KeyboardData.PreferredPos.DEFAULT); - } - return ks; - } - - boolean _attached = false; /** Whether it has already been attached. */ - - public ExtraKeysPreference(Context context, AttributeSet attrs) - { - super(context, attrs); - setOrderingAsAdded(true); - } - - @Override - protected void onAttachedToActivity() - { - if (_attached) - return; - _attached = true; - for (String key_name : extra_keys) - addPreference(new ExtraKeyCheckBoxPreference(getContext(), key_name, - default_checked(key_name))); - } - - public static String pref_key_of_key_name(String key_name) - { - return "extra_key_" + key_name; - } - - static class ExtraKeyCheckBoxPreference extends CheckBoxPreference - { - boolean _key_font; - - public ExtraKeyCheckBoxPreference(Context ctx, String key_name, - boolean default_checked) - { - super(ctx); - KeyValue kv = KeyValue.getKeyByName(key_name); - String title = kv.getString(); - String descr = key_description(ctx.getResources(), key_name); - if (descr != null) - title += " (" + descr + ")"; - setKey(pref_key_of_key_name(key_name)); - setDefaultValue(default_checked); - setTitle(title); - _key_font = kv.hasFlags(KeyValue.FLAG_KEY_FONT); - } - - @Override - protected void onBindView(View view) - { - super.onBindView(view); - TextView title = (TextView)view.findViewById(android.R.id.title); - title.setTypeface(_key_font ? Theme.getKeyFont(getContext()) : null); - } - } -} diff --git a/srcs/juloo.keyboard2/KeyEventHandler.java b/srcs/juloo.keyboard2/KeyEventHandler.java index e903bdb..c79d002 100644 --- a/srcs/juloo.keyboard2/KeyEventHandler.java +++ b/srcs/juloo.keyboard2/KeyEventHandler.java @@ -7,7 +7,7 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -class KeyEventHandler implements Config.IKeyEventHandler +public final class KeyEventHandler implements Config.IKeyEventHandler { IReceiver _recv; Autocapitalisation _autocap; diff --git a/srcs/juloo.keyboard2/KeyModifier.java b/srcs/juloo.keyboard2/KeyModifier.java index 984db2e..c0f5beb 100644 --- a/srcs/juloo.keyboard2/KeyModifier.java +++ b/srcs/juloo.keyboard2/KeyModifier.java @@ -4,7 +4,7 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import java.util.HashMap; -class KeyModifier +public final class KeyModifier { /** Cache key is KeyValue's name */ private static HashMap> _cache = diff --git a/srcs/juloo.keyboard2/KeyValue.java b/srcs/juloo.keyboard2/KeyValue.java index fdb1f25..24eab0b 100644 --- a/srcs/juloo.keyboard2/KeyValue.java +++ b/srcs/juloo.keyboard2/KeyValue.java @@ -3,7 +3,7 @@ package juloo.keyboard2; import android.view.KeyEvent; import java.util.HashMap; -final class KeyValue +public final class KeyValue { public static enum Event { diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 66380e4..daa9ea0 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; +import juloo.keyboard2.prefs.LayoutsPreference; public class Keyboard2 extends InputMethodService implements SharedPreferences.OnSharedPreferenceChangeListener diff --git a/srcs/juloo.keyboard2/KeyboardData.java b/srcs/juloo.keyboard2/KeyboardData.java index 557e42f..3cbd4aa 100644 --- a/srcs/juloo.keyboard2/KeyboardData.java +++ b/srcs/juloo.keyboard2/KeyboardData.java @@ -14,7 +14,7 @@ import java.util.Set; import java.util.function.Function; import org.xmlpull.v1.XmlPullParser; -class KeyboardData +public final class KeyboardData { public final List rows; /** Total width of the keyboard. */ diff --git a/srcs/juloo.keyboard2/LayoutsPreference.java b/srcs/juloo.keyboard2/LayoutsPreference.java deleted file mode 100644 index d1cffcf..0000000 --- a/srcs/juloo.keyboard2/LayoutsPreference.java +++ /dev/null @@ -1,301 +0,0 @@ -package juloo.keyboard2; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.widget.ArrayAdapter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import org.json.JSONException; -import org.json.JSONObject; - -public class LayoutsPreference extends ListGroupPreference -{ - static final String KEY = "layouts"; - static final List DEFAULT = - Collections.singletonList((Layout)new SystemLayout()); - static final ListGroupPreference.Serializer SERIALIZER = - new Serializer(); - - /** Text displayed for each layout in the dialog list. */ - String[] _layout_display_names; - - public LayoutsPreference(Context ctx, AttributeSet attrs) - { - super(ctx, attrs); - setKey(KEY); - Resources res = ctx.getResources(); - _layout_display_names = res.getStringArray(R.array.pref_layout_entries); - } - - /** Obtained from [res/values/layouts.xml]. */ - static List _unsafe_layout_ids_str = null; - static TypedArray _unsafe_layout_ids_res = null; - - /** Layout internal names. Contains "system" and "custom". */ - public static List get_layout_names(Resources res) - { - if (_unsafe_layout_ids_str == null) - _unsafe_layout_ids_str = Arrays.asList( - res.getStringArray(R.array.pref_layout_values)); - return _unsafe_layout_ids_str; - } - - /** Layout resource id for a layout name. [-1] if not found. */ - public static int layout_id_of_name(Resources res, String name) - { - if (_unsafe_layout_ids_res == null) - _unsafe_layout_ids_res = res.obtainTypedArray(R.array.layout_ids); - int i = get_layout_names(res).indexOf(name); - if (i >= 0) - return _unsafe_layout_ids_res.getResourceId(i, 0); - return -1; - } - - /** [null] for the "system" layout. */ - public static List load_from_preferences(Resources res, SharedPreferences prefs) - { - List layouts = new ArrayList(); - for (Layout l : load_from_preferences(KEY, prefs, DEFAULT, SERIALIZER)) - { - if (l instanceof NamedLayout) - layouts.add(layout_of_string(res, ((NamedLayout)l).name)); - else if (l instanceof CustomLayout) - layouts.add(((CustomLayout)l).parsed); - else // instanceof SystemLayout - layouts.add(null); - } - return layouts; - } - - /** Does not call [prefs.commit()]. */ - public static void save_to_preferences(SharedPreferences.Editor prefs, List items) - { - save_to_preferences(KEY, prefs, items, SERIALIZER); - } - - public static KeyboardData layout_of_string(Resources res, String name) - { - int id = layout_id_of_name(res, name); - if (id > 0) - return KeyboardData.load(res, id); - // Might happen when the app is downgraded, return the system layout. - return null; - } - - @Override - protected void onSetInitialValue(boolean restoreValue, Object defaultValue) - { - super.onSetInitialValue(restoreValue, defaultValue); - if (_values.size() == 0) - set_values(new ArrayList(DEFAULT), false); - } - - String label_of_layout(Layout l) - { - if (l instanceof NamedLayout) - { - String lname = ((NamedLayout)l).name; - int value_i = get_layout_names(getContext().getResources()).indexOf(lname); - return value_i < 0 ? lname : _layout_display_names[value_i]; - } - else if (l instanceof CustomLayout) - { - // Use the layout's name if possible - CustomLayout cl = (CustomLayout)l; - if (cl.parsed != null && cl.parsed.name != null - && !cl.parsed.name.equals("")) - return cl.parsed.name; - else - return getContext().getString(R.string.pref_layout_e_custom); - } - else // instanceof SystemLayout - return getContext().getString(R.string.pref_layout_e_system); - } - - @Override - String label_of_value(Layout value, int i) - { - return getContext().getString(R.string.pref_layouts_item, i + 1, - label_of_layout(value)); - } - - @Override - AddButton on_attach_add_button(AddButton prev_btn) - { - if (prev_btn == null) - return new LayoutsAddButton(getContext()); - return prev_btn; - } - - @Override - boolean should_allow_remove_item(Layout value) - { - return (_values.size() > 1 && !(value instanceof CustomLayout)); - } - - @Override - ListGroupPreference.Serializer get_serializer() { return SERIALIZER; } - - @Override - void select(final SelectionCallback callback) - { - ArrayAdapter layouts = new ArrayAdapter(getContext(), android.R.layout.simple_list_item_1, _layout_display_names); - new AlertDialog.Builder(getContext()) - .setView(R.layout.dialog_edit_text) - .setAdapter(layouts, new DialogInterface.OnClickListener(){ - public void onClick(DialogInterface _dialog, int which) - { - String name = get_layout_names(getContext().getResources()).get(which); - switch (name) - { - case "system": - callback.select(new SystemLayout()); - break; - case "custom": - select_custom(callback, read_initial_custom_layout()); - break; - default: - callback.select(new NamedLayout(name)); - break; - } - } - }) - .show(); - } - - /** Dialog for specifying a custom layout. [initial_text] is the layout - description when modifying a layout. */ - void select_custom(final SelectionCallback callback, String initial_text) - { - boolean allow_remove = callback.allow_remove() && _values.size() > 1; - CustomLayoutEditDialog.show(getContext(), initial_text, allow_remove, - new CustomLayoutEditDialog.Callback() - { - public void select(String text) - { - if (text == null) - callback.select(null); - else - callback.select(CustomLayout.parse(text)); - } - - public String validate(String text) - { - try - { - KeyboardData.load_string_exn(text); - return null; // Validation passed - } - catch (Exception e) - { - return e.getMessage(); - } - } - }); - } - - /** Called when modifying a layout. Custom layouts behave differently. */ - @Override - void select(final SelectionCallback callback, Layout prev_layout) - { - if (prev_layout instanceof CustomLayout) - select_custom(callback, ((CustomLayout)prev_layout).xml); - else - select(callback); - } - - /** The initial text for the custom layout entry box. The qwerty_us layout is - a good default and contains a bit of documentation. */ - String read_initial_custom_layout() - { - try - { - Resources res = getContext().getResources(); - return Utils.read_all_utf8(res.openRawResource(R.raw.latn_qwerty_us)); - } - catch (Exception _e) - { - return ""; - } - } - - class LayoutsAddButton extends AddButton - { - public LayoutsAddButton(Context ctx) - { - super(ctx); - setLayoutResource(R.layout.pref_layouts_add_btn); - } - } - - /** A layout selected by the user. The only implementations are - [NamedLayout], [SystemLayout] and [CustomLayout]. */ - interface Layout {} - - static final class SystemLayout implements Layout - { - public SystemLayout() {} - } - - /** The name of a layout defined in [res/xml]. */ - static final class NamedLayout implements Layout - { - public final String name; - public NamedLayout(String n) { name = n; } - } - - /** The XML description of a custom layout. */ - static final class CustomLayout implements Layout - { - public final String xml; - /** Might be null. */ - public final KeyboardData parsed; - public CustomLayout(String xml_, KeyboardData k) { xml = xml_; parsed = k; } - public static CustomLayout parse(String xml) - { - KeyboardData parsed = null; - try { parsed = KeyboardData.load_string_exn(xml); } - catch (Exception e) {} - return new CustomLayout(xml, parsed); - } - } - - /** Named layouts are serialized to strings and custom layouts to JSON - objects with a [kind] field. */ - static class Serializer implements ListGroupPreference.Serializer - { - public Layout load_item(Object obj) throws JSONException - { - if (obj instanceof String) - { - String name = (String)obj; - if (name.equals("system")) - return new SystemLayout(); - return new NamedLayout(name); - } - JSONObject obj_ = (JSONObject)obj; - switch (obj_.getString("kind")) - { - case "custom": return CustomLayout.parse(obj_.getString("xml")); - case "system": default: return new SystemLayout(); - } - } - - public Object save_item(Layout v) throws JSONException - { - if (v instanceof NamedLayout) - return ((NamedLayout)v).name; - if (v instanceof CustomLayout) - return new JSONObject().put("kind", "custom") - .put("xml", ((CustomLayout)v).xml); - return new JSONObject().put("kind", "system"); - } - } -} diff --git a/srcs/juloo.keyboard2/ListGroupPreference.java b/srcs/juloo.keyboard2/ListGroupPreference.java deleted file mode 100644 index 075b38c..0000000 --- a/srcs/juloo.keyboard2/ListGroupPreference.java +++ /dev/null @@ -1,293 +0,0 @@ -package juloo.keyboard2; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.Preference; -import android.preference.PreferenceGroup; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import java.util.ArrayList; -import java.util.List; -import org.json.JSONArray; -import org.json.JSONException; - -/** A list of preferences where the users can add items to the end and modify - and remove items. Backed by a string list. Implement user selection in - [select()]. */ -public abstract class ListGroupPreference extends PreferenceGroup -{ - boolean _attached = false; - List _values; - /** The "add" button currently displayed. */ - AddButton _add_button = null; - - public ListGroupPreference(Context context, AttributeSet attrs) - { - super(context, attrs); - setOrderingAsAdded(true); - setLayoutResource(R.layout.pref_listgroup_group); - _values = new ArrayList(); - } - - /** Overrideable */ - - /** The label to display on the item for a given value. */ - abstract String label_of_value(E value, int i); - - /** Called every time the list changes and allows to change the "Add" button - appearance. - [prev_btn] is the previously attached button, might be null. */ - AddButton on_attach_add_button(AddButton prev_btn) - { - if (prev_btn == null) - return new AddButton(getContext()); - return prev_btn; - } - - /** Called every time the list changes and allows to disable the "Remove" - buttons on every items. Might be used to enforce a minimum number of - items. */ - boolean should_allow_remove_item(E _value) - { - return true; - } - - /** Called when an item is added or modified. */ - abstract void select(SelectionCallback callback); - - /** Called when an item is modified. */ - void select(SelectionCallback callback, E _old_value) - { - select(callback); - } - - /** A separate class is used as the same serializer must be used in the - static context. See [Serializer] below. */ - abstract Serializer get_serializer(); - - /** Load/save utils */ - - /** Read a value saved by preference from a [SharedPreferences] object. - [serializer] must be the same that is returned by [get_serializer()]. - Returns [null] on error. */ - static List load_from_preferences(String key, - SharedPreferences prefs, List def, Serializer serializer) - { - String s = prefs.getString(key, null); - return (s != null) ? load_from_string(s, serializer) : def; - } - - /** Save items into the preferences. Does not call [prefs.commit()]. */ - static void save_to_preferences(String key, SharedPreferences.Editor prefs, List items, Serializer serializer) - { - prefs.putString(key, save_to_string(items, serializer)); - } - - /** Decode a list of string previously encoded with [save_to_string]. Returns - [null] on error. */ - static List load_from_string(String inp, Serializer serializer) - { - try - { - List l = new ArrayList(); - JSONArray arr = new JSONArray(inp); - for (int i = 0; i < arr.length(); i++) - l.add(serializer.load_item(arr.get(i))); - return l; - } - catch (JSONException e) - { - Logs.exn("load_from_string", e); - return null; - } - } - - /** Encode a list of string so it can be passed to - [Preference.persistString()]. Decode with [load_from_string]. */ - static String save_to_string(List items, Serializer serializer) - { - List serialized_items = new ArrayList(); - for (E it : items) - { - try - { - serialized_items.add(serializer.save_item(it)); - } - catch (JSONException e) - { - Logs.exn("save_to_string", e); - } - } - return (new JSONArray(serialized_items)).toString(); - } - - /** Protected API */ - - /** Set the values. If [persist] is [true], persist into the store. */ - void set_values(List vs, boolean persist) - { - _values = vs; - reattach(); - if (persist) - persistString(save_to_string(vs, get_serializer())); - } - - void add_item(E v) - { - _values.add(v); - set_values(_values, true); - } - - void change_item(int i, E v) - { - _values.set(i, v); - set_values(_values, true); - } - - void remove_item(int i) - { - _values.remove(i); - set_values(_values, true); - } - - /** Internal */ - - @Override - protected void onSetInitialValue(boolean restoreValue, Object defaultValue) - { - String input = (restoreValue) ? getPersistedString(null) : (String)defaultValue; - if (input != null) - { - List values = load_from_string(input, get_serializer()); - if (values != null) - set_values(values, false); - } - } - - @Override - protected void onAttachedToActivity() - { - super.onAttachedToActivity(); - if (_attached) - return; - _attached = true; - reattach(); - } - - void reattach() - { - if (!_attached) - return; - removeAll(); - int i = 0; - for (E v : _values) - { - addPreference(this.new Item(getContext(), i, v)); - i++; - } - _add_button = on_attach_add_button(_add_button); - _add_button.setOrder(Preference.DEFAULT_ORDER); - addPreference(_add_button); - } - - class Item extends Preference - { - final E _value; - final int _index; - - public Item(Context ctx, int index, E value) - { - super(ctx); - _value = value; - _index = index; - setPersistent(false); - setTitle(label_of_value(value, index)); - if (should_allow_remove_item(value)) - setWidgetLayoutResource(R.layout.pref_listgroup_item_widget); - } - - @Override - protected View onCreateView(ViewGroup parent) - { - View v = super.onCreateView(parent); - View remove_btn = v.findViewById(R.id.pref_listgroup_remove_btn); - if (remove_btn != null) - remove_btn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View _v) - { - remove_item(_index); - } - }); - v.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View _v) - { - select(new SelectionCallback() { - public void select(E value) - { - if (value == null) - remove_item(_index); - else - change_item(_index, value); - } - - public boolean allow_remove() { return true; } - }, _value); - } - }); - return v; - } - } - - class AddButton extends Preference - { - public AddButton(Context ctx) - { - super(ctx); - setPersistent(false); - setLayoutResource(R.layout.pref_listgroup_add_btn); - } - - @Override - protected void onClick() - { - select(new SelectionCallback() { - public void select(E value) - { - add_item(value); - } - - public boolean allow_remove() { return false; } - }); - } - } - - public interface SelectionCallback - { - public void select(E value); - - /** If this method returns [true], [null] might be passed to [select] to - remove the item. */ - public boolean allow_remove(); - } - - /** Methods for serializing and deserializing abstract items. - [StringSerializer] is an implementation. */ - public interface Serializer - { - /** [obj] is an object returned by [save_item()]. */ - E load_item(Object obj) throws JSONException; - - /** Serialize an item into JSON. Might return an object that can be inserted - in a [JSONArray]. */ - Object save_item(E v) throws JSONException; - } - - public static class StringSerializer implements Serializer - { - public String load_item(Object obj) { return (String)obj; } - public Object save_item(String v) { return v; } - } -} diff --git a/srcs/juloo.keyboard2/Utils.java b/srcs/juloo.keyboard2/Utils.java index 0eee945..712c83d 100644 --- a/srcs/juloo.keyboard2/Utils.java +++ b/srcs/juloo.keyboard2/Utils.java @@ -7,7 +7,7 @@ import android.view.WindowManager; import java.io.InputStream; import java.io.InputStreamReader; -class Utils +public final class Utils { /** Turn the first letter of a string uppercase. */ public static String capitalize_string(String s) diff --git a/srcs/juloo.keyboard2/prefs/CustomExtraKeysPreference.java b/srcs/juloo.keyboard2/prefs/CustomExtraKeysPreference.java new file mode 100644 index 0000000..9d8395f --- /dev/null +++ b/srcs/juloo.keyboard2/prefs/CustomExtraKeysPreference.java @@ -0,0 +1,70 @@ +package juloo.keyboard2.prefs; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import juloo.keyboard2.*; +import org.json.JSONArray; +import org.json.JSONException; + +/** Allows to enter custom keys to be added to the keyboard. This shows up at + the top of the "Add keys to the keyboard" option. */ +public class CustomExtraKeysPreference extends ListGroupPreference +{ + /** This pref stores a list of strings encoded as JSON. */ + static final String KEY = "custom_extra_keys"; + static final ListGroupPreference.Serializer SERIALIZER = + new ListGroupPreference.StringSerializer(); + + public CustomExtraKeysPreference(Context context, AttributeSet attrs) + { + super(context, attrs); + setKey(KEY); + } + + public static Map get(SharedPreferences prefs) + { + Map kvs = + new HashMap(); + List key_names = load_from_preferences(KEY, prefs, null, SERIALIZER); + if (key_names != null) + { + for (String key_name : key_names) + kvs.put(KeyValue.makeStringKey(key_name), KeyboardData.PreferredPos.DEFAULT); + } + return kvs; + } + + String label_of_value(String value, int i) { return value; } + + @Override + void select(final SelectionCallback callback) + { + new AlertDialog.Builder(getContext()) + .setView(R.layout.dialog_edit_text) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){ + public void onClick(DialogInterface dialog, int which) + { + EditText input = (EditText)((AlertDialog)dialog).findViewById(R.id.text); + final String k = input.getText().toString(); + if (!k.equals("")) + callback.select(k); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + } + + @Override + Serializer get_serializer() { return SERIALIZER; } +} diff --git a/srcs/juloo.keyboard2/prefs/ExtraKeysPreference.java b/srcs/juloo.keyboard2/prefs/ExtraKeysPreference.java new file mode 100644 index 0000000..47ca535 --- /dev/null +++ b/srcs/juloo.keyboard2/prefs/ExtraKeysPreference.java @@ -0,0 +1,177 @@ +package juloo.keyboard2.prefs; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.preference.CheckBoxPreference; +import android.preference.PreferenceCategory; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import juloo.keyboard2.*; + +/** This class implements the "extra keys" preference but also defines the + possible extra keys. */ +public class ExtraKeysPreference extends PreferenceCategory +{ + /** Array of the keys that can be selected. */ + public static String[] extra_keys = new String[] + { + "alt", + "meta", + "voice_typing", + "accent_aigu", + "accent_grave", + "accent_double_aigu", + "accent_dot_above", + "accent_circonflexe", + "accent_tilde", + "accent_cedille", + "accent_trema", + "accent_ring", + "accent_caron", + "accent_macron", + "accent_ogonek", + "accent_breve", + "accent_slash", + "accent_bar", + "accent_dot_below", + "accent_hook_above", + "accent_horn", + "€", + "ß", + "£", + "§", + "†", + "ª", + "º", + "page_up", + "page_down", + "home", + "end", + "switch_greekmath", + "capslock", + "copy", + "paste", + "cut", + "selectAll", + "shareText", + "pasteAsPlainText", + "undo", + "redo", + "superscript", + "subscript", + }; + + /** Whether an extra key is enabled by default. */ + public static boolean default_checked(String name) + { + switch (name) + { + case "voice_typing": + return true; + default: + return false; + } + } + + /** Text that describe a key. Might be null. */ + static String key_description(Resources res, String name) + { + int id = 0; + switch (name) + { + case "capslock": id = R.string.key_descr_capslock; break; + case "switch_greekmath": id = R.string.key_descr_switch_greekmath; break; + case "voice_typing": id = R.string.key_descr_voice_typing; break; + case "copy": id = R.string.key_descr_copy; break; + case "paste": id = R.string.key_descr_paste; break; + case "cut": id = R.string.key_descr_cut; break; + case "selectAll": id = R.string.key_descr_selectAll; break; + case "shareText": id = R.string.key_descr_shareText; break; + case "pasteAsPlainText": id = R.string.key_descr_pasteAsPlainText; break; + case "undo": id = R.string.key_descr_undo; break; + case "redo": id = R.string.key_descr_redo; break; + case "ª": id = R.string.key_descr_ª; break; + case "º": id = R.string.key_descr_º; break; + case "superscript": id = R.string.key_descr_superscript; break; + case "subscript": id = R.string.key_descr_subscript; break; + case "page_up": id = R.string.key_descr_page_up; break; + case "page_down": id = R.string.key_descr_page_down; break; + case "home": id = R.string.key_descr_home; break; + case "end": id = R.string.key_descr_end; break; + } + if (id == 0) + return null; + return res.getString(id); + } + + /** Get the set of enabled extra keys. */ + public static Map get_extra_keys(SharedPreferences prefs) + { + Map ks = + new HashMap(); + for (String key_name : extra_keys) + { + if (prefs.getBoolean(pref_key_of_key_name(key_name), + default_checked(key_name))) + ks.put(KeyValue.getKeyByName(key_name), KeyboardData.PreferredPos.DEFAULT); + } + return ks; + } + + boolean _attached = false; /** Whether it has already been attached. */ + + public ExtraKeysPreference(Context context, AttributeSet attrs) + { + super(context, attrs); + setOrderingAsAdded(true); + } + + @Override + protected void onAttachedToActivity() + { + if (_attached) + return; + _attached = true; + for (String key_name : extra_keys) + addPreference(new ExtraKeyCheckBoxPreference(getContext(), key_name, + default_checked(key_name))); + } + + public static String pref_key_of_key_name(String key_name) + { + return "extra_key_" + key_name; + } + + static class ExtraKeyCheckBoxPreference extends CheckBoxPreference + { + boolean _key_font; + + public ExtraKeyCheckBoxPreference(Context ctx, String key_name, + boolean default_checked) + { + super(ctx); + KeyValue kv = KeyValue.getKeyByName(key_name); + String title = kv.getString(); + String descr = key_description(ctx.getResources(), key_name); + if (descr != null) + title += " (" + descr + ")"; + setKey(pref_key_of_key_name(key_name)); + setDefaultValue(default_checked); + setTitle(title); + _key_font = kv.hasFlags(KeyValue.FLAG_KEY_FONT); + } + + @Override + protected void onBindView(View view) + { + super.onBindView(view); + TextView title = (TextView)view.findViewById(android.R.id.title); + title.setTypeface(_key_font ? Theme.getKeyFont(getContext()) : null); + } + } +} diff --git a/srcs/juloo.keyboard2/prefs/IntSlideBarPreference.java b/srcs/juloo.keyboard2/prefs/IntSlideBarPreference.java new file mode 100644 index 0000000..bb6bf7d --- /dev/null +++ b/srcs/juloo.keyboard2/prefs/IntSlideBarPreference.java @@ -0,0 +1,120 @@ +package juloo.keyboard2.prefs; + +import android.content.Context; +import android.content.res.TypedArray; +import android.preference.DialogPreference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.SeekBar; + +/* + ** IntSlideBarPreference + ** - + ** Open a dialog showing a seekbar + ** - + ** xml attrs: + ** android:defaultValue Default value (int) + ** min min value (int) + ** max max value (int) + ** - + ** Summary field allow to show the current value using %s flag + */ +public class IntSlideBarPreference extends DialogPreference + implements SeekBar.OnSeekBarChangeListener +{ + private LinearLayout _layout; + private TextView _textView; + private SeekBar _seekBar; + + private int _min; + + private String _initialSummary; + + public IntSlideBarPreference(Context context, AttributeSet attrs) + { + super(context, attrs); + _initialSummary = getSummary().toString(); + _textView = new TextView(context); + _textView.setPadding(48, 40, 48, 40); + _seekBar = new SeekBar(context); + _seekBar.setOnSeekBarChangeListener(this); + _min = attrs.getAttributeIntValue(null, "min", 0); + int max = attrs.getAttributeIntValue(null, "max", 0); + _seekBar.setMax(max - _min); + _layout = new LinearLayout(getContext()); + _layout.setOrientation(LinearLayout.VERTICAL); + _layout.addView(_textView); + _layout.addView(_seekBar); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) + { + updateText(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) + { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) + { + } + + @Override + protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) + { + int value; + + if (restorePersistedValue) + { + value = getPersistedInt(_min); + } + else + { + value = (Integer)defaultValue; + persistInt(value); + } + _seekBar.setProgress(value - _min); + updateText(); + } + + @Override + protected Object onGetDefaultValue(TypedArray a, int index) + { + return (a.getInt(index, _min)); + } + + @Override + protected void onDialogClosed(boolean positiveResult) + { + if (positiveResult) + persistInt(_seekBar.getProgress() + _min); + else + _seekBar.setProgress(getPersistedInt(_min) - _min); + + updateText(); + } + + protected View onCreateDialogView() + { + ViewGroup parent = (ViewGroup)_layout.getParent(); + + if (parent != null) + parent.removeView(_layout); + return (_layout); + } + + private void updateText() + { + String f = String.format(_initialSummary, _seekBar.getProgress() + _min); + + _textView.setText(f); + setSummary(f); + } +} diff --git a/srcs/juloo.keyboard2/prefs/LayoutsPreference.java b/srcs/juloo.keyboard2/prefs/LayoutsPreference.java new file mode 100644 index 0000000..787800c --- /dev/null +++ b/srcs/juloo.keyboard2/prefs/LayoutsPreference.java @@ -0,0 +1,302 @@ +package juloo.keyboard2.prefs; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.ArrayAdapter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import juloo.keyboard2.*; +import org.json.JSONException; +import org.json.JSONObject; + +public class LayoutsPreference extends ListGroupPreference +{ + static final String KEY = "layouts"; + static final List DEFAULT = + Collections.singletonList((Layout)new SystemLayout()); + static final ListGroupPreference.Serializer SERIALIZER = + new Serializer(); + + /** Text displayed for each layout in the dialog list. */ + String[] _layout_display_names; + + public LayoutsPreference(Context ctx, AttributeSet attrs) + { + super(ctx, attrs); + setKey(KEY); + Resources res = ctx.getResources(); + _layout_display_names = res.getStringArray(R.array.pref_layout_entries); + } + + /** Obtained from [res/values/layouts.xml]. */ + static List _unsafe_layout_ids_str = null; + static TypedArray _unsafe_layout_ids_res = null; + + /** Layout internal names. Contains "system" and "custom". */ + public static List get_layout_names(Resources res) + { + if (_unsafe_layout_ids_str == null) + _unsafe_layout_ids_str = Arrays.asList( + res.getStringArray(R.array.pref_layout_values)); + return _unsafe_layout_ids_str; + } + + /** Layout resource id for a layout name. [-1] if not found. */ + public static int layout_id_of_name(Resources res, String name) + { + if (_unsafe_layout_ids_res == null) + _unsafe_layout_ids_res = res.obtainTypedArray(R.array.layout_ids); + int i = get_layout_names(res).indexOf(name); + if (i >= 0) + return _unsafe_layout_ids_res.getResourceId(i, 0); + return -1; + } + + /** [null] for the "system" layout. */ + public static List load_from_preferences(Resources res, SharedPreferences prefs) + { + List layouts = new ArrayList(); + for (Layout l : load_from_preferences(KEY, prefs, DEFAULT, SERIALIZER)) + { + if (l instanceof NamedLayout) + layouts.add(layout_of_string(res, ((NamedLayout)l).name)); + else if (l instanceof CustomLayout) + layouts.add(((CustomLayout)l).parsed); + else // instanceof SystemLayout + layouts.add(null); + } + return layouts; + } + + /** Does not call [prefs.commit()]. */ + public static void save_to_preferences(SharedPreferences.Editor prefs, List items) + { + save_to_preferences(KEY, prefs, items, SERIALIZER); + } + + public static KeyboardData layout_of_string(Resources res, String name) + { + int id = layout_id_of_name(res, name); + if (id > 0) + return KeyboardData.load(res, id); + // Might happen when the app is downgraded, return the system layout. + return null; + } + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) + { + super.onSetInitialValue(restoreValue, defaultValue); + if (_values.size() == 0) + set_values(new ArrayList(DEFAULT), false); + } + + String label_of_layout(Layout l) + { + if (l instanceof NamedLayout) + { + String lname = ((NamedLayout)l).name; + int value_i = get_layout_names(getContext().getResources()).indexOf(lname); + return value_i < 0 ? lname : _layout_display_names[value_i]; + } + else if (l instanceof CustomLayout) + { + // Use the layout's name if possible + CustomLayout cl = (CustomLayout)l; + if (cl.parsed != null && cl.parsed.name != null + && !cl.parsed.name.equals("")) + return cl.parsed.name; + else + return getContext().getString(R.string.pref_layout_e_custom); + } + else // instanceof SystemLayout + return getContext().getString(R.string.pref_layout_e_system); + } + + @Override + String label_of_value(Layout value, int i) + { + return getContext().getString(R.string.pref_layouts_item, i + 1, + label_of_layout(value)); + } + + @Override + AddButton on_attach_add_button(AddButton prev_btn) + { + if (prev_btn == null) + return new LayoutsAddButton(getContext()); + return prev_btn; + } + + @Override + boolean should_allow_remove_item(Layout value) + { + return (_values.size() > 1 && !(value instanceof CustomLayout)); + } + + @Override + ListGroupPreference.Serializer get_serializer() { return SERIALIZER; } + + @Override + void select(final SelectionCallback callback) + { + ArrayAdapter layouts = new ArrayAdapter(getContext(), android.R.layout.simple_list_item_1, _layout_display_names); + new AlertDialog.Builder(getContext()) + .setView(R.layout.dialog_edit_text) + .setAdapter(layouts, new DialogInterface.OnClickListener(){ + public void onClick(DialogInterface _dialog, int which) + { + String name = get_layout_names(getContext().getResources()).get(which); + switch (name) + { + case "system": + callback.select(new SystemLayout()); + break; + case "custom": + select_custom(callback, read_initial_custom_layout()); + break; + default: + callback.select(new NamedLayout(name)); + break; + } + } + }) + .show(); + } + + /** Dialog for specifying a custom layout. [initial_text] is the layout + description when modifying a layout. */ + void select_custom(final SelectionCallback callback, String initial_text) + { + boolean allow_remove = callback.allow_remove() && _values.size() > 1; + CustomLayoutEditDialog.show(getContext(), initial_text, allow_remove, + new CustomLayoutEditDialog.Callback() + { + public void select(String text) + { + if (text == null) + callback.select(null); + else + callback.select(CustomLayout.parse(text)); + } + + public String validate(String text) + { + try + { + KeyboardData.load_string_exn(text); + return null; // Validation passed + } + catch (Exception e) + { + return e.getMessage(); + } + } + }); + } + + /** Called when modifying a layout. Custom layouts behave differently. */ + @Override + void select(final SelectionCallback callback, Layout prev_layout) + { + if (prev_layout instanceof CustomLayout) + select_custom(callback, ((CustomLayout)prev_layout).xml); + else + select(callback); + } + + /** The initial text for the custom layout entry box. The qwerty_us layout is + a good default and contains a bit of documentation. */ + String read_initial_custom_layout() + { + try + { + Resources res = getContext().getResources(); + return Utils.read_all_utf8(res.openRawResource(R.raw.latn_qwerty_us)); + } + catch (Exception _e) + { + return ""; + } + } + + class LayoutsAddButton extends AddButton + { + public LayoutsAddButton(Context ctx) + { + super(ctx); + setLayoutResource(R.layout.pref_layouts_add_btn); + } + } + + /** A layout selected by the user. The only implementations are + [NamedLayout], [SystemLayout] and [CustomLayout]. */ + public interface Layout {} + + public static final class SystemLayout implements Layout + { + public SystemLayout() {} + } + + /** The name of a layout defined in [res/xml]. */ + public static final class NamedLayout implements Layout + { + public final String name; + public NamedLayout(String n) { name = n; } + } + + /** The XML description of a custom layout. */ + public static final class CustomLayout implements Layout + { + public final String xml; + /** Might be null. */ + public final KeyboardData parsed; + public CustomLayout(String xml_, KeyboardData k) { xml = xml_; parsed = k; } + public static CustomLayout parse(String xml) + { + KeyboardData parsed = null; + try { parsed = KeyboardData.load_string_exn(xml); } + catch (Exception e) {} + return new CustomLayout(xml, parsed); + } + } + + /** Named layouts are serialized to strings and custom layouts to JSON + objects with a [kind] field. */ + public static class Serializer implements ListGroupPreference.Serializer + { + public Layout load_item(Object obj) throws JSONException + { + if (obj instanceof String) + { + String name = (String)obj; + if (name.equals("system")) + return new SystemLayout(); + return new NamedLayout(name); + } + JSONObject obj_ = (JSONObject)obj; + switch (obj_.getString("kind")) + { + case "custom": return CustomLayout.parse(obj_.getString("xml")); + case "system": default: return new SystemLayout(); + } + } + + public Object save_item(Layout v) throws JSONException + { + if (v instanceof NamedLayout) + return ((NamedLayout)v).name; + if (v instanceof CustomLayout) + return new JSONObject().put("kind", "custom") + .put("xml", ((CustomLayout)v).xml); + return new JSONObject().put("kind", "system"); + } + } +} diff --git a/srcs/juloo.keyboard2/prefs/ListGroupPreference.java b/srcs/juloo.keyboard2/prefs/ListGroupPreference.java new file mode 100644 index 0000000..e332764 --- /dev/null +++ b/srcs/juloo.keyboard2/prefs/ListGroupPreference.java @@ -0,0 +1,294 @@ +package juloo.keyboard2.prefs; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.Preference; +import android.preference.PreferenceGroup; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import java.util.ArrayList; +import java.util.List; +import juloo.keyboard2.*; +import org.json.JSONArray; +import org.json.JSONException; + +/** A list of preferences where the users can add items to the end and modify + and remove items. Backed by a string list. Implement user selection in + [select()]. */ +public abstract class ListGroupPreference extends PreferenceGroup +{ + boolean _attached = false; + List _values; + /** The "add" button currently displayed. */ + AddButton _add_button = null; + + public ListGroupPreference(Context context, AttributeSet attrs) + { + super(context, attrs); + setOrderingAsAdded(true); + setLayoutResource(R.layout.pref_listgroup_group); + _values = new ArrayList(); + } + + /** Overrideable */ + + /** The label to display on the item for a given value. */ + abstract String label_of_value(E value, int i); + + /** Called every time the list changes and allows to change the "Add" button + appearance. + [prev_btn] is the previously attached button, might be null. */ + AddButton on_attach_add_button(AddButton prev_btn) + { + if (prev_btn == null) + return new AddButton(getContext()); + return prev_btn; + } + + /** Called every time the list changes and allows to disable the "Remove" + buttons on every items. Might be used to enforce a minimum number of + items. */ + boolean should_allow_remove_item(E _value) + { + return true; + } + + /** Called when an item is added or modified. */ + abstract void select(SelectionCallback callback); + + /** Called when an item is modified. */ + void select(SelectionCallback callback, E _old_value) + { + select(callback); + } + + /** A separate class is used as the same serializer must be used in the + static context. See [Serializer] below. */ + abstract Serializer get_serializer(); + + /** Load/save utils */ + + /** Read a value saved by preference from a [SharedPreferences] object. + [serializer] must be the same that is returned by [get_serializer()]. + Returns [null] on error. */ + static List load_from_preferences(String key, + SharedPreferences prefs, List def, Serializer serializer) + { + String s = prefs.getString(key, null); + return (s != null) ? load_from_string(s, serializer) : def; + } + + /** Save items into the preferences. Does not call [prefs.commit()]. */ + static void save_to_preferences(String key, SharedPreferences.Editor prefs, List items, Serializer serializer) + { + prefs.putString(key, save_to_string(items, serializer)); + } + + /** Decode a list of string previously encoded with [save_to_string]. Returns + [null] on error. */ + static List load_from_string(String inp, Serializer serializer) + { + try + { + List l = new ArrayList(); + JSONArray arr = new JSONArray(inp); + for (int i = 0; i < arr.length(); i++) + l.add(serializer.load_item(arr.get(i))); + return l; + } + catch (JSONException e) + { + Logs.exn("load_from_string", e); + return null; + } + } + + /** Encode a list of string so it can be passed to + [Preference.persistString()]. Decode with [load_from_string]. */ + static String save_to_string(List items, Serializer serializer) + { + List serialized_items = new ArrayList(); + for (E it : items) + { + try + { + serialized_items.add(serializer.save_item(it)); + } + catch (JSONException e) + { + Logs.exn("save_to_string", e); + } + } + return (new JSONArray(serialized_items)).toString(); + } + + /** Protected API */ + + /** Set the values. If [persist] is [true], persist into the store. */ + void set_values(List vs, boolean persist) + { + _values = vs; + reattach(); + if (persist) + persistString(save_to_string(vs, get_serializer())); + } + + void add_item(E v) + { + _values.add(v); + set_values(_values, true); + } + + void change_item(int i, E v) + { + _values.set(i, v); + set_values(_values, true); + } + + void remove_item(int i) + { + _values.remove(i); + set_values(_values, true); + } + + /** Internal */ + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) + { + String input = (restoreValue) ? getPersistedString(null) : (String)defaultValue; + if (input != null) + { + List values = load_from_string(input, get_serializer()); + if (values != null) + set_values(values, false); + } + } + + @Override + protected void onAttachedToActivity() + { + super.onAttachedToActivity(); + if (_attached) + return; + _attached = true; + reattach(); + } + + void reattach() + { + if (!_attached) + return; + removeAll(); + int i = 0; + for (E v : _values) + { + addPreference(this.new Item(getContext(), i, v)); + i++; + } + _add_button = on_attach_add_button(_add_button); + _add_button.setOrder(Preference.DEFAULT_ORDER); + addPreference(_add_button); + } + + class Item extends Preference + { + final E _value; + final int _index; + + public Item(Context ctx, int index, E value) + { + super(ctx); + _value = value; + _index = index; + setPersistent(false); + setTitle(label_of_value(value, index)); + if (should_allow_remove_item(value)) + setWidgetLayoutResource(R.layout.pref_listgroup_item_widget); + } + + @Override + protected View onCreateView(ViewGroup parent) + { + View v = super.onCreateView(parent); + View remove_btn = v.findViewById(R.id.pref_listgroup_remove_btn); + if (remove_btn != null) + remove_btn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View _v) + { + remove_item(_index); + } + }); + v.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View _v) + { + select(new SelectionCallback() { + public void select(E value) + { + if (value == null) + remove_item(_index); + else + change_item(_index, value); + } + + public boolean allow_remove() { return true; } + }, _value); + } + }); + return v; + } + } + + class AddButton extends Preference + { + public AddButton(Context ctx) + { + super(ctx); + setPersistent(false); + setLayoutResource(R.layout.pref_listgroup_add_btn); + } + + @Override + protected void onClick() + { + select(new SelectionCallback() { + public void select(E value) + { + add_item(value); + } + + public boolean allow_remove() { return false; } + }); + } + } + + public interface SelectionCallback + { + public void select(E value); + + /** If this method returns [true], [null] might be passed to [select] to + remove the item. */ + public boolean allow_remove(); + } + + /** Methods for serializing and deserializing abstract items. + [StringSerializer] is an implementation. */ + public interface Serializer + { + /** [obj] is an object returned by [save_item()]. */ + E load_item(Object obj) throws JSONException; + + /** Serialize an item into JSON. Might return an object that can be inserted + in a [JSONArray]. */ + Object save_item(E v) throws JSONException; + } + + public static class StringSerializer implements Serializer + { + public String load_item(Object obj) { return (String)obj; } + public Object save_item(String v) { return v; } + } +} diff --git a/srcs/juloo.keyboard2/prefs/SlideBarPreference.java b/srcs/juloo.keyboard2/prefs/SlideBarPreference.java new file mode 100644 index 0000000..cacf37e --- /dev/null +++ b/srcs/juloo.keyboard2/prefs/SlideBarPreference.java @@ -0,0 +1,131 @@ +package juloo.keyboard2.prefs; + +import android.content.Context; +import android.content.res.TypedArray; +import android.preference.DialogPreference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.SeekBar; + +/* + ** SideBarPreference + ** - + ** Open a dialog showing a seekbar + ** - + ** xml attrs: + ** android:defaultValue Default value (float) + ** min min value (float) + ** max max value (float) + ** - + ** Summary field allow to show the current value using %f or %s flag + */ +public class SlideBarPreference extends DialogPreference + implements SeekBar.OnSeekBarChangeListener +{ + private static final int STEPS = 100; + + private LinearLayout _layout; + private TextView _textView; + private SeekBar _seekBar; + + private float _min; + private float _max; + private float _value; + + private String _initialSummary; + + public SlideBarPreference(Context context, AttributeSet attrs) + { + super(context, attrs); + _initialSummary = getSummary().toString(); + _textView = new TextView(context); + _textView.setPadding(48, 40, 48, 40); + _seekBar = new SeekBar(context); + _seekBar.setOnSeekBarChangeListener(this); + _seekBar.setMax(STEPS); + _min = float_of_string(attrs.getAttributeValue(null, "min")); + _value = _min; + _max = Math.max(1f, float_of_string(attrs.getAttributeValue(null, "max"))); + _layout = new LinearLayout(getContext()); + _layout.setOrientation(LinearLayout.VERTICAL); + _layout.addView(_textView); + _layout.addView(_seekBar); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) + { + _value = Math.round(progress * (_max - _min)) / (float)STEPS + _min; + updateText(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) + { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) + { + } + + @Override + protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) + { + if (restorePersistedValue) + { + _value = getPersistedFloat(_min); + } + else + { + _value = (Float)defaultValue; + persistFloat(_value); + } + _seekBar.setProgress((int)((_value - _min) * STEPS / (_max - _min))); + updateText(); + } + + @Override + protected Object onGetDefaultValue(TypedArray a, int index) + { + return (a.getFloat(index, _min)); + } + + @Override + protected void onDialogClosed(boolean positiveResult) + { + if (positiveResult) + persistFloat(_value); + else + _seekBar.setProgress((int)((getPersistedFloat(_min) - _min) * STEPS / (_max - _min))); + + updateText(); + } + + protected View onCreateDialogView() + { + ViewGroup parent = (ViewGroup)_layout.getParent(); + + if (parent != null) + parent.removeView(_layout); + return (_layout); + } + + private void updateText() + { + String f = String.format(_initialSummary, _value); + + _textView.setText(f); + setSummary(f); + } + + private static float float_of_string(String str) + { + if (str == null) + return (0f); + return (Float.parseFloat(str)); + } +} -- cgit v1.2.3