Bug 1333589 - 1. Use GeckoBundle for DoorHanger.OnButtonClickListener; r=sebastian
authorJim Chen <nchen@mozilla.com>
Wed, 25 Jan 2017 18:57:31 -0500
changeset 466667 edeef4fcdb7b4dfb43a1ae9d31a8f19a46c8e9fc
parent 466666 45316394ec1d7a06fa15404ba21c67f1ca4027d6
child 466668 114c35019621332a2d5113b63db03a99526af820
push id42948
push userbmo:gasolin@mozilla.com
push dateThu, 26 Jan 2017 07:49:21 +0000
reviewerssebastian
bugs1333589
milestone54.0a1
Bug 1333589 - 1. Use GeckoBundle for DoorHanger.OnButtonClickListener; r=sebastian Convert the onButtonClick method to provide GeckoBundle as the response.
mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
mobile/android/base/java/org/mozilla/gecko/prompts/PromptInput.java
mobile/android/base/java/org/mozilla/gecko/toolbar/SiteIdentityPopup.java
mobile/android/base/java/org/mozilla/gecko/widget/ContentSecurityDoorHanger.java
mobile/android/base/java/org/mozilla/gecko/widget/DefaultDoorHanger.java
mobile/android/base/java/org/mozilla/gecko/widget/DoorHanger.java
mobile/android/base/java/org/mozilla/gecko/widget/LoginDoorHanger.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/strings.xml.in
--- a/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
+++ b/mobile/android/base/java/org/mozilla/gecko/DoorHangerPopup.java
@@ -4,17 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import java.util.HashSet;
 
 import android.text.TextUtils;
 import android.widget.PopupWindow;
-import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.util.BundleEventListener;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.GeckoBundle;
 import org.mozilla.gecko.util.ThreadUtils;
 import org.mozilla.gecko.widget.AnchoredPopup;
 import org.mozilla.gecko.widget.DoorHanger;
 
@@ -184,18 +183,18 @@ public class DoorHangerPopup extends Anc
             updatePopup();
     }
 
 
     /*
      * DoorHanger.OnButtonClickListener implementation
      */
     @Override
