Bug 1305351 - Add ChromeCastDisplay for Presentation API. r=snorp

MozReview-Commit-ID: A9yXeADOA0Y

--HG--
extra : rebase_source : cebed59abe241a81e8e116245a85be7488931ba9
This commit is contained in:
KuoE0 2016-09-21 20:24:57 +08:00
Родитель 0b06159c5f
Коммит 11677f5c9c
6 изменённых файлов: 159 добавлений и 25 удалений

Просмотреть файл

@ -105,8 +105,10 @@ android {
}
if (!mozconfig.substs.MOZ_NATIVE_DEVICES) {
exclude 'org/mozilla/gecko/ChromeCast.java'
exclude 'org/mozilla/gecko/ChromeCastDisplay.java'
exclude 'org/mozilla/gecko/ChromeCastPlayer.java'
exclude 'org/mozilla/gecko/GeckoMediaPlayer.java'
exclude 'org/mozilla/gecko/GeckoPresentationDisplay.java'
exclude 'org/mozilla/gecko/MediaPlayerManager.java'
}

Просмотреть файл

@ -0,0 +1,66 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* vim: ts=4 sw=4 expandtab:
* 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 org.json.JSONObject;
import org.json.JSONException;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.EventCallback;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.CastRemoteDisplayLocalService;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.Status;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
public class ChromeCastDisplay implements GeckoPresentationDisplay {
static final String REMOTE_DISPLAY_APP_ID = "4574A331";
private static final String LOGTAG = "GeckoChromeCastDisplay";
private final RouteInfo route;
private CastDevice castDevice;
public ChromeCastDisplay(Context context, RouteInfo route) {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (status != ConnectionResult.SUCCESS) {
throw new IllegalStateException("Play services are required for Chromecast support (got status code " + status + ")");
}
this.route = route;
this.castDevice = CastDevice.getFromBundle(route.getExtras());
}
public JSONObject toJSON() {
final JSONObject obj = new JSONObject();
try {
if (castDevice == null) {
return null;
}
obj.put("uuid", route.getId());
obj.put("friendlyName", castDevice.getFriendlyName());
obj.put("type", "chromecast");
} catch (JSONException ex) {
Log.d(LOGTAG, "Error building route", ex);
}
return obj;
}
@Override
public void start(EventCallback callback) { }
@Override
public void stop(EventCallback callback) { }
}

Просмотреть файл

@ -34,7 +34,7 @@ import android.support.v7.media.MediaRouter.RouteInfo;
import android.util.Log;
/* Implementation of GeckoMediaPlayer for talking to ChromeCast devices */
class ChromeCast implements GeckoMediaPlayer {
class ChromeCastPlayer implements GeckoMediaPlayer {
private static final boolean SHOW_DEBUG = false;
static final String MIRROR_RECEIVER_APP_ID = "08FF1091";
@ -168,7 +168,7 @@ class ChromeCast implements GeckoMediaPlayer {
}
}
public ChromeCast(Context context, RouteInfo route) {
public ChromeCastPlayer(Context context, RouteInfo route) {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (status != ConnectionResult.SUCCESS) {
throw new IllegalStateException("Play services are required for Chromecast support (got status code " + status + ")");
@ -493,7 +493,7 @@ class ChromeCast implements GeckoMediaPlayer {
apiClient.connect();
}
private static final String LOGTAG = "GeckoChromeCast";
private static final String LOGTAG = "GeckoChromeCastPlayer";
private void debug(String msg, Exception e) {
if (SHOW_DEBUG) {
Log.e(LOGTAG, msg, e);

Просмотреть файл

@ -0,0 +1,22 @@
/* -*- 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 org.json.JSONObject;
import org.mozilla.gecko.util.EventCallback;
/**
* Wrapper for MediaRouter types supported by Android to use for
* Presentation API, such as Chromecast, Miracast, etc.
*/
interface GeckoPresentationDisplay {
/**
* Can return null.
*/
JSONObject toJSON();
void start(EventCallback callback);
void stop(EventCallback callback);
}

Просмотреть файл

@ -64,7 +64,8 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
}
protected MediaRouter mediaRouter = null;
protected final Map<String, GeckoMediaPlayer> displays = new HashMap<String, GeckoMediaPlayer>();
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) {
@ -100,9 +101,9 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
public void handleMessage(String event, final NativeJSObject message, final EventCallback callback) {
debug(event);
final GeckoMediaPlayer 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);
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);
}
@ -110,24 +111,24 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
}
if ("MediaPlayer:Play".equals(event)) {
display.play(callback);
player.play(callback);
} else if ("MediaPlayer:Start".equals(event)) {
display.start(callback);
player.start(callback);
} else if ("MediaPlayer:Stop".equals(event)) {
display.stop(callback);
player.stop(callback);
} else if ("MediaPlayer:Pause".equals(event)) {
display.pause(callback);
player.pause(callback);
} else if ("MediaPlayer:End".equals(event)) {
display.end(callback);
player.end(callback);
} else if ("MediaPlayer:Mirror".equals(event)) {
display.mirror(callback);
player.mirror(callback);
} else if ("MediaPlayer:Message".equals(event) && message.has("data")) {
display.message(message.getString("data"), callback);
player.message(message.getString("data"), callback);
} else if ("MediaPlayer:Load".equals(event)) {
final String url = message.optString("source", "");
final String type = message.optString("type", "video/mp4");
final String title = message.optString("title", "");
display.load(title, url, type, callback);
player.load(title, url, type, callback);
}
}
@ -136,9 +137,15 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
@Override
public void onRouteRemoved(MediaRouter router, RouteInfo route) {
debug("onRouteRemoved: route=" + route);
displays.remove(route.getId());
// Remove from media player list.
players.remove(route.getId());
GeckoAppShell.notifyObservers("MediaPlayer:Removed", route.getId());
updatePresentation();
// Remove from presentation display list.
displays.remove(route.getId());
GeckoAppShell.notifyObservers("AndroidCastDevice:Removed", route.getId());
}
@SuppressWarnings("unused")
@ -164,21 +171,44 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
@Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
debug("onRouteAdded: route=" + route);
final GeckoMediaPlayer display = getMediaPlayerForRoute(route);
saveAndNotifyOfDisplay("MediaPlayer:Added", route, display);
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 display = displays.get(route.getId());
saveAndNotifyOfDisplay("MediaPlayer:Changed", route, display);
final GeckoMediaPlayer player = players.get(route.getId());
saveAndNotifyOfPlayer("MediaPlayer:Changed", route, player);
updatePresentation();
final GeckoPresentationDisplay display = displays.get(route.getId());
saveAndNotifyOfDisplay("AndroidCastDevice:Changed", route, display);
}
private void saveAndNotifyOfPlayer(final String eventName,
MediaRouter.RouteInfo route,
final GeckoMediaPlayer player) {
if (player == null) {
return;
}
final JSONObject json = player.toJSON();
if (json == null) {
return;
}
players.put(route.getId(), player);
GeckoAppShell.notifyObservers(eventName, json.toString());
}
private void saveAndNotifyOfDisplay(final String eventName,
MediaRouter.RouteInfo route, final GeckoMediaPlayer display) {
MediaRouter.RouteInfo route,
final GeckoPresentationDisplay display) {
if (display == null) {
return;
}
@ -196,7 +226,7 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
private GeckoMediaPlayer getMediaPlayerForRoute(MediaRouter.RouteInfo route) {
try {
if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
return new ChromeCast(getActivity(), route);
return new ChromeCastPlayer(getActivity(), route);
}
} catch (Exception ex) {
debug("Error handling presentation", ex);
@ -205,6 +235,17 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
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();
@ -225,7 +266,8 @@ public class MediaPlayerManager extends Fragment implements NativeEventListener
final MediaRouteSelector selectorBuilder = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.addControlCategory(CastMediaControlIntent.categoryForCast(ChromeCast.MIRROR_RECEIVER_APP_ID))
.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);
}

Просмотреть файл

@ -839,8 +839,10 @@ moz_native_devices_jars = [
CONFIG['ANDROID_PLAY_SERVICES_CAST_AAR_LIB'],
]
moz_native_devices_sources = ['java/org/mozilla/gecko/' + x for x in [
'ChromeCast.java',
'ChromeCastDisplay.java',
'ChromeCastPlayer.java',
'GeckoMediaPlayer.java',
'GeckoPresentationDisplay.java',
'MediaPlayerManager.java',
'PresentationMediaPlayerManager.java',
]]