mirror of
https://github.com/Julow/Unexpected-Keyboard.git
synced 2024-12-01 19:53:14 +01:00
d5676d683f
Incompatible APIs were used in the custom layouts and the extra keys options. Add @TargetApi annotations to help catch similar issues in the future with the help of 'gradle lint'.
304 lines
9.1 KiB
Java
304 lines
9.1 KiB
Java
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.view.View;
|
|
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(View.inflate(getContext(), R.layout.dialog_edit_text, null))
|
|
.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 [srcs/layouts]. */
|
|
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");
|
|
}
|
|
}
|
|
}
|