abouttreesummaryrefslogcommitdiff
path: root/srcs/juloo.keyboard2/prefs/LayoutsPreference.java
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/juloo.keyboard2/prefs/LayoutsPreference.java')
-rw-r--r--srcs/juloo.keyboard2/prefs/LayoutsPreference.java302
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");
+ }
+ }
+}