Bug 959742 - Close dialogs when inputs change if there are no buttons on the dialog. r=bnicholson
authorWesley Johnston <wjohnston>
Thu, 06 Feb 2014 16:26:00 -0800
changeset 171783 f89196ce3d3ea017636579637a41aa423c4f3432
parent 171782 96e8a0819361d6727daea3e0d38338a5f6483003
child 171784 ad1574c13ebf67a3b85ef1f00017f72a11aaea5c
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersbnicholson
bugs959742
milestone30.0a1
Bug 959742 - Close dialogs when inputs change if there are no buttons on the dialog. r=bnicholson
mobile/android/base/prompts/IconGridInput.java
mobile/android/base/prompts/Prompt.java
mobile/android/base/prompts/PromptInput.java
mobile/android/base/prompts/TabInput.java
mobile/android/modules/Prompt.jsm
--- a/mobile/android/base/prompts/IconGridInput.java
+++ b/mobile/android/base/prompts/IconGridInput.java
@@ -87,16 +87,17 @@ public class IconGridInput extends Promp
         view.setAdapter(mAdapter);
         mView = view;
         return mView;
     }
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         mSelected = position;
+        notifyListeners(Integer.toString(position));
     }
 
     @Override
     public Object getValue() {
         return new Integer(mSelected);
     }
 
     @Override
--- a/mobile/android/base/prompts/Prompt.java
+++ b/mobile/android/base/prompts/Prompt.java
@@ -33,17 +33,18 @@ import android.widget.ArrayAdapter;
 import android.widget.CheckedTextView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.ScrollView;
 import android.widget.TextView;
 
 import java.util.ArrayList;
 
-public class Prompt implements OnClickListener, OnCancelListener, OnItemClickListener {
+public class Prompt implements OnClickListener, OnCancelListener, OnItemClickListener,
+                               PromptInput.OnChangeListener {
     private static final String LOGTAG = "GeckoPromptService";
 
     private String[] mButtons;
     private PromptInput[] mInputs;
     private AlertDialog mDialog;
 
     private final LayoutInflater mInflater;
     private final Context mContext;
@@ -143,16 +144,20 @@ public class Prompt implements OnClickLi
     }
 
     /* Adds to a result value from the lists that can be shown in dialogs.
      *  Will set the selected value(s) to the button attribute of the
      *  object that's passed in. If this is a multi-select dialog, sets a
      *  selected attribute to an array of booleans.
      */
     private void addListResult(final JSONObject result, int which) {
+        if (mAdapter == null) {
+            return;
+        }
+
         try {
             JSONArray selected = new JSONArray();
 
             // If the button has already been filled in
             ArrayList<Integer> selectedItems = mAdapter.getSelected();
             for (Integer item : selectedItems) {
                 selected.put(item);
             }
@@ -198,33 +203,17 @@ public class Prompt implements OnClickLi
         try {
             result.put("button", button);
         } catch(JSONException ex) { }
     }
 
     @Override
     public void onClick(DialogInterface dialog, int which) {
         ThreadUtils.assertOnUiThread();
-        JSONObject ret = new JSONObject();
-        try {
-            addButtonResult(ret, which);
-            addInputValues(ret);
-
-            if (mAdapter != null) {
-                addListResult(ret, which);
-            }
-        } catch(Exception ex) {
-            Log.i(LOGTAG, "Error building return: " + ex);
-        }
-
-        if (dialog != null) {
-            dialog.dismiss();
-        }
-
-        finishDialog(ret);
+        closeDialog(which);
     }
 
     /* Adds a set of list items to the prompt. This can be used for either context menu type dialogs, checked lists,
      * or multiple selection lists.
      *
      * @param builder
      *        The alert builder currently building this dialog.
      * @param listItems
@@ -270,17 +259,22 @@ public class Prompt implements OnClickLi
      *
      * @param builder
      *        the alert builder currently building this dialog.
      * @param listItems
      *        The items to add.
      */
     private void addSingleSelectList(AlertDialog.Builder builder, PromptListItem[] listItems) {
         mAdapter = new PromptListAdapter(mContext, R.layout.select_dialog_singlechoice, listItems);
-        builder.setSingleChoiceItems(mAdapter, mAdapter.getSelectedIndex(), this);
+        builder.setSingleChoiceItems(mAdapter, mAdapter.getSelectedIndex(), new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                closeIfNoButtons(which);
+            }
+        });
     }
 
     /* Shows a single-select list.
      *
      * @param builder
      *        the alert builder currently building this dialog.
      * @param listItems
      *        The items to add.
@@ -355,16 +349,30 @@ public class Prompt implements OnClickLi
 
     /* AdapterView.OnItemClickListener
      * Called when a list item is clicked
      */
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         ThreadUtils.assertOnUiThread();
         mAdapter.toggleSelected(position);
