Bug 1290467 - part3 : notify MediaControlService with event 'MEDIA_PLAYING_CHANGE'. draft
authorAlastor Wu <alwu@mozilla.com>
Fri, 09 Sep 2016 09:50:25 +0800
changeset 411965 495569272fad8e7e03dc7968687eaf48b099b6dd
parent 411964 9d32b976f1fd1dd75c54ef5e5d5f3be883377daf
child 411966 c39577fb5b583b74eca3a7076641ef4ddf7c0035
push id29020
push useralwu@mozilla.com
push dateFri, 09 Sep 2016 01:50:40 +0000
bugs1290467
milestone51.0a1
Bug 1290467 - part3 : notify MediaControlService with event 'MEDIA_PLAYING_CHANGE'. The 'MEDIA_PLAYING_CHANGE' is used for controling media control interface and the 'AUDIO_PLAYING_CHANGE' is used for showing the tab sound indicator. MozReview-Commit-ID: 8hZjC77Ju71
mobile/android/base/java/org/mozilla/gecko/Tab.java
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/java/org/mozilla/gecko/Tab.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tab.java
@@ -79,16 +79,17 @@ public class Tab {
     private Bitmap mThumbnailBitmap;
     private boolean mDesktopMode;
     private boolean mEnteringReaderMode;
     private final Context mAppContext;
     private ErrorType mErrorType = ErrorType.NONE;
     private volatile int mLoadProgress;
     private volatile int mRecordingCount;
     private volatile boolean mIsAudioPlaying;
+    private volatile boolean mIsMediaPlaying;
     private String mMostRecentHomePanel;
     private boolean mShouldShowToolbarWithoutAnimationOnFirstSelection;
 
     /*
      * Bundle containing restore data for the panel referenced in mMostRecentHomePanel. This can be
      * e.g. the most recent folder for the bookmarks panel, or any other state that should be
      * persisted. This is then used e.g. when returning to homepanels via history.
      */
@@ -807,16 +808,36 @@ public class Tab {
             mRecordingCount--;
         }
     }
 
     public boolean isRecording() {
         return mRecordingCount > 0;
     }
 
