Bug 1330409 - Convert CastingApps events to bundle events; r=sebastian
authorJim Chen <nchen@mozilla.com>
Wed, 25 Jan 2017 18:53:57 -0500
changeset 466657 2740c0e269054538fe69c6ed5b6afe82d9407370
parent 466656 6057588e27441ec4ce22e05b5a9f7eee7774995b
child 466658 65b07eac83b329667ae01b036e7e5f5b067c31c4
push id42948
push userbmo:gasolin@mozilla.com
push dateThu, 26 Jan 2017 07:49:21 +0000
reviewerssebastian
bugs1330409
milestone54.0a1
Bug 1330409 - Convert CastingApps events to bundle events; r=sebastian Convert the events used in MediaCastingBar and ChromeCastPlayer to GeckoBundle/BundleEventListener events. UI thread events are used because the listener performs operations on the UI thread.
mobile/android/base/java/org/mozilla/gecko/ChromeCastPlayer.java
mobile/android/base/java/org/mozilla/gecko/MediaCastingBar.java
mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
mobile/android/chrome/content/CastingApps.js
mobile/android/modules/MediaPlayerApp.jsm
mobile/android/modules/TabMirror.jsm
widget/android/nsIAndroidBridge.idl
--- a/mobile/android/base/java/org/mozilla/gecko/ChromeCastPlayer.java
+++ b/mobile/android/base/java/org/mozilla/gecko/ChromeCastPlayer.java
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import java.io.IOException;
 
 import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.GeckoBundle;
 import org.json.JSONObject;
 import org.json.JSONException;
 
 import com.google.android.gms.cast.Cast.MessageReceivedCallback;
 import com.google.android.gms.cast.ApplicationMetadata;
 import com.google.android.gms.cast.Cast;
 import com.google.android.gms.cast.Cast.ApplicationConnectionResult;
 import com.google.android.gms.cast.CastDevice;
@@ -84,25 +85,25 @@ class ChromeCastPlayer implements GeckoM
         }
 
         @Override
         public void onStatusUpdated() {
             MediaStatus mediaStatus = remoteMediaPlayer.getMediaStatus();
 
             switch (mediaStatus.getPlayerState()) {
             case MediaStatus.PLAYER_STATE_PLAYING:
-                GeckoAppShell.notifyObservers("MediaPlayer:Playing", null);
+                EventDispatcher.getInstance().dispatch("MediaPlayer:Playing", null);
                 break;
             case MediaStatus.PLAYER_STATE_PAUSED:
-                GeckoAppShell.notifyObservers("MediaPlayer:Paused", null);
+                EventDispatcher.getInstance().dispatch("MediaPlayer:Paused", null);
                 break;
             case MediaStatus.PLAYER_STATE_IDLE:
                 // TODO: Do we want to shutdown when there are errors?
                 if (mediaStatus.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) {
-                    GeckoAppShell.notifyObservers("Casting:Stop", null);
+                    EventDispatcher.getInstance().dispatch("Casting:Stop", null);
                 }
                 break;
             default:
                 // TODO: Do we need to handle other status such as buffering / unknown?
                 break;
             }
         }
 
