Cast MVP to start videos

This commit is contained in:
Adam Mills 2018-04-25 19:25:14 -04:00
parent ae8e82f656
commit eb869ef20b
No known key found for this signature in database
GPG Key ID: 7733DCD6D0428689
16 changed files with 846 additions and 77 deletions

2
extern/GmsApi vendored

@ -1 +1 @@
Subproject commit a811b2f64572a10e34a0fd9a0eac189735849f5b Subproject commit 5af21c0ff2777e61caba5d111991cbb165167793

View File

@ -16,6 +16,10 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
repositories {
mavenLocal()
}
dependencies { dependencies {
implementation "com.android.support:support-v4:$supportLibraryVersion" implementation "com.android.support:support-v4:$supportLibraryVersion"
implementation "com.android.support:appcompat-v7:$supportLibraryVersion" implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
@ -23,6 +27,12 @@ dependencies {
implementation "com.takisoft.fix:preference-v7:$supportLibraryVersion.0" implementation "com.takisoft.fix:preference-v7:$supportLibraryVersion.0"
implementation "de.hdodenhof:circleimageview:1.3.0" implementation "de.hdodenhof:circleimageview:1.3.0"
implementation "com.squareup.wire:wire-runtime:1.6.1" implementation "com.squareup.wire:wire-runtime:1.6.1"
implementation "su.litvak.chromecast:api-v2:0.10.3-SNAPSHOT"
// Specified manually due to
// https://github.com/vitalidze/chromecast-java-api-v2/issues/91
api "org.slf4j:slf4j-api:1.7.25"
api "uk.uuid.slf4j:slf4j-android:1.7.25-1"
implementation project(':microg-ui-tools') implementation project(':microg-ui-tools')
implementation project(':play-services-api') implementation project(':play-services-api')
@ -101,6 +111,10 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
packagingOptions {
exclude 'META-INF/ASL2.0'
}
} }
if (file('user.gradle').exists()) { if (file('user.gradle').exists()) {

View File

@ -595,6 +595,12 @@
</intent-filter> </intent-filter>
</service> </service>
<service android:name="org.microg.gms.cast.CastDeviceControllerService">
<intent-filter>
<action android:name="com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE"/>
</intent-filter>
</service>
<service android:name="org.microg.gms.DummyService"> <service android:name="org.microg.gms.DummyService">
<intent-filter> <intent-filter>
<action android:name="com.google.android.gms.plus.service.START"/> <action android:name="com.google.android.gms.plus.service.START"/>
@ -603,7 +609,6 @@
<action android:name="com.google.android.gms.appstate.service.START"/> <action android:name="com.google.android.gms.appstate.service.START"/>
<action android:name="com.google.android.gms.ads.service.START"/> <action android:name="com.google.android.gms.ads.service.START"/>
<action android:name="com.google.android.gms.accounts.ACCOUNT_SERVICE"/> <action android:name="com.google.android.gms.accounts.ACCOUNT_SERVICE"/>
<action android:name="com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE"/>
<action android:name="com.google.android.gms.identity.service.BIND"/> <action android:name="com.google.android.gms.identity.service.BIND"/>
<action android:name="com.google.android.gms.wearable.BIND"/> <action android:name="com.google.android.gms.wearable.BIND"/>
<action android:name="com.google.android.gms.auth.service.START"/> <action android:name="com.google.android.gms.auth.service.START"/>

View File

@ -18,15 +18,20 @@ package com.google.android.gms.cast.framework.internal;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouteSelector; import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.util.Log; import android.util.Log;
import com.google.android.gms.cast.CastMediaControlIntent;
import com.google.android.gms.cast.framework.CastOptions; import com.google.android.gms.cast.framework.CastOptions;
import com.google.android.gms.cast.framework.IAppVisibilityListener;
import com.google.android.gms.cast.framework.ICastContext; import com.google.android.gms.cast.framework.ICastContext;
import com.google.android.gms.cast.framework.IDiscoveryManager; import com.google.android.gms.cast.framework.IDiscoveryManager;
import com.google.android.gms.cast.framework.ISessionManager; import com.google.android.gms.cast.framework.ISessionManager;
import com.google.android.gms.cast.framework.ISessionProvider;
import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper;
@ -41,22 +46,39 @@ public class CastContextImpl extends ICastContext.Stub {
private Context context; private Context context;
private CastOptions options; private CastOptions options;
private IMediaRouter router; private IMediaRouter router;
private Map map; private Map<String, IBinder> sessionProviders;
public ISessionProvider defaultSessionProvider;
private MediaRouteSelector mergedSelector; private MediaRouteSelector mergedSelector;
public CastContextImpl(IObjectWrapper context, CastOptions options, IMediaRouter router, Map map) { public CastContextImpl(IObjectWrapper context, CastOptions options, IMediaRouter router, Map<String, IBinder> sessionProviders) throws RemoteException {
Log.d(TAG, "Creating new cast context");
this.context = (Context) ObjectWrapper.unwrap(context); this.context = (Context) ObjectWrapper.unwrap(context);
this.options = options; this.options = options;
this.router = router; this.router = router;
this.map = map; this.sessionProviders = sessionProviders;
String receiverApplicationId = options.getReceiverApplicationId();
String defaultCategory = CastMediaControlIntent.categoryForCast(receiverApplicationId);
this.defaultSessionProvider = ISessionProvider.Stub.asInterface(this.sessionProviders.get(defaultCategory));
// TODO: This should incorporate passed options // TODO: This should incorporate passed options
this.mergedSelector = new MediaRouteSelector.Builder() this.mergedSelector = new MediaRouteSelector.Builder()
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO) .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build(); .build();
// TODO: Find a home for this once the rest of the implementation
// becomes more clear. Uncomment this to enable discovery of devices.
// Note that the scan currently isn't ever disabled as part of the
// lifecycle, so we don't want to ship with this.
/*
Bundle selectorBundle = this.mergedSelector.asBundle();
router.clearCallbacks();
router.registerMediaRouterCallbackImpl(selectorBundle, new MediaRouterCallbackImpl(this));
router.addCallback(selectorBundle, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
*/
} }
@Override @Override
@ -64,6 +86,16 @@ public class CastContextImpl extends ICastContext.Stub {
return this.mergedSelector.asBundle(); return this.mergedSelector.asBundle();
} }
@Override
public void addVisibilityChangeListener(IAppVisibilityListener listener) {
Log.d(TAG, "unimplemented Method: addVisibilityChangeListener");
}
@Override
public void removeVisibilityChangeListener(IAppVisibilityListener listener) {
Log.d(TAG, "unimplemented Method: removeVisibilityChangeListener");
}
@Override @Override
public boolean isApplicationVisible() throws RemoteException { public boolean isApplicationVisible() throws RemoteException {
Log.d(TAG, "unimplemented Method: isApplicationVisible"); Log.d(TAG, "unimplemented Method: isApplicationVisible");
@ -71,9 +103,9 @@ public class CastContextImpl extends ICastContext.Stub {
} }
@Override @Override
public ISessionManager getSessionManagerImpl() throws RemoteException { public SessionManagerImpl getSessionManagerImpl() {
if (this.sessionManager == null) { if (this.sessionManager == null) {
this.sessionManager = new SessionManagerImpl(); this.sessionManager = new SessionManagerImpl(this);
} }
return this.sessionManager; return this.sessionManager;
} }
@ -103,8 +135,8 @@ public class CastContextImpl extends ICastContext.Stub {
} }
@Override @Override
public void unknown(String s1, Map m1) throws RemoteException { public void setReceiverApplicationId(String receiverApplicationId, Map sessionProvidersByCategory) throws RemoteException {
Log.d(TAG, "unimplemented Method: unknown"); Log.d(TAG, "unimplemented Method: setReceiverApplicationId");
} }
public Context getContext() { public Context getContext() {
@ -119,6 +151,10 @@ public class CastContextImpl extends ICastContext.Stub {
return this.mergedSelector; return this.mergedSelector;
} }
public CastOptions getOptions() {
return this.options;
}
@Override @Override
public IObjectWrapper getWrappedThis() throws RemoteException { public IObjectWrapper getWrappedThis() throws RemoteException {
return ObjectWrapper.wrap(this); return ObjectWrapper.wrap(this);

View File

@ -18,6 +18,7 @@ package com.google.android.gms.cast.framework.internal;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v7.media.MediaRouter; import android.support.v7.media.MediaRouter;
import android.util.Log; import android.util.Log;
@ -45,34 +46,18 @@ public class CastDynamiteModuleImpl extends ICastDynamiteModule.Stub {
private static final String TAG = CastDynamiteModuleImpl.class.getSimpleName(); private static final String TAG = CastDynamiteModuleImpl.class.getSimpleName();
@Override @Override
public ICastContext newCastContextImpl(IObjectWrapper context, CastOptions options, IMediaRouter router, Map map) throws RemoteException { public ICastContext newCastContextImpl(IObjectWrapper context, CastOptions options, IMediaRouter router, Map sessionProviders) throws RemoteException {
CastContextImpl castContextImpl = new CastContextImpl(context, options, router, map); return new CastContextImpl(context, options, router, sessionProviders);
// TODO: Find a home for this once the rest of the implementation
// becomes more clear. Uncomment this to enable discovery of devices.
// Note that the scan currently isn't ever disabled as part of the
// lifecycle, so we don't want to ship with this.
/*
Bundle selectorBundle = castContextImpl.getMergedSelector().asBundle();
router.clearCallbacks();
router.registerMediaRouterCallbackImpl(selectorBundle, new MediaRouterCallbackImpl());
router.addCallback(selectorBundle, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
*/
return castContextImpl;
} }
@Override @Override
public ISession newSessionImpl(String s1, String s2, ISessionProxy proxy) throws RemoteException { public ISession newSessionImpl(String category, String sessionId, ISessionProxy proxy) throws RemoteException {
Log.d(TAG, "unimplemented Method: newSessionImpl"); return new SessionImpl(category, sessionId, proxy);
return new SessionImpl();
} }
@Override @Override
public ICastSession newCastSessionImpl(CastOptions options, IObjectWrapper session, ICastConnectionController controller) throws RemoteException { public ICastSession newCastSessionImpl(CastOptions options, IObjectWrapper session, ICastConnectionController controller) throws RemoteException {
Log.d(TAG, "unimplemented Method: newCastSessionImpl"); return new CastSessionImpl(options, session, controller);
return new CastSessionImpl();
} }
@Override @Override

View File

@ -18,6 +18,62 @@ package com.google.android.gms.cast.framework.internal;
import com.google.android.gms.cast.framework.ICastSession; import com.google.android.gms.cast.framework.ICastSession;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.framework.CastOptions;
import com.google.android.gms.cast.framework.ICastConnectionController;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper;
public class CastSessionImpl extends ICastSession.Stub { public class CastSessionImpl extends ICastSession.Stub {
private static final String TAG = CastSessionImpl.class.getSimpleName(); private static final String TAG = CastSessionImpl.class.getSimpleName();
private CastOptions options;
private SessionImpl session;
private ICastConnectionController controller;
public CastSessionImpl(CastOptions options, IObjectWrapper session, ICastConnectionController controller) throws RemoteException {
this.options = options;
this.session = (SessionImpl) ObjectWrapper.unwrap(session);
this.controller = controller;
this.session.setCastSession(this);
}
public void launchApplication() throws RemoteException {
this.controller.launchApplication(this.options.getReceiverApplicationId(), this.options.getLaunchOptions());
}
@Override
public void onConnected(Bundle routeInfoExtra) throws RemoteException {
this.controller.launchApplication(this.options.getReceiverApplicationId(), this.options.getLaunchOptions());
}
@Override
public void onConnectionSuspended(int reason) {
Log.d(TAG, "unimplemented Method: onConnectionSuspended");
}
@Override
public void onConnectionFailed(Status status) {
Log.d(TAG, "unimplemented Method: onConnectionFailed");
}
@Override
public void onApplicationConnectionSuccess(ApplicationMetadata applicationMetadata, String applicationStatus, String sessionId, boolean wasLaunched) {
this.session.onApplicationConnectionSuccess(applicationMetadata, applicationStatus, sessionId, wasLaunched);
}
@Override
public void onApplicationConnectionFailure(int statusCode) {
this.session.onApplicationConnectionFailure(statusCode);
}
@Override
public void disconnectFromDevice(boolean boolean1, int int1) {
Log.d(TAG, "unimplemented Method: disconnectFromDevice");
}
} }

View File

@ -36,7 +36,6 @@ public class DiscoveryManagerImpl extends IDiscoveryManager.Stub {
private Set discoveryManagerListeners = new HashSet(); private Set discoveryManagerListeners = new HashSet();
public DiscoveryManagerImpl(CastContextImpl castContextImpl) { public DiscoveryManagerImpl(CastContextImpl castContextImpl) {
Log.d(TAG, "Creating new discovery manager");
this.castContextImpl = castContextImpl; this.castContextImpl = castContextImpl;
} }

View File

@ -16,12 +16,27 @@
package com.google.android.gms.cast.framework.internal; package com.google.android.gms.cast.framework.internal;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.framework.ISession;
import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper;
import android.support.v7.media.MediaControlIntent;
public class MediaRouterCallbackImpl extends IMediaRouterCallback.Stub { public class MediaRouterCallbackImpl extends IMediaRouterCallback.Stub {
private static final String TAG = MediaRouterCallbackImpl.class.getSimpleName(); private static final String TAG = MediaRouterCallbackImpl.class.getSimpleName();
private CastContextImpl castContext;
public MediaRouterCallbackImpl(CastContextImpl castContext) {
this.castContext = castContext;
}
@Override @Override
public void onRouteAdded(String routeId, Bundle extras) { public void onRouteAdded(String routeId, Bundle extras) {
Log.d(TAG, "unimplemented Method: onRouteAdded"); Log.d(TAG, "unimplemented Method: onRouteAdded");
@ -35,8 +50,14 @@ public class MediaRouterCallbackImpl extends IMediaRouterCallback.Stub {
Log.d(TAG, "unimplemented Method: onRouteRemoved"); Log.d(TAG, "unimplemented Method: onRouteRemoved");
} }
@Override @Override
public void onRouteSelected(String routeId, Bundle extras) { public void onRouteSelected(String routeId, Bundle extras) throws RemoteException {
Log.d(TAG, "unimplemented Method: onRouteSelected"); CastDevice castDevice = CastDevice.getFromBundle(extras);
SessionImpl session = (SessionImpl) ObjectWrapper.unwrap(this.castContext.defaultSessionProvider.getSession(null));
Bundle routeInfoExtras = this.castContext.getRouter().getRouteInfoExtrasById(routeId);
if (routeInfoExtras != null) {
session.start(this.castContext, castDevice, routeId, routeInfoExtras);
}
} }
@Override @Override
public void unknown(String routeId, Bundle extras) { public void unknown(String routeId, Bundle extras) {

View File

@ -16,24 +16,136 @@
package com.google.android.gms.cast.framework.internal; package com.google.android.gms.cast.framework.internal;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.framework.ISession; import com.google.android.gms.cast.framework.ISession;
import com.google.android.gms.cast.framework.ISessionProxy;
import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper;
public class SessionImpl extends ISession.Stub { public class SessionImpl extends ISession.Stub {
private static final String TAG = SessionImpl.class.getSimpleName(); private static final String TAG = SessionImpl.class.getSimpleName();
private String category;
private String sessionId;
private ISessionProxy proxy;
private CastSessionImpl castSession;
private CastContextImpl castContext;
private CastDevice castDevice;
private Bundle routeInfoExtra;
private boolean mIsConnecting = false;
private boolean mIsConnected = false;
private String routeId = null;
public SessionImpl(String category, String sessionId, ISessionProxy proxy) {
this.category = category;
this.sessionId = sessionId;
this.proxy = proxy;
}
public void start(CastContextImpl castContext, CastDevice castDevice, String routeId, Bundle routeInfoExtra) throws RemoteException {
this.castContext = castContext;
this.castDevice = castDevice;
this.routeInfoExtra = routeInfoExtra;
this.routeId = routeId;
this.mIsConnecting = true;
this.mIsConnected = false;
this.castContext.getSessionManagerImpl().onSessionStarting(this);
this.proxy.start(routeInfoExtra);
}
public void onApplicationConnectionSuccess(ApplicationMetadata applicationMetadata, String applicationStatus, String sessionId, boolean wasLaunched) {
this.mIsConnecting = false;
this.mIsConnected = true;
this.castContext.getSessionManagerImpl().onSessionStarted(this, sessionId);
try {
this.castContext.getRouter().selectRouteById(this.getRouteId());
} catch (RemoteException ex) {
Log.e(TAG, "Error calling selectRouteById: " + ex.getMessage());
}
}
public void onApplicationConnectionFailure(int statusCode) {
this.mIsConnecting = false;
this.mIsConnected = false;
this.routeId = null;
this.castContext = null;
this.castDevice = null;
this.routeInfoExtra = null;
this.castContext.getSessionManagerImpl().onSessionStartFailed(this, statusCode);
try {
this.castContext.getRouter().selectDefaultRoute();
} catch (RemoteException ex) {
Log.e(TAG, "Error calling selectDefaultRoute: " + ex.getMessage());
}
}
public void onRouteSelected(Bundle extras) {
}
public CastSessionImpl getCastSession() {
return this.castSession;
}
public void setCastSession(CastSessionImpl castSession) {
this.castSession = castSession;
}
public ISessionProxy getSessionProxy() {
return this.proxy;
}
public IObjectWrapper getWrappedSession() throws RemoteException {
if (this.proxy == null) {
return ObjectWrapper.wrap(null);
}
return this.proxy.getWrappedSession();
}
@Override @Override
public void notifySessionEnded(int error) { public String getCategory() {
Log.d(TAG, "unimplemented Method: notifySessionEnded"); return this.category;
}
@Override
public String getSessionId() {
return this.sessionId;
}
@Override
public String getRouteId() {
return this.routeId;
} }
@Override @Override
public boolean isConnected() { public boolean isConnected() {
Log.d(TAG, "unimplemented Method: isConnected"); return this.mIsConnected;
return true; }
@Override
public boolean isConnecting() {
return this.mIsConnecting;
}
@Override
public boolean isDisconnecting() {
Log.d(TAG, "unimplemented Method: isDisconnecting");
return false;
}
@Override
public boolean isDisconnected() {
Log.d(TAG, "unimplemented Method: isDisconnected");
return false;
} }
@Override @Override
@ -43,7 +155,43 @@ public class SessionImpl extends ISession.Stub {
} }
@Override @Override
public IObjectWrapper getWrappedThis() { public boolean isSuspended() {
Log.d(TAG, "unimplemented Method: isSuspended");
return false;
}
@Override
public void notifySessionStarted(String sessionId) {
Log.d(TAG, "unimplemented Method: notifySessionStarted");
}
@Override
public void notifyFailedToStartSession(int error) {
Log.d(TAG, "unimplemented Method: notifyFailedToStartSession");
}
@Override
public void notifySessionEnded(int error) {
Log.d(TAG, "unimplemented Method: notifySessionEnded");
}
@Override
public void notifySessionResumed(boolean wasSuspended) {
Log.d(TAG, "unimplemented Method: notifySessionResumed");
}
@Override
public void notifyFailedToResumeSession(int error) {
Log.d(TAG, "unimplemented Method: notifyFailedToResumeSession");
}
@Override
public void notifySessionSuspended(int reason) {
Log.d(TAG, "unimplemented Method: notifySessionSuspended");
}
@Override
public IObjectWrapper getWrappedObject() {
return ObjectWrapper.wrap(this); return ObjectWrapper.wrap(this);
} }
} }

View File

@ -16,12 +16,16 @@
package com.google.android.gms.cast.framework.internal; package com.google.android.gms.cast.framework.internal;
import android.os.Bundle;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import com.google.android.gms.cast.framework.CastState;
import com.google.android.gms.cast.framework.ICastStateListener; import com.google.android.gms.cast.framework.ICastStateListener;
import com.google.android.gms.cast.framework.ISession;
import com.google.android.gms.cast.framework.ISessionManager; import com.google.android.gms.cast.framework.ISessionManager;
import com.google.android.gms.cast.framework.ISessionManagerListener; import com.google.android.gms.cast.framework.ISessionManagerListener;
import com.google.android.gms.cast.framework.internal.CastContextImpl;
import com.google.android.gms.cast.framework.internal.SessionImpl; import com.google.android.gms.cast.framework.internal.SessionImpl;
import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper;
@ -29,18 +33,33 @@ import com.google.android.gms.dynamic.ObjectWrapper;
import java.util.Set; import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
public class SessionManagerImpl extends ISessionManager.Stub { public class SessionManagerImpl extends ISessionManager.Stub {
private static final String TAG = SessionManagerImpl.class.getSimpleName(); private static final String TAG = SessionManagerImpl.class.getSimpleName();
private Set sessionManagerListeners = new HashSet(); private CastContextImpl castContext;
private Set castStateListeners = new HashSet();
private Set<ISessionManagerListener> sessionManagerListeners = new HashSet<ISessionManagerListener>();
private Set<ICastStateListener> castStateListeners = new HashSet<ICastStateListener>();
private Map<String, SessionImpl> routeSessions = new HashMap<String, SessionImpl>();
private SessionImpl currentSession; private SessionImpl currentSession;
private int castState = CastState.NO_DEVICES_AVAILABLE;
public SessionManagerImpl(CastContextImpl castContext) {
this.castContext = castContext;
}
@Override @Override
public IObjectWrapper getWrappedCurrentSession() throws RemoteException { public IObjectWrapper getWrappedCurrentSession() throws RemoteException {
Log.d(TAG, "unimplemented Method: getWrappedCurrentSession"); if (this.currentSession == null) {
return ObjectWrapper.wrap(this.currentSession); return ObjectWrapper.wrap(null);
}
return this.currentSession.getWrappedSession();
} }
@Override @Override
@ -76,4 +95,136 @@ public class SessionManagerImpl extends ISessionManager.Stub {
public IObjectWrapper getWrappedThis() throws RemoteException { public IObjectWrapper getWrappedThis() throws RemoteException {
return ObjectWrapper.wrap(this); return ObjectWrapper.wrap(this);
} }
@Override
public int getCastState() {
return this.castState;
}
@Override
public void startSession(Bundle params) {
Log.d(TAG, "unimplemented Method: startSession");
String routeId = params.getString("CAST_INTENT_TO_CAST_ROUTE_ID_KEY");
String sessionId = params.getString("CAST_INTENT_TO_CAST_SESSION_ID_KEY");
}
public void onRouteSelected(String routeId, Bundle extras) {
Log.d(TAG, "unimplemented Method: onRouteSelected: " + routeId);
}
private void setCastState(int castState) {
this.castState = castState;
this.onCastStateChanged();
}
public void onCastStateChanged() {
for (ICastStateListener listener : this.castStateListeners) {
try {
listener.onCastStateChanged(this.castState);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onCastStateChanged: " + e.getMessage());
}
}
}
public void onSessionStarting(SessionImpl session) {
this.setCastState(CastState.CONNECTING);
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionStarting(session.getSessionProxy().getWrappedSession());
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionStarting: " + e.getMessage());
}
}
}
public void onSessionStartFailed(SessionImpl session, int error) {
this.currentSession = null;
this.setCastState(CastState.NOT_CONNECTED);
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionStartFailed(session.getSessionProxy().getWrappedSession(), error);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionStartFailed: " + e.getMessage());
}
}
}
public void onSessionStarted(SessionImpl session, String sessionId) {
this.currentSession = session;
this.setCastState(CastState.CONNECTED);
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionStarted(session.getSessionProxy().getWrappedSession(), sessionId);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionStarted: " + e.getMessage());
}
}
}
public void onSessionResumed(SessionImpl session, boolean wasSuspended) {
this.setCastState(CastState.CONNECTED);
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionResumed(session.getSessionProxy().getWrappedSession(), wasSuspended);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionResumed: " + e.getMessage());
}
}
}
public void onSessionEnding(SessionImpl session) {
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionEnding(session.getSessionProxy().getWrappedSession());
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionEnding: " + e.getMessage());
}
}
}
public void onSessionEnded(SessionImpl session, int error) {
this.currentSession = null;
this.setCastState(CastState.NOT_CONNECTED);
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionEnded(session.getSessionProxy().getWrappedSession(), error);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionEnded: " + e.getMessage());
}
}
}
public void onSessionResuming(SessionImpl session, String sessionId) {
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionResuming(session.getSessionProxy().getWrappedSession(), sessionId);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionResuming: " + e.getMessage());
}
}
}
public void onSessionResumeFailed(SessionImpl session, int error) {
this.currentSession = null;
this.setCastState(CastState.NOT_CONNECTED);
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionResumeFailed(session.getSessionProxy().getWrappedSession(), error);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionResumeFailed: " + e.getMessage());
}
}
}
public void onSessionSuspended(SessionImpl session, int reason) {
this.setCastState(CastState.NOT_CONNECTED);
for (ISessionManagerListener listener : this.sessionManagerListeners) {
try {
listener.onSessionSuspended(session.getSessionProxy().getWrappedSession(), reason);
} catch (RemoteException e) {
Log.d(TAG, "Remote exception calling onSessionSuspended: " + e.getMessage());
}
}
}
} }

