|
1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
|
2 * This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 package org.mozilla.gecko.prompts; |
|
7 |
|
8 import java.text.SimpleDateFormat; |
|
9 import java.util.Calendar; |
|
10 import java.util.GregorianCalendar; |
|
11 |
|
12 import org.json.JSONObject; |
|
13 import org.mozilla.gecko.widget.AllCapsTextView; |
|
14 import org.mozilla.gecko.widget.DateTimePicker; |
|
15 |
|
16 import android.content.Context; |
|
17 import android.content.res.Configuration; |
|
18 import android.os.Build; |
|
19 import android.text.Html; |
|
20 import android.text.InputType; |
|
21 import android.text.TextUtils; |
|
22 import android.text.format.DateFormat; |
|
23 import android.util.Log; |
|
24 import android.view.View; |
|
25 import android.view.ViewGroup.LayoutParams; |
|
26 import android.view.inputmethod.InputMethodManager; |
|
27 import android.widget.ArrayAdapter; |
|
28 import android.widget.CheckBox; |
|
29 import android.widget.DatePicker; |
|
30 import android.widget.EditText; |
|
31 import android.widget.LinearLayout; |
|
32 import android.widget.Spinner; |
|
33 import android.widget.TextView; |
|
34 import android.widget.TimePicker; |
|
35 |
|
36 public class PromptInput { |
|
37 protected final String mLabel; |
|
38 protected final String mType; |
|
39 protected final String mId; |
|
40 protected final String mValue; |
|
41 protected OnChangeListener mListener; |
|
42 protected View mView; |
|
43 public static final String LOGTAG = "GeckoPromptInput"; |
|
44 |
|
45 public interface OnChangeListener { |
|
46 public void onChange(PromptInput input); |
|
47 } |
|
48 |
|
49 public void setListener(OnChangeListener listener) { |
|
50 mListener = listener; |
|
51 } |
|
52 |
|
53 public static class EditInput extends PromptInput { |
|
54 protected final String mHint; |
|
55 protected final boolean mAutofocus; |
|
56 public static final String INPUT_TYPE = "textbox"; |
|
57 |
|
58 public EditInput(JSONObject object) { |
|
59 super(object); |
|
60 mHint = object.optString("hint"); |
|
61 mAutofocus = object.optBoolean("autofocus"); |
|
62 } |
|
63 |
|
64 public View getView(final Context context) throws UnsupportedOperationException { |
|
65 EditText input = new EditText(context); |
|
66 input.setInputType(InputType.TYPE_CLASS_TEXT); |
|
67 input.setText(mValue); |
|
68 |
|
69 if (!TextUtils.isEmpty(mHint)) { |
|
70 input.setHint(mHint); |
|
71 } |
|
72 |
|
73 if (mAutofocus) { |
|
74 input.setOnFocusChangeListener(new View.OnFocusChangeListener() { |
|
75 @Override |
|
76 public void onFocusChange(View v, boolean hasFocus) { |
|
77 if (hasFocus) { |
|
78 ((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(v, 0); |
|
79 } |
|
80 } |
|
81 }); |
|
82 input.requestFocus(); |
|
83 } |
|
84 |
|
85 mView = (View)input; |
|
86 return mView; |
|
87 } |
|
88 |
|
89 @Override |
|
90 public Object getValue() { |
|
91 EditText edit = (EditText)mView; |
|
92 return edit.getText(); |
|
93 } |
|
94 } |
|
95 |
|
96 public static class NumberInput extends EditInput { |
|
97 public static final String INPUT_TYPE = "number"; |
|
98 public NumberInput(JSONObject obj) { |
|
99 super(obj); |
|
100 } |
|
101 |
|
102 public View getView(final Context context) throws UnsupportedOperationException { |
|
103 EditText input = (EditText) super.getView(context); |
|
104 input.setRawInputType(Configuration.KEYBOARD_12KEY); |
|
105 input.setInputType(InputType.TYPE_CLASS_NUMBER | |
|
106 InputType.TYPE_NUMBER_FLAG_SIGNED); |
|
107 return input; |
|
108 } |
|
109 } |
|
110 |
|
111 public static class PasswordInput extends EditInput { |
|
112 public static final String INPUT_TYPE = "password"; |
|
113 public PasswordInput(JSONObject obj) { |
|
114 super(obj); |
|
115 } |
|
116 |
|
117 public View getView(Context context) throws UnsupportedOperationException { |
|
118 EditText input = (EditText) super.getView(context); |
|
119 input.setInputType(InputType.TYPE_CLASS_TEXT | |
|
120 InputType.TYPE_TEXT_VARIATION_PASSWORD | |
|
121 InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); |
|
122 return input; |
|
123 } |
|
124 |
|
125 @Override |
|
126 public Object getValue() { |
|
127 EditText edit = (EditText)mView; |
|
128 return edit.getText(); |
|
129 } |
|
130 } |
|
131 |
|
132 public static class CheckboxInput extends PromptInput { |
|
133 public static final String INPUT_TYPE = "checkbox"; |
|
134 private boolean mChecked; |
|
135 |
|
136 public CheckboxInput(JSONObject obj) { |
|
137 super(obj); |
|
138 mChecked = obj.optBoolean("checked"); |
|
139 } |
|
140 |
|
141 public View getView(Context context) throws UnsupportedOperationException { |
|
142 CheckBox checkbox = new CheckBox(context); |
|
143 checkbox.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); |
|
144 checkbox.setText(mLabel); |
|
145 checkbox.setChecked(mChecked); |
|
146 mView = (View)checkbox; |
|
147 return mView; |
|
148 } |
|
149 |
|
150 @Override |
|
151 public Object getValue() { |
|
152 CheckBox checkbox = (CheckBox)mView; |
|
153 return checkbox.isChecked() ? Boolean.TRUE : Boolean.FALSE; |
|
154 } |
|
155 } |
|
156 |
|
157 public static class DateTimeInput extends PromptInput { |
|
158 public static final String[] INPUT_TYPES = new String[] { |
|
159 "date", |
|
160 "week", |
|
161 "time", |
|
162 "datetime-local", |
|
163 "datetime", |
|
164 "month" |
|
165 }; |
|
166 |
|
167 public DateTimeInput(JSONObject obj) { |
|
168 super(obj); |
|
169 } |
|
170 |
|
171 public View getView(Context context) throws UnsupportedOperationException { |
|
172 if (mType.equals("date")) { |
|
173 try { |
|
174 DateTimePicker input = new DateTimePicker(context, "yyyy-MM-dd", mValue, |
|
175 DateTimePicker.PickersState.DATE); |
|
176 input.toggleCalendar(true); |
|
177 mView = (View)input; |
|
178 } catch (UnsupportedOperationException ex) { |
|
179 // We can't use our custom version of the DatePicker widget because the sdk is too old. |
|
180 // But we can fallback on the native one. |
|
181 DatePicker input = new DatePicker(context); |
|
182 try { |
|
183 if (!TextUtils.isEmpty(mValue)) { |
|
184 GregorianCalendar calendar = new GregorianCalendar(); |
|
185 calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(mValue)); |
|
186 input.updateDate(calendar.get(Calendar.YEAR), |
|
187 calendar.get(Calendar.MONTH), |
|
188 calendar.get(Calendar.DAY_OF_MONTH)); |
|
189 } |
|
190 } catch (Exception e) { |
|
191 Log.e(LOGTAG, "error parsing format string: " + e); |
|
192 } |
|
193 mView = (View)input; |
|
194 } |
|
195 } else if (mType.equals("week")) { |
|
196 DateTimePicker input = new DateTimePicker(context, "yyyy-'W'ww", mValue, |
|
197 DateTimePicker.PickersState.WEEK); |
|
198 mView = (View)input; |
|
199 } else if (mType.equals("time")) { |
|
200 TimePicker input = new TimePicker(context); |
|
201 input.setIs24HourView(DateFormat.is24HourFormat(context)); |
|
202 |
|
203 GregorianCalendar calendar = new GregorianCalendar(); |
|
204 if (!TextUtils.isEmpty(mValue)) { |
|
205 try { |
|
206 calendar.setTime(new SimpleDateFormat("HH:mm").parse(mValue)); |
|
207 } catch (Exception e) { } |
|
208 } |
|
209 input.setCurrentHour(calendar.get(GregorianCalendar.HOUR_OF_DAY)); |
|
210 input.setCurrentMinute(calendar.get(GregorianCalendar.MINUTE)); |
|
211 mView = (View)input; |
|
212 } else if (mType.equals("datetime-local") || mType.equals("datetime")) { |
|
213 DateTimePicker input = new DateTimePicker(context, "yyyy-MM-dd HH:mm", mValue, |
|
214 DateTimePicker.PickersState.DATETIME); |
|
215 input.toggleCalendar(true); |
|
216 mView = (View)input; |
|
217 } else if (mType.equals("month")) { |
|
218 DateTimePicker input = new DateTimePicker(context, "yyyy-MM", mValue, |
|
219 DateTimePicker.PickersState.MONTH); |
|
220 mView = (View)input; |
|
221 } |
|
222 return mView; |
|
223 } |
|
224 |
|
225 private static String formatDateString(String dateFormat, Calendar calendar) { |
|
226 return new SimpleDateFormat(dateFormat).format(calendar.getTime()); |
|
227 } |
|
228 |
|
229 @Override |
|
230 public Object getValue() { |
|
231 if (Build.VERSION.SDK_INT < 11 && mType.equals("date")) { |
|
232 // We can't use the custom DateTimePicker with a sdk older than 11. |
|
233 // Fallback on the native DatePicker. |
|
234 DatePicker dp = (DatePicker)mView; |
|
235 GregorianCalendar calendar = |
|
236 new GregorianCalendar(dp.getYear(),dp.getMonth(),dp.getDayOfMonth()); |
|
237 return formatDateString("yyyy-MM-dd",calendar); |
|
238 } else if (mType.equals("time")) { |
|
239 TimePicker tp = (TimePicker)mView; |
|
240 GregorianCalendar calendar = |
|
241 new GregorianCalendar(0,0,0,tp.getCurrentHour(),tp.getCurrentMinute()); |
|
242 return formatDateString("HH:mm",calendar); |
|
243 } else { |
|
244 DateTimePicker dp = (DateTimePicker)mView; |
|
245 GregorianCalendar calendar = new GregorianCalendar(); |
|
246 calendar.setTimeInMillis(dp.getTimeInMillis()); |
|
247 if (mType.equals("date")) { |
|
248 return formatDateString("yyyy-MM-dd",calendar); |
|
249 } else if (mType.equals("week")) { |
|
250 return formatDateString("yyyy-'W'ww",calendar); |
|
251 } else if (mType.equals("datetime-local")) { |
|
252 return formatDateString("yyyy-MM-dd HH:mm",calendar); |
|
253 } else if (mType.equals("datetime")) { |
|
254 calendar.set(GregorianCalendar.ZONE_OFFSET,0); |
|
255 calendar.setTimeInMillis(dp.getTimeInMillis()); |
|
256 return formatDateString("yyyy-MM-dd HH:mm",calendar); |
|
257 } else if (mType.equals("month")) { |
|
258 return formatDateString("yyyy-MM",calendar); |
|
259 } |
|
260 } |
|
261 return super.getValue(); |
|
262 } |
|
263 } |
|
264 |
|
265 public static class MenulistInput extends PromptInput { |
|
266 public static final String INPUT_TYPE = "menulist"; |
|
267 private static String[] mListitems; |
|
268 private static int mSelected; |
|
269 |
|
270 public Spinner spinner; |
|
271 public AllCapsTextView textView; |
|
272 |
|
273 public MenulistInput(JSONObject obj) { |
|
274 super(obj); |
|
275 mListitems = Prompt.getStringArray(obj, "values"); |
|
276 mSelected = obj.optInt("selected"); |
|
277 } |
|
278 |
|
279 public View getView(final Context context) throws UnsupportedOperationException { |
|
280 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { |
|
281 spinner = new Spinner(context); |
|
282 } else { |
|
283 spinner = new Spinner(context, Spinner.MODE_DIALOG); |
|
284 } |
|
285 try { |
|
286 if (mListitems.length > 0) { |
|
287 ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, mListitems); |
|
288 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); |
|
289 |
|
290 spinner.setAdapter(adapter); |
|
291 spinner.setSelection(mSelected); |
|
292 } |
|
293 } catch(Exception ex) { } |
|
294 |
|
295 if (!TextUtils.isEmpty(mLabel)) { |
|
296 LinearLayout container = new LinearLayout(context); |
|
297 container.setOrientation(LinearLayout.VERTICAL); |
|
298 |
|
299 textView = new AllCapsTextView(context, null); |
|
300 textView.setText(mLabel); |
|
301 container.addView(textView); |
|
302 |
|
303 container.addView(spinner); |
|
304 return container; |
|
305 } |
|
306 |
|
307 return spinner; |
|
308 } |
|
309 |
|
310 @Override |
|
311 public Object getValue() { |
|
312 return new Integer(spinner.getSelectedItemPosition()); |
|
313 } |
|
314 } |
|
315 |
|
316 public static class LabelInput extends PromptInput { |
|
317 public static final String INPUT_TYPE = "label"; |
|
318 public LabelInput(JSONObject obj) { |
|
319 super(obj); |
|
320 } |
|
321 |
|
322 public View getView(Context context) throws UnsupportedOperationException { |
|
323 // not really an input, but a way to add labels and such to the dialog |
|
324 TextView view = new TextView(context); |
|
325 view.setText(Html.fromHtml(mLabel)); |
|
326 mView = view; |
|
327 return mView; |
|
328 } |
|
329 } |
|
330 |
|
331 public PromptInput(JSONObject obj) { |
|
332 mLabel = obj.optString("label"); |
|
333 mType = obj.optString("type"); |
|
334 String id = obj.optString("id"); |
|
335 mId = TextUtils.isEmpty(id) ? mType : id; |
|
336 mValue = obj.optString("value"); |
|
337 } |
|
338 |
|
339 public static PromptInput getInput(JSONObject obj) { |
|
340 String type = obj.optString("type"); |
|
341 if (EditInput.INPUT_TYPE.equals(type)) { |
|
342 return new EditInput(obj); |
|
343 } else if (NumberInput.INPUT_TYPE.equals(type)) { |
|
344 return new NumberInput(obj); |
|
345 } else if (PasswordInput.INPUT_TYPE.equals(type)) { |
|
346 return new PasswordInput(obj); |
|
347 } else if (CheckboxInput.INPUT_TYPE.equals(type)) { |
|
348 return new CheckboxInput(obj); |
|
349 } else if (MenulistInput.INPUT_TYPE.equals(type)) { |
|
350 return new MenulistInput(obj); |
|
351 } else if (LabelInput.INPUT_TYPE.equals(type)) { |
|
352 return new LabelInput(obj); |
|
353 } else if (IconGridInput.INPUT_TYPE.equals(type)) { |
|
354 return new IconGridInput(obj); |
|
355 } else if (ColorPickerInput.INPUT_TYPE.equals(type)) { |
|
356 return new ColorPickerInput(obj); |
|
357 } else if (TabInput.INPUT_TYPE.equals(type)) { |
|
358 return new TabInput(obj); |
|
359 } else { |
|
360 for (String dtType : DateTimeInput.INPUT_TYPES) { |
|
361 if (dtType.equals(type)) { |
|
362 return new DateTimeInput(obj); |
|
363 } |
|
364 } |
|
365 } |
|
366 return null; |
|
367 } |
|
368 |
|
369 public View getView(Context context) throws UnsupportedOperationException { |
|
370 return null; |
|
371 } |
|
372 |
|
373 public String getId() { |
|
374 return mId; |
|
375 } |
|
376 |
|
377 public Object getValue() { |
|
378 return null; |
|
379 } |
|
380 |
|
381 public boolean getScrollable() { |
|
382 return false; |
|
383 } |
|
384 |
|
385 public boolean canApplyInputStyle() { |
|
386 return true; |
|
387 } |
|
388 |
|
389 protected void notifyListeners(String val) { |
|
390 if (mListener != null) { |
|
391 mListener.onChange(this); |
|
392 } |
|
393 } |
|
394 } |