diff --git a/.gitignore b/.gitignore index d4c41dd..84effb2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ bin-debug/ bin-release/ .settings/ .DS_Store -/build/android/aircast-jar.jar diff --git a/README.md b/README.md index a35e4e1..4d74330 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,19 @@ AirCast: Chromecast ANE for Adobe AIR ===================================== -A native extension for Adobe AIR that enables you to cast audio and video content to Chromecast devices from iOS apps, with Android (hopefully) coming soon. +A native extension for Adobe AIR that enables you to cast audio and video content to Chromecast devices from iOS apps. -Requires Adobe AIR 16+. +Requires Adobe AIR 20+. iOS --- -Based on [Renaud Bardet's seemingly abandoned iOS-only ANE project](https://github.com/renaudbardet/ANE-chromecast), support for iOS is now largely complete, including: +Based on [Renaud Bardet's abandoned ANE](https://github.com/renaudbardet/ANE-chromecast), this native extension supports all of the basic features and functionality required to connect your iOS apps to Chromecast devices, including: -* Support for [default Chromecast receiver app](https://developers.google.com/cast/docs/receiver_apps#default), no registration required -* Upgrade to the latest Google Cast SDK -* Support for amd64 and Adobe AIR 16, required for submission to the App Store - -Android -------- - -The current Android implementation, based on based on [cordova-chromecast](https://github.com/videostream/cordova-chromecast), is (in theory at least) nearly complete, but there are a few (hopefully minor) dependency issues to be resolved. - -Can you help? +* Detects Chromecast devices on the network +* Launch receiver applications +* Control media playback and volume +* Send and receive custom messages +* Support for [default Chromecast receiver app](https://developers.google.com/cast/docs/receiver_apps#default), so no registration required +* Uses the latest Google Cast SDK +* Supports amd64 and the latest Adobe AIR SDK, required for submission to the App Store diff --git a/android/.classpath b/android/.classpath deleted file mode 100644 index 0ac43ec..0000000 --- a/android/.classpath +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/android/.gitignore b/android/.gitignore deleted file mode 100644 index a22392a..0000000 --- a/android/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/bin -/gen diff --git a/android/.project b/android/.project deleted file mode 100644 index f4cee00..0000000 --- a/android/.project +++ /dev/null @@ -1,33 +0,0 @@ - - - aircast-jar - - - - - - com.android.ide.eclipse.adt.ResourceManagerBuilder - - - - - com.android.ide.eclipse.adt.PreCompilerBuilder - - - - - org.eclipse.jdt.core.javabuilder - - - - - com.android.ide.eclipse.adt.ApkBuilder - - - - - - com.android.ide.eclipse.adt.AndroidNature - org.eclipse.jdt.core.javanature - - diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml deleted file mode 100644 index b11c57c..0000000 --- a/android/AndroidManifest.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/android/gen/com/mesmotronic/ane/aircast/BuildConfig.java b/android/gen/com/mesmotronic/ane/aircast/BuildConfig.java deleted file mode 100644 index e388a86..0000000 --- a/android/gen/com/mesmotronic/ane/aircast/BuildConfig.java +++ /dev/null @@ -1,6 +0,0 @@ -/** Automatically generated file. DO NOT MODIFY */ -package com.mesmotronic.ane.aircast; - -public final class BuildConfig { - public final static boolean DEBUG = true; -} \ No newline at end of file diff --git a/android/libs/FlashRuntimeExtensions.jar b/android/libs/FlashRuntimeExtensions.jar deleted file mode 100644 index 9f73378..0000000 Binary files a/android/libs/FlashRuntimeExtensions.jar and /dev/null differ diff --git a/android/libs/android-support-v4.jar b/android/libs/android-support-v4.jar deleted file mode 100644 index 96644ed..0000000 Binary files a/android/libs/android-support-v4.jar and /dev/null differ diff --git a/android/project.properties b/android/project.properties deleted file mode 100644 index af8e7ef..0000000 --- a/android/project.properties +++ /dev/null @@ -1,17 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=Google Inc.:Google APIs:23 -android.library=true -android.library.reference.1=C:/Program Files (x86)/Android/android-sdk/extras/android/support/v7/mediarouter -android.library.reference.2=C:/Program Files (x86)/Android/android-sdk/extras/google/google_play_services/libproject/google-play-services_lib diff --git a/android/src/com/mesmotronic/ane/aircast/AirCastExtension.java b/android/src/com/mesmotronic/ane/aircast/AirCastExtension.java deleted file mode 100644 index f8f1686..0000000 --- a/android/src/com/mesmotronic/ane/aircast/AirCastExtension.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import com.adobe.fre.FREContext; -import com.adobe.fre.FREExtension; - -public class AirCastExtension implements FREExtension -{ - public static String TAG = "AirCast"; - - private static AirCastExtensionContext context; - - @Override - public FREContext createContext(String extId) - { - context = new AirCastExtensionContext(); - return context; - } - - @Override - public void dispose() - { - context = null; - } - - @Override - public void initialize() - { - // Nothing to do here - } - -} diff --git a/android/src/com/mesmotronic/ane/aircast/AirCastExtensionContext.java b/android/src/com/mesmotronic/ane/aircast/AirCastExtensionContext.java deleted file mode 100644 index 2585aac..0000000 --- a/android/src/com/mesmotronic/ane/aircast/AirCastExtensionContext.java +++ /dev/null @@ -1,838 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.app.Activity; -import android.content.SharedPreferences; -import android.support.v7.media.MediaRouteSelector; -import android.support.v7.media.MediaRouter; -import android.support.v7.media.MediaRouter.RouteInfo; - -import com.adobe.fre.FREContext; -import com.adobe.fre.FREFunction; -import com.adobe.fre.FREObject; -import com.adobe.fre.FREWrongThreadException; -import com.google.android.gms.cast.CastMediaControlIntent; -import com.google.android.gms.cast.MediaInfo; - -public class AirCastExtensionContext - extends FREContext - implements ChromecastOnMediaUpdatedListener, ChromecastOnSessionUpdatedListener -{ - private static final String SETTINGS_NAME = "AirCastSettings"; - - private static final String EVENT_DEVICE_LIST_CHANGED = "AirCast.deviceListChanged"; - private static final String EVENT_CONNECT_TO_DEVICE = "AirCast.didConnectToDevice"; - private static final String EVENT_DISCONNECTED = "AirCast.didDisconnect"; - private static final String EVENT_RECEIVED_MEDIA_STATE_CHANGE = "AirCast.didReceiveMediaStateChange"; - private static final String EVENT_RECEIVED_CUSTOM_EVENT = "AirCast.didReceiveCustomEvent"; - private static final String EVENT_LOGGING = "LOGGING"; - - private MediaRouter mMediaRouter; - private MediaRouteSelector mMediaRouteSelector; - private volatile ChromecastMediaRouterCallback mMediaRouterCallback = new ChromecastMediaRouterCallback(); - private String appId; - - private boolean isScanning = false; - private boolean autoConnect = false; - private String lastSessionId = null; - private String lastAppId = null; - - private SharedPreferences settings; - - private volatile ChromecastSession currentSession; - - public AirCastExtensionContext() {} - - /* - * NATIVE EXTENSION API - */ - - /** - * Initialize the ANE - */ - public BaseFunction initNE = new BaseFunction() - { - @Override - public FREObject call(final FREContext context, FREObject[] args) - { - final Activity activity = getActivity(); - - settings = activity.getSharedPreferences(SETTINGS_NAME, 0); - lastSessionId = settings.getString("lastSessionId", ""); - lastAppId = settings.getString("lastAppId", ""); - - appId = getStringFromFREObject(args[0]); - - mMediaRouter = MediaRouter.getInstance(activity); - - mMediaRouteSelector = new MediaRouteSelector.Builder() - .addControlCategory(CastMediaControlIntent.categoryForCast(appId)) - .build(); - - log("Initialize AirCast with app ID "+appId); - - activity.runOnUiThread(new Runnable() - { - public void run() - { - mMediaRouter = MediaRouter.getInstance(activity.getApplicationContext()); - mMediaRouteSelector = new MediaRouteSelector.Builder() - .addControlCategory(CastMediaControlIntent.categoryForCast(appId)) - .build(); - - mMediaRouterCallback.registerCallbacks(AirCastExtensionContext.this); - mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - - isScanning = true; - - emitAllRoutes(); - } - }); - - return null; - } - }; - - /** - * TODO Implement this! - */ - public BaseFunction scan = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - if (!isScanning) - { - mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); - } - - isScanning = true; - - emitAllRoutes(); - - return null; - } - }; - - /** - * TODO Implement this! - */ - public BaseFunction stopScan = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - if (isScanning) - { - mMediaRouter.removeCallback(mMediaRouterCallback); - } - - isScanning = false; - - return null; - } - }; - - /** - * Connect to a Chromecast device - */ - public BaseFunction connectToDevice = new BaseFunction() - { - @Override - public FREObject call(final FREContext context, FREObject[] args) - { - final String routeId = getStringFromFREObject(args[0]); - - if (currentSession != null) - { - // TODO Should we disconnect and create a new session? - - return null; - } - - setLastSessionId(""); - - final Activity activity = getActivity(); - - activity.runOnUiThread(new Runnable() - { - public void run() - { - mMediaRouter = MediaRouter.getInstance(activity.getApplicationContext()); - final List routeList = mMediaRouter.getRoutes(); - - for (RouteInfo route : routeList) - { - if (route.getId().equals(routeId)) - { - createSession(route); - return; - } - } - - // TODO Dispatch "No route found" error? - - } - }); - - return null; - } - }; - - /** - * Helper for the creating of a session! The user-selected RouteInfo needs to be passed to a new ChromecastSession - * @param route - * @param callbackContext - */ - private void createSession(final RouteInfo route) - { - currentSession = new ChromecastSession(route, this, this, this); - - // Launch the app. - currentSession.launch(appId, new ChromecastSessionCallback() - { - @Override - void onSuccess(Object object) - { - ChromecastSession session = (ChromecastSession) object; - - if (object == null) - { - onError("unknown"); - } - else if (session == currentSession) - { - setLastSessionId(currentSession.getSessionId()); - currentSession.createSessionObject(); - - dispatchStatus(EVENT_CONNECT_TO_DEVICE, routeToJSON(route)); - } - } - - @Override - void onError(String reason) - { - if (reason != null) - { - // log("createSession onError " + reason); - // TODO Somehow return error to AIR app - } - else - { - // TODO Somehow return unknown error to AIR app - } - } - - }); - } - - private void joinSession(RouteInfo routeInfo) - { - ChromecastSession sessionJoinAttempt = new ChromecastSession(routeInfo, this, this, this); - - sessionJoinAttempt.join(this.appId, this.lastSessionId, new ChromecastSessionCallback() - { - @Override - void onSuccess(Object object) - { - if (currentSession == null) - { - try - { - currentSession = (ChromecastSession) object; - setLastSessionId(currentSession.getSessionId()); - - dispatchStateChange(); - -// sendJavascript("chrome.cast._.sessionJoined(" + currentSession.createSessionObject().toString() + ");"); - } - catch (Exception e) - { - log(e.getMessage()); - } - } - } - - @Override - void onError(String reason) - { -// log("sessionJoinAttempt error " +reason); - } - - }); - } - - /** - * Disconnect from Chromecast device - */ - public BaseFunction disconnectFromDevice = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - mMediaRouter.selectRoute(null); - return null; - } - }; - - /** - * Load media on the connected Chromecast device - */ - public BaseFunction loadMedia = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - if (currentSession != null) - { - // url, thumbnailURL, title, desc, mimeType, startTime, autoPlay; - - String contentId = getStringFromFREObject(args[0]); - String contentType = getStringFromFREObject(args[4]); - Long duration = MediaInfo.UNKNOWN_DURATION; - String streamType = ""; // TODO Make this auto detect? - Boolean autoPlay = getBooleanFromFREObject(args[6]); - Double currentTime = getDoubleFromFREObject(args[5]); - - Map matadataMap = new HashMap(); - matadataMap.put("title", getStringFromFREObject(args[2])); - matadataMap.put("subtitle", getStringFromFREObject(args[3])); - matadataMap.put("thumbnail", getStringFromFREObject(args[1])); - - JSONObject metadata = new JSONObject(matadataMap); - - currentSession.loadMedia - ( - contentId, - contentType, - duration, - streamType, - autoPlay, - currentTime, - metadata, - - new ChromecastSessionCallback() - { - @Override - void onSuccess(Object obj) - { - if (obj == null) - { - onError("unknown"); - } - else - { - dispatchStatus(EVENT_RECEIVED_MEDIA_STATE_CHANGE, (JSONObject)obj); - } - } - - @Override - void onError(String reason) - { - // TODO Error event to AIR - } - } - ); - } - else - { - // TODO Error event to AIR; "session_error" - } - - return null; - } - }; - - /** - * Are we currently connected to a Chromecast device? - */ - public BaseFunction isConnected = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - try - { - return FREObject.newObject(currentSession != null); - } - catch (FREWrongThreadException e) - { - return null; - } - } - }; - - /** - * TODO Implement this - */ - public BaseFunction isPlayingMedia = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - return null; - } - }; - - /** - * Play on the current media in the current session - */ - public BaseFunction playCast = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - if (currentSession != null) - { - currentSession.mediaPlay(stateChangeCallback); - } - - return null; - } - }; - - /** - * Pause on the current media in the current session - */ - public BaseFunction pauseCast = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - if (currentSession != null) - { - currentSession.mediaPause(stateChangeCallback); - } - - return null; - } - }; - - /** - * TODO Implement this - */ - public BaseFunction updateStatsFromDevice = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - dispatchStateChange(); - return null; - } - }; - - /** - * Seeks the current media in the current session - */ - public BaseFunction seek = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - if (currentSession != null) - { - Double seekTime = getDoubleFromFREObject(args[0]); - String resumeState = ""; - - currentSession.mediaSeek(seekTime.longValue() * 1000, resumeState, stateChangeCallback); - } - - return null; - } - }; - - /** - * Stop the currently playing media - */ - public BaseFunction stopCast = new BaseFunction() - { - @Override public FREObject call(FREContext context, FREObject[] args) - { - if (currentSession != null) - { - currentSession.mediaStop(stateChangeCallback); - } - - return null; - } - }; - - /** - * Set volume on device - */ - public BaseFunction setVolume = new BaseFunction() - { - @Override public FREObject call(FREContext context, FREObject[] args) - { - if (currentSession != null) - { - currentSession.setVolume(getDoubleFromFREObject(args[0]), stateChangeCallback); - } - - return null; - } - }; - - /** - * Mute the device - */ - public BaseFunction setMuted = new BaseFunction() - { - @Override public FREObject call(FREContext context, FREObject[] args) - { - if (currentSession != null) - { - Boolean muted = getBooleanFromFREObject(args[0]); - currentSession.mediaSetMuted(muted, stateChangeCallback); - } - - return null; - } - }; - - /** - * Send a custom event to the device - */ - public BaseFunction sendCustomEvent = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - String namespace = getStringFromFREObject(args[0]); - String message = getStringFromFREObject(args[1]); - - if (currentSession != null) - { - currentSession.sendMessage(namespace, message, new ChromecastSessionCallback() - { - @Override - void onSuccess(Object obj) - { - dispatchStatus(EVENT_RECEIVED_CUSTOM_EVENT, (JSONObject) obj); - } - - @Override - void onError(String reason) - { - // TODO Error event in AIR - } - }); - } - - return null; - } - }; - - /** - * Adds a listener to a specific namespace - */ - public BaseFunction addMessageListener = new BaseFunction() - { - @Override - public FREObject call(FREContext context, FREObject[] args) - { - String namespace = getStringFromFREObject(args[0]); - - if (currentSession != null) - { - currentSession.addMessageListener(namespace); - } - - return null; - } - }; - - /* - * INTERNAL FUNCTIONS - */ - - private void setLastSessionId(String sessionId) - { - this.lastSessionId = sessionId; - this.settings.edit().putString("lastSessionId", sessionId).apply(); - } - - @Override - public void dispose() - { - mMediaRouter.removeCallback(mMediaRouterCallback); - } - - /** - * Stops the session - * @param callbackContext - * @return - */ - public boolean sessionStop() - { - if (currentSession != null) - { - currentSession.kill(stateChangeCallback); - currentSession = null; - setLastSessionId(""); - } - - return true; - } - - /** - * Stops the session - * @param callbackContext - * @return - */ - public boolean sessionLeave() - { - if (this.currentSession != null) - { - this.currentSession.leave(stateChangeCallback); - this.currentSession = null; - this.setLastSessionId(""); - } - - return true; - } - - public void emitAllRoutes() - { - final Activity activity = getActivity(); - - activity.runOnUiThread(new Runnable() - { - public void run() - { - mMediaRouter = MediaRouter.getInstance(activity.getApplicationContext()); - List routeList = mMediaRouter.getRoutes(); - - JSONArray devices = new JSONArray(); - - for (RouteInfo route : routeList) - { - if (!route.getName().equals("Phone") && route.getId().indexOf("Cast") > -1) - { - devices.put(routeToJSON(route)); - } - } - - dispatchStatus(EVENT_DEVICE_LIST_CHANGED, devices); - } - }); - } - - /** - * Called when a route is discovered - * @param router - * @param route - */ - protected void onRouteAdded(MediaRouter router, final RouteInfo route, FREContext context) - { -// if (this.autoConnect && this.currentSession == null && !route.getName().equals("Phone")) -// { -// log("Attempting to join route " + route.getName()); -// this.joinSession(route, context); -// } -// else -// { -// log("For some reason, not attempting to join route " + route.getName() + ", " + this.currentSession + ", " + this.autoConnect); -// } -// -// if (!route.getName().equals("Phone") && route.getId().indexOf("Cast") > -1) -// { -// sendJavascript("chrome.cast._.routeAdded(" + routeToJSON(route) + ")"); -// } - - emitAllRoutes(); - } - - /** - * Called when a discovered route is lost - * @param router - * @param route - */ - protected void onRouteRemoved(MediaRouter router, RouteInfo route) - { - emitAllRoutes(); - } - - /** - * Called when a route is selected through the MediaRouter - * - * @param router - * @param route - */ - protected void onRouteSelected(MediaRouter router, RouteInfo route) - { - createSession(route); - } - - /** - * Called when a route is unselected through the MediaRouter - * - * @param router - * @param route - */ - protected void onRouteUnselected(MediaRouter router, RouteInfo route) - { - dispatchStatus(EVENT_DISCONNECTED, routeToJSON(route)); - } - - /** - * Converts RouteInfo data into a format consistent with the iOS elements - * of the ANE ready to be returned to AIR - * - * @param route - * @return - */ - private JSONObject routeToJSON(RouteInfo route) - { - JSONObject obj = new JSONObject(); - - try - { - obj.put("ipAddress", ""); - obj.put("servicePort", ""); - obj.put("deviceID", route.getId()); - obj.put("friendlyName", route.getName()); - obj.put("manufacturer", ""); - obj.put("modelName", ""); - obj.put("icons", new JSONArray()); - } - catch (JSONException e) - { - e.printStackTrace(); - } - - return obj; - } - - @Override - public void onMediaUpdated(boolean isAlive, JSONObject media) - { - dispatchStateChange(); - -// if (isAlive) -// { -// sendJavascript("chrome.cast._.mediaUpdated(true, " + media.toString() +");"); -// } -// else -// { -// sendJavascript("chrome.cast._.mediaUpdated(false, " + media.toString() +");"); -// } - } - - @Override - public void onSessionUpdated(boolean isAlive, JSONObject session) - { - if (isAlive) - { - dispatchStateChange(); -// sendJavascript("chrome.cast._.sessionUpdated(true, " + session.toString() + ");"); - } - else - { -// sendJavascript("chrome.cast._.sessionUpdated(false, " + session.toString() + ");"); - this.currentSession = null; - } - } - - @Override - public void onMediaLoaded(JSONObject media) - { - dispatchStateChange(); -// sendJavascript("chrome.cast._.mediaLoaded(true, " + media.toString() +");"); - } - - @Override - public void onMessage(ChromecastSession session, String namespace, String message) - { - dispatchStateChange(); -// sendJavascript("chrome.cast._.onMessage('" + session.getSessionId() +"', '" + namespace + "', '" + message + "')"); - } - - /** - * Default callback for any action that results in the media state changing - * - * TODO Implement this - */ - private ChromecastSessionCallback stateChangeCallback = new ChromecastSessionCallback() - { - @Override - void onSuccess(Object object) - { - dispatchStateChange(); - } - - @Override - void onError(String reason) - { - // TODO Dispatch errors back to AIR? - } - }; - - /** - * Dispatches the current media state back to AIR - */ - private void dispatchStateChange() - { - dispatchStatus(EVENT_RECEIVED_MEDIA_STATE_CHANGE, currentSession.createSessionObject()); - } - - /** - * Dispatch a status event to AIR - * - * @param context - * @param level - * @param code - */ - private void dispatchStatus(String type, String data) - { - dispatchStatusEventAsync(type, data); - } - - private void dispatchStatus(String type, JSONObject data) - { - dispatchStatus(type, data.toString()); - } - - private void dispatchStatus(String type, JSONArray data) - { - dispatchStatus(type, data.toString()); - } - - private void log(String s) - { - dispatchStatus(EVENT_LOGGING, s); - } - - @Override - public Map getFunctions() - { - Map functions = new HashMap(); - - functions.put("initNE", initNE); - functions.put("scan", scan); - functions.put("stopScan", stopScan); - functions.put("connectToDevice", connectToDevice); - functions.put("disconnectFromDevice", disconnectFromDevice); - functions.put("loadMedia", loadMedia); - functions.put("isConnected", isConnected); - functions.put("isPlayingMedia", isPlayingMedia); - functions.put("playCast", playCast); - functions.put("pauseCast", pauseCast); - functions.put("updateStatsFromDevice", updateStatsFromDevice); - functions.put("seek", seek); - functions.put("stopCast", stopCast); - functions.put("setVolume", setVolume); - functions.put("setMuted", setMuted); - functions.put("sendCustomEvent", sendCustomEvent); - - return functions; - } - -} diff --git a/android/src/com/mesmotronic/ane/aircast/BaseFunction.java b/android/src/com/mesmotronic/ane/aircast/BaseFunction.java deleted file mode 100644 index 189b06b..0000000 --- a/android/src/com/mesmotronic/ane/aircast/BaseFunction.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import java.util.ArrayList; -import java.util.List; - -import android.os.Bundle; - -import com.adobe.fre.FREArray; -import com.adobe.fre.FREContext; -import com.adobe.fre.FREFunction; -import com.adobe.fre.FREObject; - -public class BaseFunction implements FREFunction -{ - @Override - public FREObject call(FREContext context, FREObject[] args) - { - return null; - } - - protected String getStringFromFREObject(FREObject object) - { - try - { - return object.getAsString(); - } - catch (Exception e) - { - e.printStackTrace(); - return ""; - } - } - - protected Boolean getBooleanFromFREObject(FREObject object) - { - try - { - return object.getAsBool(); - } - catch(Exception e) - { - e.printStackTrace(); - return false; - } - } - - protected Double getDoubleFromFREObject(FREObject object) - { - try - { - return object.getAsDouble(); - } - catch(Exception e) - { - e.printStackTrace(); - return 0.0; - } - } - - protected List getListOfStringFromFREArray(FREArray array) - { - List result = new ArrayList(); - - try - { - for (int i = 0; i < array.getLength(); i++) - { - try - { - result.add(getStringFromFREObject(array.getObjectAt((long)i))); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - } - catch (Exception e) - { - e.printStackTrace(); - return null; - } - - return result; - } - - protected Bundle getBundleOfStringFromFREArrays(FREArray keys, FREArray values) - { - Bundle result = new Bundle(); - - try - { - long length = Math.min(keys.getLength(), values.getLength()); - for (int i = 0; i < length; i++) - { - try - { - String key = getStringFromFREObject(keys.getObjectAt((long)i)); - String value = getStringFromFREObject(values.getObjectAt((long)i)); - result.putString(key, value); - } - catch (Exception e) - { - e.printStackTrace(); - } - } - } - catch (Exception e) - { - e.printStackTrace(); - return null; - } - - return result; - } -} diff --git a/android/src/com/mesmotronic/ane/aircast/ChromecastException.java b/android/src/com/mesmotronic/ane/aircast/ChromecastException.java deleted file mode 100644 index 04b4edb..0000000 --- a/android/src/com/mesmotronic/ane/aircast/ChromecastException.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.mesmotronic.ane.aircast; - -public class ChromecastException extends Exception { - -} diff --git a/android/src/com/mesmotronic/ane/aircast/ChromecastMediaController.java b/android/src/com/mesmotronic/ane/aircast/ChromecastMediaController.java deleted file mode 100644 index 033c85e..0000000 --- a/android/src/com/mesmotronic/ane/aircast/ChromecastMediaController.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import com.google.android.gms.cast.MediaInfo; -import com.google.android.gms.cast.MediaMetadata; -import com.google.android.gms.cast.RemoteMediaPlayer; -import com.google.android.gms.cast.RemoteMediaPlayer.MediaChannelResult; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.PendingResult; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.images.WebImage; - -import org.json.JSONException; -import org.json.JSONObject; - -import android.net.Uri; - -public class ChromecastMediaController -{ - private RemoteMediaPlayer remote = null; - - public ChromecastMediaController(RemoteMediaPlayer mRemoteMediaPlayer) - { - remote = mRemoteMediaPlayer; - } - - public MediaInfo createLoadUrlRequest(String contentId, String contentType, long duration, String streamType, JSONObject metadata) - { - // Try creating a GENERIC MediaMetadata obj - MediaMetadata mediaMetadata = new MediaMetadata(); - - try - { - int metadataType = metadata.has("metadataType") - ? metadata.getInt("metadataType") - : MediaMetadata.MEDIA_TYPE_MOVIE; - - // GENERIC - if (metadataType == MediaMetadata.MEDIA_TYPE_GENERIC) - { - mediaMetadata = new MediaMetadata(); // Creates GENERIC MediaMetaData - mediaMetadata.putString(MediaMetadata.KEY_TITLE, (metadata.has("title")) ? metadata.getString("title") : ""); - mediaMetadata.putString(MediaMetadata.KEY_SUBTITLE, (metadata.has("title")) ? metadata.getString("subtitle") : ""); - mediaMetadata = addImages(metadata, mediaMetadata); - } - - } - catch(Exception e) - { - e.printStackTrace(); - // Fallback - mediaMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE); - } - - int _streamType = MediaInfo.STREAM_TYPE_BUFFERED; - - if (streamType.equals("live")) - { - _streamType = MediaInfo.STREAM_TYPE_LIVE; - } - else if (streamType.equals("other")) - { - _streamType = MediaInfo.STREAM_TYPE_NONE; - } - - MediaInfo mediaInfo = new MediaInfo.Builder(contentId) - .setContentType(contentType) - .setStreamType(_streamType) - .setStreamDuration(duration) - .setMetadata(mediaMetadata) - .build(); - - return mediaInfo; - } - - public void play(GoogleApiClient apiClient, ChromecastSessionCallback callback) - { - PendingResult res = this.remote.play(apiClient); - res.setResultCallback(this.createMediaCallback(callback)); - } - - public void pause(GoogleApiClient apiClient, ChromecastSessionCallback callback) - { - PendingResult res = this.remote.pause(apiClient); - res.setResultCallback(this.createMediaCallback(callback)); - } - - public void stop(GoogleApiClient apiClient, ChromecastSessionCallback callback) - { - PendingResult res = this.remote.stop(apiClient); - res.setResultCallback(this.createMediaCallback(callback)); - } - - public void seek(long seekPosition, String resumeState, GoogleApiClient apiClient, ChromecastSessionCallback callback) - { - PendingResult res = null; - - if (resumeState != null && !resumeState.equals("")) - { - if (resumeState.equals("PLAYBACK_PAUSE")) - { - res = this.remote.seek(apiClient, seekPosition, RemoteMediaPlayer.RESUME_STATE_PAUSE); - } - else if (resumeState.equals("PLAYBACK_START")) - { - res = this.remote.seek(apiClient, seekPosition, RemoteMediaPlayer.RESUME_STATE_PLAY); - } - else - { - res = this.remote.seek(apiClient, seekPosition, RemoteMediaPlayer.RESUME_STATE_UNCHANGED); - } - } - - if (res == null) - { - res = this.remote.seek(apiClient, seekPosition); - } - - res.setResultCallback(this.createMediaCallback(callback)); - } - - public void setVolume(double volume, GoogleApiClient apiClient, ChromecastSessionCallback callback) - { - PendingResult res = this.remote.setStreamVolume(apiClient, volume); - res.setResultCallback(this.createMediaCallback(callback)); - } - - public void setMuted(boolean muted, GoogleApiClient apiClient, ChromecastSessionCallback callback) - { - PendingResult res = this.remote.setStreamMute(apiClient, muted); - res.setResultCallback(this.createMediaCallback(callback)); - } - - private ResultCallback createMediaCallback(final ChromecastSessionCallback callback) - { - return new ResultCallback() - { - @Override - public void onResult(MediaChannelResult result) - { - if (result.getStatus().isSuccess()) - { - callback.onSuccess(); - } - else - { - callback.onError("channel_error"); - } - } - }; - } - - private MediaMetadata addImages(JSONObject metadata, MediaMetadata mediaMetadata) throws JSONException - { - if (metadata.has("thumbnail")) - { - String imageUrl = metadata.getString("thumbnail"); - - if (imageUrl.indexOf("http") == 0) - { - Uri imageURI = Uri.parse( imageUrl ); - WebImage webImage = new WebImage(imageURI); - mediaMetadata.addImage(webImage); - } - } - - return mediaMetadata; - } - -} diff --git a/android/src/com/mesmotronic/ane/aircast/ChromecastMediaRouterCallback.java b/android/src/com/mesmotronic/ane/aircast/ChromecastMediaRouterCallback.java deleted file mode 100644 index 17e9c63..0000000 --- a/android/src/com/mesmotronic/ane/aircast/ChromecastMediaRouterCallback.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import java.util.ArrayList; -import java.util.Collection; - -import android.support.v7.media.MediaRouter; -import android.support.v7.media.MediaRouter.RouteInfo; - -public class ChromecastMediaRouterCallback extends MediaRouter.Callback { - private volatile ArrayList routes = new ArrayList(); - - private AirCastExtensionContext callback = null; - - public void registerCallbacks(AirCastExtensionContext instance) - { - callback = instance; - } - - public synchronized RouteInfo getRoute(String id) - { - for (RouteInfo i : this.routes) - { - if (i.getId().equals(id)) - { - return i; - } - } - return null; - } - - public synchronized RouteInfo getRoute(int index) - { - return routes.get(index); - } - - public synchronized Collection getRoutes() - { - return routes; - } - - @Override - public synchronized void onRouteAdded(MediaRouter router, RouteInfo route) - { - routes.add(route); - - if (callback != null) - { - callback.onRouteAdded(router, route, null); - } - } - - @Override - public void onRouteRemoved(MediaRouter router, RouteInfo route) - { - routes.remove(route); - - if (callback != null) - { - callback.onRouteRemoved(router, route); - } - } - - @Override - public void onRouteSelected(MediaRouter router, RouteInfo info) - { - if (callback != null) - { - callback.onRouteSelected(router, info); - } - } - - @Override - public void onRouteUnselected(MediaRouter router, RouteInfo info) - { - if (callback != null) - { - callback.onRouteUnselected(router, info); - } - } -} diff --git a/android/src/com/mesmotronic/ane/aircast/ChromecastOnMediaUpdatedListener.java b/android/src/com/mesmotronic/ane/aircast/ChromecastOnMediaUpdatedListener.java deleted file mode 100644 index 945a745..0000000 --- a/android/src/com/mesmotronic/ane/aircast/ChromecastOnMediaUpdatedListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import org.json.JSONObject; - -public interface ChromecastOnMediaUpdatedListener -{ - void onMediaLoaded(JSONObject media); - void onMediaUpdated(boolean isAlive, JSONObject media); -} \ No newline at end of file diff --git a/android/src/com/mesmotronic/ane/aircast/ChromecastOnSessionUpdatedListener.java b/android/src/com/mesmotronic/ane/aircast/ChromecastOnSessionUpdatedListener.java deleted file mode 100644 index bcfd7c5..0000000 --- a/android/src/com/mesmotronic/ane/aircast/ChromecastOnSessionUpdatedListener.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import org.json.JSONObject; - -public interface ChromecastOnSessionUpdatedListener -{ - void onSessionUpdated(boolean isAlive, JSONObject properties); - void onMessage(ChromecastSession session, String namespace, String message); -} \ No newline at end of file diff --git a/android/src/com/mesmotronic/ane/aircast/ChromecastSession.java b/android/src/com/mesmotronic/ane/aircast/ChromecastSession.java deleted file mode 100644 index f9b29db..0000000 --- a/android/src/com/mesmotronic/ane/aircast/ChromecastSession.java +++ /dev/null @@ -1,790 +0,0 @@ -package com.mesmotronic.ane.aircast; - -import java.io.IOException; -import java.util.HashSet; -import java.util.List; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import com.adobe.fre.FREContext; -import com.google.android.gms.cast.ApplicationMetadata; -import com.google.android.gms.cast.Cast; -import com.google.android.gms.cast.Cast.ApplicationConnectionResult; -import com.google.android.gms.cast.CastDevice; -import com.google.android.gms.cast.MediaInfo; -import com.google.android.gms.cast.MediaStatus; -import com.google.android.gms.cast.RemoteMediaPlayer; -import com.google.android.gms.cast.RemoteMediaPlayer.MediaChannelResult; -import com.google.android.gms.cast.RemoteMediaPlayer.OnMetadataUpdatedListener; -import com.google.android.gms.cast.RemoteMediaPlayer.OnStatusUpdatedListener; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; -import com.google.android.gms.common.images.WebImage; - -import android.os.Bundle; -import android.support.v7.media.MediaRouter.RouteInfo; - -/* - * All of the Chromecast session specific functions should start here. - */ -public class ChromecastSession - extends Cast.Listener - implements - GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener, - OnMetadataUpdatedListener, - OnStatusUpdatedListener, - Cast.MessageReceivedCallback { - - private RouteInfo routeInfo = null; - private volatile GoogleApiClient mApiClient = null; - private volatile RemoteMediaPlayer mRemoteMediaPlayer; - private FREContext context = null; - private CastDevice device = null; - private ChromecastMediaController chromecastMediaController; - private ChromecastOnMediaUpdatedListener onMediaUpdatedListener; - private ChromecastOnSessionUpdatedListener onSessionUpdatedListener; - - private volatile String appId; - private volatile String displayName; - private volatile List appImages; - private volatile String sessionId = null; - private volatile String lastSessionId = null; - private boolean isConnected = false; - - private ChromecastSessionCallback launchCallback; - private ChromecastSessionCallback joinSessionCallback; - - private boolean joinInsteadOfConnecting = false; - private HashSet messageNamespaces = new HashSet(); - - public ChromecastSession(RouteInfo routeInfo, FREContext freContext, - ChromecastOnMediaUpdatedListener onMediaUpdatedListener, - ChromecastOnSessionUpdatedListener onSessionUpdatedListener - ) - { - this.context = freContext; - this.onMediaUpdatedListener = onMediaUpdatedListener; - this.onSessionUpdatedListener = onSessionUpdatedListener; - this.routeInfo = routeInfo; - this.device = CastDevice.getFromBundle(this.routeInfo.getExtras()); - - this.mRemoteMediaPlayer = new RemoteMediaPlayer(); - this.mRemoteMediaPlayer.setOnMetadataUpdatedListener(this); - this.mRemoteMediaPlayer.setOnStatusUpdatedListener(this); - - this.chromecastMediaController = new ChromecastMediaController(mRemoteMediaPlayer); - } - - - /** - * Sets the wheels in motion - connects to the Chromecast and launches the given app - * @param appId - */ - public void launch(String appId, ChromecastSessionCallback launchCallback) - { - this.appId = appId; - this.launchCallback = launchCallback; - this.connectToDevice(); - } - - public boolean isConnected() - { - return this.isConnected; - } - - /** - * Adds a message listener if one does not already exist - * @param namespace - */ - public void addMessageListener(String namespace) - { - if (messageNamespaces.contains(namespace) == false) - { - try - { - Cast.CastApi.setMessageReceivedCallbacks(mApiClient, namespace, this); - messageNamespaces.add(namespace); - } - catch(Exception e) - { - // - } - } - } - - /** - * Sends a message to a specified namespace - * @param namespace - * @param message - * @param callback - */ - public void sendMessage(String namespace, String message, final ChromecastSessionCallback callback) - { - try - { - Cast.CastApi.sendMessage(mApiClient, namespace, message).setResultCallback(new ResultCallback() - { - @Override - public void onResult(Status result) - { - if (result.isSuccess()) - { - callback.onSuccess(); - } - else - { - callback.onError(result.toString()); - } - } - }); - } - catch(Exception e) - { - callback.onError(e.getMessage()); - } - } - - /** - * Join a currently running app with an appId and a session - * @param appId - * @param sessionId - * @param joinSessionCallback - */ - public void join (String appId, String sessionId, ChromecastSessionCallback joinSessionCallback) - { - this.appId = appId; - this.joinSessionCallback = joinSessionCallback; - this.joinInsteadOfConnecting = true; - this.lastSessionId = sessionId; - this.connectToDevice(); - } - - /** - * Kills a session and it's underlying media player - * @param callback - */ - public void kill (final ChromecastSessionCallback callback) - { - try - { - Cast.CastApi.stopApplication(mApiClient); - mApiClient.disconnect(); - } - catch(Exception e) - { - // - } - - callback.onSuccess(); - } - - /** - * Leaves the session. - * @param callback - */ - public void leave (final ChromecastSessionCallback callback) - { - try - { - Cast.CastApi.leaveApplication(mApiClient); - } - catch(Exception e) - { - // - } - - callback.onSuccess(); - } - - /** - * Loads media over the media API - * @param contentId - The URL of the content - * @param contentType - The MIME type of the content - * @param duration - The length of the video (if known) - * @param streamType - * @param autoPlay - Whether or not to start the video playing or not - * @param currentTime - Where in the video to begin playing from - * @param callback - * @return - */ - public boolean loadMedia(String contentId, String contentType, long duration, - String streamType, boolean autoPlay, double currentTime, - JSONObject metadata, final ChromecastSessionCallback callback - ) - { - try - { - MediaInfo mediaInfo = chromecastMediaController.createLoadUrlRequest(contentId, contentType, duration, streamType, metadata); - - mRemoteMediaPlayer.load - ( - mApiClient, - mediaInfo, - autoPlay, - (long)(currentTime * 1000)) - - .setResultCallback(new ResultCallback() - { - @Override - public void onResult(MediaChannelResult result) - { - if (result.getStatus().isSuccess()) - { - System.out.println("Media loaded successfully"); - - ChromecastSession.this.onMediaUpdatedListener.onMediaLoaded(ChromecastSession.this.createMediaObject()); - callback.onSuccess(ChromecastSession.this.createMediaObject()); - } - else - { - callback.onError("session_error"); - } - } - } - ); - } - catch (IllegalStateException e) - { - e.printStackTrace(); - System.out.println("Problem occurred with media during loading"); - callback.onError("session_error"); - - return false; - } - catch (Exception e) - { - e.printStackTrace(); - callback.onError("session_error"); - System.out.println("Problem opening media during loading"); - - return false; - } - - return true; - } - - /** - * Media API - Calls play on the current media - * @param callback - */ - public void mediaPlay(ChromecastSessionCallback callback) - { - chromecastMediaController.play(mApiClient, callback); - } - - /** - * Media API - Calls pause on the current media - * @param callback - */ - public void mediaPause(ChromecastSessionCallback callback) - { - chromecastMediaController.pause(mApiClient, callback); - } - - /** - * Media API - Seeks the current playing media - * @param seekPosition - Seconds to seek to - * @param resumeState - Resume state once seeking is complete: PLAYBACK_PAUSE or PLAYBACK_START - * @param callback - */ - public void mediaSeek(long seekPosition, String resumeState, ChromecastSessionCallback callback) - { - chromecastMediaController.seek(seekPosition, resumeState, mApiClient, callback); - } - - /** - * Media API - Sets the volume on the current playing media object NOT ON THE CHROMECAST DIRECTLY - * @param level - * @param callback - */ - public void mediaSetVolume(double level, ChromecastSessionCallback callback) - { - chromecastMediaController.setVolume(level, mApiClient, callback); - } - - /** - * Media API - Sets the muted state on the current playing media NOT THE CHROMECAST DIRECTLY - * @param muted - * @param callback - */ - public void mediaSetMuted(boolean muted, ChromecastSessionCallback callback) - { - chromecastMediaController.setMuted(muted, mApiClient, callback); - } - - /** - * Media API - Stops and unloads the current playing media - * @param callback - */ - public void mediaStop(ChromecastSessionCallback callback) - { - chromecastMediaController.stop(mApiClient, callback); - } - - - /** - * Sets the receiver volume level - * @param volume - * @param callback - */ - public void setVolume(double volume, ChromecastSessionCallback callback) - { - try - { - Cast.CastApi.setVolume(mApiClient, volume); - callback.onSuccess(); - } - catch (Exception e) - { - e.printStackTrace(); - callback.onError(e.getMessage()); - } - } - - /** - * Mutes the receiver - * @param muted - * @param callback - */ - public void setMute(boolean muted, ChromecastSessionCallback callback) - { - try - { - Cast.CastApi.setMute(mApiClient, muted); - callback.onSuccess(); - } - catch (Exception e) - { - e.printStackTrace(); - callback.onError(e.getMessage()); - } - } - - /** - * Connects to the device with all callbacks and things - */ - private void connectToDevice() - { - try - { - Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions.builder(this.device, this); - - this.mApiClient = new GoogleApiClient.Builder(this.context.getActivity().getApplicationContext()) - .addApi(Cast.API, apiOptionsBuilder.build()) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .build(); - - this.mApiClient.connect(); - } - catch(Exception e) - { - e.printStackTrace(); - } - } - - /** - * Launches the application and gets a new session - */ - private void launchApplication() - { - Cast.CastApi.launchApplication(mApiClient, this.appId, false) - .setResultCallback(launchApplicationResultCallback); - } - - /** - * Attemps to join an already running session - */ - private void joinApplication() - { - Cast.CastApi.joinApplication(this.mApiClient, this.appId, this.lastSessionId) - .setResultCallback(joinApplicationResultCallback); - } - - /** - * Connects to the remote media player on the receiver - * @throws IllegalStateException - * @throws IOException - */ - private void connectRemoteMediaPlayer() throws IllegalStateException, IOException - { - Cast.CastApi.setMessageReceivedCallbacks(mApiClient, mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer); - - mRemoteMediaPlayer.requestStatus(mApiClient) - .setResultCallback(connectRemoteMediaPlayerCallback); - } - - /** - * launchApplication callback - */ - private ResultCallback launchApplicationResultCallback = new ResultCallback() - { - @Override - public void onResult(ApplicationConnectionResult result) - { - ApplicationMetadata metadata = result.getApplicationMetadata(); - ChromecastSession.this.sessionId = result.getSessionId(); - ChromecastSession.this.displayName = metadata.getName(); - ChromecastSession.this.appImages = metadata.getImages(); - - Status status = result.getStatus(); - - if (status.isSuccess()) - { - try - { - ChromecastSession.this.launchCallback.onSuccess(ChromecastSession.this); - connectRemoteMediaPlayer(); - ChromecastSession.this.isConnected = true; - } - catch (IllegalStateException e) - { - e.printStackTrace(); - } - catch (IOException e) - { - e.printStackTrace(); - } - } - else - { - ChromecastSession.this.isConnected = false; - } - } - }; - - /** - * joinApplication callback - */ - private ResultCallback joinApplicationResultCallback = new ResultCallback() - { - @Override - public void onResult(ApplicationConnectionResult result) - { - Status status = result.getStatus(); - - if (status.isSuccess()) - { - try - { - ApplicationMetadata metadata = result.getApplicationMetadata(); - ChromecastSession.this.sessionId = result.getSessionId(); - ChromecastSession.this.displayName = metadata.getName(); - ChromecastSession.this.appImages = metadata.getImages(); - - ChromecastSession.this.joinSessionCallback.onSuccess(ChromecastSession.this); - connectRemoteMediaPlayer(); - ChromecastSession.this.isConnected = true; - } - catch (IllegalStateException e) - { - e.printStackTrace(); - } - catch (IOException e) - { - e.printStackTrace(); - } - } - else - { - ChromecastSession.this.joinSessionCallback.onError(status.toString()); - ChromecastSession.this.isConnected = false; - } - } - }; - - /** - * connectRemoteMediaPlayer callback - */ - private ResultCallback connectRemoteMediaPlayerCallback = new ResultCallback() - { - @Override - public void onResult(MediaChannelResult result) - { - if (result.getStatus().isSuccess()) - { - ChromecastSession.this.onMediaUpdatedListener.onMediaUpdated(true, ChromecastSession.this.createMediaObject()); - /*ChromecastSession.this.onMediaUpdatedListener.onMediaLoaded(ChromecastSession.this.createMediaObject());*/ - } - else - { - System.out.println("Failed to request status."); - } - } - }; - - /** - * Creates a JSON representation of this session - * @return - */ - public JSONObject createSessionObject() - { - JSONObject out = new JSONObject(); - - try - { - out.put("appId", this.appId); - - if (this.appImages != null) - { - JSONArray appImages = new JSONArray(); - - for(WebImage o : this.appImages) - { - appImages.put(o.toString()); - } - } - - out.put("appImages", appImages); - out.put("sessionId", this.sessionId); - out.put("displayName", this.displayName); - - JSONObject receiver = new JSONObject(); - receiver.put("friendlyName", this.device.getFriendlyName()); - receiver.put("label", this.device.getDeviceId()); - - JSONObject volume = new JSONObject(); - - try - { - volume.put("level", Cast.CastApi.getVolume(mApiClient)); - volume.put("muted", Cast.CastApi.isMute(mApiClient)); - } - catch(Exception e) - { - // - } - - receiver.put("volume", volume); - - out.put("receiver", receiver); - - } - catch(JSONException e) - { - // - } - - return out; - } - - /** - * Creates a JSON representation of the current playing media - * @return - */ - private JSONObject createMediaObject() - { - JSONObject out = new JSONObject(); - JSONObject objInfo = new JSONObject(); - - MediaStatus mediaStatus = mRemoteMediaPlayer.getMediaStatus(); - - if (mediaStatus == null) - { - return out; - } - - MediaInfo mediaInfo = mediaStatus.getMediaInfo(); - - try - { - out.put("media", objInfo); - out.put("mediaSessionId", 1); - out.put("sessionId", this.sessionId); - out.put("currentTime", mediaStatus.getStreamPosition() / 1000.0); - out.put("playbackRate", mediaStatus.getPlaybackRate()); - out.put("customData", mediaStatus.getCustomData()); - - switch(mediaStatus.getPlayerState()) - { - case MediaStatus.PLAYER_STATE_BUFFERING: - out.put("playerState", "BUFFERING"); break; - case MediaStatus.PLAYER_STATE_IDLE: - out.put("playerState", "IDLE"); break; - case MediaStatus.PLAYER_STATE_PAUSED: - out.put("playerState", "PAUSED"); break; - case MediaStatus.PLAYER_STATE_PLAYING: - out.put("playerState", "PLAYING"); break; - case MediaStatus.PLAYER_STATE_UNKNOWN: - out.put("playerState", "UNKNOWN"); break; - } - - switch (mediaStatus.getIdleReason()) - { - case MediaStatus.IDLE_REASON_CANCELED: - out.put("idleReason", "canceled"); break; - case MediaStatus.IDLE_REASON_ERROR: - out.put("idleReason", "error"); break; - case MediaStatus.IDLE_REASON_FINISHED: - out.put("idleReason", "finished"); break; - case MediaStatus.IDLE_REASON_INTERRUPTED: - out.put("idleReason", "iterrupted"); break; - case MediaStatus.IDLE_REASON_NONE: - out.put("idleReason", "none"); break; - } - - JSONObject volume = new JSONObject(); - volume.put("level", mediaStatus.getStreamVolume()); - volume.put("muted", mediaStatus.isMute()); - - out.put("volume", volume); - - try - { - objInfo.put("duration", mediaInfo.getStreamDuration() / 1000.0); - - switch(mediaInfo.getStreamType()) - { - case MediaInfo.STREAM_TYPE_BUFFERED: - objInfo.put("streamType", "buffered"); break; - case MediaInfo.STREAM_TYPE_LIVE: - objInfo.put("streamType", "live"); break; - case MediaInfo.STREAM_TYPE_NONE: - objInfo.put("streamType", "other"); break; - } - } - catch (Exception e) - { - // - } - - } - catch(JSONException e) - { - // - } - - return out; - } - - - - /* GoogleApiClient.ConnectionCallbacks implementation - * Called when we successfully connect to the API - * (non-Javadoc) - * @see com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks#onConnected(android.os.Bundle) - */ - @Override - public void onConnected(Bundle connectionHint) - { - if (this.joinInsteadOfConnecting) - { - this.joinApplication(); - } - else - { - this.launchApplication(); - } - } - - - /* GoogleApiClient.ConnectionCallbacks implementation - * (non-Javadoc) - * @see com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks#onConnectionSuspended(android.os.Bundle) - */ - @Override - public void onConnectionSuspended(int cause) - { - if (this.onSessionUpdatedListener != null) - { - this.isConnected = false; - this.onSessionUpdatedListener.onSessionUpdated(false, this.createSessionObject()); - } - } - - /* - * GoogleApiClient.OnConnectionFailedListener implementation - * When Google API fails to connect. - * (non-Javadoc) - * @see com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener#onConnectionFailed(com.google.android.gms.common.ConnectionResult) - */ - @Override - public void onConnectionFailed(ConnectionResult result) - { - if (this.launchCallback != null) - { - this.isConnected = false; - this.launchCallback.onError("channel_error"); - } - } - - /** - * Cast.Listener implementation - * When Chromecast application status changed - */ - @Override - public void onApplicationStatusChanged() - { - if (this.onSessionUpdatedListener != null) - { - ChromecastSession.this.isConnected = true; - this.onSessionUpdatedListener.onSessionUpdated(true, createSessionObject()); - } - } - - /** - * Cast.Listener implementation - * When the volume is changed on the Chromecast - */ - @Override - public void onVolumeChanged() - { - if (this.onSessionUpdatedListener != null) - { - this.onSessionUpdatedListener.onSessionUpdated(true, createSessionObject()); - } - } - - /** - * Cast.Listener implementation - * When the application is disconnected - */ - @Override - public void onApplicationDisconnected(int errorCode) - { - if (this.onSessionUpdatedListener != null) - { - this.isConnected = false; - this.onSessionUpdatedListener.onSessionUpdated(false, this.createSessionObject()); - } - } - - - @Override - public void onMetadataUpdated() - { - if (this.onMediaUpdatedListener != null) - { - this.onMediaUpdatedListener.onMediaUpdated(true, this.createMediaObject()); - } - } - - - @Override - public void onStatusUpdated() - { - if (this.onMediaUpdatedListener != null) - { - this.onMediaUpdatedListener.onMediaUpdated(true, this.createMediaObject()); - } - } - - /// GETTERS - public String getSessionId() - { - return this.sessionId; - } - - @Override - public void onMessageReceived(CastDevice castDevice, String namespace, String message) - { - if (this.onSessionUpdatedListener != null) { - this.onSessionUpdatedListener.onMessage(this, namespace, message); - } - } -} diff --git a/android/src/com/mesmotronic/ane/aircast/ChromecastSessionCallback.java b/android/src/com/mesmotronic/ane/aircast/ChromecastSessionCallback.java deleted file mode 100644 index d68e6c0..0000000 --- a/android/src/com/mesmotronic/ane/aircast/ChromecastSessionCallback.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mesmotronic.ane.aircast; - -public abstract class ChromecastSessionCallback -{ - public void onSuccess() - { - onSuccess(null); - } - - abstract void onSuccess(Object object); - abstract void onError(String reason); -} \ No newline at end of file diff --git a/build/android/library.swf b/build/android/library.swf deleted file mode 100644 index ca90739..0000000 Binary files a/build/android/library.swf and /dev/null differ diff --git a/build/build.cmd b/build/build.cmd index 9012b56..bc14acf 100644 --- a/build/build.cmd +++ b/build/build.cmd @@ -1,13 +1,9 @@ @echo off -copy ..\android\bin\aircast-jar.jar .\android\ - adt ^ -package ^ -target ane ../example/libs/AirCast.ane extension.xml ^ -swc ../swc/bin/aircast-lib.swc ^ - -platform Android-ARM -C android . ^ - -platform Android-x86 -C android . ^ -platform iPhone-ARM -C ios . ^ -platform default -C default . diff --git a/build/extension.xml b/build/extension.xml index ebf7075..4b3bebd 100755 --- a/build/extension.xml +++ b/build/extension.xml @@ -12,22 +12,6 @@ AirCastFinalizer - - - - aircast-jar.jar - com.mesmotronic.ane.aircast.AirCastExtension - com.mesmotronic.ane.aircast.AirCastExtension - - - - - - aircast-jar.jar - com.mesmotronic.ane.aircast.AirCastExtension - com.mesmotronic.ane.aircast.AirCastExtension - -