View File

@ -0,0 +1,151 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.cast;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Base64;
import android.util.Log;
import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.JoinOptions;
import com.google.android.gms.cast.LaunchOptions;
import com.google.android.gms.cast.internal.ICastDeviceController;
import com.google.android.gms.cast.internal.ICastDeviceControllerListener;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.common.images.WebImage;
import com.google.android.gms.common.internal.BinderWrapper;
import com.google.android.gms.common.internal.GetServiceRequest;
import su.litvak.chromecast.api.v2.Application;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.Namespace;
public class CastDeviceControllerImpl extends ICastDeviceController.Stub {
private static final String TAG = "GmsCastDeviceControllerImpl";
private Context context;
private String packageName;
private CastDevice castDevice;
boolean notificationEnabled;
long castFlags;
ICastDeviceControllerListener listener;
ChromeCast chromecast;
String sessionId = null;
public CastDeviceControllerImpl(Context context, String packageName, Bundle extras) {
this.context = context;
this.packageName = packageName;
extras.setClassLoader(BinderWrapper.class.getClassLoader());
this.castDevice = CastDevice.getFromBundle(extras);
this.notificationEnabled = extras.getBoolean("com.google.android.gms.cast.EXTRA_CAST_FRAMEWORK_NOTIFICATION_ENABLED");
this.castFlags = extras.getLong("com.google.android.gms.cast.EXTRA_CAST_FLAGS");
BinderWrapper listenerWrapper = (BinderWrapper)extras.get("listener");
this.listener = ICastDeviceControllerListener.Stub.asInterface(listenerWrapper.binder);
this.chromecast = new ChromeCast(this.castDevice.getAddress());
}
@Override
public void disconnect() {
Log.d(TAG, "unimplemented Method: disconnect");
this.sessionId = null;
}
@Override
public void sendMessage(String namespace, String message, long requestId) {
String response = null;
try {
response = this.chromecast.sendRawRequest(namespace, message, requestId);
} catch (IOException e) {
Log.w(TAG, "Error sending cast message: " + e.getMessage());
return;
}
try {
this.listener.onSendMessageSuccess(response, requestId);
} catch (RemoteException ex) {
Log.e(TAG, "Error calling onSendMessageSuccess: " + ex.getMessage());
}
}
@Override
public void stopApplication(String sessionId) {
Log.d(TAG, "unimplemented Method: stopApplication");
this.sessionId = null;
}
@Override
public void registerNamespace(String namespace) {
Log.d(TAG, "unimplemented Method: registerNamespace");
}
@Override
public void unregisterNamespace(String namespace) {
Log.d(TAG, "unimplemented Method: unregisterNamespace");
}
@Override
public void launchApplication(String applicationId, LaunchOptions launchOptions) {
Application app = null;
try {
app = this.chromecast.launchApp(applicationId);
} catch (IOException e) {
Log.w(TAG, "Error launching cast application: " + e.getMessage());
try {
this.listener.onApplicationConnectionFailure(CommonStatusCodes.NETWORK_ERROR);
} catch (RemoteException ex) {
Log.e(TAG, "Error calling onApplicationConnectionFailure: " + ex.getMessage());
}
return;
}
this.sessionId = app.sessionId;
ApplicationMetadata metadata = new ApplicationMetadata();
metadata.applicationId = applicationId;
metadata.name = app.name;
Log.d(TAG, "unimplemented: ApplicationMetadata.images");
metadata.images = new ArrayList<WebImage>();
metadata.namespaces = new ArrayList<String>();
Log.d(TAG, "unimplemented: ApplicationMetadata.senderAppLaunchUri");
for(Namespace namespace : app.namespaces) {
metadata.namespaces.add(namespace.name);
}
metadata.senderAppIdentifier = this.context.getPackageName();
try {
this.listener.onApplicationConnectionSuccess(metadata, app.statusText, app.sessionId, true);
} catch (RemoteException e) {
Log.e(TAG, "Error calling onApplicationConnectionSuccess: " + e.getMessage());
}
}
@Override
public void joinApplication(String applicationId, String sessionId, JoinOptions joinOptions) {
Log.d(TAG, "unimplemented Method: joinApplication");
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.cast;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.Parcel;
import android.util.ArrayMap;
import android.util.Log;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.internal.ICastDeviceControllerListener;
import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.BinderWrapper;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
import org.microg.gms.common.GmsService;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.ChromeCasts;
import su.litvak.chromecast.api.v2.Status;
import su.litvak.chromecast.api.v2.ChromeCastsListener;
public class CastDeviceControllerService extends BaseService {
private static final String TAG = CastDeviceControllerService.class.getSimpleName();
public CastDeviceControllerService() {
super("GmsCastDeviceControllerSvc", GmsService.CAST);
}
@Override
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
callback.onPostInitComplete(0, new CastDeviceControllerImpl(this, request.packageName, request.extras), null);
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2013-2017 microG Project Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.microg.gms.cast;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouteDescriptor;
import android.support.v7.media.MediaRouteDiscoveryRequest;
import android.support.v7.media.MediaRouteProvider;
import android.support.v7.media.MediaRouteProviderDescriptor;
import android.support.v7.media.MediaRouter;
import android.util.Log;
import com.google.android.gms.common.images.WebImage;
import com.google.android.gms.cast.CastDevice;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.io.IOException;
import java.lang.Thread;
import java.lang.Runnable;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.ChromeCasts;
import su.litvak.chromecast.api.v2.Status;
import su.litvak.chromecast.api.v2.ChromeCastsListener;
public class CastMediaRouteController extends MediaRouteProvider.RouteController {
private static final String TAG = CastMediaRouteController.class.getSimpleName();
private CastMediaRouteProvider provider;
private String routeId;
private ChromeCast chromecast;
public CastMediaRouteController(CastMediaRouteProvider provider, String routeId, ChromeCast chromecast) {
super();
this.provider = provider;
this.routeId = routeId;
this.chromecast = chromecast;
}
public boolean onControlRequest(Intent intent, MediaRouter.ControlRequestCallback callback) {
Log.d(TAG, "unimplemented Method: onControlRequest: " + this.routeId);
return false;
}
public void onRelease() {
Log.d(TAG, "unimplemented Method: onRelease: " + this.routeId);
}
public void onSelect() {
Log.d(TAG, "unimplemented Method: onSelect: " + this.routeId);
}
public void onSetVolume(int volume) {
Log.d(TAG, "unimplemented Method: onSetVolume: " + this.routeId);
}
public void onUnselect() {
Log.d(TAG, "unimplemented Method: onUnselect: " + this.routeId);
}
public void onUnselect(int reason) {
Log.d(TAG, "unimplemented Method: onUnselect: " + this.routeId);
}
public void onUpdateVolume(int delta) {
Log.d(TAG, "unimplemented Method: onUpdateVolume: " + this.routeId);
}
}

View File

@ -20,6 +20,8 @@ import android.content.Context;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaControlIntent;
import android.support.v7.media.MediaRouteDescriptor; import android.support.v7.media.MediaRouteDescriptor;
import android.support.v7.media.MediaRouteDiscoveryRequest; import android.support.v7.media.MediaRouteDiscoveryRequest;
@ -31,19 +33,68 @@ import android.util.Log;
import com.google.android.gms.common.images.WebImage; import com.google.android.gms.common.images.WebImage;
import com.google.android.gms.cast.CastDevice; import com.google.android.gms.cast.CastDevice;
import java.util.ArrayList;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.io.IOException;
import java.lang.Thread;
import java.lang.Runnable;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.ChromeCasts;
import su.litvak.chromecast.api.v2.Status;
import su.litvak.chromecast.api.v2.ChromeCastsListener;
public class CastMediaRouteProvider extends MediaRouteProvider { public class CastMediaRouteProvider extends MediaRouteProvider {
private static final String TAG = CastMediaRouteProvider.class.getSimpleName(); private static final String TAG = CastMediaRouteProvider.class.getSimpleName();
private Map<String, ChromeCast> chromecasts = new HashMap<String, ChromeCast>();
public CastMediaRouteProvider(Context context) { public CastMediaRouteProvider(Context context) {
super(context); super(context);
Log.d(TAG, "unimplemented Method: CastMediaRouteProvider");
// Uncomment this to create the mock device // TODO: Uncomment this to manually discover a chromecast on the local
// publishRoutes(); // network. Discovery not yet implemented.
/*
InetAddress addr = null;
try {
addr = InetAddress.getByName("192.168.1.11");
} catch (UnknownHostException e) {
Log.d(TAG, "Chromecast status exception getting host: " + e.getMessage());
return;
}
onChromeCastDiscovered(addr);
*/
}
private void onChromeCastDiscovered(InetAddress address) {
ChromeCast chromecast = new ChromeCast(address.getHostAddress());
new Thread(new Runnable() {
public void run() {
Status status = null;
try {
status = chromecast.getStatus();
} catch (IOException e) {
Log.e(TAG, "Exception getting chromecast status: " + e.getMessage());
return;
}
Handler mainHandler = new Handler(CastMediaRouteProvider.this.getContext().getMainLooper());
mainHandler.post(new Runnable() {
@Override
public void run() {
String routeId = address.getHostAddress();
CastMediaRouteProvider.this.chromecasts.put(routeId, chromecast);
publishRoutes();
}
});
}
}).start();
} }
/** /**
@ -67,37 +118,37 @@ public class CastMediaRouteProvider extends MediaRouteProvider {
@Override @Override
public RouteController onCreateRouteController(String routeId) { public RouteController onCreateRouteController(String routeId) {
Log.d(TAG, "unimplemented Method: onCreateRouteController"); ChromeCast chromecast = this.chromecasts.get(routeId);
return null; return new CastMediaRouteController(this, routeId, chromecast);
} }
/**
* TODO: Currently this method simply publishes a single cast route for
* testing.
*/
private void publishRoutes() { private void publishRoutes() {
Log.d(TAG, "unimplemented Method: publishRoutes"); MediaRouteProviderDescriptor.Builder builder = new MediaRouteProviderDescriptor.Builder();
Bundle extras = new Bundle(); for(Map.Entry<String, ChromeCast> entry : this.chromecasts.entrySet()) {
CastDevice castDevice = new CastDevice("abc123"); String routeId = entry.getKey();
castDevice.putInBundle(extras); ChromeCast chromecast = entry.getValue();
MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder( Bundle extras = new Bundle();
"abc123", CastDevice castDevice = new CastDevice(
"Rotue Friendly Name") routeId,
.setDescription("Chromecast") chromecast.getAddress()
.addControlFilters(CONTROL_FILTERS) );
.setDeviceType(MediaRouter.RouteInfo.DEVICE_TYPE_TV) castDevice.putInBundle(extras);
.setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE) builder.addRoute(new MediaRouteDescriptor.Builder(
.setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED) routeId,
.setVolumeMax(20) routeId)
.setVolume(0) .setDescription("Chromecast")
.setEnabled(true) .addControlFilters(CONTROL_FILTERS)
.setExtras(extras) .setDeviceType(MediaRouter.RouteInfo.DEVICE_TYPE_TV)
.setConnectionState(MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED) .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
.build(); .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED)
MediaRouteProviderDescriptor providerDescriptor = .setVolumeMax(20)
new MediaRouteProviderDescriptor.Builder() .setVolume(0)
.addRoute(routeDescriptor1) .setEnabled(true)
.build(); .setExtras(extras)
this.setDescriptor(providerDescriptor); .setConnectionState(MediaRouter.RouteInfo.CONNECTION_STATE_DISCONNECTED)
.build());
}
this.setDescriptor(builder.build());
} }
} }