+    /**
+     * The "MediaPlaying" is used for controling media control interface and
+     * means the tab has playing media.
+     *
+     * @param isMediaPlaying the tab has any playing media or not
+     */
+    public void setIsMediaPlaying(boolean isMediaPlaying) {
+        mIsMediaPlaying = isMediaPlaying;
+    }
+
+    public boolean isMediaPlaying() {
+        return mIsMediaPlaying;
+    }
+
+    /**
+     * The "AudioPlaying" is used for showing the tab sound indicator and means
+     * the tab has playing media and the media is audible.
+     *
+     * @param isAudioPlaying the tab has any audible playing media or not
+     */
     public void setIsAudioPlaying(boolean isAudioPlaying) {
         mIsAudioPlaying = isAudioPlaying;
     }
 
     public boolean isAudioPlaying() {
         return mIsAudioPlaying;
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -118,17 +118,18 @@ public class Tabs implements GeckoEventL
             "Link:Favicon",
             "Link:Touchicon",
             "Link:Feed",
             "Link:OpenSearch",
             "DesktopMode:Changed",
             "Tab:ViewportMetadata",
             "Tab:StreamStart",
             "Tab:StreamStop",
-            "Tab:AudioPlayingChange");
+            "Tab:AudioPlayingChange",
+            "Tab:MediaPlaybackChange");
 
         mPrivateClearColor = Color.RED;
 
     }
 
     public synchronized void attachToContext(Context context, LayerView layerView) {
         final Context appContext = context.getApplicationContext();
         if (mAppContext == appContext) {
@@ -561,16 +562,19 @@ public class Tabs implements GeckoEventL
                 tab.setRecording(true);
                 notifyListeners(tab, TabEvents.RECORDING_CHANGE);
             } else if (event.equals("Tab:StreamStop")) {
                 tab.setRecording(false);
                 notifyListeners(tab, TabEvents.RECORDING_CHANGE);
             } else if (event.equals("Tab:AudioPlayingChange")) {
                 tab.setIsAudioPlaying(message.getBoolean("isAudioPlaying"));
                 notifyListeners(tab, TabEvents.AUDIO_PLAYING_CHANGE);
+            } else if (event.equals("Tab:MediaPlaybackChange")) {
+                tab.setIsMediaPlaying(message.getBoolean("active"));
+                notifyListeners(tab, TabEvents.MEDIA_PLAYING_CHANGE);
             }
 
         } catch (Exception e) {
             Log.w(LOGTAG, "handleMessage threw for " + event, e);
         }
     }
 
     public void refreshThumbnails() {
@@ -621,16 +625,17 @@ public class Tabs implements GeckoEventL
         SECURITY_CHANGE,
         DESKTOP_MODE_CHANGE,
         VIEWPORT_CHANGE,
         RECORDING_CHANGE,
         BOOKMARK_ADDED,
         BOOKMARK_REMOVED,
         AUDIO_PLAYING_CHANGE,
         OPENED_FROM_TABS_TRAY,
+        MEDIA_PLAYING_CHANGE,
     }
 
     public void notifyListeners(Tab tab, TabEvents msg) {
         notifyListeners(tab, msg, "");
     }
 
     public void notifyListeners(final Tab tab, final TabEvents msg, final String data) {
         if (tab == null &&
--- a/mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
@@ -99,35 +99,31 @@ public class MediaControlService extends
     }
 
     @Override
     public void onTabChanged(Tab tab, Tabs.TabEvents msg, String data) {
         if (!mIsInitMediaSession) {
             return;
         }
 
+        final Tab playingTab = mTabReference.get();
         switch (msg) {
-            case AUDIO_PLAYING_CHANGE:
-                if (tab == mTabReference.get()) {
-                    return;
+            case MEDIA_PLAYING_CHANGE:
+                if (playingTab != tab && tab.isMediaPlaying()) {
+                    mTabReference = new WeakReference<>(tab);
+                    mController.getTransportControls().sendCustomAction(ACTION_START, null);
+                } else if (playingTab == tab && !tab.isMediaPlaying()) {
+                    mController.getTransportControls().stop();
                 }
-
-                if (!tab.isAudioPlaying()) {
-                    return;
-                }
-
-                mTabReference = new WeakReference<>(tab);
-                notifyControlInterfaceChanged(ACTION_PAUSE);
                 break;
 
             case CLOSED:
-                final Tab playingTab = mTabReference.get();
                 if (playingTab == null || playingTab == tab) {
-                    // The playing tab disappeared or was closed. Remove the controls and stop the service.
-                    notifyControlInterfaceChanged(ACTION_REMOVE_CONTROL);
+                    // Remove the controls when the playing tab disappeared or was closed.
+                    mController.getTransportControls().stop();
                 }
                 break;
         }
     }
 
     private boolean isAndroidVersionLollopopOrHigher() {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
     }
@@ -231,16 +227,17 @@ public class MediaControlService extends
 
             @Override
             public void onStop() {
                 Log.d(LOGTAG, "Controller, onStop");
                 super.onStop();
                 notifyControlInterfaceChanged(ACTION_STOP);
                 notifyObservers("MediaControl", "mediaControlStopped");
                 mActionState = ACTION_STOP;
+                mTabReference = new WeakReference<>(null);
                 stopSelf();
             }
         });
         mIsInitMediaSession = true;
     }
 
     private void notifyObservers(String topic, String data) {
         GeckoAppShell.notifyObservers(topic, data);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3561,16 +3561,17 @@ Tab.prototype = {
     this.browser.addEventListener("TabPreZombify", this, true);
 
     // Note that the XBL binding is untrusted
     this.browser.addEventListener("PluginBindingAttached", this, true, true);
     this.browser.addEventListener("VideoBindingAttached", this, true, true);
     this.browser.addEventListener("VideoBindingCast", this, true, true);
 
     Services.obs.addObserver(this, "before-first-paint", false);
+    Services.obs.addObserver(this, "media-playback", false);
 
     // Always intialise new tabs with basic session store data to avoid
     // problems with functions that always expect it to be present
     this.browser.__SS_data = {
       entries: [{
         url: aURL,
         title: truncate(title, MAX_TITLE_LENGTH)
       }],
@@ -3670,16 +3671,17 @@ Tab.prototype = {
     this.browser.removeEventListener("MozApplicationManifest", this, true);
     this.browser.removeEventListener("TabPreZombify", this, true);
 
     this.browser.removeEventListener("PluginBindingAttached", this, true, true);
     this.browser.removeEventListener("VideoBindingAttached", this, true, true);
     this.browser.removeEventListener("VideoBindingCast", this, true, true);
 
     Services.obs.removeObserver(this, "before-first-paint");
+    Services.obs.removeObserver(this, "media-playback", false);
 
     // Make sure the previously selected panel remains selected. The selected panel of a deck is
     // not stable when panels are removed.
     let selectedPanel = BrowserApp.deck.selectedPanel;
     BrowserApp.deck.removeChild(this.browser);
     BrowserApp.deck.selectedPanel = selectedPanel;
 
     this.browser = null;
@@ -4447,16 +4449,31 @@ Tab.prototype = {
           }
           this.contentDocumentIsDisplayed = true;
 
           if (contentDocument instanceof Ci.nsIImageDocument) {
             contentDocument.shrinkToFit();
           }
         }
         break;
+
+      case "media-playback":
+        if (!aSubject) {
+          return;
+        }
+
+        let winId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+        if (this.browser.outerWindowID == winId) {
+          Messaging.sendRequest({
+            type: "Tab:MediaPlaybackChange",
+            tabID: this.id,
+            active: aData === "active"
+          });
+        }
+        break;
     }
   },
 
   // nsIBrowserTab
   get window() {
     if (!this.browser)
       return null;
     return this.browser.contentWindow;