mirror of
https://github.com/VibedByKaKi/t3-code-android-nightly.git
synced 2026-10-09 11:51:15 +02:00
Co-authored-by: Horus Lugo <horusgoul@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com> Co-authored-by: Ben Davis <45952064+bmdavis419@users.noreply.github.com> Co-authored-by: Alex <me@pixp.cc> Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: Julius Marminge <julius@mac.lan>
104 lines
4 KiB
JavaScript
104 lines
4 KiB
JavaScript
const { withMainActivity } = require("expo/config-plugins");
|
|
|
|
// predictiveBackGestureEnabled writes android:enableOnBackInvokedCallback="true",
|
|
// which retires the legacy KEYCODE_BACK/onBackPressed() delivery on Android 13+.
|
|
// From then on back gestures only reach the app through OnBackPressedDispatcher
|
|
// callbacks. react-native 0.85 registers its own always-enabled callback, but
|
|
// only on Android 16 with targetSdk 36 (ReactActivity's enforced-predictive-back
|
|
// workaround) — on Android 13-15 nothing is registered, the system consumes
|
|
// every back gesture itself, and JS back handling (React Navigation pops,
|
|
// BackHandler listeners) silently dies: each gesture just backgrounds the app.
|
|
// This plugin mirrors react-native's shim on API 33-35 so back keeps flowing
|
|
// to JS there. See https://github.com/software-mansion/react-native-screens/discussions/2540.
|
|
|
|
const CALLBACK_PROPERTY = `
|
|
// Routes predictive-back gestures to JS on Android 13-15, where react-native
|
|
// registers no OnBackPressedDispatcher callback of its own (it only does on
|
|
// Android 16+). Registered in onCreate; added by withAndroidPredictiveBackCompat.
|
|
private val predictiveBackCompatCallback = object : OnBackPressedCallback(true) {
|
|
override fun handleOnBackPressed() {
|
|
isEnabled = false
|
|
onBackPressed()
|
|
isEnabled = true
|
|
}
|
|
}
|
|
`;
|
|
|
|
const CALLBACK_REGISTRATION = `
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && Build.VERSION.SDK_INT < Build.VERSION_CODES.BAKLAVA) {
|
|
onBackPressedDispatcher.addCallback(this, predictiveBackCompatCallback)
|
|
}`;
|
|
|
|
// Wraps the template's invokeDefaultOnBackPressed: the default action ends in
|
|
// ComponentActivity.onBackPressed(), which re-enters the dispatcher — with the
|
|
// compat callback still enabled that would bounce back into JS forever instead
|
|
// of backgrounding the app.
|
|
const INVOKE_DEFAULT_WRAPPER = `override fun invokeDefaultOnBackPressed() {
|
|
predictiveBackCompatCallback.isEnabled = false
|
|
try {
|
|
invokeDefaultOnBackPressedLegacy()
|
|
} finally {
|
|
predictiveBackCompatCallback.isEnabled = true
|
|
}
|
|
}
|
|
|
|
private fun invokeDefaultOnBackPressedLegacy() {`;
|
|
|
|
function insertAfter(contents, anchor, insertion, description) {
|
|
const index = contents.indexOf(anchor);
|
|
if (index === -1) {
|
|
throw new Error(
|
|
`withAndroidPredictiveBackCompat: could not find ${description} in MainActivity — the Expo template changed; update the plugin anchors.`,
|
|
);
|
|
}
|
|
const end = index + anchor.length;
|
|
return contents.slice(0, end) + insertion + contents.slice(end);
|
|
}
|
|
|
|
module.exports = function withAndroidPredictiveBackCompat(config) {
|
|
if (config.android?.predictiveBackGestureEnabled !== true) {
|
|
return config;
|
|
}
|
|
|
|
return withMainActivity(config, (nextConfig) => {
|
|
let contents = nextConfig.modResults.contents;
|
|
if (nextConfig.modResults.language !== "kt") {
|
|
throw new Error("withAndroidPredictiveBackCompat: MainActivity must be Kotlin.");
|
|
}
|
|
if (contents.includes("predictiveBackCompatCallback")) {
|
|
return nextConfig;
|
|
}
|
|
|
|
contents = insertAfter(
|
|
contents,
|
|
"import android.os.Bundle",
|
|
"\nimport androidx.activity.OnBackPressedCallback",
|
|
"the android.os.Bundle import",
|
|
);
|
|
contents = insertAfter(
|
|
contents,
|
|
"class MainActivity : ReactActivity() {",
|
|
CALLBACK_PROPERTY,
|
|
"the MainActivity class declaration",
|
|
);
|
|
contents = insertAfter(
|
|
contents,
|
|
"super.onCreate(null)",
|
|
CALLBACK_REGISTRATION,
|
|
"the super.onCreate call",
|
|
);
|
|
|
|
if (!contents.includes("override fun invokeDefaultOnBackPressed() {")) {
|
|
throw new Error(
|
|
"withAndroidPredictiveBackCompat: could not find invokeDefaultOnBackPressed in MainActivity — the Expo template changed; update the plugin anchors.",
|
|
);
|
|
}
|
|
contents = contents.replace(
|
|
"override fun invokeDefaultOnBackPressed() {",
|
|
INVOKE_DEFAULT_WRAPPER,
|
|
);
|
|
|
|
nextConfig.modResults.contents = contents;
|
|
return nextConfig;
|
|
});
|
|
};
|