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; }); };