+
+        // If there are no buttons on this dialog, then we take selecting an item as a sign to close
+        // the dialog. Note that means it will be hard to select multiple things in this list, but
+        // given there is no way to confirm+close the dialog, it seems reasonable.
+        closeIfNoButtons(position);
+    }
+
+    private boolean closeIfNoButtons(int selected) {
+        ThreadUtils.assertOnUiThread();
+        if (mButtons == null || mButtons.length == 0) {
+            closeDialog(selected);
+            return true;
+        }
+        return false;
     }
 
     /* @DialogInterface.OnCancelListener
      * Called when the user hits back to cancel a dialog. The dialog will close itself when this
      * ends. Setup the correct return values here.
      *
      * @param aDialog
      *          A dialog interface for the dialog that's being closed.
@@ -385,16 +393,30 @@ public class Prompt implements OnClickLi
         } catch(Exception ex) { }
         addInputValues(ret);
         finishDialog(ret);
     }
 
     /* Called any time we're closing the dialog to cleanup and notify listeners that the dialog
      * is closing.
      */
+    private void closeDialog(int which) {
+        JSONObject ret = new JSONObject();
+        mDialog.dismiss();
+
+        addButtonResult(ret, which);
+        addListResult(ret, which);
+        addInputValues(ret);
+
+        finishDialog(ret);
+    }
+
+    /* Called any time we're closing the dialog to cleanup and notify listeners that the dialog
+     * is closing.
+     */
     public void finishDialog(JSONObject aReturn) {
         mInputs = null;
         mButtons = null;
         mDialog = null;
         try {
             aReturn.put("guid", mGuid);
         } catch(JSONException ex) { }
 
@@ -416,32 +438,42 @@ public class Prompt implements OnClickLi
 
         mButtons = getStringArray(geckoObject, "buttons");
 
         JSONArray inputs = getSafeArray(geckoObject, "inputs");
         mInputs = new PromptInput[inputs.length()];
         for (int i = 0; i < mInputs.length; i++) {
             try {
                 mInputs[i] = PromptInput.getInput(inputs.getJSONObject(i));
+                mInputs[i].setListener(this);
             } catch(Exception ex) { }
         }
 
         PromptListItem[] menuitems = PromptListItem.getArray(geckoObject.optJSONArray("listitems"));
         String selected = geckoObject.optString("choiceMode");
 
         int choiceMode = ListView.CHOICE_MODE_NONE;
         if ("single".equals(selected)) {
             choiceMode = ListView.CHOICE_MODE_SINGLE;
         } else if ("multiple".equals(selected)) {
             choiceMode = ListView.CHOICE_MODE_MULTIPLE;
         }
 
         show(title, text, menuitems, choiceMode);
     }
 
+    // Called when the prompt inputs on the dialog change
+    @Override
+    public void onChange(PromptInput input) {
+        // If there are no buttons on this dialog, assuming that "changing" an input
+        // means something was selected and we can close. This provides a way to tap
+        // on a list item and close the dialog automatically.
+        closeIfNoButtons(-1);
+    }
+
     private static JSONArray getSafeArray(JSONObject json, String key) {
         try {
             return json.getJSONArray(key);
         } catch (Exception e) {
             return new JSONArray();
         }
     }
 
--- a/mobile/android/base/prompts/PromptInput.java
+++ b/mobile/android/base/prompts/PromptInput.java
@@ -33,19 +33,28 @@ import android.widget.Spinner;
 import android.widget.TextView;
 import android.widget.TimePicker;
 
 public class PromptInput {
     protected final String mLabel;
     protected final String mType;
     protected final String mId;
     protected final String mValue;
+    protected OnChangeListener mListener;
     protected View mView;
     public static final String LOGTAG = "GeckoPromptInput";
 
+    public interface OnChangeListener {
+        public void onChange(PromptInput input);
+    }
+
+    public void setListener(OnChangeListener listener) {
+        mListener = listener;
+    }
+
     public static class EditInput extends PromptInput {
         protected final String mHint;
         protected final boolean mAutofocus;
         public static final String INPUT_TYPE = "textbox";
 
         public EditInput(JSONObject object) {
             super(object);
             mHint = object.optString("hint");
@@ -371,9 +380,15 @@ public class PromptInput {
 
     public boolean getScrollable() {
         return false;
     }
 
     public boolean canApplyInputStyle() {
         return true;
     }
+
+    protected void notifyListeners(String val) {
+        if (mListener != null) {
+            mListener.onChange(this);
+        }
+    }
 }
--- a/mobile/android/base/prompts/TabInput.java
+++ b/mobile/android/base/prompts/TabInput.java
@@ -99,11 +99,12 @@ public class TabInput extends PromptInpu
     public boolean canApplyInputStyle() {
         return false;
     }
 
     @Override
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         ThreadUtils.assertOnUiThread();
         mPosition = position;
+        notifyListeners(Integer.toString(position));
     }
 
 }
--- a/mobile/android/modules/Prompt.jsm
+++ b/mobile/android/modules/Prompt.jsm
@@ -7,17 +7,17 @@ let Cc = Components.classes;
 let Ci = Components.interfaces;
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/Messaging.jsm");
 
 this.EXPORTED_SYMBOLS = ["Prompt"];
 
 function log(msg) {
-  //Services.console.logStringMessage(msg);
+  Services.console.logStringMessage(msg);
 }
 
 function Prompt(aOptions) {
   this.window = "window" in aOptions ? aOptions.window : null;
   this.msg = { async: true };
 
   if (aOptions.priority === 1)
     this.msg.type = "Prompt:ShowTop"