View File

@ -14,6 +14,7 @@
-dontwarn org.oscim.tiling.source.OkHttpEngine$OkHttpFactory -dontwarn org.oscim.tiling.source.OkHttpEngine$OkHttpFactory
-dontwarn com.caverock.androidsvg.** -dontwarn com.caverock.androidsvg.**
-dontwarn org.slf4j.** -dontwarn org.slf4j.**
-dontwarn org.codehaus.jackson.**
# Disable ProGuard Notes, they won't help here # Disable ProGuard Notes, they won't help here
-dontnote -dontnote
@ -46,3 +47,8 @@
-keep public class com.squareup.wire.Message -keep public class com.squareup.wire.Message
-keep public class * extends com.squareup.wire.Message -keep public class * extends com.squareup.wire.Message
-keep public class * extends com.squareup.wire.Message$Builder { public <init>(...); } -keep public class * extends com.squareup.wire.Message$Builder { public <init>(...); }
# Proguard configuration for Jackson 1.x
-keepclassmembers class * {
@org.codehaus.jackson.annotate.* *;
}

View File

@ -1,4 +1,4 @@
include ':wearable-lib' include ':wearable-lib', ':safe-parcel'
include ':unifiednlp-api' include ':unifiednlp-api'
include ':unifiednlp-base' include ':unifiednlp-base'