react-native-google-cast
Version:
React Native wrapper for the Google Cast SDK for iOS and Android
294 lines (254 loc) • 7.53 kB
text/typescript
import {
AndroidConfig,
ConfigPlugin,
withAndroidManifest,
withAppBuildGradle,
withMainActivity,
withProjectBuildGradle,
} from '@expo/config-plugins'
import { addImports } from '@expo/config-plugins/build/android/codeMod'
import {
mergeContents,
removeContents,
} from '@expo/config-plugins/build/utils/generateCode'
const { addMetaDataItemToMainApplication, getMainApplicationOrThrow } =
AndroidConfig.Manifest
const META_PROVIDER_CLASS =
'com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME'
const META_RECEIVER_APP_ID =
'com.reactnative.googlecast.RECEIVER_APPLICATION_ID'
const MAIN_ACTIVITY_LANGUAGES: Record<
'java' | 'kt',
{ code: string; anchor: RegExp }
> = {
java: {
code: 'RNGCCastContext.getSharedInstance(this)',
anchor: /super\.onCreate\(\w+\)/,
},
kt: {
code: 'RNGCCastContext.getSharedInstance(this)',
anchor: /super\.onCreate\(\w+\)/,
},
}
type Props = {
expandedController?: boolean
receiverAppId?: string
}
const EXPANDED_CONTROLLER_ACTIVITY =
'com.reactnative.googlecast.RNGCExpandedControllerActivity'
async function ensureExpandedControllerActivity({
mainApplication,
}: {
mainApplication: AndroidConfig.Manifest.ManifestApplication
}) {
if (Array.isArray(mainApplication.activity)) {
// If the expanded controller activity is already added
mainApplication.activity = mainApplication.activity.filter((activity) => {
return activity.$?.['android:name'] !== EXPANDED_CONTROLLER_ACTIVITY
})
} else {
mainApplication.activity = []
}
// adds `<activity android:name="${EXPANDED_CONTROLLER_ACTIVITY}" />` to the manifest
mainApplication.activity.push({
$: {
'android:name': EXPANDED_CONTROLLER_ACTIVITY,
},
})
return mainApplication
}
const withAndroidManifestCast: ConfigPlugin<Props> = (
config,
{ expandedController, receiverAppId } = {}
) => {
return withAndroidManifest(config, async (config_) => {
const mainApplication = getMainApplicationOrThrow(config_.modResults)
if (expandedController) {
ensureExpandedControllerActivity({ mainApplication })
}
addMetaDataItemToMainApplication(
mainApplication,
META_PROVIDER_CLASS,
// This is the native Java class
'com.reactnative.googlecast.GoogleCastOptionsProvider'
)
if (receiverAppId) {
addMetaDataItemToMainApplication(
mainApplication,
META_RECEIVER_APP_ID,
receiverAppId
)
}
return config_
})
}
const withProjectBuildGradleVersion: ConfigPlugin<{ version?: string }> = (
config,
{ version }
) => {
return withProjectBuildGradle(config, (config_) => {
if (config_.modResults.language !== 'groovy')
throw new Error(
'react-native-google-cast config plugin does not support Kotlin /build.gradle yet.'
)
config_.modResults.contents = addGoogleCastVersionImport(
config_.modResults.contents,
{
version,
}
)
return config_
})
}
const withAppBuildGradleImport: ConfigPlugin<{ version?: string }> = (
config,
{ version }
) => {
return withAppBuildGradle(config, (config_) => {
if (config_.modResults.language !== 'groovy')
throw new Error(
'react-native-google-cast config plugin does not support Kotlin app/build.gradle yet.'
)
config_.modResults.contents = addSafeExtGet(config_.modResults.contents)
config_.modResults.contents = addGoogleCastImport(
config_.modResults.contents,
{
version,
}
).contents
return config_
})
}
const withMainActivityLazyLoading: ConfigPlugin = (config) => {
return withMainActivity(config, async (config_) => {
const src = addImports(
config_.modResults.contents,
['com.reactnative.googlecast.api.RNGCCastContext'],
config_.modResults.language === 'java'
)
config_.modResults.contents = addGoogleCastLazyLoadingImport(
src,
config_.modResults.language
).contents
return config_
})
}
export const withAndroidGoogleCast: ConfigPlugin<{
/**
* @default '+'
*/
androidPlayServicesCastFrameworkVersion?: string
expandedController?: boolean
/**
* ??
*/
receiverAppId?: string
}> = (config, props) => {
config = withAndroidManifestCast(config, {
expandedController: props.expandedController,
receiverAppId: props.receiverAppId,
})
config = withMainActivityLazyLoading(config)
config = withProjectBuildGradleVersion(config, {
// gradle dep version
version: props.androidPlayServicesCastFrameworkVersion ?? '+',
})
config = withAppBuildGradleImport(config, {
// gradle dep version
version: props.androidPlayServicesCastFrameworkVersion ?? '+',
})
return config
}
function addGoogleCastLazyLoadingImport(
src: string,
language: keyof typeof MAIN_ACTIVITY_LANGUAGES
) {
const mainActivity = MAIN_ACTIVITY_LANGUAGES[language]
if (!mainActivity) {
throw new Error(
`react-native-google-cast config plugin does not support MainActivity.${language} yet`
)
}
const newSrc = []
newSrc.push(` ${mainActivity.code}`)
return mergeContents({
tag: 'react-native-google-cast-onCreate',
src,
newSrc: newSrc.join('\n'),
anchor: mainActivity.anchor,
offset: 1,
comment: '//',
})
}
// TODO: Add this ability to autolinking
// dependencies { implementation "com.google.android.gms:play-services-cast-framework:+" }
function addGoogleCastImport(
src: string,
{ version }: { version?: string } = {}
) {
const newSrc = []
newSrc.push(
` implementation "com.google.android.gms:play-services-cast-framework:\${safeExtGet('castFrameworkVersion', '${version}')}"`
)
return mergeContents({
tag: 'react-native-google-cast-dependencies',
src,
newSrc: newSrc.join('\n'),
anchor: /dependencies(?:\s+)?\{/,
offset: 1,
comment: '//',
})
}
function addSafeExtGet(src: string) {
const tag = 'safeExtGet'
src = removeContents({ src, tag }).contents
// If the source already has a safeExtGet method after removing this one, then go with the existing one.
if (src.match(/def(?:\s+)?safeExtGet\(/)) {
return src
}
// Otherwise add a new one
const newSrc = []
newSrc.push(
'def safeExtGet(prop, fallback) {',
' rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback',
'}'
)
return mergeContents({
tag: 'safeExtGet',
src,
newSrc: newSrc.join('\n'),
// This block can go anywhere in the upper scope
anchor: /apply plugin/,
offset: 1,
comment: '//',
}).contents
}
function addGoogleCastVersionImport(
src: string,
{ version }: { version?: string } = {}
) {
const tag = 'react-native-google-cast-version-import'
src = removeContents({ src, tag }).contents
// If the source already has a castFrameworkVersion set, then do not add it again.
if (src.match(/castFrameworkVersion\s*=/)) {
console.warn(
`react-native-google-cast config plugin: Skipping adding castFrameworkVersion as it already exists in the project build.gradle.`
)
return src
}
const newSrc = [` castFrameworkVersion = "${version}"`]
const hasExtBlock = src.match(/ext(?:\s+)?\{/)
const anchor = hasExtBlock ? /ext(?:\s+)?\{/ : /buildscript(?:\s+)?\{/
if (!hasExtBlock) {
newSrc.unshift(' ext {')
newSrc.push(' }')
}
return mergeContents({
tag,
src,
newSrc: newSrc.join('\n'),
anchor,
offset: 1,
comment: '//',
}).contents
}