diff --git a/mobile/android/base/DoorHangerPopup.java b/mobile/android/base/DoorHangerPopup.java index 0d5448a00ad2..c1ef92f6cda1 100644 --- a/mobile/android/base/DoorHangerPopup.java +++ b/mobile/android/base/DoorHangerPopup.java @@ -6,13 +6,10 @@ package org.mozilla.gecko; import java.util.HashSet; -import java.util.List; -import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.AppConstants.Versions; -import org.mozilla.gecko.prompts.PromptInput; import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.widget.AnchoredPopup; @@ -21,7 +18,6 @@ import org.mozilla.gecko.widget.DoorHanger; import android.content.Context; import android.util.Log; import android.view.View; -import android.widget.CheckBox; import org.mozilla.gecko.widget.DoorhangerConfig; public class DoorHangerPopup extends AnchoredPopup @@ -109,15 +105,16 @@ public class DoorHangerPopup extends AnchoredPopup private DoorhangerConfig makeConfigFromJSON(JSONObject json) throws JSONException { final int tabId = json.getInt("tabID"); final String id = json.getString("value"); - final DoorhangerConfig config = new DoorhangerConfig(tabId, id); + + final String typeString = json.optString("category"); + final boolean isLogin = DoorHanger.Type.LOGIN.toString().equals(typeString); + final DoorHanger.Type doorhangerType = isLogin ? DoorHanger.Type.LOGIN : DoorHanger.Type.DEFAULT; + + final DoorhangerConfig config = new DoorhangerConfig(tabId, id, doorhangerType, this); config.setMessage(json.getString("message")); - config.setButtons(json.getJSONArray("buttons")); + config.appendButtonsFromJSON(json.getJSONArray("buttons")); config.setOptions(json.getJSONObject("options")); - final String typeString = json.optString("category"); - if (DoorHanger.Type.LOGIN.toString().equals(typeString)) { - config.setType(DoorHanger.Type.LOGIN); - } return config; } @@ -181,18 +178,6 @@ public class DoorHangerPopup extends AnchoredPopup final DoorHanger newDoorHanger = DoorHanger.Get(mContext, config); - final JSONArray buttons = config.getButtons(); - for (int i = 0; i < buttons.length(); i++) { - try { - JSONObject buttonObject = buttons.getJSONObject(i); - String label = buttonObject.getString("label"); - String tag = String.valueOf(buttonObject.getInt("callback")); - newDoorHanger.addButton(label, tag, this); - } catch (JSONException e) { - Log.e(LOGTAG, "Error creating doorhanger button", e); - } - } - mDoorHangers.add(newDoorHanger); mContent.addView(newDoorHanger); @@ -206,32 +191,10 @@ public class DoorHangerPopup extends AnchoredPopup * DoorHanger.OnButtonClickListener implementation */ @Override - public void onButtonClick(DoorHanger dh, String tag) { - JSONObject response = new JSONObject(); - try { - response.put("callback", tag); - - CheckBox checkBox = dh.getCheckBox(); - // If the checkbox is being used, pass its value - if (checkBox != null) { - response.put("checked", checkBox.isChecked()); - } - - List doorHangerInputs = dh.getInputs(); - if (doorHangerInputs != null) { - JSONObject inputs = new JSONObject(); - for (PromptInput input : doorHangerInputs) { - inputs.put(input.getId(), input.getValue()); - } - response.put("inputs", inputs); - } - } catch (JSONException e) { - Log.e(LOGTAG, "Error creating onClick response", e); - } - + public void onButtonClick(JSONObject response, DoorHanger doorhanger) { GeckoEvent e = GeckoEvent.createBroadcastEvent("Doorhanger:Reply", response.toString()); GeckoAppShell.sendEventToGecko(e); - removeDoorHanger(dh); + removeDoorHanger(doorhanger); updatePopup(); } diff --git a/mobile/android/base/toolbar/SiteIdentityPopup.java b/mobile/android/base/toolbar/SiteIdentityPopup.java index b43dff94e4cf..7edfbc3c3a34 100644 --- a/mobile/android/base/toolbar/SiteIdentityPopup.java +++ b/mobile/android/base/toolbar/SiteIdentityPopup.java @@ -17,7 +17,6 @@ import org.mozilla.gecko.Tabs; import org.mozilla.gecko.widget.AnchoredPopup; import org.mozilla.gecko.widget.DoorHanger; import org.mozilla.gecko.widget.DoorHanger.OnButtonClickListener; -import org.json.JSONException; import org.json.JSONObject; import android.content.Context; @@ -34,6 +33,8 @@ import org.mozilla.gecko.widget.DoorhangerConfig; * an arrow panel popup hanging from the lock icon in the browser toolbar. */ public class SiteIdentityPopup extends AnchoredPopup { + public static enum ButtonType { DISABLE, ENABLE, KEEP_BLOCKING }; + private static final String LOGTAG = "GeckoSiteIdentityPopup"; private static final String MIXED_CONTENT_SUPPORT_URL = @@ -142,7 +143,7 @@ public class SiteIdentityPopup extends AnchoredPopup { // Remove any existing mixed content notification. removeMixedContentNotification(); - final DoorhangerConfig config = new DoorhangerConfig(); + final DoorhangerConfig config = new DoorhangerConfig(DoorHanger.Type.MIXED_CONTENT, mButtonClickListener); int icon; if (blocked) { icon = R.drawable.shield_enabled_doorhanger; @@ -154,11 +155,11 @@ public class SiteIdentityPopup extends AnchoredPopup { } config.setLink(mContext.getString(R.string.learn_more), MIXED_CONTENT_SUPPORT_URL, "\n\n"); - config.setType(DoorHanger.Type.SITE); + addNotificationButtons(config, blocked); + mMixedContentNotification = DoorHanger.Get(mContext, config); mMixedContentNotification.setIcon(icon); - addNotificationButtons(mMixedContentNotification, blocked); mContent.addView(mMixedContentNotification); mDivider.setVisibility(View.VISIBLE); @@ -175,7 +176,7 @@ public class SiteIdentityPopup extends AnchoredPopup { // Remove any existing tracking content notification. removeTrackingContentNotification(); - final DoorhangerConfig config = new DoorhangerConfig(); + final DoorhangerConfig config = new DoorhangerConfig(DoorHanger.Type.TRACKING, mButtonClickListener); int icon; if (blocked) { @@ -189,12 +190,12 @@ public class SiteIdentityPopup extends AnchoredPopup { } config.setLink(mContext.getString(R.string.learn_more), TRACKING_CONTENT_SUPPORT_URL, "\n\n"); - config.setType(DoorHanger.Type.SITE); + addNotificationButtons(config, blocked); + mTrackingContentNotification = DoorHanger.Get(mContext, config); mTrackingContentNotification.setIcon(icon); - addNotificationButtons(mTrackingContentNotification, blocked); mContent.addView(mTrackingContentNotification); mDivider.setVisibility(View.VISIBLE); @@ -207,13 +208,12 @@ public class SiteIdentityPopup extends AnchoredPopup { } } - private void addNotificationButtons(DoorHanger dh, boolean blocked) { - // TODO: Add support for buttons in DoorHangerConfig. + private void addNotificationButtons(DoorhangerConfig config, boolean blocked) { if (blocked) { - dh.addButton(mContext.getString(R.string.disable_protection), "disable", mButtonClickListener); - dh.addButton(mContext.getString(R.string.keep_blocking), "keepBlocking", mButtonClickListener); + config.appendButton(mContext.getString(R.string.disable_protection), ButtonType.DISABLE.ordinal()); + config.appendButton(mContext.getString(R.string.keep_blocking), ButtonType.KEEP_BLOCKING.ordinal()); } else { - dh.addButton(mContext.getString(R.string.enable_protection), "enable", mButtonClickListener); + config.appendButton(mContext.getString(R.string.enable_protection), ButtonType.ENABLE.ordinal()); } } @@ -290,18 +290,9 @@ public class SiteIdentityPopup extends AnchoredPopup { private class PopupButtonListener implements OnButtonClickListener { @Override - public void onButtonClick(DoorHanger dh, String tag) { - try { - JSONObject data = new JSONObject(); - data.put("allowContent", tag.equals("disable")); - data.put("contentType", (dh == mMixedContentNotification ? "mixed" : "tracking")); - - GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", data.toString()); - GeckoAppShell.sendEventToGecko(e); - } catch (JSONException e) { - Log.e(LOGTAG, "Exception creating message to enable/disable content blocking", e); - } - + public void onButtonClick(JSONObject response, DoorHanger doorhanger) { + GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", response.toString()); + GeckoAppShell.sendEventToGecko(e); dismiss(); } } diff --git a/mobile/android/base/widget/DefaultDoorHanger.java b/mobile/android/base/widget/DefaultDoorHanger.java index 94627ea48d45..c7b0f9411b4f 100644 --- a/mobile/android/base/widget/DefaultDoorHanger.java +++ b/mobile/android/base/widget/DefaultDoorHanger.java @@ -5,6 +5,9 @@ package org.mozilla.gecko.widget; +import android.util.Log; +import android.view.LayoutInflater; +import android.widget.Button; import org.mozilla.gecko.R; import org.mozilla.gecko.prompts.PromptInput; @@ -18,6 +21,7 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; +import org.mozilla.gecko.toolbar.SiteIdentityPopup; import java.util.ArrayList; import java.util.List; @@ -31,10 +35,6 @@ public class DefaultDoorHanger extends DoorHanger { private List mInputs; private CheckBox mCheckBox; - public DefaultDoorHanger(Context context, DoorhangerConfig config) { - this(context, config, Type.DEFAULT); - } - public DefaultDoorHanger(Context context, DoorhangerConfig config, Type type) { super(context, config, type); @@ -62,15 +62,15 @@ public class DefaultDoorHanger extends DoorHanger { if (link != null) { addLink(link.label, link.url, link.delimiter); } + + setButtons(config); } - @Override - public List getInputs() { + private List getInputs() { return mInputs; } - @Override - public CheckBox getCheckBox() { + private CheckBox getCheckBox() { return mCheckBox; } @@ -115,6 +115,54 @@ public class DefaultDoorHanger extends DoorHanger { } } + @Override + protected Button createButtonInstance(final String text, final int id) { + final Button button = (Button) LayoutInflater.from(getContext()).inflate(R.layout.doorhanger_button, null); + button.setText(text); + + button.setOnClickListener(new Button.OnClickListener() { + @Override + public void onClick(View v) { + final JSONObject response = new JSONObject(); + try { + // TODO: Bug 1149359 - Split this into each Doorhanger Type class. + switch (mType) { + case MIXED_CONTENT: + response.put("allowContent", (id == SiteIdentityPopup.ButtonType.DISABLE.ordinal())); + response.put("contentType", ("mixed")); + break; + case TRACKING: + response.put("allowContent", (id == SiteIdentityPopup.ButtonType.DISABLE.ordinal())); + response.put("contentType", ("tracking")); + break; + default: + response.put("callback", id); + + CheckBox checkBox = getCheckBox(); + // If the checkbox is being used, pass its value + if (checkBox != null) { + response.put("checked", checkBox.isChecked()); + } + + List doorHangerInputs = getInputs(); + if (doorHangerInputs != null) { + JSONObject inputs = new JSONObject(); + for (PromptInput input : doorHangerInputs) { + inputs.put(input.getId(), input.getValue()); + } + response.put("inputs", inputs); + } + } + mOnButtonClickListener.onButtonClick(response, DefaultDoorHanger.this); + } catch (JSONException e) { + Log.e(LOGTAG, "Error creating onClick response", e); + } + } + }); + + return button; + } + private void styleInput(PromptInput input, View view) { if (input instanceof PromptInput.MenulistInput) { styleDropdownInputs(input, view); diff --git a/mobile/android/base/widget/DoorHanger.java b/mobile/android/base/widget/DoorHanger.java index 384a19665d61..8c9001323aeb 100644 --- a/mobile/android/base/widget/DoorHanger.java +++ b/mobile/android/base/widget/DoorHanger.java @@ -12,43 +12,40 @@ import android.text.Spanned; import android.text.method.LinkMovementMethod; import android.text.style.ForegroundColorSpan; import android.text.style.URLSpan; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; -import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.R; import org.mozilla.gecko.Tabs; -import org.mozilla.gecko.prompts.PromptInput; - -import java.util.List; public abstract class DoorHanger extends LinearLayout { public static DoorHanger Get(Context context, DoorhangerConfig config) { final Type type = config.getType(); - if (type != null) { - switch (type) { - case LOGIN: - return new LoginDoorHanger(context, config); - case SITE: - return new DefaultDoorHanger(context, config, type); - } + switch (type) { + case LOGIN: + return new LoginDoorHanger(context, config); + case TRACKING: + case MIXED_CONTENT: + return new DefaultDoorHanger(context, config, type); } - - return new DefaultDoorHanger(context, config); + return new DefaultDoorHanger(context, config, type); } - public static enum Type { DEFAULT, LOGIN, SITE } + public static enum Type { DEFAULT, LOGIN, TRACKING, MIXED_CONTENT} public interface OnButtonClickListener { - public void onButtonClick(DoorHanger dh, String tag); + public void onButtonClick(JSONObject response, DoorHanger doorhanger); } - private static final LayoutParams sButtonParams; + protected static final LayoutParams sButtonParams; static { sButtonParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f); } @@ -58,7 +55,8 @@ public abstract class DoorHanger extends LinearLayout { // Divider between doorhangers. private final View mDivider; - private final LinearLayout mButtonsContainer; + protected final LinearLayout mButtonsContainer; + protected final OnButtonClickListener mOnButtonClickListener; // The tab this doorhanger is associated with. private final int mTabId; @@ -66,6 +64,8 @@ public abstract class DoorHanger extends LinearLayout { // DoorHanger identifier. private final String mIdentifier; + protected final Type mType; + private final ImageView mIcon; private final TextView mMessage; @@ -96,16 +96,22 @@ public abstract class DoorHanger extends LinearLayout { mDivider = findViewById(R.id.divider_doorhanger); mIcon = (ImageView) findViewById(R.id.doorhanger_icon); mMessage = (TextView) findViewById(R.id.doorhanger_message); - if (type == Type.SITE) { + + // TODO: Bug 1149359 - split this into DoorHanger subclasses. + if (type == Type.TRACKING || type == Type.MIXED_CONTENT) { mMessage.setTextAppearance(getContext(), R.style.TextAppearance_DoorHanger_Small); } + + mType = type; + mButtonsContainer = (LinearLayout) findViewById(R.id.doorhanger_buttons); + mOnButtonClickListener = config.getButtonClickListener(); mDividerColor = getResources().getColor(R.color.divider_light); setOrientation(VERTICAL); } - abstract protected void loadConfig(DoorhangerConfig config); + protected abstract void loadConfig(DoorhangerConfig config); protected void setOptions(final JSONObject options) { final int persistence = options.optInt("persistence"); @@ -119,6 +125,21 @@ public abstract class DoorHanger extends LinearLayout { if (timeout > 0) { mTimeout = timeout; } + } + + protected void setButtons(DoorhangerConfig config) { + final JSONArray buttons = config.getButtons(); + final OnButtonClickListener listener = config.getButtonClickListener(); + for (int i = 0; i < buttons.length(); i++) { + try { + final JSONObject buttonObject = buttons.getJSONObject(i); + final String label = buttonObject.getString("label"); + final int callbackId = buttonObject.getInt("callback"); + addButtonToLayout(label, callbackId); + } catch (JSONException e) { + Log.e(LOGTAG, "Error creating doorhanger button", e); + } + } } public int getTabId() { @@ -166,18 +187,13 @@ public abstract class DoorHanger extends LinearLayout { mMessage.setMovementMethod(LinkMovementMethod.getInstance()); } - public void addButton(final String text, final String tag, final OnButtonClickListener listener) { - final Button button = (Button) LayoutInflater.from(getContext()).inflate(R.layout.doorhanger_button, null); - button.setText(text); - button.setTag(tag); - - button.setOnClickListener(new Button.OnClickListener() { - @Override - public void onClick(View v) { - listener.onButtonClick(DoorHanger.this, tag); - } - }); - + /** + * Creates and adds a button into the DoorHanger. + * @param text Button text + * @param id Identifier associated with the button + */ + private void addButtonToLayout(String text, int id) { + final Button button = createButtonInstance(text, id); if (mButtonsContainer.getChildCount() == 0) { // If this is the first button we're adding, make the choices layout visible. mButtonsContainer.setVisibility(View.VISIBLE); @@ -195,6 +211,8 @@ public abstract class DoorHanger extends LinearLayout { mButtonsContainer.addView(button, sButtonParams); } + protected abstract Button createButtonInstance(String text, int id); + /* * Checks with persistence and timeout options to see if it's okay to remove a doorhanger. * @@ -222,14 +240,4 @@ public abstract class DoorHanger extends LinearLayout { return true; } - - // TODO: remove and expose through instance Button Handler. - public List getInputs() { - return null; - } - - public CheckBox getCheckBox() { - return null; - } - } diff --git a/mobile/android/base/widget/DoorhangerConfig.java b/mobile/android/base/widget/DoorhangerConfig.java index 6faf38101621..143bbb818cec 100644 --- a/mobile/android/base/widget/DoorhangerConfig.java +++ b/mobile/android/base/widget/DoorhangerConfig.java @@ -5,7 +5,9 @@ package org.mozilla.gecko.widget; +import android.util.Log; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.widget.DoorHanger.Type; @@ -24,23 +26,29 @@ public class DoorhangerConfig { } } + private static final String LOGTAG = "DoorhangerConfig"; + private final int tabId; private final String id; - private DoorHanger.Type type; + private final DoorHanger.OnButtonClickListener buttonClickListener; + private final DoorHanger.Type type; private String message; private JSONObject options; private Link link; - private JSONArray buttons; + private JSONArray buttons = new JSONArray(); - public DoorhangerConfig() { + public DoorhangerConfig(Type type, DoorHanger.OnButtonClickListener listener) { // XXX: This should only be used by SiteIdentityPopup doorhangers which // don't need tab or id references, until bug 1141904 unifies doorhangers. - this(-1, null); + + this(-1, null, type, listener); } - public DoorhangerConfig(int tabId, String id) { + public DoorhangerConfig(int tabId, String id, DoorHanger.Type type, DoorHanger.OnButtonClickListener buttonClickListener) { this.tabId = tabId; this.id = id; + this.type = type; + this.buttonClickListener = buttonClickListener; } public int getTabId() { @@ -51,10 +59,6 @@ public class DoorhangerConfig { return id; } - public void setType(Type type) { - this.type = type; - } - public Type getType() { return type; } @@ -75,8 +79,33 @@ public class DoorhangerConfig { return options; } - public void setButtons(JSONArray buttons) { - this.buttons = buttons; + /** + * Add buttons from JSON to the Config object. + * @param buttons JSONArray of JSONObjects of the form { label: