Bug 1264901 - implement the remote media-control front-end draft
authorScott Wu <scottcwwu@gmail.com>
Tue, 12 Apr 2016 17:50:57 +0800
changeset 351961 cd1089d8e463d15e71991bc830b095acc8abd8e9
parent 351943 3aa05c6558fbe8c365a2ba812c890934df79658c
child 518536 fc5b6039272294aa60dbe826b79063f8eb32f850
push id15566
push userbmo:scwwu@mozilla.com
push dateFri, 15 Apr 2016 09:54:01 +0000
bugs1264901
milestone47.0a1
Bug 1264901 - implement the remote media-control front-end MozReview-Commit-ID: KDLUhVUOarR
mobile/android/base/java/org/mozilla/gecko/Tabs.java
mobile/android/base/java/org/mozilla/gecko/media/AudioFocusAgent.java
mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabStrip.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabsGridLayout.java
mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java
+++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java
@@ -582,18 +582,23 @@ public class Tabs implements GeckoEventL
             } else if (event.equals("Tab:StreamStart")) {
                 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")) {
                  // TODO : remove icon when user pause from media control.
-                tab.setIsAudioPlaying(message.getBoolean("isAudioPlaying"));
-                notifyListeners(tab, TabEvents.AUDIO_PLAYING_CHANGE);
+                boolean isAudioPlaying = message.getBoolean("isAudioPlaying");
+                tab.setIsAudioPlaying(isAudioPlaying);
+                if (isAudioPlaying) {
+                    notifyListeners(tab, TabEvents.AUDIO_PLAYING_START);
+                } else {
+                    notifyListeners(tab, TabEvents.AUDIO_PLAYING_STOP);
+                }
             }
 
         } catch (Exception e) {
             Log.w(LOGTAG, "handleMessage threw for " + event, e);
         }
     }
 
     public void refreshThumbnails() {
@@ -644,17 +649,18 @@ public class Tabs implements GeckoEventL
         SECURITY_CHANGE,
         DESKTOP_MODE_CHANGE,
         VIEWPORT_CHANGE,
         RECORDING_CHANGE,
         BOOKMARK_ADDED,
         BOOKMARK_REMOVED,
         READING_LIST_ADDED,
         READING_LIST_REMOVED,
-        AUDIO_PLAYING_CHANGE,
+        AUDIO_PLAYING_START,
+        AUDIO_PLAYING_STOP,
     }
 
     public void notifyListeners(Tab tab, TabEvents msg) {
         notifyListeners(tab, msg, "");
     }
 
     public void notifyListeners(final Tab tab, final TabEvents msg, final Object data) {
         if (tab == null &&
--- a/mobile/android/base/java/org/mozilla/gecko/media/AudioFocusAgent.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/AudioFocusAgent.java
@@ -102,28 +102,28 @@ public class AudioFocusAgent {
                                                      AudioManager.AUDIOFOCUS_GAIN);
 
         String focusMsg = (result == AudioManager.AUDIOFOCUS_GAIN) ?
             "AudioFocus request granted" : "AudioFoucs request failed";
         Log.d(LOGTAG, focusMsg);
         if (result == AudioManager.AUDIOFOCUS_GAIN) {
             // TODO : android version check
             // TODO : use pref
-            notifyMediaControlService(MediaControlService.ACTION_PLAY);
+            notifyMediaControlService(MediaControlService.FOCUS_ACTION_PLAY);
         }
     }
 
     private void abandonAudioFocusIfNeeded() {
         if (!isLastAudibleElement()) {
             return;
         }
 
         Log.d(LOGTAG, "Abandon AudioFocus");
         mAudioManager.abandonAudioFocus(mAfChangeListener);
-        notifyMediaControlService(MediaControlService.ACTION_STOP);
+        notifyMediaControlService(MediaControlService.FOCUS_ACTION_STOP);
     }
 
     private boolean isFirstAudibleElement() {
         return (++mAudibleElementCounts == 1);
     }
 
     private boolean isLastAudibleElement() {
         return (--mAudibleElementCounts == 0);
--- a/mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
@@ -1,35 +1,44 @@
 package org.mozilla.gecko.media;
 
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.Tab;
 
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.os.IBinder;
 import android.util.Log;
 import android.R;
 
-public class MediaControlService extends Service {
+public class MediaControlService extends Service
+                                 implements Tabs.OnTabsChangedListener {
     private static final String LOGTAG = "MediaControlService";
 
     public static final String ACTION_PLAY = "action_play";
     public static final String ACTION_PAUSE = "action_pause";
     public static final String ACTION_STOP = "action_stop";
+    public static final String FOCUS_ACTION_PLAY = "focus_action_play";
+    public static final String FOCUS_ACTION_STOP = "focus_action_stop";
+
+    public static Tab mTab = null;
 
     private static final int MEDIA_CONTROL_ID = 1;
 
     private MediaSession mSession;
     private MediaController mController;
 
     @Override
     public void onCreate() {
@@ -58,22 +67,40 @@ public class MediaControlService extends
         return super.onUnbind(intent);
     }
 
     @Override
     public void onTaskRemoved(Intent rootIntent) {
         stopSelf();
     }
 
+    @Override
+    public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
+        switch (msg) {
+            case AUDIO_PLAYING_START:
+                mTab = tab;
+                notifyMediaAction(ACTION_PAUSE);
+                break;
+        }
+    }
+
     private void handleIntent(Intent intent) {
         if(intent == null || intent.getAction() == null) {
             return;
         }
 
         switch (intent.getAction()) {
+            case FOCUS_ACTION_PLAY :
+                Tabs.registerOnTabsChangedListener(this);
+                mController.getTransportControls().play();
+                break;
+            case FOCUS_ACTION_STOP :
+                Tabs.unregisterOnTabsChangedListener(this);
+                mController.getTransportControls().stop();
+                break;
             case ACTION_PLAY :
                 mController.getTransportControls().play();
                 break;
             case ACTION_PAUSE :
                 mController.getTransportControls().pause();
                 break;
             case ACTION_STOP :
                 mController.getTransportControls().stop();
@@ -122,50 +149,62 @@ public class MediaControlService extends
         if (action.equalsIgnoreCase(ACTION_STOP)) {
             notificationManager.cancel(MEDIA_CONTROL_ID);
             return;
         }
         notificationManager.notify(MEDIA_CONTROL_ID, getNotification(action));
     }
 
     private Notification getNotification(String action) {
-        // TODO : replace with website name, content and our icon.
         return new Notification.Builder(this)
             .setSmallIcon(android.R.drawable.ic_media_play)
-            .setContentTitle("Media Title")
-            .setContentText("Media Artist")
+            .setLargeIcon(getFavicon())
+            .setContentTitle(getTitle())
+            .setContentText(getURL())
             .setDeleteIntent(getDeletePendingIntent())
             .setContentIntent(getClickPendingIntent())
             .setStyle(getMediaStyle())
             .addAction(getAction(action))
             .build();
     }
 
     private Notification.Action getAction(String action) {
         int icon = getActionIcon(action);
-        String tittle = getActionTittle(action);
+        String title = getActionTitle(action);
 
         Intent intent = new Intent(getApplicationContext(), MediaControlService.class);
         intent.setAction(action);
         PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
-        return new Notification.Action.Builder(icon, tittle, pendingIntent).build();
+        return new Notification.Action.Builder(icon, title, pendingIntent).build();
     }
 
     private int getActionIcon(String action) {
         switch (action) {
             case ACTION_PLAY :
                 return android.R.drawable.ic_media_play;
             case ACTION_PAUSE :
                 return android.R.drawable.ic_media_pause;
             default:
                 return 0;
         }
     }
 
-    private String getActionTittle(String action) {
+    private Bitmap getFavicon() {
+        return mTab == null ? Favicons.defaultFavicon : mTab.getFavicon();
+    }
+
+    private String getTitle() {
+        return mTab == null ? "Media Title" : mTab.getTitle();
+    }
+
+    private String getURL() {
+        return mTab == null ? "" : mTab.getURL();
+    }
+
+    private String getActionTitle(String action) {
         switch (action) {
             case ACTION_PLAY :
                 return "Play";
             case ACTION_PAUSE :
                 return "Pause";
             default:
                 return null;
         }
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabStrip.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabStrip.java
@@ -132,17 +132,18 @@ public class TabStrip extends ThemedLine
                     // Update the selected position, then fall through...
                     tabStripView.selectTab(tab);
                     setPrivateMode(tab.isPrivate());
                 case UNSELECTED:
                     // We just need to update the style for the unselected tab...
                 case TITLE:
                 case FAVICON:
                 case RECORDING_CHANGE:
-                case AUDIO_PLAYING_CHANGE:
+                case AUDIO_PLAYING_START:
+                case AUDIO_PLAYING_STOP:
                     tabStripView.updateTab(tab);
                     break;
             }
         }
     }
 
     @Override
     public void refresh() {
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsGridLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsGridLayout.java
@@ -248,17 +248,18 @@ class TabsGridLayout extends GridView
             case SELECTED:
                 // Update the selected position, then fall through...
                 updateSelectedPosition();
             case UNSELECTED:
                 // We just need to update the style for the unselected tab...
             case THUMBNAIL:
             case TITLE:
             case RECORDING_CHANGE:
-            case AUDIO_PLAYING_CHANGE:
+            case AUDIO_PLAYING_START:
+            case AUDIO_PLAYING_STOP:
                 View view = getChildAt(tabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
                 if (view == null)
                     return;
 
                 ((TabsLayoutItemView) view).assignValues(tab);
                 break;
         }
     }
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsListLayout.java
@@ -128,17 +128,18 @@ class TabsListLayout extends TwoWayView
             case SELECTED:
                 // Update the selected position, then fall through...
                 updateSelectedPosition();
             case UNSELECTED:
                 // We just need to update the style for the unselected tab...
             case THUMBNAIL:
             case TITLE:
             case RECORDING_CHANGE:
-            case AUDIO_PLAYING_CHANGE:
+            case AUDIO_PLAYING_START:
+            case AUDIO_PLAYING_STOP:
                 View view = getChildAt(tabsAdapter.getPositionForTab(tab) - getFirstVisiblePosition());
                 if (view == null) {
                     return;
                 }
 
                 TabsLayoutItemView item = (TabsLayoutItemView) view;
                 item.assignValues(tab);
                 break;