Files
react-native-omni/plugin/src/withPip.ts
T

170 lines
4.5 KiB
TypeScript

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(";
function patchMainActivityForPip(
mainActivity: string,
language: "java" | "kt",
): string {
const isJava = language === "java";
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,
);
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",
];
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",
];
output = appendContentsInsideDeclarationBlock(
output,
"class MainActivity",
uiStateChangedBlock.join("\n"),
);
}
return output;
}
function ensureUsesFeature(
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,
);
if (existing) {
existing.$["android:required"] = required;
} else {
usesFeature.push({
$: {
"android:name": featureName,
"android:required": required,
},
});
}
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),
);
requiredChanges.forEach((item) => existingSet.add(item));
mainActivity.$["android:configChanges"] = Array.from(existingSet).join("|");
}
function patchManifestForPip(
androidManifest: AndroidConfig.Manifest.AndroidManifest,
): AndroidConfig.Manifest.AndroidManifest {
ensureUsesFeature(
androidManifest,
"android.software.picture_in_picture",
"false",
);
const mainActivity =
AndroidConfig.Manifest.getMainActivityOrThrow(androidManifest);
mainActivity.$["android:supportsPictureInPicture"] = "true";
mainActivity.$["android:resizeableActivity"] = "true";
ensureConfigChanges(mainActivity);
return androidManifest;
}
export const withPip: ConfigPlugin = (config) => {
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;
});
};