fune/mobile/android/base/java/org/mozilla/gecko/MediaPlayerManager.java
Michael Kaply 27dd77631b Bug 1342288 - Remove PresentationMediaPlayerManager. r=snorp
MozReview-Commit-ID: 4H46ahWTntR

--HG--
extra : rebase_source : 647b6f702b14bbb13bc44812870069164ccbddfd
2018-03-06 16:33:42 -06:00

319 lines
13 KiB
Java

/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* 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 android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
import com.google.android.gms.cast.CastMediaControlIntent;
import org.json.JSONObject;
import org.mozilla.gecko.annotation.ReflectionTarget;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Manages a list of GeckoMediaPlayers methods (i.e. Chromecast/Miracast). Routes messages
* from Gecko to the correct caster based on the id of the display
*/
public class MediaPlayerManager extends Fragment implements BundleEventListener {
/**
* Create a new instance of DetailsFragment, initialized to
* show the text at 'index'.
*/
private static MediaPlayerManager instance = null;
@ReflectionTarget
public static MediaPlayerManager getInstance() {
if (instance != null) {
return instance;
}
return new MediaPlayerManager();
}
private static final String LOGTAG = "GeckoMediaPlayerManager";
protected boolean isPresentationMode = false; // Used to prevent mirroring when Presentation API is used.
@ReflectionTarget
public static final String MEDIA_PLAYER_TAG = "MPManagerFragment";
private static final boolean SHOW_DEBUG = false;
// Simplified debugging interfaces
private static void debug(String msg, Exception e) {
if (SHOW_DEBUG) {
Log.e(LOGTAG, msg, e);
}
}
private static void debug(String msg) {
if (SHOW_DEBUG) {
Log.d(LOGTAG, msg);
}
}
protected MediaRouter mediaRouter = null;
protected final Map<String, GeckoMediaPlayer> players = new HashMap<String, GeckoMediaPlayer>();
protected final Map<String, GeckoPresentationDisplay> displays = new HashMap<String, GeckoPresentationDisplay>(); // used for Presentation API
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventDispatcher.getInstance().registerGeckoThreadListener(this,
"MediaPlayer:Load",
"MediaPlayer:Start",
"MediaPlayer:Stop",
"MediaPlayer:Play",
"MediaPlayer:Pause",
"MediaPlayer:End",
"MediaPlayer:Message",
"AndroidCastDevice:Start",
"AndroidCastDevice:Stop",
"AndroidCastDevice:SyncDevice");
}
@Override
public void onDestroy() {
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"MediaPlayer:Load",
"MediaPlayer:Start",
"MediaPlayer:Stop",
"MediaPlayer:Play",
"MediaPlayer:Pause",
"MediaPlayer:End",
"MediaPlayer:Message",
"AndroidCastDevice:Start",
"AndroidCastDevice:Stop",
"AndroidCastDevice:SyncDevice");
super.onDestroy();
}
// BundleEventListener implementation
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
debug(event);
if (event.startsWith("MediaPlayer:")) {
final GeckoMediaPlayer player = players.get(message.getString("id"));
if (player == null) {
Log.e(LOGTAG, "Couldn't find a player for this id: " + message.getString("id") + " for message: " + event);
if (callback != null) {
callback.sendError(null);
}
return;
}
if ("MediaPlayer:Play".equals(event)) {
player.play(callback);
} else if ("MediaPlayer:Start".equals(event)) {
player.start(callback);
} else if ("MediaPlayer:Stop".equals(event)) {
player.stop(callback);
} else if ("MediaPlayer:Pause".equals(event)) {
player.pause(callback);
} else if ("MediaPlayer:End".equals(event)) {
player.end(callback);
} else if ("MediaPlayer:Message".equals(event) && message.containsKey("data")) {
player.message(message.getString("data"), callback);
} else if ("MediaPlayer:Load".equals(event)) {
final String url = message.getString("source", "");
final String type = message.getString("type", "video/mp4");
final String title = message.getString("title", "");
player.load(title, url, type, callback);
}
}
if (event.startsWith("AndroidCastDevice:")) {
if ("AndroidCastDevice:Start".equals(event)) {
final GeckoPresentationDisplay display = displays.get(message.getString("id"));
if (display == null) {
Log.e(LOGTAG, "Couldn't find a display for this id: " + message.getString("id") + " for message: " + event);
return;
}
display.start(callback);
} else if ("AndroidCastDevice:Stop".equals(event)) {
final GeckoPresentationDisplay display = displays.get(message.getString("id"));
if (display == null) {
Log.e(LOGTAG, "Couldn't find a display for this id: " + message.getString("id") + " for message: " + event);
return;
}
display.stop(callback);
} else if ("AndroidCastDevice:SyncDevice".equals(event)) {
for (Map.Entry<String, GeckoPresentationDisplay> entry : displays.entrySet()) {
GeckoPresentationDisplay display = entry.getValue();
final GeckoBundle data = display.toBundle();
if (data == null) {
break;
}
EventDispatcher.getInstance().dispatch("AndroidCastDevice:Added", data);
}
}
}
}
private final MediaRouter.Callback callback =
new MediaRouter.Callback() {
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
debug("onRouteRemoved: route=" + route);
// Remove from media player list.
players.remove(route.getId());
final GeckoBundle data = new GeckoBundle(1);
data.putString("id", route.getId());
EventDispatcher.getInstance().dispatch("MediaPlayer:Removed", data);
updatePresentation();
// Remove from presentation display list.
if (displays.remove(route.getId()) != null) {
EventDispatcher.getInstance().dispatch("AndroidCastDevice:Removed", data);
}
}
@Override
public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
updatePresentation();
}
@Override
public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
updatePresentation();
}
@Override
public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) {
updatePresentation();
}
@Override
public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) {
}
@Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
debug("onRouteAdded: route=" + route);
final GeckoMediaPlayer player = getMediaPlayerForRoute(route);
saveAndNotifyOfPlayer("MediaPlayer:Added", route, player);
updatePresentation();
final GeckoPresentationDisplay display = getPresentationDisplayForRoute(route);
saveAndNotifyOfDisplay("AndroidCastDevice:Added", route, display);
}
@Override
public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
debug("onRouteChanged: route=" + route);
final GeckoMediaPlayer player = players.get(route.getId());
saveAndNotifyOfPlayer("MediaPlayer:Changed", route, player);
updatePresentation();
// onRouteAdded might not be called in some devices.
final GeckoPresentationDisplay display = getPresentationDisplayForRoute(route);
saveAndNotifyOfDisplay("AndroidCastDevice:Changed", route, display);
}
private void saveAndNotifyOfPlayer(final String eventName,
MediaRouter.RouteInfo route,
final GeckoMediaPlayer player) {
if (player == null) {
return;
}
final GeckoBundle data = player.toBundle();
if (data == null) {
return;
}
players.put(route.getId(), player);
EventDispatcher.getInstance().dispatch(eventName, data);
}
private void saveAndNotifyOfDisplay(final String eventName,
MediaRouter.RouteInfo route,
final GeckoPresentationDisplay display) {
if (display == null) {
return;
}
final GeckoBundle data = display.toBundle();
if (data == null) {
return;
}
displays.put(route.getId(), display);
EventDispatcher.getInstance().dispatch(eventName, data);
}
};
private GeckoMediaPlayer getMediaPlayerForRoute(MediaRouter.RouteInfo route) {
try {
if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
return new ChromeCastPlayer(getActivity(), route);
}
} catch (Exception ex) {
debug("Error handling presentation", ex);
}
return null;
}
private GeckoPresentationDisplay getPresentationDisplayForRoute(MediaRouter.RouteInfo route) {
try {
if (route.supportsControlCategory(CastMediaControlIntent.categoryForCast(ChromeCastDisplay.REMOTE_DISPLAY_APP_ID))) {
return new ChromeCastDisplay(getActivity(), route);
}
} catch (Exception ex) {
debug("Error handling presentation", ex);
}
return null;
}
@Override
public void onPause() {
super.onPause();
mediaRouter.removeCallback(callback);
mediaRouter = null;
}
@Override
public void onResume() {
super.onResume();
// The mediaRouter shouldn't exist here, but this is a nice safety check.
if (mediaRouter != null) {
return;
}
mediaRouter = MediaRouter.getInstance(getActivity());
final MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCastPlayer.MIRROR_RECEIVER_APP_ID))
.addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCastDisplay.REMOTE_DISPLAY_APP_ID))
.build();
mediaRouter.addCallback(selectorBuilder, callback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
}
public void setPresentationMode(boolean isPresentationMode) {
this.isPresentationMode = isPresentationMode;
}
protected void updatePresentation() { /* Overridden in sub-classes. */ }
}