1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
|
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 android.widget.EditText;
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<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(KeyboardData.load_string(((CustomLayout)l).xml));
else // instanceof SystemLayout
layouts.add(null);
}
return layouts;
}
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)
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()
{
return (_values.size() > 1);
}
@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, "");
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)
{
final EditText input = new EditText(getContext());
input.setText(initial_text);
new AlertDialog.Builder(getContext())
.setView(input)
.setTitle(R.string.pref_custom_layout_title)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface _dialog, int _which)
{
callback.select(new CustomLayout(input.getText().toString()));
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
/** 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);
}
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;
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");
}
}
}
|