Bug 1240423 - implement the remote media-control front-end
MozReview-Commit-ID: KDLUhVUOarR
--- 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;