mirror of
https://github.com/zoriya/react-native-omni.git
synced 2026-08-05 05:36:17 +00:00
feat(chromecast): send custom message for overlay subtitles
This commit is contained in:
+9
-9
@@ -1,15 +1,15 @@
|
||||
import { createRunOncePlugin } from '@expo/config-plugins';
|
||||
import type { ConfigPlugin } from '@expo/config-plugins';
|
||||
import pkg from '../../package.json';
|
||||
import { withMediaNotifications } from './withMediaNotifications';
|
||||
import { withPip } from './withPip';
|
||||
import type { ConfigPlugin } from "@expo/config-plugins";
|
||||
import { createRunOncePlugin } from "@expo/config-plugins";
|
||||
import pkg from "../../package.json";
|
||||
import { withMediaNotifications } from "./withMediaNotifications";
|
||||
import { withPip } from "./withPip";
|
||||
|
||||
const withOmni: ConfigPlugin = (config) => {
|
||||
let nextConfig = withPip(config);
|
||||
nextConfig = withMediaNotifications(nextConfig);
|
||||
return nextConfig;
|
||||
let nextConfig = withPip(config);
|
||||
nextConfig = withMediaNotifications(nextConfig);
|
||||
return nextConfig;
|
||||
};
|
||||
|
||||
export default createRunOncePlugin(withOmni, pkg.name, pkg.version);
|
||||
|
||||
export { withPip, withMediaNotifications };
|
||||
export { withMediaNotifications, withPip };
|
||||
|
||||
@@ -1,68 +1,85 @@
|
||||
import { AndroidConfig, withAndroidManifest, type ConfigPlugin } from '@expo/config-plugins';
|
||||
import {
|
||||
AndroidConfig,
|
||||
type ConfigPlugin,
|
||||
withAndroidManifest,
|
||||
} from "@expo/config-plugins";
|
||||
|
||||
function ensureUsesPermission(
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest,
|
||||
permission: string
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest,
|
||||
permission: string,
|
||||
): void {
|
||||
const usesPermissions = androidManifest.manifest['uses-permission'] ?? [];
|
||||
const alreadyDefined = usesPermissions.some((item) => item.$['android:name'] === permission);
|
||||
const usesPermissions = androidManifest.manifest["uses-permission"] ?? [];
|
||||
const alreadyDefined = usesPermissions.some(
|
||||
(item) => item.$["android:name"] === permission,
|
||||
);
|
||||
|
||||
if (!alreadyDefined) {
|
||||
usesPermissions.push({
|
||||
$: {
|
||||
'android:name': permission,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (!alreadyDefined) {
|
||||
usesPermissions.push({
|
||||
$: {
|
||||
"android:name": permission,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
androidManifest.manifest['uses-permission'] = usesPermissions;
|
||||
androidManifest.manifest["uses-permission"] = usesPermissions;
|
||||
}
|
||||
|
||||
function patchManifestForMediaNotifications(
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest,
|
||||
): AndroidConfig.Manifest.AndroidManifest {
|
||||
const mainApplication = AndroidConfig.Manifest.getMainApplication(androidManifest);
|
||||
if (!mainApplication) {
|
||||
console.warn(
|
||||
'AndroidManifest.xml is missing a <application android:name=".MainApplication" /> element - skipping Omni media service config.'
|
||||
);
|
||||
return androidManifest;
|
||||
}
|
||||
const mainApplication =
|
||||
AndroidConfig.Manifest.getMainApplication(androidManifest);
|
||||
if (!mainApplication) {
|
||||
console.warn(
|
||||
'AndroidManifest.xml is missing a <application android:name=".MainApplication" /> element - skipping Omni media service config.',
|
||||
);
|
||||
return androidManifest;
|
||||
}
|
||||
|
||||
const services = mainApplication.service ?? [];
|
||||
const serviceName = 'dev.zoriya.omni.OmniPlayerService';
|
||||
const hasService = services.some((service) => service.$['android:name'] === serviceName);
|
||||
if (!hasService) {
|
||||
services.push({
|
||||
$: {
|
||||
'android:name': serviceName,
|
||||
'android:enabled': 'true',
|
||||
'android:exported': 'true',
|
||||
'android:foregroundServiceType': 'mediaPlayback',
|
||||
},
|
||||
'intent-filter': [
|
||||
{
|
||||
action: [
|
||||
{
|
||||
$: {
|
||||
'android:name': 'androidx.media3.session.MediaSessionService',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
mainApplication.service = services;
|
||||
const services = mainApplication.service ?? [];
|
||||
const serviceName = "dev.zoriya.omni.OmniPlayerService";
|
||||
const hasService = services.some(
|
||||
(service) => service.$["android:name"] === serviceName,
|
||||
);
|
||||
if (!hasService) {
|
||||
services.push({
|
||||
$: {
|
||||
"android:name": serviceName,
|
||||
"android:enabled": "true",
|
||||
"android:exported": "true",
|
||||
"android:foregroundServiceType": "mediaPlayback",
|
||||
},
|
||||
"intent-filter": [
|
||||
{
|
||||
action: [
|
||||
{
|
||||
$: {
|
||||
"android:name": "androidx.media3.session.MediaSessionService",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
mainApplication.service = services;
|
||||
|
||||
ensureUsesPermission(androidManifest, 'android.permission.FOREGROUND_SERVICE');
|
||||
ensureUsesPermission(androidManifest, 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK');
|
||||
return androidManifest;
|
||||
ensureUsesPermission(
|
||||
androidManifest,
|
||||
"android.permission.FOREGROUND_SERVICE",
|
||||
);
|
||||
ensureUsesPermission(
|
||||
androidManifest,
|
||||
"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK",
|
||||
);
|
||||
return androidManifest;
|
||||
}
|
||||
|
||||
export const withMediaNotifications: ConfigPlugin = (config) => {
|
||||
return withAndroidManifest(config, (manifestConfig) => {
|
||||
manifestConfig.modResults = patchManifestForMediaNotifications(manifestConfig.modResults);
|
||||
return manifestConfig;
|
||||
});
|
||||
return withAndroidManifest(config, (manifestConfig) => {
|
||||
manifestConfig.modResults = patchManifestForMediaNotifications(
|
||||
manifestConfig.modResults,
|
||||
);
|
||||
return manifestConfig;
|
||||
});
|
||||
};
|
||||
|
||||
+139
-99
@@ -1,129 +1,169 @@
|
||||
import { AndroidConfig, withAndroidManifest, withMainActivity, type ConfigPlugin } from '@expo/config-plugins';
|
||||
import { addImports, appendContentsInsideDeclarationBlock } from '@expo/config-plugins/build/android/codeMod';
|
||||
import {
|
||||
AndroidConfig,
|
||||
type ConfigPlugin,
|
||||
withAndroidManifest,
|
||||
withMainActivity,
|
||||
} from "@expo/config-plugins";
|
||||
import {
|
||||
addImports,
|
||||
appendContentsInsideDeclarationBlock,
|
||||
} from "@expo/config-plugins/build/android/codeMod";
|
||||
|
||||
const MODE_MARKER = 'OmniView.onActivityPipModeChanged(';
|
||||
const UI_STATE_MARKER = 'OmniView.onActivityPipTransitionToPip(';
|
||||
const MODE_MARKER = "OmniView.onActivityPipModeChanged(";
|
||||
const UI_STATE_MARKER = "OmniView.onActivityPipTransitionToPip(";
|
||||
|
||||
function patchMainActivityForPip(mainActivity: string, language: 'java' | 'kt'): string {
|
||||
const isJava = language === 'java';
|
||||
function patchMainActivityForPip(
|
||||
mainActivity: string,
|
||||
language: "java" | "kt",
|
||||
): string {
|
||||
const isJava = language === "java";
|
||||
|
||||
if (mainActivity.includes(MODE_MARKER) && mainActivity.includes(UI_STATE_MARKER)) {
|
||||
return mainActivity;
|
||||
}
|
||||
if (
|
||||
mainActivity.includes(MODE_MARKER) &&
|
||||
mainActivity.includes(UI_STATE_MARKER)
|
||||
) {
|
||||
return mainActivity;
|
||||
}
|
||||
|
||||
const withRequiredImports = addImports(
|
||||
mainActivity,
|
||||
['android.app.PictureInPictureUiState', 'android.os.Build', 'dev.zoriya.omni.OmniView'],
|
||||
isJava
|
||||
);
|
||||
const withRequiredImports = addImports(
|
||||
mainActivity,
|
||||
[
|
||||
"android.app.PictureInPictureUiState",
|
||||
"android.os.Build",
|
||||
"dev.zoriya.omni.OmniView",
|
||||
],
|
||||
isJava,
|
||||
);
|
||||
|
||||
let output = withRequiredImports;
|
||||
let output = withRequiredImports;
|
||||
|
||||
if (!output.includes(MODE_MARKER)) {
|
||||
const modeChangedBlock = isJava
|
||||
? [
|
||||
'\n @Override',
|
||||
' public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {',
|
||||
' super.onPictureInPictureModeChanged(isInPictureInPictureMode);',
|
||||
' OmniView.onActivityPipModeChanged(this, isInPictureInPictureMode);',
|
||||
' }\n',
|
||||
]
|
||||
: [
|
||||
'\n override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {',
|
||||
' super.onPictureInPictureModeChanged(isInPictureInPictureMode)',
|
||||
' OmniView.onActivityPipModeChanged(this, isInPictureInPictureMode)',
|
||||
' }\n',
|
||||
];
|
||||
if (!output.includes(MODE_MARKER)) {
|
||||
const modeChangedBlock = isJava
|
||||
? [
|
||||
"\n @Override",
|
||||
" public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {",
|
||||
" super.onPictureInPictureModeChanged(isInPictureInPictureMode);",
|
||||
" OmniView.onActivityPipModeChanged(this, isInPictureInPictureMode);",
|
||||
" }\n",
|
||||
]
|
||||
: [
|
||||
"\n override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {",
|
||||
" super.onPictureInPictureModeChanged(isInPictureInPictureMode)",
|
||||
" OmniView.onActivityPipModeChanged(this, isInPictureInPictureMode)",
|
||||
" }\n",
|
||||
];
|
||||
|
||||
output = appendContentsInsideDeclarationBlock(output, 'class MainActivity', modeChangedBlock.join('\n'));
|
||||
}
|
||||
output = appendContentsInsideDeclarationBlock(
|
||||
output,
|
||||
"class MainActivity",
|
||||
modeChangedBlock.join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
if (!output.includes(UI_STATE_MARKER)) {
|
||||
const uiStateChangedBlock = isJava
|
||||
? [
|
||||
'\n @Override',
|
||||
' public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {',
|
||||
' super.onPictureInPictureUiStateChanged(pipState);',
|
||||
' if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && pipState.isTransitioningToPip()) {',
|
||||
' OmniView.onActivityPipTransitionToPip(this);',
|
||||
' }',
|
||||
' }\n',
|
||||
]
|
||||
: [
|
||||
'\n override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {',
|
||||
' super.onPictureInPictureUiStateChanged(pipState)',
|
||||
' if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && pipState.isTransitioningToPip) {',
|
||||
' OmniView.onActivityPipTransitionToPip(this)',
|
||||
' }',
|
||||
' }\n',
|
||||
];
|
||||
if (!output.includes(UI_STATE_MARKER)) {
|
||||
const uiStateChangedBlock = isJava
|
||||
? [
|
||||
"\n @Override",
|
||||
" public void onPictureInPictureUiStateChanged(PictureInPictureUiState pipState) {",
|
||||
" super.onPictureInPictureUiStateChanged(pipState);",
|
||||
" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && pipState.isTransitioningToPip()) {",
|
||||
" OmniView.onActivityPipTransitionToPip(this);",
|
||||
" }",
|
||||
" }\n",
|
||||
]
|
||||
: [
|
||||
"\n override fun onPictureInPictureUiStateChanged(pipState: PictureInPictureUiState) {",
|
||||
" super.onPictureInPictureUiStateChanged(pipState)",
|
||||
" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && pipState.isTransitioningToPip) {",
|
||||
" OmniView.onActivityPipTransitionToPip(this)",
|
||||
" }",
|
||||
" }\n",
|
||||
];
|
||||
|
||||
output = appendContentsInsideDeclarationBlock(output, 'class MainActivity', uiStateChangedBlock.join('\n'));
|
||||
}
|
||||
output = appendContentsInsideDeclarationBlock(
|
||||
output,
|
||||
"class MainActivity",
|
||||
uiStateChangedBlock.join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
return output;
|
||||
return output;
|
||||
}
|
||||
|
||||
function ensureUsesFeature(
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest,
|
||||
featureName: string,
|
||||
required: 'true' | 'false'
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest,
|
||||
featureName: string,
|
||||
required: "true" | "false",
|
||||
): void {
|
||||
const usesFeature = androidManifest.manifest['uses-feature'] ?? [];
|
||||
const existing = usesFeature.find((feature) => feature.$['android:name'] === featureName);
|
||||
const usesFeature = androidManifest.manifest["uses-feature"] ?? [];
|
||||
const existing = usesFeature.find(
|
||||
(feature) => feature.$["android:name"] === featureName,
|
||||
);
|
||||
|
||||
if (existing) {
|
||||
existing.$['android:required'] = required;
|
||||
} else {
|
||||
usesFeature.push({
|
||||
$: {
|
||||
'android:name': featureName,
|
||||
'android:required': required,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (existing) {
|
||||
existing.$["android:required"] = required;
|
||||
} else {
|
||||
usesFeature.push({
|
||||
$: {
|
||||
"android:name": featureName,
|
||||
"android:required": required,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
androidManifest.manifest['uses-feature'] = usesFeature;
|
||||
androidManifest.manifest["uses-feature"] = usesFeature;
|
||||
}
|
||||
|
||||
function ensureConfigChanges(mainActivity: AndroidConfig.Manifest.ManifestActivity): void {
|
||||
const requiredChanges = ['screenSize', 'smallestScreenSize', 'screenLayout', 'orientation'];
|
||||
const existing = mainActivity.$['android:configChanges'] ?? '';
|
||||
const existingSet = new Set(
|
||||
existing
|
||||
.split('|')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
);
|
||||
function ensureConfigChanges(
|
||||
mainActivity: AndroidConfig.Manifest.ManifestActivity,
|
||||
): void {
|
||||
const requiredChanges = [
|
||||
"screenSize",
|
||||
"smallestScreenSize",
|
||||
"screenLayout",
|
||||
"orientation",
|
||||
];
|
||||
const existing = mainActivity.$["android:configChanges"] ?? "";
|
||||
const existingSet = new Set(
|
||||
existing
|
||||
.split("|")
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean),
|
||||
);
|
||||
|
||||
requiredChanges.forEach((item) => existingSet.add(item));
|
||||
mainActivity.$['android:configChanges'] = Array.from(existingSet).join('|');
|
||||
requiredChanges.forEach((item) => existingSet.add(item));
|
||||
mainActivity.$["android:configChanges"] = Array.from(existingSet).join("|");
|
||||
}
|
||||
|
||||
function patchManifestForPip(
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest
|
||||
androidManifest: AndroidConfig.Manifest.AndroidManifest,
|
||||
): AndroidConfig.Manifest.AndroidManifest {
|
||||
ensureUsesFeature(androidManifest, 'android.software.picture_in_picture', 'false');
|
||||
ensureUsesFeature(
|
||||
androidManifest,
|
||||
"android.software.picture_in_picture",
|
||||
"false",
|
||||
);
|
||||
|
||||
const mainActivity = AndroidConfig.Manifest.getMainActivityOrThrow(androidManifest);
|
||||
mainActivity.$['android:supportsPictureInPicture'] = 'true';
|
||||
mainActivity.$['android:resizeableActivity'] = 'true';
|
||||
ensureConfigChanges(mainActivity);
|
||||
const mainActivity =
|
||||
AndroidConfig.Manifest.getMainActivityOrThrow(androidManifest);
|
||||
mainActivity.$["android:supportsPictureInPicture"] = "true";
|
||||
mainActivity.$["android:resizeableActivity"] = "true";
|
||||
ensureConfigChanges(mainActivity);
|
||||
|
||||
return androidManifest;
|
||||
return androidManifest;
|
||||
}
|
||||
|
||||
export const withPip: ConfigPlugin = (config) => {
|
||||
const withMainActivityPatched = withMainActivity(config, (activityConfig) => {
|
||||
activityConfig.modResults.contents = patchMainActivityForPip(
|
||||
activityConfig.modResults.contents,
|
||||
activityConfig.modResults.language
|
||||
);
|
||||
return activityConfig;
|
||||
});
|
||||
const withMainActivityPatched = withMainActivity(config, (activityConfig) => {
|
||||
activityConfig.modResults.contents = patchMainActivityForPip(
|
||||
activityConfig.modResults.contents,
|
||||
activityConfig.modResults.language,
|
||||
);
|
||||
return activityConfig;
|
||||
});
|
||||
|
||||
return withAndroidManifest(withMainActivityPatched, (manifestConfig) => {
|
||||
manifestConfig.modResults = patchManifestForPip(manifestConfig.modResults);
|
||||
return manifestConfig;
|
||||
});
|
||||
return withAndroidManifest(withMainActivityPatched, (manifestConfig) => {
|
||||
manifestConfig.modResults = patchManifestForPip(manifestConfig.modResults);
|
||||
return manifestConfig;
|
||||
});
|
||||
};
|
||||
|
||||
+12
-12
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "CommonJS",
|
||||
"declaration": true,
|
||||
"outDir": "build",
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "CommonJS",
|
||||
"declaration": true,
|
||||
"outDir": "build",
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user