1 /*
2  * Copyright (C) 2016 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.googlecode.android_scripting.facade.ui;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.AlertDialog.Builder;
22 import android.content.DialogInterface;
23 import android.text.method.PasswordTransformationMethod;
24 import android.widget.EditText;
25 
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.TreeSet;
32 
33 import org.json.JSONArray;
34 import org.json.JSONException;
35 
36 /**
37  * Wrapper class for alert dialog running in separate thread.
38  *
39  * @author MeanEYE.rcf (meaneye.rcf@gmail.com)
40  */
41 class AlertDialogTask extends DialogTask {
42 
43   private final String mTitle;
44   private final String mMessage;
45 
46   private final List<String> mItems;
47   private final Set<Integer> mSelectedItems;
48   private final Map<String, Object> mResultMap;
49   private InputType mInputType;
50   private int mEditInputType = 0;
51 
52   private String mPositiveButtonText;
53   private String mNegativeButtonText;
54   private String mNeutralButtonText;
55 
56   private EditText mEditText;
57   private String mDefaultText;
58 
59   private enum InputType {
60     DEFAULT, MENU, SINGLE_CHOICE, MULTI_CHOICE, PLAIN_TEXT, PASSWORD;
61   }
62 
AlertDialogTask(String title, String message)63   public AlertDialogTask(String title, String message) {
64     mTitle = title;
65     mMessage = message;
66     mInputType = InputType.DEFAULT;
67     mItems = new ArrayList<String>();
68     mSelectedItems = new TreeSet<Integer>();
69     mResultMap = new HashMap<String, Object>();
70   }
71 
setPositiveButtonText(String text)72   public void setPositiveButtonText(String text) {
73     mPositiveButtonText = text;
74   }
75 
setNegativeButtonText(String text)76   public void setNegativeButtonText(String text) {
77     mNegativeButtonText = text;
78   }
79 
setNeutralButtonText(String text)80   public void setNeutralButtonText(String text) {
81     mNeutralButtonText = text;
82   }
83 
84   /**
85    * Set list items.
86    *
87    * @param items
88    */
setItems(JSONArray items)89   public void setItems(JSONArray items) {
90     mItems.clear();
91     for (int i = 0; i < items.length(); i++) {
92       try {
93         mItems.add(items.getString(i));
94       } catch (JSONException e) {
95         throw new RuntimeException(e);
96       }
97     }
98     mInputType = InputType.MENU;
99   }
100 
101   /**
102    * Set single choice items.
103    *
104    * @param items
105    *          a list of items as {@link String}s to display
106    * @param selected
107    *          the index of the item that is selected by default
108    */
setSingleChoiceItems(JSONArray items, int selected)109   public void setSingleChoiceItems(JSONArray items, int selected) {
110     setItems(items);
111     mSelectedItems.clear();
112     mSelectedItems.add(selected);
113     mInputType = InputType.SINGLE_CHOICE;
114   }
115 
116   /**
117    * Set multi choice items.
118    *
119    * @param items
120    *          a list of items as {@link String}s to display
121    * @param selected
122    *          a list of indices for items that should be selected by default
123    * @throws JSONException
124    */
setMultiChoiceItems(JSONArray items, JSONArray selected)125   public void setMultiChoiceItems(JSONArray items, JSONArray selected) throws JSONException {
126     setItems(items);
127     mSelectedItems.clear();
128     if (selected != null) {
129       for (int i = 0; i < selected.length(); i++) {
130         mSelectedItems.add(selected.getInt(i));
131       }
132     }
133     mInputType = InputType.MULTI_CHOICE;
134   }
135 
136   /**
137    * Returns the list of selected items.
138    */
getSelectedItems()139   public Set<Integer> getSelectedItems() {
140     return mSelectedItems;
141   }
142 
setTextInput(String defaultText)143   public void setTextInput(String defaultText) {
144     mDefaultText = defaultText;
145     mInputType = InputType.PLAIN_TEXT;
146     setEditInputType("text");
147   }
148 
setEditInputType(String editInputType)149   public void setEditInputType(String editInputType) {
150     String[] list = editInputType.split("\\|");
151     Map<String, Integer> types = ViewInflater.getInputTypes();
152     mEditInputType = 0;
153     for (String flag : list) {
154       Integer v = types.get(flag.trim());
155       if (v != null) {
156         mEditInputType |= v;
157       }
158     }
159     if (mEditInputType == 0) {
160       mEditInputType = android.text.InputType.TYPE_CLASS_TEXT;
161     }
162   }
163 
setPasswordInput()164   public void setPasswordInput() {
165     mInputType = InputType.PASSWORD;
166   }
167 
168   @Override
onCreate()169   public void onCreate() {
170     super.onCreate();
171     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
172     if (mTitle != null) {
173       builder.setTitle(mTitle);
174     }
175     // Can't display both a message and items. We'll elect to show the items instead.
176     if (mMessage != null && mItems.isEmpty()) {
177       builder.setMessage(mMessage);
178     }
179     switch (mInputType) {
180     // Add single choice menu items to dialog.
181     case SINGLE_CHOICE:
182       builder.setSingleChoiceItems(getItemsAsCharSequenceArray(), mSelectedItems.iterator().next(),
183           new DialogInterface.OnClickListener() {
184             @Override
185             public void onClick(DialogInterface dialog, int item) {
186               mSelectedItems.clear();
187               mSelectedItems.add(item);
188             }
189           });
190       break;
191     // Add multiple choice items to the dialog.
192     case MULTI_CHOICE:
193       boolean[] selectedItems = new boolean[mItems.size()];
194       for (int i : mSelectedItems) {
195         selectedItems[i] = true;
196       }
197       builder.setMultiChoiceItems(getItemsAsCharSequenceArray(), selectedItems,
198           new DialogInterface.OnMultiChoiceClickListener() {
199             @Override
200             public void onClick(DialogInterface dialog, int item, boolean isChecked) {
201               if (isChecked) {
202                 mSelectedItems.add(item);
203               } else {
204                 mSelectedItems.remove(item);
205               }
206             }
207           });
208       break;
209     // Add standard, menu-like, items to dialog.
210     case MENU:
211       builder.setItems(getItemsAsCharSequenceArray(), new DialogInterface.OnClickListener() {
212         public void onClick(DialogInterface dialog, int item) {
213           Map<String, Integer> result = new HashMap<String, Integer>();
214           result.put("item", item);
215           dismissDialog();
216           setResult(result);
217         }
218       });
219       break;
220     case PLAIN_TEXT:
221       mEditText = new EditText(getActivity());
222       if (mDefaultText != null) {
223         mEditText.setText(mDefaultText);
224       }
225       mEditText.setInputType(mEditInputType);
226       builder.setView(mEditText);
227       break;
228     case PASSWORD:
229       mEditText = new EditText(getActivity());
230       mEditText.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
231       mEditText.setTransformationMethod(new PasswordTransformationMethod());
232       builder.setView(mEditText);
233       break;
234     default:
235       // No input type specified.
236     }
237     configureButtons(builder, getActivity());
238     addOnCancelListener(builder, getActivity());
239     mDialog = builder.show();
240     mShowLatch.countDown();
241   }
242 
getItemsAsCharSequenceArray()243   private CharSequence[] getItemsAsCharSequenceArray() {
244     return mItems.toArray(new CharSequence[mItems.size()]);
245   }
246 
addOnCancelListener(final AlertDialog.Builder builder, final Activity activity)247   private Builder addOnCancelListener(final AlertDialog.Builder builder, final Activity activity) {
248     return builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
249       @Override
250       public void onCancel(DialogInterface dialog) {
251         mResultMap.put("canceled", true);
252         setResult();
253       }
254     });
255   }
256 
257   private void configureButtons(final AlertDialog.Builder builder, final Activity activity) {
258     DialogInterface.OnClickListener buttonListener = new DialogInterface.OnClickListener() {
259       @Override
260       public void onClick(DialogInterface dialog, int which) {
261         switch (which) {
262         case DialogInterface.BUTTON_POSITIVE:
263           mResultMap.put("which", "positive");
264           break;
265         case DialogInterface.BUTTON_NEGATIVE:
266           mResultMap.put("which", "negative");
267           break;
268         case DialogInterface.BUTTON_NEUTRAL:
269           mResultMap.put("which", "neutral");
270 
271           break;
272         }
273         setResult();
274       }
275     };
276     if (mNegativeButtonText != null) {
277       builder.setNegativeButton(mNegativeButtonText, buttonListener);
278     }
279     if (mPositiveButtonText != null) {
280       builder.setPositiveButton(mPositiveButtonText, buttonListener);
281     }
282     if (mNeutralButtonText != null) {
283       builder.setNeutralButton(mNeutralButtonText, buttonListener);
284     }
285   }
286 
287   private void setResult() {
288     dismissDialog();
289     if (mInputType == InputType.PLAIN_TEXT || mInputType == InputType.PASSWORD) {
290       mResultMap.put("value", mEditText.getText().toString());
291     }
292     setResult(mResultMap);
293   }
294 
295 }
296