abouttreesummaryrefslogcommitdiff
path: root/srcs/juloo.keyboard2
diff options
context:
space:
mode:
authorJules Aguillon2023-08-10 12:57:31 +0200
committerJules Aguillon2023-08-16 12:21:23 +0200
commit500f4e41d3a72f5865aa489c89ad11b947fa54b7 (patch)
tree677781ff5e82c320456f21e91b8ffa41819fd439 /srcs/juloo.keyboard2
parent8611dbcfa0ef6d551bf3acb65e5a1990a3ced75b (diff)
downloadunexpected-keyboard-500f4e41d3a72f5865aa489c89ad11b947fa54b7.tar.gz
unexpected-keyboard-500f4e41d3a72f5865aa489c89ad11b947fa54b7.zip
Allow multiple custom layouts
This merges the "Layouts" option with the "Custom layout" option. A custom layout becomes an item in the "Layouts" list among the other layouts. It's possible to add several custom layouts. Selecting the "Custom layout" item in the list opens a second dialog for entering the layout description. Layouts are serialized as JSON object and are decoded solely in the LayoutsPreference class.
Diffstat (limited to 'srcs/juloo.keyboard2')
-rw-r--r--srcs/juloo.keyboard2/Config.java38
-rw-r--r--srcs/juloo.keyboard2/Keyboard2.java2
-rw-r--r--srcs/juloo.keyboard2/LayoutsPreference.java173
3 files changed, 159 insertions, 54 deletions
diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java
index 24565ab..4684194 100644
--- a/srcs/juloo.keyboard2/Config.java
+++ b/srcs/juloo.keyboard2/Config.java
@@ -3,7 +3,6 @@ package juloo.keyboard2;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -28,7 +27,6 @@ final class Config
// From preferences
/** [null] represent the [system] layout. */
public List<KeyboardData> layouts;
- public KeyboardData custom_layout; // Might be 'null'
public boolean show_numpad = false;
// From the 'numpad_layout' option, also apply to the numeric pane.
public boolean inverse_numpad = false;
@@ -114,11 +112,7 @@ final class Config
{
keyboardHeightPercent = _prefs.getInt("keyboard_height", 35);
}
- List<String> layout_names = LayoutsPreference.load_from_preferences(_prefs);
- layouts = new ArrayList<KeyboardData>();
- for (String l : layout_names)
- layouts.add(layout_of_string(res, l));
- custom_layout = KeyboardData.load_string(_prefs.getString("custom_layout", ""));
+ layouts = LayoutsPreference.load_from_preferences(res, _prefs);
inverse_numpad = _prefs.getString("numpad_layout", "default").equals("low_first");
number_row = _prefs.getBoolean("number_row", false);
// The baseline for the swipe distance correspond to approximately the
@@ -335,36 +329,6 @@ final class Config
}
}
- /** Obtained from XML. */
- static List<String> layout_ids_str = null;
- static TypedArray layout_ids_res = null;
-
- /** Might return [null] if the selected layout is "system", "custom" or if
- the name is not recognized. */
- public KeyboardData layout_of_string(Resources res, String name)
- {
- if (layout_ids_str == null)
- {
- layout_ids_str = Arrays.asList(res.getStringArray(R.array.pref_layout_values));
- layout_ids_res = res.obtainTypedArray(R.array.layout_ids);
- }
- int i = layout_ids_str.indexOf(name);
- if (i >= 0)
- {
- int id = layout_ids_res.getResourceId(i, 0);
- if (id > 0)
- return KeyboardData.load(res, id);
- // Fallthrough
- }
- switch (name)
- {
- case "custom": return custom_layout;
- case "system":
- case "none":
- default: return null;
- }
- }
-
char inverse_numpad_char(char c)
{
switch (c)
diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java
index 5f7442b..92507ed 100644
--- a/srcs/juloo.keyboard2/Keyboard2.java
+++ b/srcs/juloo.keyboard2/Keyboard2.java
@@ -155,7 +155,7 @@ public class Keyboard2 extends InputMethodService
{
String s = subtype.getExtraValueOf("default_layout");
if (s != null)
- default_layout = _config.layout_of_string(getResources(), s);
+ default_layout = LayoutsPreference.layout_of_string(getResources(), s);
refreshAccentsOption(imm, subtype);
}
}
diff --git a/srcs/juloo.keyboard2/LayoutsPreference.java b/srcs/juloo.keyboard2/LayoutsPreference.java
index ba28630..a65b350 100644
--- a/srcs/juloo.keyboard2/LayoutsPreference.java
+++ b/srcs/juloo.keyboard2/LayoutsPreference.java
@@ -5,6 +5,7 @@ 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 android.widget.EditText;
@@ -12,16 +13,17 @@ 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<String>
+public class LayoutsPreference extends ListGroupPreference<LayoutsPreference.Layout>
{
static final String KEY = "layouts";
- static final List<String> DEFAULT = Collections.singletonList("system");
- static final ListGroupPreference.Serializer<String> SERIALIZER =
- new ListGroupPreference.StringSerializer();
+ static final List<Layout> DEFAULT =
+ Collections.singletonList((Layout)new SystemLayout());
+ static final ListGroupPreference.Serializer<Layout> SERIALIZER =
+ new Serializer();
- /** Layout names as stored in the preferences. */
- List<String> _layout_names;
/** Text displayed for each layout in the dialog list. */
String[] _layout_display_names;
@@ -30,13 +32,56 @@ public class LayoutsPreference extends ListGroupPreference<String>
super(ctx, attrs);
setKey(KEY);
Resources res = ctx.getResources();
- _layout_names = Arrays.asList(res.getStringArray(R.array.pref_layout_values));
_layout_display_names = res.getStringArray(R.array.pref_layout_entries);
}
- public static List<String> load_from_preferences(SharedPreferences prefs)
+ /** 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(KeyboardData.load_string(((CustomLayout)l).xml));
+ else // instanceof SystemLayout
+ layouts.add(null);
+ }
+ return layouts;
+ }
+
+ public static KeyboardData layout_of_string(Resources res, String name)
{
- return load_from_preferences(KEY, prefs, DEFAULT, SERIALIZER);
+ 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
@@ -44,15 +89,28 @@ public class LayoutsPreference extends ListGroupPreference<String>
{
super.onSetInitialValue(restoreValue, defaultValue);
if (_values.size() == 0)
- set_values(new ArrayList<String>(DEFAULT), false);
+ 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)
+ 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(String value, int i)
+ String label_of_value(Layout value, int i)
{
- int value_i = _layout_names.indexOf(value);
- String lname = value_i < 0 ? value : _layout_display_names[value_i];
- return getContext().getString(R.string.pref_layouts_item, i + 1, lname);
+ return getContext().getString(R.string.pref_layouts_item, i + 1,
+ label_of_layout(value));
}
@Override
@@ -70,7 +128,7 @@ public class LayoutsPreference extends ListGroupPreference<String>
}
@Override
- Serializer<String> get_serializer() { return SERIALIZER; }
+ ListGroupPreference.Serializer<Layout> get_serializer() { return SERIALIZER; }
@Override
void select(final SelectionCallback callback)
@@ -81,9 +139,37 @@ public class LayoutsPreference extends ListGroupPreference<String>
.setAdapter(layouts, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface _dialog, int which)
{
- callback.select(_layout_names.get(which));
+ String name = get_layout_names(getContext().getResources()).get(which);
+ switch (name)
+ {
+ case "system":
+ callback.select(new SystemLayout());
+ break;
+ case "custom":
+ select_custom(callback);
+ break;
+ default:
+ callback.select(new NamedLayout(name));
+ break;
+ }
+ }
+ })
+ .show();
+ }
+
+ void select_custom(final SelectionCallback callback)
+ {
+ new AlertDialog.Builder(getContext())
+ .setView(R.layout.dialog_edit_text)
+ .setTitle(R.string.pref_custom_layout_title)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
+ public void onClick(DialogInterface dialog, int _which)
+ {
+ EditText input = (EditText)((AlertDialog)dialog).findViewById(R.id.text);
+ callback.select(new CustomLayout(input.getText().toString()));
}
})
+ .setNegativeButton(android.R.string.cancel, null)
.show();
}
@@ -95,4 +181,59 @@ public class LayoutsPreference extends ListGroupPreference<String>
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;
+ public CustomLayout(String c) { xml = c; }
+ }
+
+ /** Named layouts are serialized to strings and custom layouts to JSON
+ objects with a [kind] field. */
+ 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 new CustomLayout(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");
+ }
+ }
}