Closed
Bug 905262
Opened 11 years ago
Closed 11 years ago
[fig] Create a JS add-on API for adding content to the promotional banner
Categories
(Firefox for Android Graveyard :: General, defect)
Tracking
(firefox26+ verified, firefox27 verified, fennec26+)
VERIFIED
FIXED
Firefox 27
People
(Reporter: Margaret, Assigned: Margaret)
References
Details
(Keywords: dev-doc-complete)
Attachments
(1 file, 4 obsolete files)
(deleted),
patch
|
wesj
:
review+
lsblakk
:
approval-mozilla-aurora+
|
Details | Diff | Splinter Review |
We'll need bug 862801 to land before starting work on this, but here's the project wiki page:
https://wiki.mozilla.org/Mobile/Projects/About:home_-_Add-ons_can_add_content_to_the_%22promotional_banner%22_tile
Assignee | ||
Updated•11 years ago
|
Assignee: nobody → margaret.leibovic
Assignee | ||
Comment 1•11 years ago
|
||
This is based on top of the patch for bug 862801, and very closely resembles the patch for bug 848242.
A few things:
* This API just lets you set text (based mainly on the fact that the patch for bug 862801 just adds a TextView), but we should expand this to let you add an image as well (if we do this with compound drawables this can still be a TextView).
* We'll need to hide the banner until we get the "HomeSnippet:Data" event, since it won't have content before then. Maybe we can animate it up from the bottom to make this delay seem more intentional.
* I decided to make JS in charge of deciding which message to show in the banner, but we still need to update this to decide how to rotate through the messages.
* Naming bikeshed opportunity: at first I wanted to call this HomeSnippets and create individual Snippet objects, but it bothered me that HomeSnippet.java was a singular snippet. I actually feel that that should probably be renamed to something more semantic, like HomeBanner, so maybe I can go back to calling my object HomeSnippets.
Attachment #792522 -
Flags: feedback?(wjohnston)
Attachment #792522 -
Flags: feedback?(mark.finkle)
Comment 2•11 years ago
|
||
(In reply to :Margaret Leibovic from comment #1)
> Created attachment 792522 [details] [diff] [review]
> WIP
>
> This is based on top of the patch for bug 862801, and very closely resembles
> the patch for bug 848242.
>
> A few things:
>
> * This API just lets you set text (based mainly on the fact that the patch
> for bug 862801 just adds a TextView), but we should expand this to let you
> add an image as well (if we do this with compound drawables this can still
> be a TextView).
Currently I'm not using Compound drawables as the size of images returned by the addons might not be same. Hence I'm using an ImageView that centers the image inside it.
>
> * Naming bikeshed opportunity: at first I wanted to call this HomeSnippets
> and create individual Snippet objects, but it bothered me that
> HomeSnippet.java was a singular snippet. I actually feel that that should
> probably be renamed to something more semantic, like HomeBanner, so maybe I
> can go back to calling my object HomeSnippets.
I'm not really sure on the difference in the names. I am fine with renaming them. Though I don't know if you would have a HomeSnippet(s).java file.
Comment 3•11 years ago
|
||
Comment on attachment 792522 [details] [diff] [review]
WIP
Review of attachment 792522 [details] [diff] [review]:
-----------------------------------------------------------------
Seems fine to me.
::: mobile/android/base/home/HomeSnippet.java
@@ +43,5 @@
> +
> + setOnClickListener(new View.OnClickListener() {
> + @Override
> + public void onClick(View v) {
> + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeSnippet:Click", null));
I wonder if its worth sending relative coordinates or something here... If we made it possible to use a large image, maybe?
@@ +53,5 @@
> + public void onAttachedToWindow() {
> + super.onAttachedToWindow();
> +
> + GeckoAppShell.getEventDispatcher().registerEventListener("HomeSnippet:Data", this);
> + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeSnippet:Get", null));
Does this fire often enough to actually update this from time to time?
@@ +107,5 @@
> + final String text = message.getString("text");
> + ThreadUtils.postToUiThread(new Runnable() {
> + @Override
> + public void run() {
> + setText(text);
We really should try to use the HTML Spannable thing here. Also, wrapping so much in a try-catch always makes me cringe.
::: mobile/android/chrome/content/browser.js
@@ +391,5 @@
> #endif
> +
> + Cu.import("resource://gre/modules/HomeSnippet.jsm");
> + HomeSnippet.add({
> + text: "Margaret is the best!",
Yeah, I think we'll want some sort of icon
::: mobile/android/modules/HomeSnippet.jsm
@@ +33,5 @@
> + if ("onclick" in options && typeof options.onclick === "function")
> + this.onclick = options.onclick;
> +}
> +
> +this.HomeSnippet = {
Hmm.. I wonder if we'll want to just create a Home object to wrap all the about:home apis in eventually. i.e.
Home.snippet/banner.add();
Home.panels.add();
Home.widget.add();
@@ +63,5 @@
> + this._currentMessage = message;
> +
> + sendMessageToJava({
> + type: "HomeSnippet:Data",
> + text: message.text || ""
I don't think we'd want to send this with "";
Attachment #792522 -
Flags: feedback?(wjohnston) → feedback+
Assignee | ||
Comment 4•11 years ago
|
||
This should probably track 26 because bug 862801 doesn't replace the about:home snippet on its own.
tracking-fennec: --- → ?
Comment 5•11 years ago
|
||
Comment on attachment 792522 [details] [diff] [review]
WIP
Wes covered all the high points. One thing I failed to realize, but now remember, is that this system will be a rotating banner system. Just adding your banner to the HomeView (or whatever) object does not immediately show the banner. It is added to a pool of potential banners, and the system will show one per startup (or some other period).
Attachment #792522 -
Flags: feedback?(mark.finkle) → feedback+
Updated•11 years ago
|
tracking-fennec: ? → 26+
Assignee | ||
Comment 6•11 years ago
|
||
(In reply to Wesley Johnston (:wesj) from comment #3)
::: mobile/android/base/home/HomeSnippet.java
> @@ +43,5 @@
> > +
> > + setOnClickListener(new View.OnClickListener() {
> > + @Override
> > + public void onClick(View v) {
> > + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeSnippet:Click", null));
>
> I wonder if its worth sending relative coordinates or something here... If
> we made it possible to use a large image, maybe?
I've thought about this, but I think that can happen in a follow-up if we want it.
> @@ +53,5 @@
> > + public void onAttachedToWindow() {
> > + super.onAttachedToWindow();
> > +
> > + GeckoAppShell.getEventDispatcher().registerEventListener("HomeSnippet:Data", this);
> > + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeSnippet:Get", null));
>
> Does this fire often enough to actually update this from time to time?
It fires every time we show about:home (including switching tabs). I think this is appropriately often to change the banner message.
> @@ +107,5 @@
> > + final String text = message.getString("text");
> > + ThreadUtils.postToUiThread(new Runnable() {
> > + @Override
> > + public void run() {
> > + setText(text);
>
> We really should try to use the HTML Spannable thing here.
What kind of markup do you imagine an add-on wanting to put in here? Just links? There's really not that much room, and the user can't zoom this, so it might be hard to hit different targets.
tracking-fennec: 26+ → ?
Assignee | ||
Comment 7•11 years ago
|
||
I still need to deal with the banner rotation, but here's another WIP that add icon support and incorporates wesj's Home API idea.
Attachment #792522 -
Attachment is obsolete: true
Attachment #799150 -
Flags: feedback?(wjohnston)
Assignee | ||
Comment 8•11 years ago
|
||
Oops, I accidentally cleared the tracking flag.
tracking-fennec: ? → 26+
Comment 9•11 years ago
|
||
Comment on attachment 799150 [details] [diff] [review]
WIP v2
Review of attachment 799150 [details] [diff] [review]:
-----------------------------------------------------------------
Seems great. Mostly some nit comments.
::: mobile/android/base/GeckoAppShell.java
@@ +2191,5 @@
> public static void registerEventListener(String event, GeckoEventListener listener) {
> sEventDispatcher.registerEventListener(event, listener);
> }
>
> + public static EventDispatcher getEventDispatcher() {
Not your bug, but we should really move singleton's like this into their respective class...
::: mobile/android/base/home/HomeBanner.java
@@ +14,3 @@
>
> +import org.json.JSONException;
> +import org.json.JSONObject;
You wound up not using these, right?
@@ +14,4 @@
>
> +import org.json.JSONException;
> +import org.json.JSONObject;
> +
ws
@@ +28,2 @@
>
> +public class HomeBanner extends LinearLayout
ws
@@ +81,5 @@
> +
> + @Override
> + public void handleMessage(String event, JSONObject message) {
> + // Don't show a banner if there's no text.
> + final String text = message.optString("text");
I tend to use message.getString("text"); if its not an optional thing, which will throw. But I kinda hate wrapping things in try-catch blocks. Maybe this is cleaner and catches the case where the caller passes up a real, but empty string.
@@ +86,5 @@
> + if (TextUtils.isEmpty(text)) {
> + return;
> + }
> +
> + final TextView textView = (TextView) findViewById(R.id.text);
Unless its going to live a different lifecycle than homeBanner, you might just cache this view in the banner. Then again, doing it this way means you don't have to worry about life cycle.
@@ +106,5 @@
> + @Override
> + public void onBitmapFound(final Drawable d) {
> + ThreadUtils.postToUiThread(new Runnable() {
> + @Override
> + public void run() {
Add a null check for d in here (or outside the runnable). Unlike the example mfinkle sent out yesterday, getDrawable will call the callback with a null result if it doesn't find anything. It takes a callback because some icons require some async file i/o.
::: mobile/android/modules/Home.jsm
@@ +18,5 @@
> +function sendMessageToJava(message) {
> + return Services.androidBridge.handleGeckoMessage(JSON.stringify(message));
> +}
> +
> +// XXX: Should this go into some shared place?
Yes!
@@ +78,5 @@
> +
> + sendMessageToJava({
> + type: "HomeBanner:Data",
> + text: message.text,
> + iconURI: message.iconURI
Even though only one of these shows at a time, it still seems like it would be good to pass the id to java and have it pass it back when its clicked, rather than relying on this component to stay in sync with Java. It matches what we do everywhere else (but everywhere else we show multiple addon items at a time...). Also, it just feels more resilient to pass it up. i.e. Have the banner always tell us exactly what was clicked.
@@ +96,5 @@
> +
> + remove: function(id) {
> + delete this._messages[id];
> +
> + if (!Object.keys(this._messages).length) {
Why the ! here instead of == 0? Feels kinda weird to read this way.
Attachment #799150 -
Flags: feedback?(wjohnston) → feedback+
Assignee | ||
Comment 10•11 years ago
|
||
I made a test add-on to start playing around with this API in a more realistic setting:
https://github.com/leibovic/home-demo
If you have this patch applied you can try it here:
https://github.com/leibovic/home-demo/releases/tag/0.1
Now I'll work on actually addressing the comments to make the patch good :)
Attachment #799150 -
Attachment is obsolete: true
Assignee | ||
Comment 11•11 years ago
|
||
I think this addresses all your concerns. It's working well! I made an add-on with multiple banner messages, and it does rotate through them properly every time you re-open the home page.
Attachment #802651 -
Attachment is obsolete: true
Attachment #803398 -
Flags: review?(wjohnston)
Comment 12•11 years ago
|
||
Comment on attachment 803398 [details] [diff] [review]
patch
Review of attachment 803398 [details] [diff] [review]:
-----------------------------------------------------------------
::: mobile/android/base/home/HomeBanner.java
@@ +61,5 @@
> +
> + setOnClickListener(new View.OnClickListener() {
> + @Override
> + public void onClick(View v) {
> + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Click", null));
Need to pass the id.
@@ +83,5 @@
> +
> + @Override
> + public void handleMessage(String event, JSONObject message) {
> + try {
> + // Store the current message id to pass back to JS in the view's OnClickListener.
I don't think you're actually doing this, are you?
@@ +98,5 @@
> + setVisibility(View.VISIBLE);
> + }
> + });
> + } catch (JSONException e) {
> + Log.e(LOGTAG, "Exception handling \"HomeBanner:Data\" message", e);
You might as well use the event parameter here so that this message is more dynamic.
@@ +104,5 @@
> + }
> +
> + final String iconURI = message.optString("iconURI");
> + if (TextUtils.isEmpty(iconURI)) {
> + return;
Do we need to hide the imageView here so that it doesn't take up space?
@@ +113,5 @@
> + @Override
> + public void onBitmapFound(final Drawable d) {
> + // Bail if getDrawable doesn't find anything.
> + if (d == null) {
> + return;
Same here? Should we hide the view?
::: mobile/android/modules/Home.jsm
@@ +45,5 @@
> +}
> +
> +let HomeBanner = {
> + // Holds the messages that will rotate through the banner.
> + _messages: {},
Do we need two lists? Can we get rid of this one?
Attachment #803398 -
Flags: review?(wjohnston) → review-
Assignee | ||
Comment 13•11 years ago
|
||
(In reply to Wesley Johnston (:wesj) from comment #12)
> Comment on attachment 803398 [details] [diff] [review]
> patch
>
> Review of attachment 803398 [details] [diff] [review]:
> -----------------------------------------------------------------
>
> ::: mobile/android/base/home/HomeBanner.java
> @@ +61,5 @@
> > +
> > + setOnClickListener(new View.OnClickListener() {
> > + @Override
> > + public void onClick(View v) {
> > + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Click", null));
>
> Need to pass the id.
Oops, good catch. I guess I didn't test out the clicking in my new version to find this didn't work :/
> @@ +104,5 @@
> > + }
> > +
> > + final String iconURI = message.optString("iconURI");
> > + if (TextUtils.isEmpty(iconURI)) {
> > + return;
>
> Do we need to hide the imageView here so that it doesn't take up space?
Good question! I had the same one, but I was only thinking about an image from a previous message showing up (that doesn't happen because the lifecycle of these views is so short that they only hang around while one is shown). I think hiding the imageView is a good call for the visual design, so I'll do that (we won't need to worry about un-hiding it, since we only go through here once per view lifetime).
> @@ +113,5 @@
> > + @Override
> > + public void onBitmapFound(final Drawable d) {
> > + // Bail if getDrawable doesn't find anything.
> > + if (d == null) {
> > + return;
>
> Same here? Should we hide the view?
Yeah, I'll do that.
> ::: mobile/android/modules/Home.jsm
> @@ +45,5 @@
> > +}
> > +
> > +let HomeBanner = {
> > + // Holds the messages that will rotate through the banner.
> > + _messages: {},
>
> Do we need two lists? Can we get rid of this one?
Do you have any good suggestions about how to do that? We need some sort of mapping from id to message to keep track of the messages, and using this object is good for that, but an object does not have the nice list-like properties that help us keep track of which message to show.
Since _messages holds the actual messages, and _queue only holds the ids, we'd need to do something differently to keep the same functionality with one data structure. Unless we can come up with something simple, I don't think it's worth it, since there really should only ever be a handful of messages in rotation here.
Actually... this makes me think of something else. If you have an add-on like "wikipedia fun facts", the add-on author could be nice and take care of adding and removing items a handful a time, but could also alternatively add a ton of messages all at once, which might hurt performance and could also drown out any other banner messages, e.g. a dynamic snippet. I don't know that we can technically prevent this, but maybe we need some best practices advice added to whatever documentation we write for this API. (Add-on reviewers would be able to see if an add-on is doing this or not.)
Assignee | ||
Comment 14•11 years ago
|
||
Addressed comments.
I turned the right margin on the ImageView into a left margin on the TextView, so that there's space between the text and the edge of the screen if the image is hidden.
I wonder that if instead of hiding the ImageView, we should have a default icon, similar to menu items. But we can talk to ibarlow and do that in a follow-up bug if that's what we decide we want.
Attachment #803398 -
Attachment is obsolete: true
Attachment #804777 -
Flags: review?(wjohnston)
Comment 15•11 years ago
|
||
Comment on attachment 804777 [details] [diff] [review]
patch v2
Review of attachment 804777 [details] [diff] [review]:
-----------------------------------------------------------------
::: mobile/android/base/home/HomeBanner.java
@@ +108,5 @@
> + final ImageView iconView = (ImageView) findViewById(R.id.icon);
> +
> + if (TextUtils.isEmpty(iconURI)) {
> + // Hide the image view if we don't have an icon to show.
> + iconView.setVisibility(View.GONE);
Make sure this shows itself if we're moving from a view without an icon to one with.
@@ +117,5 @@
> + @Override
> + public void onBitmapFound(final Drawable d) {
> + // Bail if getDrawable doesn't find anything.
> + if (d == null) {
> + iconView.setVisibility(View.GONE);
Same here
::: mobile/android/modules/Home.jsm
@@ +79,5 @@
> + },
> +
> + _handleClick: function(id) {
> + let message = this._messages[id];
> + if (message.onclick)
This will fail if the banner is removed while its showing. Maybe thats ok...
Attachment #804777 -
Flags: review?(wjohnston) → review+
Assignee | ||
Comment 16•11 years ago
|
||
(In reply to Wesley Johnston (:wesj) from comment #15)
> Comment on attachment 804777 [details] [diff] [review]
> patch v2
>
> Review of attachment 804777 [details] [diff] [review]:
> -----------------------------------------------------------------
>
> ::: mobile/android/base/home/HomeBanner.java
> @@ +108,5 @@
> > + final ImageView iconView = (ImageView) findViewById(R.id.icon);
> > +
> > + if (TextUtils.isEmpty(iconURI)) {
> > + // Hide the image view if we don't have an icon to show.
> > + iconView.setVisibility(View.GONE);
>
> Make sure this shows itself if we're moving from a view without an icon to
> one with.
This works because we create a new view every time this banner is shown, so we only ever go through here once.
> ::: mobile/android/modules/Home.jsm
> @@ +79,5 @@
> > + },
> > +
> > + _handleClick: function(id) {
> > + let message = this._messages[id];
> > + if (message.onclick)
>
> This will fail if the banner is removed while its showing. Maybe thats ok...
Yeah, I think that's okay. If someone really wants a click on their message to be handled, they shouldn't remove the message.
This points out that once a message is shown, it won't be dynamically un-shown if it's removed. But I also think that's okay.
Assignee | ||
Comment 17•11 years ago
|
||
Status: NEW → RESOLVED
Closed: 11 years ago
Resolution: --- → FIXED
Target Milestone: --- → Firefox 27
Assignee | ||
Comment 19•11 years ago
|
||
Comment on attachment 804777 [details] [diff] [review]
patch v2
[Approval Request Comment]
Bug caused by (feature/regressing bug #): about:home re-write (bug 862793)
User impact if declined: we won't be able to show users promotional content on about:home (e.g. the current "set up sync" banner on beta/release)
Testing completed (on m-c, etc.): landed on m-c 9/18
Risk to taking this patch (and alternatives if risky): low-risk, nothing happens unless an add-on is installed that uses this API
String or IDL/UUID changes made by this patch: none
Attachment #804777 -
Flags: approval-mozilla-aurora?
Comment 20•11 years ago
|
||
Comment on attachment 804777 [details] [diff] [review]
patch v2
Margaret, do you have a test addon that QA could use to verify this? Would be great to ensure there's some testing before this gets to Beta and people do potentially start to code to it.
Attachment #804777 -
Flags: approval-mozilla-aurora? → approval-mozilla-aurora+
Updated•11 years ago
|
Flags: needinfo?(margaret.leibovic)
Assignee | ||
Comment 21•11 years ago
|
||
(In reply to lsblakk@mozilla.com [:lsblakk] from comment #20)
> Comment on attachment 804777 [details] [diff] [review]
> patch v2
>
> Margaret, do you have a test addon that QA could use to verify this? Would
> be great to ensure there's some testing before this gets to Beta and people
> do potentially start to code to it.
In fact I have 2 of them! The first one here is simpler than the second.
https://github.com/leibovic/home-demo/releases/tag/0.1
https://github.com/leibovic/promo-banner/releases/tag/0.1
Flags: needinfo?(margaret.leibovic)
Comment 22•11 years ago
|
||
Kevin, not sure which of you or Aaron is point for 26 so starting with you, adjust if needed
Comment 23•11 years ago
|
||
status-firefox26:
--- → fixed
status-firefox27:
--- → fixed
Assignee | ||
Comment 24•11 years ago
|
||
Adding dev-doc-needed to remind myself to make an MDN page for this. Will aim to do that this week.
Keywords: dev-doc-needed
Assignee | ||
Comment 25•11 years ago
|
||
Keywords: dev-doc-needed → dev-doc-complete
Updated•4 years ago
|
Product: Firefox for Android → Firefox for Android Graveyard
You need to log in
before you can comment on or make changes to this bug.
Description
•