@@ -383,17 +384,19 @@ class ChromeCastPlayer implements GeckoM
         }
 
         /*
          * Receive message from the receiver app
          */
         @Override
         public void onMessageReceived(CastDevice castDevice, String namespace,
                                       String message) {
-            GeckoAppShell.notifyObservers("MediaPlayer:Response", message);
+            final GeckoBundle data = new GeckoBundle(1);
+            data.putString("message", message);
+            EventDispatcher.getInstance().dispatch("MediaPlayer:Response", data);
         }
 
         public void sendMessage(String message) {
             if (apiClient != null && mMirrorChannel != null) {
                 try {
                     Cast.CastApi.sendMessage(apiClient, mMirrorChannel.getNamespace(), message)
                         .setResultCallback(
                                            new ResultCallback<Status>() {
@@ -432,17 +435,19 @@ class ChromeCastPlayer implements GeckoM
                                                              mMirrorChannel
                                                              .getNamespace(),
                                                              mMirrorChannel);
                     sendSuccess(callback, null);
                 } catch (IOException e) {
                     Log.e(LOGTAG, "Exception while creating channel", e);
                 }
 
-                GeckoAppShell.notifyObservers("Casting:Mirror", route.getId());
+                final GeckoBundle message = new GeckoBundle(1);
+                message.putString("id", route.getId());
+                EventDispatcher.getInstance().dispatch("Casting:Mirror", message);
             } else {
                 sendError(callback, status.toString());
             }
         }
     }
 
     @Override
     public void message(String msg, final EventCallback callback) {
--- a/mobile/android/base/java/org/mozilla/gecko/MediaCastingBar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/MediaCastingBar.java
@@ -1,44 +1,44 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.GeckoApp;
-import org.mozilla.gecko.util.GeckoEventListener;
+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.json.JSONObject;
-
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageButton;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
-public class MediaCastingBar extends RelativeLayout implements View.OnClickListener, GeckoEventListener  {
+public class MediaCastingBar extends RelativeLayout implements View.OnClickListener, BundleEventListener  {
     private static final String LOGTAG = "GeckoMediaCastingBar";
 
     private TextView mCastingTo;
     private ImageButton mMediaPlay;
     private ImageButton mMediaPause;
     private ImageButton mMediaStop;
 
     private boolean mInflated;
 
     public MediaCastingBar(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        EventDispatcher.getInstance().registerGeckoThreadListener(this,
+        EventDispatcher.getInstance().registerUiThreadListener(this,
             "Casting:Started",
             "Casting:Paused",
             "Casting:Playing",
             "Casting:Stopped");
     }
 
     public void inflateContent() {
         LayoutInflater inflater = LayoutInflater.from(getContext());
@@ -67,65 +67,55 @@ public class MediaCastingBar extends Rel
         setVisibility(VISIBLE);
     }
 
     public void hide() {
         setVisibility(GONE);
     }
 
     public void onDestroy() {
-        EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
+        EventDispatcher.getInstance().unregisterUiThreadListener(this,
             "Casting:Started",
             "Casting:Paused",
             "Casting:Playing",
             "Casting:Stopped");
     }
 
     // View.OnClickListener implementation
     @Override
     public void onClick(View v) {
         final int viewId = v.getId();
 
         if (viewId == R.id.media_play) {
-            GeckoAppShell.notifyObservers("Casting:Play", "");
+            EventDispatcher.getInstance().dispatch("Casting:Play", null);
             mMediaPlay.setVisibility(GONE);
             mMediaPause.setVisibility(VISIBLE);
         } else if (viewId == R.id.media_pause) {
-            GeckoAppShell.notifyObservers("Casting:Pause", "");
+            EventDispatcher.getInstance().dispatch("Casting:Pause", null);
             mMediaPause.setVisibility(GONE);
             mMediaPlay.setVisibility(VISIBLE);
         } else if (viewId == R.id.media_stop) {
-            GeckoAppShell.notifyObservers("Casting:Stop", "");
+            EventDispatcher.getInstance().dispatch("Casting:Stop", null);
         }
     }
 
-    // GeckoEventListener implementation
-    @Override
-    public void handleMessage(final String event, final JSONObject message) {
-        final String device = message.optString("device");
+    @Override // BundleEventListener
+    public void handleMessage(final String event, final GeckoBundle message,
+                              final EventCallback callback) {
+        if ("Casting:Started".equals(event)) {
+            show();
+            mCastingTo.setText(message.getString("device", ""));
+            mMediaPlay.setVisibility(GONE);
+            mMediaPause.setVisibility(VISIBLE);
 
-        ThreadUtils.postToUiThread(new Runnable() {
-            @Override
-            public void run() {
-                if (event.equals("Casting:Started")) {
-                    show();
-                    if (!TextUtils.isEmpty(device)) {
-                        mCastingTo.setText(device);
-                    } else {
-                        // Should not happen
-                        mCastingTo.setText("");
-                        Log.d(LOGTAG, "Device name is empty.");
-                    }
-                    mMediaPlay.setVisibility(GONE);
-                    mMediaPause.setVisibility(VISIBLE);
-                } else if (event.equals("Casting:Paused")) {
-                    mMediaPause.setVisibility(GONE);
-                    mMediaPlay.setVisibility(VISIBLE);
-                } else if (event.equals("Casting:Playing")) {
-                    mMediaPlay.setVisibility(GONE);
-                    mMediaPause.setVisibility(VISIBLE);
-                } else if (event.equals("Casting:Stopped")) {
-                    hide();
-                }
-            }
-        });
+        } else if ("Casting:Paused".equals(event)) {
+            mMediaPause.setVisibility(GONE);
+            mMediaPlay.setVisibility(VISIBLE);
+
+        } else if ("Casting:Playing".equals(event)) {
+            mMediaPlay.setVisibility(GONE);
+            mMediaPause.setVisibility(VISIBLE);
+
+        } else if ("Casting:Stopped".equals(event)) {
+            hide();
+        }
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/RemotePresentationService.java
@@ -1,32 +1,25 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * vim: ts=4 sw=4 expandtab:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
-import org.json.JSONObject;
-import org.json.JSONException;
-
-import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.PresentationView;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.ScreenManagerHelper;
 import org.mozilla.gecko.annotation.JNITarget;
 import org.mozilla.gecko.annotation.ReflectionTarget;
 import org.mozilla.gecko.annotation.WrapForJNI;
 import org.mozilla.gecko.gfx.LayerView;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.NativeEventListener;
-import org.mozilla.gecko.util.NativeJSObject;
 
 import com.google.android.gms.cast.CastMediaControlIntent;
 import com.google.android.gms.cast.CastPresentation;
 import com.google.android.gms.cast.CastRemoteDisplayLocalService;
 import com.google.android.gms.common.ConnectionResult;
 import com.google.android.gms.common.GooglePlayServicesUtil;
 
 import android.app.Activity;
--- a/mobile/android/chrome/content/CastingApps.js
+++ b/mobile/android/chrome/content/CastingApps.js
@@ -83,20 +83,23 @@ var CastingApps = {
     SimpleServiceDiscovery.search(this._interval);
 
     this._castMenuId = NativeWindow.contextmenus.add(
       Strings.browser.GetStringFromName("contextmenu.sendToDevice"),
       this.filterCast,
       this.handleContextMenu.bind(this)
     );
 
-    Services.obs.addObserver(this, "Casting:Play", false);
-    Services.obs.addObserver(this, "Casting:Pause", false);
-    Services.obs.addObserver(this, "Casting:Stop", false);
-    Services.obs.addObserver(this, "Casting:Mirror", false);
+    GlobalEventDispatcher.registerListener(this, [
+      "Casting:Play",
+      "Casting:Pause",
+      "Casting:Stop",
+      "Casting:Mirror",
+    ]);
+
     Services.obs.addObserver(this, "ssdp-service-found", false);
     Services.obs.addObserver(this, "ssdp-service-lost", false);
     Services.obs.addObserver(this, "application-background", false);
     Services.obs.addObserver(this, "application-foreground", false);
 
     BrowserApp.deck.addEventListener("TabSelect", this, true);
     BrowserApp.deck.addEventListener("pageshow", this, true);
     BrowserApp.deck.addEventListener("playing", this, true);
@@ -167,18 +170,18 @@ var CastingApps = {
   isCastingEnabled: function isCastingEnabled() {
     return Services.prefs.getBoolPref("browser.casting.enabled");
   },
 
   isMirroringEnabled: function isMirroringEnabled() {
     return Services.prefs.getBoolPref("browser.mirroring.enabled");
   },
 
-  observe: function (aSubject, aTopic, aData) {
-    switch (aTopic) {
+  onEvent: function (event, message, callback) {
+    switch (event) {
       case "Casting:Play":
         if (this.session && this.session.remoteMedia.status == "paused") {
           this.session.remoteMedia.play();
         }
         break;
       case "Casting:Pause":
         if (this.session && this.session.remoteMedia.status == "started") {
           this.session.remoteMedia.pause();
@@ -187,21 +190,26 @@ var CastingApps = {
       case "Casting:Stop":
         if (this.session) {
           this.closeExternal();
         }
         break;
       case "Casting:Mirror":
         {
           Cu.import("resource://gre/modules/TabMirror.jsm");
-          this.tabMirror = new TabMirror(aData, window);
+          this.tabMirror = new TabMirror(message.id, window);
           NativeWindow.menu.update(this.mirrorStartMenuId, { visible: false });
           NativeWindow.menu.update(this.mirrorStopMenuId, { visible: true });
         }
         break;
+    }
+  },
+
+  observe: function (aSubject, aTopic, aData) {
+    switch (aTopic) {
       case "ssdp-service-found":
         this.serviceAdded(SimpleServiceDiscovery.findServiceForID(aData));
         break;
       case "ssdp-service-lost":
         this.serviceLost(SimpleServiceDiscovery.findServiceForID(aData));
         break;
       case "application-background":
         // Turn off polling while in the background
@@ -746,41 +754,44 @@ var CastingApps = {
 
   // RemoteMedia callback API methods
   onRemoteMediaStart: function(aRemoteMedia) {
     if (!this.session) {
       return;
     }
 
     aRemoteMedia.load(this.session.data);
-    Messaging.sendRequest({ type: "Casting:Started", device: this.session.service.friendlyName });
+    GlobalEventDispatcher.sendRequest({
+        type: "Casting:Started",
+        device: this.session.service.friendlyName,
+    });
 
     let video = this.session.videoRef.get();
     if (video) {
       this._sendEventToVideo(video, { active: true });
       this._updatePageAction(video);
     }
   },
 
   onRemoteMediaStop: function(aRemoteMedia) {
-    Messaging.sendRequest({ type: "Casting:Stopped" });
+    GlobalEventDispatcher.sendRequest({ type: "Casting:Stopped" });
     this._shutdown();
   },
 
   onRemoteMediaStatus: function(aRemoteMedia) {
     if (!this.session) {
       return;
     }
 
     let status = aRemoteMedia.status;
     switch (status) {
       case "started":
-        Messaging.sendRequest({ type: "Casting:Playing" });
+        GlobalEventDispatcher.sendRequest({ type: "Casting:Playing" });
         break;
       case "paused":
-        Messaging.sendRequest({ type: "Casting:Paused" });
+        GlobalEventDispatcher.sendRequest({ type: "Casting:Paused" });
         break;
       case "completed":
         this.closeExternal();
         break;
     }
   }
 };
--- a/mobile/android/modules/MediaPlayerApp.jsm
+++ b/mobile/android/modules/MediaPlayerApp.jsm
@@ -78,18 +78,20 @@ function RemoteMedia(id, listener) {
     Services.tm.mainThread.dispatch((function() {
       this._listener.onRemoteMediaStart(this);
     }).bind(this), Ci.nsIThread.DISPATCH_NORMAL);
   }
 }
 
 RemoteMedia.prototype = {
   shutdown: function shutdown() {
-    Services.obs.removeObserver(this, "MediaPlayer:Playing");
-    Services.obs.removeObserver(this, "MediaPlayer:Paused");
+    EventDispatcher.instance.unregisterListener(this, [
+      "MediaPlayer:Playing",
+      "MediaPlayer:Paused",
+    ]);
 
     this._send("MediaPlayer:End", {}, (result, err) => {
       this._status = "shutdown";
       if ("onRemoteMediaStop" in this._listener) {
         this._listener.onRemoteMediaStop(this);
       }
     });
   },
@@ -121,46 +123,46 @@ RemoteMedia.prototype = {
   load: function load(aData) {
     this._send("MediaPlayer:Load", aData, (result, err) => {
       if (err) {
         Cu.reportError("Can't load " + err);
         this.shutdown();
         return;
       }
 
-      Services.obs.addObserver(this, "MediaPlayer:Playing", false);
-      Services.obs.addObserver(this, "MediaPlayer:Paused", false);
+      EventDispatcher.instance.registerListener(this, [
+        "MediaPlayer:Playing",
+        "MediaPlayer:Paused",
+      ]);
       this._status = "started";
     })
   },
 
   get status() {
     return this._status;
   },
 
-  observe: function (aSubject, aTopic, aData) {
-    switch (aTopic) {
+  onEvent: function (event, message, callback) {
+    switch (event) {
       case "MediaPlayer:Playing":
         if (this._status !== "started") {
           this._status = "started";
           if ("onRemoteMediaStatus" in this._listener) {
             this._listener.onRemoteMediaStatus(this);
           }
         }
         break;
       case "MediaPlayer:Paused":
         if (this._status !== "paused") {
           this._status = "paused";
           if ("onRemoteMediaStatus" in this._listener) {
             this._listener.onRemoteMediaStatus(this);
           }
         }
         break;
-      default:
-        break;
     }
   },
 
   _send: function(msg, data, callback) {
     data.id = this._id;
     send(msg, data, callback);
   }
 }
--- a/mobile/android/modules/TabMirror.jsm
+++ b/mobile/android/modules/TabMirror.jsm
@@ -18,17 +18,19 @@ var failure = function(x) {
 
 var TabMirror = function(deviceId, window) {
 
   this.deviceId = deviceId;
   // Save RTCSessionDescription and RTCIceCandidate for later when the window object is not available.
   this.RTCSessionDescription = window.RTCSessionDescription;
   this.RTCIceCandidate = window.RTCIceCandidate;
 
-  Services.obs.addObserver((aSubject, aTopic, aData) => this._processMessage(aData), "MediaPlayer:Response", false);
+  EventDispatcher.instance.registerListener(
+      (event, data, callback) => this._processMessage(data.message), "MediaPlayer:Response");
+
   this._sendMessage({ start: true });
   this._window = window;
   this._pc = new window.RTCPeerConnection(CONFIG, {});
   if (!this._pc) {
     throw "Failure creating Webrtc object";
   }
 
 };
@@ -53,23 +55,17 @@ TabMirror.prototype = {
           { aspectRatio: this._screenSize.width / this._screenSize.height }
         ]
       }
     };
 
     this._window.navigator.mozGetUserMedia(constraints, this._onGumSuccess.bind(this), this._onGumFailure.bind(this));
   },
 
-  _processMessage: function(data) {
-    if (!data) {
-      return;
-    }
-
-    let msg = JSON.parse(data);
-
+  _processMessage: function(msg) {
     if (!msg) {
       return;
     }
 
     if (msg.sdp && msg.type === "answer") {
       this._processAnswer(msg);
     } else if (msg.type == "size") {
       if (msg.height) {
@@ -139,15 +135,15 @@ TabMirror.prototype = {
   },
 
   stop: function() {
     if (this.deviceId) {
       let obj = {
         type: "MediaPlayer:End",
         id: this.deviceId
       };
-      Services.androidBridge.handleGeckoMessage(obj);
+      EventDispatcher.instance.sendRequest(obj);
     }
   },
 };
 
 
 this.EXPORTED_SYMBOLS = ["TabMirror"];
--- a/widget/android/nsIAndroidBridge.idl
+++ b/widget/android/nsIAndroidBridge.idl
@@ -34,17 +34,17 @@ interface nsIAndroidBrowserApp : nsISupp
 
 [scriptable, uuid(e64c39b8-b8ec-477d-aef5-89d517ff9219)]
 interface nsIAndroidEventCallback : nsISupports
 {
   void onSuccess([optional] in jsval data);
   void onError([optional] in jsval data);
 };
 
-[scriptable, uuid(73569a75-78eb-4c7f-82b9-2d4f5ccf44c3)]
+[scriptable, function, uuid(73569a75-78eb-4c7f-82b9-2d4f5ccf44c3)]
 interface nsIAndroidEventListener : nsISupports
 {
   void onEvent(in AString event,
                [optional] in jsval data,
                [optional] in nsIAndroidEventCallback callback);
 };
 
 [scriptable, uuid(e98bf792-4145-411e-b298-8219d9b03817)]