Compare commits

...
Author SHA1 Message Date
Mohamed Boudra 15635531c6 fix(app): keep tablet orientation policy in activity
Android resolves manifest resources without screen size qualifiers, so preserve the portrait declaration and unlock tablets when the activity starts.
2026-10-30 03:50:14 +01:00
b678e122d5 fix(app): allow Android tablets to rotate
Choose the orientation before the first activity frame, and update it when the available screen size changes.

Co-authored-by: yz <43207690+yzim@users.noreply.github.com>
Co-authored-by: magiclaw <930198090@qq.com>
2026-10-30 03:42:37 +01:00
3 changed files with 68 additions and 0 deletions
+2
View File
@@ -1,3 +1,4 @@
const withAndroidOrientation = require("./plugins/with-android-orientation");
const fs = require("node:fs");
const path = require("node:path");
const pkg = require("./package.json");
@@ -142,6 +143,7 @@ export default {
searchPaths: ["../../node_modules", "./node_modules"],
},
plugins: [
withAndroidOrientation,
"expo-router",
withPasteInput,
withAndroidScroll,
+28
View File
@@ -59,3 +59,31 @@ describe("Android app config", () => {
30_000,
);
});
it("keeps the Android manifest portrait for phone startup", async () => {
const manifest = await resolveAndroidManifest();
const activity = manifest.application[0].activity.find(
(entry: ManifestEntry) => entry.$["android:name"] === ".MainActivity",
);
expect(activity.$["android:screenOrientation"]).toBe("portrait");
}, 30_000);
it("updates the native orientation before launch and when screen size changes", async () => {
const plugin = createRequire(import.meta.url)("./plugins/with-android-orientation.js");
const config = plugin({ name: "test", slug: "test" });
const result = await config.mods.android.mainActivity({
modRequest: {},
modResults: {
language: "kt",
contents:
"class MainActivity {\n override fun onCreate(savedInstanceState: Bundle?) {\n super.onCreate(null)\n }\n}",
},
});
expect(result.modResults.contents).toContain("smallestScreenWidthDp >= 600");
expect(result.modResults.contents).toContain("SCREEN_ORIENTATION_UNSPECIFIED");
expect(result.modResults.contents).toContain("SCREEN_ORIENTATION_PORTRAIT");
expect(result.modResults.contents).toContain(
"updateOrientation(resources.configuration)\n super.onCreate(null)",
);
expect(result.modResults.contents).toContain("updateOrientation(newConfig)");
});
@@ -0,0 +1,38 @@
const { withMainActivity } = require("expo/config-plugins");
module.exports = function withAndroidOrientation(config) {
return withMainActivity(config, (mod) => {
const activity = mod.modResults;
if (activity.language !== "kt") throw new Error("Expected Kotlin MainActivity");
const marker = " // Paseo orientation policy";
if (!activity.contents.includes(marker)) {
const anchor = " override fun onCreate(savedInstanceState: Bundle?)";
if (!activity.contents.includes(anchor)) throw new Error("MainActivity anchor missing");
activity.contents = activity.contents.replace(
anchor,
`${marker}
private fun updateOrientation(configuration: android.content.res.Configuration) {
val orientation = if (configuration.smallestScreenWidthDp >= 600)
android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
else
android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
if (requestedOrientation != orientation) requestedOrientation = orientation
}
override fun onConfigurationChanged(newConfig: android.content.res.Configuration) {
super.onConfigurationChanged(newConfig)
updateOrientation(newConfig)
}
${anchor}`,
);
if (!activity.contents.includes(" super.onCreate(null)"))
throw new Error("MainActivity onCreate missing");
activity.contents = activity.contents.replace(
" super.onCreate(null)",
" updateOrientation(resources.configuration)\n super.onCreate(null)",
);
}
return mod;
});
};