From e9783d293dce4397d3e131b1996788799d7e6e18 Mon Sep 17 00:00:00 2001 From: jakequade Date: Sat, 24 Aug 2024 14:37:49 +1000 Subject: [PATCH] extended cast controls on android --- app.json | 6 +++ components/PlayButton.tsx | 21 +++++++++- plugins/withAndroidMainActivityAttributes.js | 42 ++++++++++++++++++++ providers/PlaybackProvider.tsx | 2 +- 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 plugins/withAndroidMainActivityAttributes.js diff --git a/app.json b/app.json index 3141a8ad..ad62a054 100644 --- a/app.json +++ b/app.json @@ -68,6 +68,12 @@ } } ], + [ + "./plugins/withAndroidMainActivityAttributes", + { + "com.reactnative.googlecast.RNGCExpandedControllerActivity": true + } + ], [ "expo-build-properties", { diff --git a/components/PlayButton.tsx b/components/PlayButton.tsx index 2a539736..32b935b5 100644 --- a/components/PlayButton.tsx +++ b/components/PlayButton.tsx @@ -9,6 +9,7 @@ import CastContext, { useRemoteMediaClient, } from "react-native-google-cast"; import { Button } from "./Button"; +import { isCancel } from "axios"; interface Props extends React.ComponentProps { item?: BaseItemDto | null; @@ -18,7 +19,7 @@ interface Props extends React.ComponentProps { export const PlayButton: React.FC = ({ item, url, ...props }) => { const { showActionSheetWithOptions } = useActionSheet(); const client = useRemoteMediaClient(); - const { setCurrentlyPlayingState } = usePlayback(); + const { setCurrentlyPlayingState, isPlaying, currentlyPlaying } = usePlayback(); const onPress = async () => { if (!url || !item) return; @@ -37,12 +38,22 @@ export const PlayButton: React.FC = ({ item, url, ...props }) => { cancelButtonIndex, }, async (selectedIndex: number | undefined) => { + const isOpeningCurrentlyPlayingMedia = isPlaying + && currentlyPlaying?.item?.Name + && currentlyPlaying?.item?.Name === item?.Name switch (selectedIndex) { case 0: await CastContext.getPlayServicesState().then((state) => { if (state && state !== PlayServicesState.SUCCESS) CastContext.showPlayServicesErrorDialog(state); else { + // If we're opening a currently playing item, don't restart the media. + // Instead just open controls + console.log({ isOpeningCurrentlyPlayingMedia, currentlyPlaying }) + if (isOpeningCurrentlyPlayingMedia) { + CastContext.showExpandedControls(); + return; + } client.loadMedia({ mediaInfo: { contentUrl: url, @@ -54,6 +65,14 @@ export const PlayButton: React.FC = ({ item, url, ...props }) => { }, }, startTime: 0, + }).then(() => { + if (isOpeningCurrentlyPlayingMedia) { + return + } + setCurrentlyPlayingState({ item, url }); + CastContext.showExpandedControls(); + }).catch(e => { + console.log({ e }) }); } }); diff --git a/plugins/withAndroidMainActivityAttributes.js b/plugins/withAndroidMainActivityAttributes.js new file mode 100644 index 00000000..d57b8c93 --- /dev/null +++ b/plugins/withAndroidMainActivityAttributes.js @@ -0,0 +1,42 @@ +const { withAndroidManifest } = require("@expo/config-plugins"); + +function addAttributesToMainActivity(androidManifest, attributes) { + const { manifest } = androidManifest; + + if (!Array.isArray(manifest["application"])) { + console.warn("withAndroidMainActivityAttributes: No application array in manifest?"); + return androidManifest; + } + + const application = manifest["application"].find( + (item) => item.$["android:name"] === ".MainApplication" + ); + if (!application) { + console.warn("withAndroidMainActivityAttributes: No .MainApplication?"); + return androidManifest; + } + + if (!Array.isArray(application["activity"])) { + console.warn("withAndroidMainActivityAttributes: No activity array in .MainApplication?"); + return androidManifest; + } + + const activity = application["activity"].find( + (item) => item.$["android:name"] === ".MainActivity" + ); + if (!activity) { + console.warn("withAndroidMainActivityAttributes: No .MainActivity?"); + return androidManifest; + } + + activity.$ = { ...activity.$, ...attributes }; + + return androidManifest; +} + +module.exports = function withAndroidMainActivityAttributes(config, attributes) { + return withAndroidManifest(config, (config) => { + config.modResults = addAttributesToMainActivity(config.modResults, attributes); + return config; + }); +}; \ No newline at end of file diff --git a/providers/PlaybackProvider.tsx b/providers/PlaybackProvider.tsx index 1f2c3bdd..bd26807d 100644 --- a/providers/PlaybackProvider.tsx +++ b/providers/PlaybackProvider.tsx @@ -181,7 +181,7 @@ export const PlaybackProvider: React.FC<{ children: ReactNode }> = ({ useEffect(() => { if (!deviceId || !api?.accessToken) return; - const url = `wss://${api?.basePath + const url = `ws://${api?.basePath .replace("https://", "") .replace("http://", "")}/socket?api_key=${ api?.accessToken