diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4b276f68b..a3c51ec08 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -58,6 +58,8 @@ android { signingConfig = releaseSigningConfig } getByName("debug") { + isMinifyEnabled = false + isShrinkResources = false applicationIdSuffix = ".debug" isDebuggable = true aaptOptions.cruncherEnabled = false diff --git a/app/src/main/assets/native/chrome.cast.js b/app/src/main/assets/native/chrome.cast.js index b9c8d8f7c..fb1b77ab0 100644 --- a/app/src/main/assets/native/chrome.cast.js +++ b/app/src/main/assets/native/chrome.cast.js @@ -530,6 +530,7 @@ var _session; */ chrome.cast.initialize = function (apiConfig, successCallback, errorCallback) { execute('initialize', apiConfig.sessionRequest.appId, apiConfig.autoJoinPolicy, apiConfig.defaultActionPolicy, function (err) { + console.log('initializing app for config:', JSON.stringify(apiConfig)); if (!err) { // Don't set the listeners config until success _initialized = true; @@ -553,6 +554,7 @@ chrome.cast.initialize = function (apiConfig, successCallback, errorCallback) { */ chrome.cast.requestSession = function (successCallback, errorCallback, opt_sessionRequest) { execute('requestSession', function (err, obj) { + console.log('requestSession', JSON.stringify(opt_sessionRequest)); if (!err) { successCallback(createNewSession(obj)); } else { @@ -594,16 +596,19 @@ chrome.cast.Session = function Session (sessionId, appId, displayName, appImages this.receiver = receiver; this.media = []; this.status = chrome.cast.SessionStatus.CONNECTED; + console.log('new session', this.sessionId, this.appId, this.displayName, this.appImages, this.receiver); }; chrome.cast.Session.prototype = Object.create(EventEmitter.prototype); function sessionPreCheck (sessionId) { if (!_session || _session.status !== chrome.cast.SessionStatus.CONNECTED) { + console.error('No active session'); return new chrome.cast.Error( chrome.cast.ErrorCode.INVALID_PARAMETER, 'No active session'); } if (sessionId !== _session.sessionId) { + console.error('Unknown session ID', sessionId, _session.sessionId); return new chrome.cast.Error( chrome.cast.ErrorCode.INVALID_PARAMETER, 'Unknown session ID'); } diff --git a/app/src/main/java/org/jellyfin/mobile/MainActivity.kt b/app/src/main/java/org/jellyfin/mobile/MainActivity.kt index 8c36e6462..35bf1df63 100644 --- a/app/src/main/java/org/jellyfin/mobile/MainActivity.kt +++ b/app/src/main/java/org/jellyfin/mobile/MainActivity.kt @@ -190,6 +190,7 @@ class MainActivity : AppCompatActivity() { fragment.onUserLeaveHint() } } + super.onUserLeaveHint() } override fun onStop() { diff --git a/app/src/proprietary/java/org/jellyfin/mobile/player/cast/Chromecast.java b/app/src/proprietary/java/org/jellyfin/mobile/player/cast/Chromecast.java index 1966c03be..f96641b63 100644 --- a/app/src/proprietary/java/org/jellyfin/mobile/player/cast/Chromecast.java +++ b/app/src/proprietary/java/org/jellyfin/mobile/player/cast/Chromecast.java @@ -33,7 +33,7 @@ public final class Chromecast implements IChromecast { /** * Object to control the media. */ - private ChromecastSession media; + private ChromecastSession chromecastSession; /** * Holds the reference to the current client initiated scan. */ @@ -53,7 +53,7 @@ public final class Chromecast implements IChromecast { public void initializePlugin(Activity activity) { try { - this.connection = new ChromecastConnection(activity, new ChromecastConnection.Listener() { + connection = new ChromecastConnection(activity, new ChromecastConnection.Listener() { @Override public void onSessionRejoin(JSONObject jsonSession) { sendEvent("SESSION_LISTENER", new JSONArray().put(jsonSession)); @@ -93,7 +93,7 @@ public void onMessageReceived(CastDevice device, String namespace, String messag sendEvent("RECEIVER_MESSAGE", new JSONArray().put(namespace).put(message)); } }); - this.media = connection.getChromecastSession(); + chromecastSession = connection.getChromecastSession(); } catch (RuntimeException e) { noChromecastError = "Could not initialize chromecast: " + e.getMessage(); e.printStackTrace(); @@ -153,13 +153,7 @@ public boolean execute(String action, JSONArray args, JavascriptCallback cbConte } else { return false; } - } catch (IllegalAccessException e) { - e.printStackTrace(); - return false; - } catch (IllegalArgumentException e) { - e.printStackTrace(); - return false; - } catch (InvocationTargetException e) { + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); return false; } @@ -173,7 +167,7 @@ public boolean execute(String action, JSONArray args, JavascriptCallback cbConte * @return true for cordova */ public boolean setup(JavascriptCallback javascriptCallback) { - this.eventCallback = javascriptCallback; + eventCallback = javascriptCallback; // Ensure any existing scan is stopped if (connection == null) return false; connection.stopRouteScan(clientScan, () -> { @@ -275,7 +269,7 @@ public boolean setReceiverVolumeLevel(Integer newLevel, JavascriptCallback javas * @return true for cordova */ public boolean setReceiverVolumeLevel(Double newLevel, JavascriptCallback javascriptCallback) { - this.media.setVolume(newLevel, javascriptCallback); + chromecastSession.setVolume(newLevel, javascriptCallback); return true; } @@ -287,7 +281,7 @@ public boolean setReceiverVolumeLevel(Double newLevel, JavascriptCallback javasc * @return true for cordova */ public boolean setReceiverMuted(Boolean muted, JavascriptCallback javascriptCallback) { - this.media.setMute(muted, javascriptCallback); + chromecastSession.setMute(muted, javascriptCallback); return true; } @@ -300,7 +294,7 @@ public boolean setReceiverMuted(Boolean muted, JavascriptCallback javascriptCall * @return true for cordova */ public boolean sendMessage(String namespace, String message, final JavascriptCallback javascriptCallback) { - this.media.sendMessage(namespace, message, javascriptCallback); + chromecastSession.sendMessage(namespace, message, javascriptCallback); return true; } @@ -312,7 +306,7 @@ public boolean sendMessage(String namespace, String message, final JavascriptCal * @return true for cordova */ public boolean addMessageListener(String namespace, JavascriptCallback javascriptCallback) { - this.media.addMessageListener(namespace); + chromecastSession.addMessageListener(namespace); javascriptCallback.success(); return true; } @@ -337,7 +331,7 @@ public boolean loadMedia(String contentId, JSONObject customData, String content } private boolean loadMedia(String contentId, JSONObject customData, String contentType, Integer duration, String streamType, Boolean autoPlay, Double currentTime, JSONObject metadata, JSONObject textTrackStyle, final JavascriptCallback javascriptCallback) { - this.media.loadMedia(contentId, customData, contentType, duration, streamType, autoPlay, currentTime, metadata, textTrackStyle, javascriptCallback); + chromecastSession.loadMedia(contentId, customData, contentType, duration, streamType, autoPlay, currentTime, metadata, textTrackStyle, javascriptCallback); return true; } @@ -348,7 +342,7 @@ private boolean loadMedia(String contentId, JSONObject customData, String conten * @return true for cordova */ public boolean mediaPlay(JavascriptCallback javascriptCallback) { - media.mediaPlay(javascriptCallback); + chromecastSession.mediaPlay(javascriptCallback); return true; } @@ -359,7 +353,7 @@ public boolean mediaPlay(JavascriptCallback javascriptCallback) { * @return true for cordova */ public boolean mediaPause(JavascriptCallback javascriptCallback) { - media.mediaPause(javascriptCallback); + chromecastSession.mediaPause(javascriptCallback); return true; } @@ -372,7 +366,7 @@ public boolean mediaPause(JavascriptCallback javascriptCallback) { * @return true for cordova */ public boolean mediaSeek(Integer seekTime, String resumeState, JavascriptCallback javascriptCallback) { - media.mediaSeek(seekTime.longValue() * 1000, resumeState, javascriptCallback); + chromecastSession.mediaSeek(seekTime.longValue() * 1000, resumeState, javascriptCallback); return true; } @@ -398,7 +392,7 @@ public boolean setMediaVolume(Integer level, Boolean muted, JavascriptCallback j * @return true for cordova */ public boolean setMediaVolume(Double level, Boolean muted, JavascriptCallback javascriptCallback) { - media.mediaSetVolume(level, muted, javascriptCallback); + chromecastSession.mediaSetVolume(level, muted, javascriptCallback); return true; } @@ -409,7 +403,7 @@ public boolean setMediaVolume(Double level, Boolean muted, JavascriptCallback ja * @return true for cordova */ public boolean mediaStop(JavascriptCallback javascriptCallback) { - media.mediaStop(javascriptCallback); + chromecastSession.mediaStop(javascriptCallback); return true; } @@ -432,7 +426,7 @@ public boolean mediaEditTracksInfo(JSONArray activeTrackIds, JSONObject textTrac Timber.tag(TAG).e("Wrong format in activeTrackIds"); } - this.media.mediaEditTracksInfo(trackIds, textTrackStyle, javascriptCallback); + chromecastSession.mediaEditTracksInfo(trackIds, textTrackStyle, javascriptCallback); return true; } @@ -444,7 +438,7 @@ public boolean mediaEditTracksInfo(JSONArray activeTrackIds, JSONObject textTrac * @return true for cordova */ public boolean queueLoad(JSONObject queueLoadRequest, final JavascriptCallback javascriptCallback) { - this.media.queueLoad(queueLoadRequest, javascriptCallback); + chromecastSession.queueLoad(queueLoadRequest, javascriptCallback); return true; } @@ -456,7 +450,7 @@ public boolean queueLoad(JSONObject queueLoadRequest, final JavascriptCallback j * @return true for cordova */ public boolean queueJumpToItem(Integer itemId, final JavascriptCallback javascriptCallback) { - this.media.queueJumpToItem(itemId, javascriptCallback); + chromecastSession.queueJumpToItem(itemId, javascriptCallback); return true; } @@ -578,11 +572,13 @@ protected void callback(boolean keep, @Nullable String err, @Nullable String res }; stopRouteScan(callback); - sessionStop(callback); - if (media != null) { - media.destroy(); + // Default behavior for youtube-like apps is to leave the session running if the app is closed. + // This, at least, allows the user to close the app and re-open without stopping media when trying to fix things. + sessionLeave(callback); + if (chromecastSession != null) { + chromecastSession.destroy(); } - media = null; + chromecastSession = null; if (connection != null) { connection.destroy(); } diff --git a/app/src/proprietary/java/org/jellyfin/mobile/player/cast/ChromecastConnection.java b/app/src/proprietary/java/org/jellyfin/mobile/player/cast/ChromecastConnection.java index cf6dd31cd..2e4e970b1 100644 --- a/app/src/proprietary/java/org/jellyfin/mobile/player/cast/ChromecastConnection.java +++ b/app/src/proprietary/java/org/jellyfin/mobile/player/cast/ChromecastConnection.java @@ -51,7 +51,7 @@ public class ChromecastConnection { /** * Controls the media. */ - private final ChromecastSession media; + private final ChromecastSession chromecastSession; /** * Lifetime variable. @@ -81,7 +81,7 @@ public class ChromecastConnection { settings = activity.getSharedPreferences("CORDOVA-PLUGIN-CHROMECAST_ChromecastConnection", 0); appId = settings.getString("appId", CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID); listener = connectionListener; - media = new ChromecastSession(activity, listener); + chromecastSession = new ChromecastSession(activity, listener); // Set the initial appId CastOptionsProvider.setAppId(appId); @@ -98,7 +98,7 @@ public class ChromecastConnection { * @return the ChromecastSession object */ ChromecastSession getChromecastSession() { - return media; + return chromecastSession; } /** @@ -142,7 +142,7 @@ void onRouteUpdate(List routes) { // If we do have a session if (session != null) { // Let the client know - media.setSession(session); + chromecastSession.setSession(session); listener.onSessionRejoin(ChromecastUtilities.createSessionObject(session)); } } @@ -170,8 +170,8 @@ private CastSession getSession() { } private void setAppId(String applicationId) { - this.appId = applicationId; - this.settings.edit().putString("appId", appId).apply(); + appId = applicationId; + settings.edit().putString("appId", appId).apply(); getContext().setReceiverApplicationId(appId); } @@ -386,7 +386,7 @@ private void listenForConnection(ConnectionCallback callback) { @Override public void onSessionStarted(@NonNull CastSession castSession, @NonNull String sessionId) { getSessionManager().removeSessionManagerListener(this, CastSession.class); - media.setSession(castSession); + chromecastSession.setSession(castSession); callback.onJoin(ChromecastUtilities.createSessionObject(castSession)); } @@ -500,7 +500,7 @@ public void run() { @Override public void onSessionEnded(@NonNull CastSession castSession, int error) { getSessionManager().removeSessionManagerListener(this, CastSession.class); - media.setSession(null); + chromecastSession.setSession(null); if (callback != null) { callback.success(); } @@ -637,7 +637,7 @@ public abstract static class ScanCallback extends MediaRouter.Callback { * @param router mediaRouter object */ void setMediaRouter(MediaRouter router) { - this.mediaRouter = router; + mediaRouter = router; } /** diff --git a/gradle.properties b/gradle.properties index 7a7264b1f..2609e45bd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ # # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2G -XX:MaxMetaspaceSize=512m +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=1G # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 91e98ea87..b302d15e5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] # Plugins + android-plugin = "8.5.0" kotlin = "2.0.20" kotlin-ksp = "2.0.20-1.0.24"