-    public void onButtonClick(JSONObject response, DoorHanger doorhanger) {
-        GeckoAppShell.notifyObservers("Doorhanger:Reply", response.toString());
+    public void onButtonClick(final GeckoBundle response, DoorHanger doorhanger) {
+        EventDispatcher.getInstance().dispatch("Doorhanger:Reply", response);
         removeDoorHanger(doorhanger);
         updatePopup();
     }
 
     /**
      * Gets a doorhanger.
      *
      * This method must be called on the UI thread.
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
@@ -248,35 +248,17 @@ public class Prompt implements OnClickLi
         if (mInputs == null) {
             return;
         }
 
         for (final PromptInput input : mInputs) {
             if (input == null) {
                 continue;
             }
-
-            final String id = input.getId();
-            final Object value = input.getValue();
-
-            if (value == null) {
-                result.putBundle(id, null);
-            } else if (value instanceof Boolean) {
-                result.putBoolean(id, (Boolean) value);
-            } else if (value instanceof Double) {
-                result.putDouble(id, (Double) value);
-            } else if (value instanceof Integer) {
-                result.putInt(id, (Integer) value);
-            } else if (value instanceof CharSequence) {
-                result.putString(id, value.toString());
-            } else if (value instanceof GeckoBundle) {
-                result.putBundle(id, (GeckoBundle) value);
-            } else {
-                throw new UnsupportedOperationException(value.getClass().toString());
-            }
+            input.putInBundle(result);
         }
     }
 
     /* Adds the selected button to a result. This should only be called if there
      * are no lists shown on the dialog, since they also write their results to the button
      * attribute.
      */
     private void addButtonResult(final GeckoBundle result, int which) {
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/PromptInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/PromptInput.java
@@ -335,16 +335,37 @@ public abstract class PromptInput {
         mType = obj.getString("type");
         String id = obj.getString("id");
         mId = TextUtils.isEmpty(id) ? mType : id;
         mValue = obj.getString("value");
         mMaxValue = obj.getString("max");
         mMinValue = obj.getString("min");
     }
 
+    public void putInBundle(final GeckoBundle bundle) {
+        final String id = getId();
+        final Object value = getValue();
+
+        if (value == null) {
+            bundle.putBundle(id, null);
+        } else if (value instanceof Boolean) {
+            bundle.putBoolean(id, (Boolean) value);
+        } else if (value instanceof Double) {
+            bundle.putDouble(id, (Double) value);
+        } else if (value instanceof Integer) {
+            bundle.putInt(id, (Integer) value);
+        } else if (value instanceof CharSequence) {
+            bundle.putString(id, value.toString());
+        } else if (value instanceof GeckoBundle) {
+            bundle.putBundle(id, (GeckoBundle) value);
+        } else {
+            throw new UnsupportedOperationException(value.getClass().toString());
+        }
+    }
+
     public static PromptInput getInput(GeckoBundle obj) {
         String type = obj.getString("type");
         switch (type) {
             case EditInput.INPUT_TYPE:
                 return new EditInput(obj);
             case NumberInput.INPUT_TYPE:
                 return new NumberInput(obj);
             case PasswordInput.INPUT_TYPE:
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/SiteIdentityPopup.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/SiteIdentityPopup.java
@@ -214,44 +214,37 @@ public class SiteIdentityPopup extends A
         }
 
         final JSONObject login = (JSONObject) logins.get(0);
 
         // Create button click listener for copying a password to the clipboard.
         final OnButtonClickListener buttonClickListener = new OnButtonClickListener() {
             Activity activity = (Activity) mContext;
             @Override
-            public void onButtonClick(JSONObject response, DoorHanger doorhanger) {
-                try {
-                    final int buttonId = response.getInt("callback");
-                    if (buttonId == ButtonType.COPY.ordinal()) {
-                        final ClipboardManager manager = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
-                        String password;
-                        if (response.has("password")) {
-                            // Click listener being called from List Dialog.
-                            password = response.optString("password");
-                        } else {
-                            password = login.getString("password");
-                        }
+            public void onButtonClick(final GeckoBundle response, final DoorHanger doorhanger) {
+                final int buttonId = response.getInt("callback");
+                if (buttonId == ButtonType.COPY.ordinal()) {
+                    final ClipboardManager manager = (ClipboardManager)
+                            mContext.getSystemService(Context.CLIPBOARD_SERVICE);
+                    final String password;
+                    if (response.containsKey("password")) {
+                        // Click listener being called from List Dialog.
+                        password = response.getString("password");
+                    } else {
+                        password = login.getString("password");
+                    }
 
-                        manager.setPrimaryClip(ClipData.newPlainText("password", password));
+                    manager.setPrimaryClip(ClipData.newPlainText("password", password));
 
-                        SnackbarBuilder.builder(activity)
-                                .message(R.string.doorhanger_login_select_toast_copy)
-                                .duration(Snackbar.LENGTH_SHORT)
-                                .buildAndShow();
-                    }
-                    dismiss();
-                } catch (JSONException e) {
-                    Log.e(LOGTAG, "Error handling Select login button click", e);
                     SnackbarBuilder.builder(activity)
-                            .message(R.string.doorhanger_login_select_toast_copy_error)
+                            .message(R.string.doorhanger_login_select_toast_copy)
                             .duration(Snackbar.LENGTH_SHORT)
                             .buildAndShow();
                 }
+                dismiss();
             }
         };
 
         final DoorhangerConfig config = new DoorhangerConfig(DoorHanger.Type.LOGIN, buttonClickListener);
 
         // Set buttons.
         config.setButton(mContext.getString(R.string.button_cancel), ButtonType.CANCEL.ordinal(), false);
         config.setButton(mContext.getString(R.string.button_copy), ButtonType.COPY.ordinal(), true);
@@ -567,14 +560,14 @@ public class SiteIdentityPopup extends A
         removeTrackingContentNotification();
         removeSelectLoginDoorhanger();
         TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(mTitle, null, null, null, null);
         mDivider.setVisibility(View.GONE);
     }
 
     private class ContentNotificationButtonListener implements OnButtonClickListener {
         @Override
-        public void onButtonClick(JSONObject response, DoorHanger doorhanger) {
+        public void onButtonClick(final GeckoBundle response, final DoorHanger doorhanger) {
             GeckoAppShell.notifyObservers("Session:Reload", response.toString());
             dismiss();
         }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/widget/ContentSecurityDoorHanger.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/ContentSecurityDoorHanger.java
@@ -5,19 +5,17 @@
 
 package org.mozilla.gecko.widget;
 
 import android.support.v4.content.ContextCompat;
 import android.util.Log;
 import android.widget.Button;
 import android.widget.TextView;
 import org.mozilla.gecko.R;
-
-import org.json.JSONException;
-import org.json.JSONObject;
+import org.mozilla.gecko.util.GeckoBundle;
 
 import android.content.Context;
 import android.view.View;
 
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.toolbar.SiteIdentityPopup;
 import org.mozilla.gecko.util.GeckoBundle;
@@ -102,27 +100,23 @@ public class ContentSecurityDoorHanger e
     @Override
     protected OnClickListener makeOnButtonClickListener(final int id, final String telemetryExtra) {
         return new Button.OnClickListener() {
             @Override
             public void onClick(View v) {
                 final String expandedExtra = mType.toString().toLowerCase(Locale.US) + "-" + telemetryExtra;
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.DOORHANGER, expandedExtra);
 
-                final JSONObject response = new JSONObject();
-                try {
-                    switch (mType) {
-                        case TRACKING:
-                            response.put("allowContent", (id == SiteIdentityPopup.ButtonType.DISABLE.ordinal()));
-                            response.put("contentType", ("tracking"));
-                            break;
-                        default:
-                            Log.w(LOGTAG, "Unknown doorhanger type " + mType.toString());
-                    }
-                } catch (JSONException e) {
-                    Log.e(LOGTAG, "Error creating onClick response", e);
+                final GeckoBundle response = new GeckoBundle(2);
+                if (mType == Type.TRACKING) {
+                    response.putBoolean("allowContent",
+                                        id == SiteIdentityPopup.ButtonType.DISABLE.ordinal());
+                    response.putString("contentType", "tracking");
+                } else {
+                    Log.w(LOGTAG, "Unknown doorhanger type " + mType.toString());
                 }
 
-                mOnButtonClickListener.onButtonClick(response, ContentSecurityDoorHanger.this);
+                mOnButtonClickListener.onButtonClick(
+                        response, ContentSecurityDoorHanger.this);
             }
         };
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/widget/DefaultDoorHanger.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/DefaultDoorHanger.java
@@ -12,20 +12,16 @@ import android.util.Log;
 import android.widget.Button;
 import android.widget.TextView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.prompts.PromptInput;
 import org.mozilla.gecko.util.GeckoBundle;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import android.content.Context;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CheckBox;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -136,36 +132,32 @@ public class DefaultDoorHanger extends D
     @Override
     protected OnClickListener makeOnButtonClickListener(final int id, final String telemetryExtra) {
         return new Button.OnClickListener() {
             @Override
             public void onClick(View v) {
                 final String expandedExtra = mType.toString().toLowerCase(Locale.US) + "-" + telemetryExtra;
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.DOORHANGER, expandedExtra);
 
-                final JSONObject response = new JSONObject();
-                try {
-                    response.put("callback", id);
-
-                    CheckBox checkBox = getCheckBox();
-                    // If the checkbox is being used, pass its value
-                    if (checkBox != null) {
-                        response.put("checked", checkBox.isChecked());
-                    }
+                final GeckoBundle response = new GeckoBundle(3);
+                response.putInt("callback", id);
 
-                    List<PromptInput> doorHangerInputs = getInputs();
-                    if (doorHangerInputs != null) {
-                        JSONObject inputs = new JSONObject();
-                        for (PromptInput input : doorHangerInputs) {
-                            inputs.put(input.getId(), input.getValue());
-                        }
-                        response.put("inputs", inputs);
+                final CheckBox checkBox = getCheckBox();
+                // If the checkbox is being used, pass its value
+                if (checkBox != null) {
+                    response.putBoolean("checked", checkBox.isChecked());
+                }
+
+                final List<PromptInput> doorHangerInputs = getInputs();
+                if (doorHangerInputs != null) {
+                    final GeckoBundle inputs = new GeckoBundle();
+                    for (final PromptInput input : doorHangerInputs) {
+                        input.putInBundle(inputs);
                     }
-                } catch (JSONException e) {
-                    Log.e(LOGTAG, "Error creating onClick response", e);
+                    response.putBundle("inputs", inputs);
                 }
 
                 mOnButtonClickListener.onButtonClick(response, DefaultDoorHanger.this);
             }
         };
     }
 
     private void setMessage(String message) {
--- a/mobile/android/base/java/org/mozilla/gecko/widget/DoorHanger.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/DoorHanger.java
@@ -13,17 +13,16 @@ import android.support.v4.content.Contex
 import android.support.v4.widget.TextViewCompat;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewStub;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import org.json.JSONObject;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.util.GeckoBundle;
 
 import java.util.Locale;
 
@@ -39,17 +38,17 @@ public abstract class DoorHanger extends
         }
         return new DefaultDoorHanger(context, config, type);
     }
 
     // Doorhanger types created from Gecko are checked against enum strings to determine type.
     public static enum Type { DEFAULT, LOGIN, TRACKING, GEOLOCATION, DESKTOPNOTIFICATION2, WEBRTC, VIBRATION }
 
     public interface OnButtonClickListener {
-        public void onButtonClick(JSONObject response, DoorHanger doorhanger);
+        public void onButtonClick(GeckoBundle response, DoorHanger doorhanger);
     }
 
     private static final String LOGTAG = "GeckoDoorHanger";
 
     // Divider between doorhangers.
     private final View mDivider;
 
     private final Button mNegativeButton;
--- a/mobile/android/base/java/org/mozilla/gecko/widget/LoginDoorHanger.java
+++ b/mobile/android/base/java/org/mozilla/gecko/widget/LoginDoorHanger.java
@@ -18,19 +18,16 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
 import android.widget.EditText;
 import android.widget.TextView;
 import android.widget.Toast;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Telemetry;
 import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.util.GeckoBundle;
 
 import java.util.Locale;
 
 public class LoginDoorHanger extends DoorHanger {
@@ -81,33 +78,27 @@ public class LoginDoorHanger extends Doo
     @Override
     protected OnClickListener makeOnButtonClickListener(final int id, final String telemetryExtra) {
         return new Button.OnClickListener() {
             @Override
             public void onClick(View v) {
                 final String expandedExtra = mType.toString().toLowerCase(Locale.US) + "-" + telemetryExtra;
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.DOORHANGER, expandedExtra);
 
-                final JSONObject response = new JSONObject();
-                try {
-                    response.put("callback", id);
-                } catch (JSONException e) {
-                    Log.e(LOGTAG, "Error making doorhanger response message", e);
-                }
+                final GeckoBundle response = new GeckoBundle(1);
+                response.putInt("callback", id);
                 mOnButtonClickListener.onButtonClick(response, LoginDoorHanger.this);
             }
         };
     }
 
     /**
      * Add sub-text to the doorhanger and add the click action.
      *
-     * If the parsing the action from the JSON throws, the text is left visible, but there is no
-     * click action.
-     * @param actionTextObj JSONObject containing blob for making an action.
+     * @param actionTextObj GeckoBundle containing blob for making an action.
      */
     private void addActionText(final GeckoBundle actionTextObj) {
         if (actionTextObj == null) {
             mLink.setVisibility(View.GONE);
             return;
         }
 
         // Make action.
@@ -142,28 +133,23 @@ public class LoginDoorHanger extends Doo
                     }
                 });
                 builder.setView(view);
 
                 builder.setPositiveButton(
                         mButtonConfig.label, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(final DialogInterface dialog, final int which) {
-                        JSONObject response = new JSONObject();
-                        try {
-                            response.put("callback", mButtonConfig.callback);
-                            final JSONObject inputs = new JSONObject();
-                            inputs.put("username", username.getText());
-                            inputs.put("password", password.getText());
-                            response.put("inputs", inputs);
-                        } catch (JSONException e) {
-                            Log.e(LOGTAG, "Error creating doorhanger reply message");
-                            response = null;
-                            Toast.makeText(mContext, mResources.getString(R.string.doorhanger_login_edit_toast_error), Toast.LENGTH_SHORT).show();
-                        }
+                        final GeckoBundle inputs = new GeckoBundle(2);
+                        inputs.putString("username", username.getText().toString());
+                        inputs.putString("password", password.getText().toString());
+
+                        final GeckoBundle response = new GeckoBundle(2);
+                        response.putInt("callback", mButtonConfig.callback);
+                        response.putBundle("inputs", inputs);
                         mOnButtonClickListener.onButtonClick(response, LoginDoorHanger.this);
                     }
                 });
                 builder.setNegativeButton(
                         R.string.button_cancel, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(final DialogInterface dialog, final int which) {
                         dialog.dismiss();
@@ -194,23 +180,19 @@ public class LoginDoorHanger extends Doo
                     }
                     usernames[i] = user;
                     passwords[i] = login.getString("password");
                 }
 
                 builder.setItems(usernames, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        final JSONObject response = new JSONObject();
-                        try {
-                            response.put("callback", mButtonConfig.callback);
-                            response.put("password", passwords[which]);
-                        } catch (JSONException e) {
-                            Log.e(LOGTAG, "Error making login select dialog JSON", e);
-                        }
+                        final GeckoBundle response = new GeckoBundle(2);
+                        response.putInt("callback", mButtonConfig.callback);
+                        response.putString("password", passwords[which]);
                         mOnButtonClickListener.onButtonClick(response, LoginDoorHanger.this);
                     }
                 });
                 builder.setNegativeButton(
                         R.string.button_cancel, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         dialog.dismiss();
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -515,20 +515,18 @@ size. -->
 <!-- Localization note (doorhanger_login_no_username): This string is used in the save-login doorhanger
      where normally a username would be displayed. In this case, no username was found, and this placeholder
      contains brackets to indicate this is not actually a username, but rather a placeholder -->
 <!ENTITY doorhanger_login_no_username "[No username]">
 <!ENTITY doorhanger_login_edit_title "Edit login">
 <!ENTITY doorhanger_login_edit_username_hint "Username">
 <!ENTITY doorhanger_login_edit_password_hint "Password">
 <!ENTITY doorhanger_login_edit_toggle "Show password">
-<!ENTITY doorhanger_login_edit_toast_error "Failed to save login">
 <!ENTITY doorhanger_login_select_message "Copy password from &formatS;?">
 <!ENTITY doorhanger_login_select_toast_copy "Password copied to clipboard">
-<!ENTITY doorhanger_login_select_toast_copy_error "Couldn\'t copy password">
 <!ENTITY doorhanger_login_select_action_text "Select another login">
 <!ENTITY doorhanger_login_select_title "Copy password from">
 
 <!-- Localization note (pref_prevent_magnifying_glass): Label for setting that controls
      whether or not the magnifying glass is disabled. -->
 <!ENTITY pref_magnifying_glass_enabled "Magnify small areas">
 <!ENTITY pref_magnifying_glass_enabled_summary2 "Enlarge links and form fields when touching near them">
 
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -413,20 +413,18 @@
   <string name="contextmenu_top_sites_unpin">&contextmenu_top_sites_unpin;</string>
   <string name="contextmenu_add_search_engine">&contextmenu_add_search_engine;</string>
 
   <string name="doorhanger_login_no_username">&doorhanger_login_no_username;</string>
   <string name="doorhanger_login_edit_title">&doorhanger_login_edit_title;</string>
   <string name="doorhanger_login_edit_username_hint">&doorhanger_login_edit_username_hint;</string>
   <string name="doorhanger_login_edit_password_hint">&doorhanger_login_edit_password_hint;</string>
   <string name="doorhanger_login_edit_toggle">&doorhanger_login_edit_toggle;</string>
-  <string name="doorhanger_login_edit_toast_error">&doorhanger_login_edit_toast_error;</string>
   <string name="doorhanger_login_select_message">&doorhanger_login_select_message;</string>
   <string name="doorhanger_login_select_toast_copy">&doorhanger_login_select_toast_copy;</string>
-  <string name="doorhanger_login_select_toast_copy_error">&doorhanger_login_select_toast_copy_error;</string>
   <string name="doorhanger_login_select_action_text">&doorhanger_login_select_action_text;</string>
   <string name="doorhanger_login_select_title">&doorhanger_login_select_title;</string>
 
   <string name="pref_magnifying_glass_enabled">&pref_magnifying_glass_enabled;</string>
   <string name="pref_magnifying_glass_enabled_summary">&pref_magnifying_glass_enabled_summary2;</string>
 
   <string name="pref_scroll_title_bar2">&pref_scroll_title_bar2;</string>
   <string name="pref_scroll_title_bar_summary">&pref_scroll_title_bar_summary2;</string>