Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove sharedPreferences and NSUserDefaults for storing the app id #2

Merged
merged 2 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,34 @@ The "*Description" key strings will be used when asking the user for permission
</platform>
```

1. In AppDelegate.m (or AppDelegate.swift) add

```
#import <GoogleCast/GoogleCast.h>
```

```swift
import GoogleCast
```

and insert the following in the `application:didFinishLaunchingWithOptions` method, ideally at the beginning (or right after Flipper initialization):

```
NSString *receiverAppID = kGCKDefaultMediaReceiverApplicationID; // or @"ABCD1234"
GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc] initWithApplicationID:receiverAppID];
GCKCastOptions* options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria];
[GCKCastContext setSharedInstanceWithOptions:options];
```

```swift
let receiverAppID = kGCKDefaultMediaReceiverApplicationID // or "ABCD1234"
let criteria = GCKDiscoveryCriteria(applicationID: receiverAppID)
let options = GCKCastOptions(discoveryCriteria: criteria)
GCKCastContext.setSharedInstanceWith(options)
```

If using a custom receiver, replace kGCKDefaultMediaReceiverApplicationID with your receiver app id.

## Chromecast Icon Assets
[chromecast-assets.zip](https://github.com/jellyfin/cordova-plugin-chromecast/wiki/chromecast-assets.zip)

Expand Down
1 change: 1 addition & 0 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
<meta-data android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME" android:value="acidhax.cordova.chromecast.CastOptionsProvider" />
<meta-data android:name="com.google.android.gms.cast.framework.RECEIVER_APPLICATION_ID" android:value="CC1AD845" />
</config-file>

<framework src="com.google.android.gms:play-services-cast-framework:21.3.0" />
Expand Down
22 changes: 13 additions & 9 deletions src/android/CastOptionsProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,35 @@

import java.util.List;

import com.google.android.gms.cast.CastMediaControlIntent;
import com.google.android.gms.cast.framework.CastOptions;
import com.google.android.gms.cast.framework.OptionsProvider;
import com.google.android.gms.cast.framework.SessionProvider;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;

public final class CastOptionsProvider implements OptionsProvider {

/** The app id. */
private static String appId;
protected String getReceiverApplicationId(Context context) {
String appId = null;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_META_DATA);
appId = packageInfo.applicationInfo.metaData.getString("com.google.android.gms.cast.framework.RECEIVER_APPLICATION_ID");
} catch (PackageManager.NameNotFoundException e) {
}

/**
* Sets the app ID.
* @param applicationId appId
*/
public static void setAppId(String applicationId) {
appId = applicationId;
return appId != null ? appId : CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
}

@Override
public CastOptions getCastOptions(Context context) {
return new CastOptions.Builder()
.setReceiverApplicationId(appId)
.setReceiverApplicationId(getReceiverApplicationId(context))
.build();
}

@Override
public List<SessionProvider> getAdditionalSessionProviders(Context context) {
return null;
Expand Down
5 changes: 2 additions & 3 deletions src/android/Chromecast.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,13 @@ public void run() {
/**
* Initialize all of the MediaRouter stuff with the AppId.
* For now, ignore the autoJoinPolicy and defaultActionPolicy; those will come later
* @param appId The appId we're going to use for ALL session requests
* @param autoJoinPolicy tab_and_origin_scoped | origin_scoped | page_scoped
* @param defaultActionPolicy create_session | cast_this_tab
* @param callbackContext called with .success or .error depending on the result
* @return true for cordova
*/
public boolean initialize(final String appId, String autoJoinPolicy, String defaultActionPolicy, final CallbackContext callbackContext) {
connection.initialize(appId, callbackContext);
public boolean initialize(String autoJoinPolicy, String defaultActionPolicy, final CallbackContext callbackContext) {
connection.initialize(callbackContext);
return true;
}

Expand Down
64 changes: 7 additions & 57 deletions src/android/ChromecastConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

import androidx.arch.core.util.Function;
import androidx.mediarouter.app.MediaRouteChooserDialog;
import androidx.mediarouter.media.MediaControlIntent;
import androidx.mediarouter.media.MediaRouteSelector;
import androidx.mediarouter.media.MediaRouter;
import androidx.mediarouter.media.MediaRouter.RouteInfo;
Expand Down Expand Up @@ -46,24 +48,16 @@ public class ChromecastConnection {
/** The Listener callback. */
private Listener listener;

/** Initialize lifetime variable. */
private String appId;

/**
* Constructor.
* @param act the current context
* @param connectionListener client callbacks for specific events
*/
ChromecastConnection(Activity act, Listener connectionListener) {
this.activity = act;
this.settings = activity.getSharedPreferences("CORDOVA-PLUGIN-CHROMECAST_ChromecastConnection", 0);
this.appId = settings.getString("appId", CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID);
this.listener = connectionListener;
this.chromecastSession = new ChromecastSession(activity, listener);

// Set the initial appId
CastOptionsProvider.setAppId(appId);

// This is the first call to getContext which will start up the
// CastContext and prep it for searching for a session to rejoin
// Also adds the receiver update callback
Expand Down Expand Up @@ -93,25 +87,11 @@ ChromecastSession getChromecastSession() {

/**
* Must be called each time the appId changes and at least once before any other method is called.
* @param applicationId the app id to use
* @param callback called when initialization is complete
*/
public void initialize(String applicationId, CallbackContext callback) {
public void initialize(CallbackContext callback) {
activity.runOnUiThread(new Runnable() {
public void run() {
// If the app Id changed
if (applicationId == null || !applicationId.equals(appId)) {
// If app Id is valid
if (isValidAppId(applicationId)) {
// Set the new app Id
setAppId(applicationId);
} else {
// Else, just return
callback.success();
return;
}
}

// Tell the client that initialization was a success
callback.success();

Expand Down Expand Up @@ -157,38 +137,6 @@ private CastSession getSession() {
return getSessionManager().getCurrentCastSession();
}

private void setAppId(String applicationId) {
this.appId = applicationId;
this.settings.edit().putString("appId", appId).apply();
getContext().setReceiverApplicationId(appId);
}

/**
* Tests if an application receiver id is valid.
* @param applicationId - application receiver id
* @return true if valid
*/
private boolean isValidAppId(String applicationId) {
try {
ScanCallback cb = new ScanCallback() {
@Override
void onRouteUpdate(List<RouteInfo> routes) { }
};
// This will throw if the applicationId is invalid
getMediaRouter().addCallback(new MediaRouteSelector.Builder()
.addControlCategory(CastMediaControlIntent.categoryForCast(applicationId))
.build(),
cb,
MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
// If no exception we passed, so remove the callback
getMediaRouter().removeCallback(cb);
return true;
} catch (IllegalArgumentException e) {
// Don't set the appId if it is not a valid receiverApplicationID
return false;
}
}

/**
* This will create a new session or seamlessly selectRoute an existing one if we created it.
* @param routeId the id of the route to selectRoute
Expand Down Expand Up @@ -343,7 +291,8 @@ public void run() {
// TODO accept theme as a config.xml option
MediaRouteChooserDialog builder = new MediaRouteChooserDialog(activity, androidx.appcompat.R.style.Theme_AppCompat_NoActionBar);
builder.setRouteSelector(new MediaRouteSelector.Builder()
.addControlCategory(CastMediaControlIntent.categoryForCast(appId))
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build());
builder.setCanceledOnTouchOutside(true);
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
Expand Down Expand Up @@ -431,7 +380,8 @@ public void run() {

// Add the callback in active scan mode
getMediaRouter().addCallback(new MediaRouteSelector.Builder()
.addControlCategory(CastMediaControlIntent.categoryForCast(appId))
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
.build(),
callback,
MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
Expand Down
66 changes: 10 additions & 56 deletions src/ios/MLPChromecast.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,53 +12,21 @@ @interface MLPChromecast()
@end

@implementation MLPChromecast
NSString* appId = nil;
CDVInvokedUrlCommand* scanCommand = nil;
int scansRunning = 0;

- (void)pluginInitialize {
[super pluginInitialize];
self.currentSession = [MLPChromecastSession alloc];

NSString* applicationId = [NSUserDefaults.standardUserDefaults stringForKey:@"appId"];
if (applicationId == nil) {
applicationId = kGCKDefaultMediaReceiverApplicationID;
}
[self setAppId:applicationId];
}

- (void)setAppId:(NSString*)applicationId {
// If the applicationId is invalid or has not changed, don't do anything
if ([self isValidAppId:applicationId] && [applicationId isEqualToString:appId]) {
return;
}
appId = applicationId;

GCKDiscoveryCriteria *criteria = [[GCKDiscoveryCriteria alloc]
initWithApplicationID:appId];
GCKCastOptions *options = [[GCKCastOptions alloc] initWithDiscoveryCriteria:criteria];
options.physicalVolumeButtonsWillControlDeviceVolume = YES;
options.disableDiscoveryAutostart = NO;
[GCKCastContext setSharedInstanceWithOptions:options];

// Enable chromecast logger.
// [GCKLogger sharedInstance].delegate = self;

// Ensure we have only 1 listener attached
[GCKCastContext.sharedInstance.discoveryManager removeListener:self];
[GCKCastContext.sharedInstance.discoveryManager addListener:self];

[GCKCastContext.sharedInstance.sessionManager removeListener: self];
[GCKCastContext.sharedInstance.sessionManager addListener: self];

self.currentSession = [self.currentSession initWithListener:self cordovaDelegate:self.commandDelegate];
}

- (BOOL)isValidAppId:(NSString*)applicationId {
if (applicationId == (id)[NSNull null] || applicationId.length == 0) {
return NO;
}
return YES;
self.currentSession = [self.currentSession initWithListener:self cordovaDelegate:self.commandDelegate];
}

// Override CDVPlugin onReset
Expand All @@ -75,21 +43,10 @@ - (void)setup:(CDVInvokedUrlCommand*) command {
}

-(void) initialize:(CDVInvokedUrlCommand*)command {
NSString* applicationId = command.arguments[0];

// If the app id is invalid just send success and return
if (![self isValidAppId:applicationId]) {
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
return;
}

[self setAppId:applicationId];

// Initialize success
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];

// Search for existing session
[self findAvailableReceiver:^{
[self.currentSession tryRejoin];
Expand Down Expand Up @@ -268,7 +225,7 @@ - (void)loadMedia:(CDVInvokedUrlCommand*) command {
NSDictionary* metadata = command.arguments[7];
NSDictionary* textTrackStyle = command.arguments[8];
GCKMediaInformation* mediaInfo = [MLPCastUtilities buildMediaInformation:contentId customData:customData contentType:contentType duration:duration streamType:streamType startTime:currentTime metaData:metadata textTrackStyle:textTrackStyle];

[self.currentSession loadMediaWithCommand:command mediaInfo:mediaInfo autoPlay:autoplay currentTime:currentTime];
}

Expand All @@ -280,7 +237,7 @@ - (void)addMessageListener:(CDVInvokedUrlCommand*)command {
- (void)sendMessage:(CDVInvokedUrlCommand*) command {
NSString* namespace = command.arguments[0];
NSString* message = command.arguments[1];

[self.currentSession sendMessageWithCommand:command namespace:namespace message:message];
}

Expand All @@ -306,7 +263,7 @@ - (void)mediaStop:(CDVInvokedUrlCommand*)command {
- (void)mediaEditTracksInfo:(CDVInvokedUrlCommand*)command {
NSArray<NSNumber*>* activeTrackIds = command.arguments[0];
NSData* textTrackStyle = command.arguments[1];

GCKMediaTextTrackStyle* textTrackStyleObject = [MLPCastUtilities buildTextTrackStyle:textTrackStyle];
[self.currentSession setActiveTracksWithCommand:command activeTrackIds:activeTrackIds textTrackStyle:textTrackStyleObject];
}
Expand All @@ -318,11 +275,11 @@ - (void)selectRoute:(CDVInvokedUrlCommand*)command {
[self sendError:@"session_error" message:@"Leave or stop current session before attempting to join new session." command:command];
return;
}

NSString* routeID = command.arguments[0];
// Ensure the scan is running
[self startRouteScan];

[MLPCastUtilities retry:^BOOL{
GCKDevice* device = [[GCKCastContext sharedInstance].discoveryManager deviceWithUniqueID:routeID];
if (device != nil) {
Expand Down Expand Up @@ -354,9 +311,6 @@ - (void) didUpdateDeviceList {
#pragma GCKSessionManagerListener

- (void)sessionManager:(GCKSessionManager *)sessionManager didStartSession:(GCKSession *)session {
// Only save the app Id after a session for that appId has been successfully created/joined
[NSUserDefaults.standardUserDefaults setObject:appId forKey:@"appId"];
[[NSUserDefaults standardUserDefaults] synchronize];
}

#pragma CastSessionListener
Expand Down Expand Up @@ -406,9 +360,9 @@ - (void)sendEvent:(NSString *)eventName args:(NSArray *)args{
}

- (void)sendError:(NSString *)code message:(NSString *)message command:(CDVInvokedUrlCommand*)command{

CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[MLPCastUtilities createError:code message:message]];

[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}

Expand Down
2 changes: 1 addition & 1 deletion www/chrome.cast.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ var _session;
* @param {function} errorCallback
*/
chrome.cast.initialize = function (apiConfig, successCallback, errorCallback) {
execute('initialize', apiConfig.sessionRequest.appId, apiConfig.autoJoinPolicy, apiConfig.defaultActionPolicy, function (err) {
execute('initialize', apiConfig.autoJoinPolicy, apiConfig.defaultActionPolicy, function (err) {
MelissaDTH marked this conversation as resolved.
Show resolved Hide resolved
if (!err) {
// Don't set the listeners config until success
_initialized = true;
Expand Down