diff options
Diffstat (limited to 'srcs/juloo.keyboard2/prefs/LayoutsPreference.java')
| -rw-r--r-- | srcs/juloo.keyboard2/prefs/LayoutsPreference.java | 302 |
1 files changed, 302 insertions, 0 deletions
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<LayoutsPreference.Layout> +{ + static final String KEY = "layouts"; + static final List<Layout> DEFAULT = + Collections.singletonList((Layout)new SystemLayout()); + static final ListGroupPreference.Serializer<Layout> 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<String> _unsafe_layout_ids_str = null; + static TypedArray _unsafe_layout_ids_res = null; + + /** Layout internal names. Contains "system" and "custom". */ + public static List<String> 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<KeyboardData> load_from_preferences(Resources res, SharedPreferences prefs) + { + List<KeyboardData> layouts = new ArrayList<KeyboardData>(); + 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<Layout> 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<Layout>(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<Layout> 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<Layout> + { + 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"); + } + } +} |
