--- a/mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/MediaControlService.java
@@ -41,33 +41,42 @@ public class MediaControlService extends
public static final String ACTION_PAUSE = "action_pause";
public static final String ACTION_STOP = "action_stop";
public static final String ACTION_RESUME_BY_AUDIO_FOCUS = "action_resume_audio_focus";
public static final String ACTION_PAUSE_BY_AUDIO_FOCUS = "action_pause_audio_focus";
private static final int MEDIA_CONTROL_ID = 1;
private static final String MEDIA_CONTROL_PREF = "dom.audiochannel.mediaControl";
- private String mActionState = ACTION_STOP;
-
private MediaSession mSession;
private MediaController mController;
private HeadSetStateReceiver mHeadSetStateReceiver;
private PrefsHelper.PrefHandler mPrefsObserver;
private final String[] mPrefs = { MEDIA_CONTROL_PREF };
private boolean mInitialize = false;
private boolean mIsMediaControlPrefOn = true;
private static WeakReference<Tab> mTabReference = new WeakReference<>(null);
private int minCoverSize;
private int coverSize;
+ /**
+ * Internal state of MediaControlService, to indicate it is playing media, or paused...etc.
+ */
+ private State mMediaState = State.STOPPED;
+
+ protected enum State {
+ PLAYING,
+ PAUSED,
+ STOPPED
+ }
+
@Override
public void onCreate() {
initialize();
mHeadSetStateReceiver = new HeadSetStateReceiver().registerReceiver(getApplicationContext());
}
@Override
public void onDestroy() {
@@ -105,47 +114,45 @@ public class MediaControlService extends
final Tab playingTab = mTabReference.get();
switch (msg) {
case MEDIA_PLAYING_CHANGE:
// The 'MEDIA_PLAYING_CHANGE' would only be received when the
// media starts or ends.
if (playingTab != tab && tab.isMediaPlaying()) {
mTabReference = new WeakReference<>(tab);
- notifyControlInterfaceChanged(ACTION_PAUSE);
+ setState(State.PLAYING);
} else if (playingTab == tab && !tab.isMediaPlaying()) {
- notifyControlInterfaceChanged(ACTION_STOP);
mTabReference = new WeakReference<>(null);
+ setState(State.STOPPED);
}
break;
case MEDIA_PLAYING_RESUME:
// user resume the paused-by-control media from page so that we
// should make the control interface consistent.
if (playingTab == tab && !isMediaPlaying()) {
- notifyControlInterfaceChanged(ACTION_PAUSE);
+ setState(State.PLAYING);
}
break;
case CLOSED:
if (playingTab == null || playingTab == tab) {
// Remove the controls when the playing tab disappeared or was closed.
- notifyControlInterfaceChanged(ACTION_STOP);
+ setState(State.STOPPED);
}
break;
case FAVICON:
if (playingTab == tab) {
- final String actionForPendingIntent = isMediaPlaying() ?
- ACTION_PAUSE : ACTION_RESUME;
- notifyControlInterfaceChanged(actionForPendingIntent);
+ setState(isMediaPlaying() ? State.PLAYING : State.PAUSED);
}
break;
}
}
private boolean isMediaPlaying() {
- return mActionState.equals(ACTION_RESUME);
+ return mMediaState.equals(State.PLAYING);
}
private void initialize() {
if (mInitialize ||
!isAndroidVersionLollopopOrHigher()) {
return;
}
@@ -161,34 +168,34 @@ public class MediaControlService extends
}
private void shutdown() {
if (!mInitialize) {
return;
}
Log.d(LOGTAG, "shutdown");
- notifyControlInterfaceChanged(ACTION_STOP);
+ setState(State.STOPPED);
PrefsHelper.removeObserver(mPrefsObserver);
Tabs.unregisterOnTabsChangedListener(this);
mInitialize = false;
stopSelf();
}
private boolean isAndroidVersionLollopopOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}
private void handleIntent(Intent intent) {
if (intent == null || intent.getAction() == null || !mInitialize) {
return;
}
- Log.d(LOGTAG, "HandleIntent, action = " + intent.getAction() + ", actionState = " + mActionState);
+ Log.d(LOGTAG, "HandleIntent, action = " + intent.getAction() + ", mediaState = " + mMediaState);
switch (intent.getAction()) {
case ACTION_INIT :
// This action is used to create a service and do the initialization,
// the actual operation would be executed via control interface's
// pending intent.
break;
case ACTION_RESUME :
mController.getTransportControls().play();
@@ -212,25 +219,24 @@ public class MediaControlService extends
mPrefsObserver = new PrefsHelper.PrefHandlerBase() {
@Override
public void prefValue(String pref, boolean value) {
if (pref.equals(MEDIA_CONTROL_PREF)) {
mIsMediaControlPrefOn = value;
// If media is playing, we just need to create or remove
// the media control interface.
- if (mActionState.equals(ACTION_RESUME)) {
- notifyControlInterfaceChanged(mIsMediaControlPrefOn ?
- ACTION_PAUSE : ACTION_STOP);
+ if (mMediaState.equals(State.PLAYING)) {
+ setState(mIsMediaControlPrefOn ? State.PLAYING : State.STOPPED);
}
// If turn off pref during pausing, except removing media
// interface, we also need to stop the service and notify
// gecko about that.
- if (mActionState.equals(ACTION_PAUSE) &&
+ if (mMediaState.equals(State.PAUSED) &&
!mIsMediaControlPrefOn) {
Intent intent = new Intent(getApplicationContext(), MediaControlService.class);
intent.setAction(ACTION_STOP);
handleIntent(intent);
}
}
}
};
@@ -244,111 +250,99 @@ public class MediaControlService extends
mController = new MediaController(getApplicationContext(),
mSession.getSessionToken());
mSession.setCallback(new MediaSession.Callback() {
@Override
public void onCustomAction(String action, Bundle extras) {
if (action.equals(ACTION_PAUSE_BY_AUDIO_FOCUS)) {
Log.d(LOGTAG, "Controller, pause by audio focus changed");
- notifyControlInterfaceChanged(ACTION_RESUME);
+ setState(State.PAUSED);
} else if (action.equals(ACTION_RESUME_BY_AUDIO_FOCUS)) {
Log.d(LOGTAG, "Controller, resume by audio focus changed");
- notifyControlInterfaceChanged(ACTION_PAUSE);
+ setState(State.PLAYING);
}
}
@Override
public void onPlay() {
Log.d(LOGTAG, "Controller, onPlay");
super.onPlay();
- notifyControlInterfaceChanged(ACTION_PAUSE);
+ setState(State.PLAYING);
notifyObservers("MediaControl", "resumeMedia");
// To make sure we always own audio focus during playing.
AudioFocusAgent.notifyStartedPlaying();
}
@Override
public void onPause() {
Log.d(LOGTAG, "Controller, onPause");
super.onPause();
- notifyControlInterfaceChanged(ACTION_RESUME);
+ setState(State.PAUSED);
notifyObservers("MediaControl", "mediaControlPaused");
AudioFocusAgent.notifyStoppedPlaying();
}
@Override
public void onStop() {
Log.d(LOGTAG, "Controller, onStop");
super.onStop();
- notifyControlInterfaceChanged(ACTION_STOP);
+ setState(State.STOPPED);
notifyObservers("MediaControl", "mediaControlStopped");
mTabReference = new WeakReference<>(null);
}
});
}
private void notifyObservers(String topic, String data) {
GeckoAppShell.notifyObservers(topic, data);
}
- private boolean isNeedToRemoveControlInterface(String action) {
- return action.equals(ACTION_STOP);
+ private boolean isNeedToRemoveControlInterface(State state) {
+ return state.equals(State.STOPPED);
}
- private void notifyControlInterfaceChanged(final String uiAction) {
+ private void setState(State newState) {
+ mMediaState = newState;
+ onStateChanged();
+ }
+
+ private void onStateChanged() {
if (!mInitialize) {
return;
}
- Log.d(LOGTAG, "notifyControlInterfaceChanged, action = " + uiAction);
+ Log.d(LOGTAG, "onStateChanged, state = " + mMediaState);
- if (isNeedToRemoveControlInterface(uiAction)) {
+ if (isNeedToRemoveControlInterface(mMediaState)) {
stopForeground(false);
NotificationManagerCompat.from(this).cancel(MEDIA_CONTROL_ID);
- setActionState(uiAction);
return;
}
if (!mIsMediaControlPrefOn) {
return;
}
final Tab tab = mTabReference.get();
if (tab == null) {
return;
}
- setActionState(uiAction);
-
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
- updateNotification(tab, uiAction);
+ updateNotification(tab);
}
});
}
- private void setActionState(final String uiAction) {
- switch (uiAction) {
- case ACTION_PAUSE:
- mActionState = ACTION_RESUME;
- break;
- case ACTION_RESUME:
- mActionState = ACTION_PAUSE;
- break;
- case ACTION_STOP:
- mActionState = ACTION_STOP;
- break;
- }
- }
-
- private void updateNotification(Tab tab, String action) {
+ protected void updateNotification(Tab tab) {
ThreadUtils.assertNotOnUiThread();
final Notification.MediaStyle style = new Notification.MediaStyle();
style.setShowActionsInCompactView(0);
final boolean isPlaying = isMediaPlaying();
final int visibility = tab.isPrivate() ?
Notification.VISIBILITY_PRIVATE : Notification.VISIBILITY_PUBLIC;
@@ -356,37 +350,38 @@ public class MediaControlService extends
final Notification notification = new Notification.Builder(this)
.setSmallIcon(R.drawable.flat_icon)
.setLargeIcon(generateCoverArt(tab))
.setContentTitle(tab.getTitle())
.setContentText(tab.getURL())
.setContentIntent(createContentIntent(tab.getId()))
.setDeleteIntent(createDeleteIntent())
.setStyle(style)
- .addAction(createNotificationAction(action))
+ .addAction(createNotificationAction())
.setOngoing(isPlaying)
.setShowWhen(false)
.setWhen(0)
.setVisibility(visibility)
.build();
if (isPlaying) {
startForeground(MEDIA_CONTROL_ID, notification);
} else {
stopForeground(false);
NotificationManagerCompat.from(this)
.notify(MEDIA_CONTROL_ID, notification);
}
}
- private Notification.Action createNotificationAction(String action) {
- boolean isPlayAction = action.equals(ACTION_RESUME);
+ private Notification.Action createNotificationAction() {
+ boolean isPlayAction = mMediaState.equals(State.PAUSED);
int icon = isPlayAction ? R.drawable.ic_media_play : R.drawable.ic_media_pause;
String title = getString(isPlayAction ? R.string.media_play : R.string.media_pause);
+ String action = isPlayAction ? ACTION_RESUME : ACTION_PAUSE;
final Intent intent = new Intent(getApplicationContext(), MediaControlService.class);
intent.setAction(action);
final PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(), 1, intent, 0);
//noinspection deprecation - The new constructor is only for API > 23
return new Notification.Action.Builder(icon, title, pendingIntent).build();
}