Files
paseo/packages/app/app.config.js
T
a8e0effb32 Follow system rotation on mobile devices (#8256)
The app no longer locks to portrait. Phones and tablets on Android and iOS follow the device rotation and respect the system rotation lock, so Android tablets fill the display in landscape instead of letterboxing.

Modals pass all orientations explicitly because React Native presents iPhone modals in portrait by default.

Closes #1669

Co-authored-by: yz <43207690+yzim@users.noreply.github.com>
Co-authored-by: magiclaw <930198090@qq.com>
2026-10-30 13:42:38 +07:00

206 lines
6.3 KiB
JavaScript

const fs = require("node:fs");
const path = require("node:path");
const pkg = require("./package.json");
const withAndroidAsyncStorageSize = require("./plugins/with-android-async-storage-size");
const withAndroidOptionalCamera = require("./plugins/with-android-optional-camera");
const withAndroidProfileable = require("./plugins/with-android-profileable");
const withFdroidAutolinking = require("./plugins/with-fdroid-autolinking");
const withPasteInput = require("./plugins/with-paste-input");
const withAndroidScroll = require("./modules/paseo-scroll/app.plugin");
const { getNativeReleaseVersion } = require("./native-release-version");
const appVariant = process.env.APP_VARIANT ?? "production";
const isFdroidBuild = process.env.PASEO_FDROID_BUILD === "1";
const isWatchdogBuild = process.env.PASEO_WATCHDOG_BUILD === "1";
const isProfileBuild = process.env.PASEO_PROFILE_BUILD === "1";
const buildProfile = isFdroidBuild
? {
androidPermissions: [
"RECORD_AUDIO",
"android.permission.RECORD_AUDIO",
"android.permission.MODIFY_AUDIO_SETTINGS",
],
cameraPlugins: [],
fdroidPlugins: [withFdroidAutolinking],
notificationPlugins: [],
}
: {
androidPermissions: [
"RECORD_AUDIO",
"android.permission.RECORD_AUDIO",
"android.permission.MODIFY_AUDIO_SETTINGS",
"CAMERA",
"android.permission.CAMERA",
],
cameraPlugins: [
[
"expo-camera",
{
cameraPermission:
"Allow $(PRODUCT_NAME) to access your camera to scan pairing QR codes.",
},
],
],
fdroidPlugins: [],
notificationPlugins: [
[
"expo-notifications",
{
icon: "./assets/images/notification-icon.png",
color: "#20744A",
},
],
],
};
function resolveSecretFile(params) {
const fromEnv = process.env[params.envKey];
if (typeof fromEnv === "string" && fromEnv.trim().length > 0) {
return fromEnv.trim();
}
const fallbackAbsolutePath = path.resolve(__dirname, params.fallbackRelativePath);
if (fs.existsSync(fallbackAbsolutePath)) {
return params.fallbackRelativePath;
}
return undefined;
}
const variants = {
production: {
name: "Paseo",
packageId: "sh.paseo",
googleServicesFile: resolveSecretFile({
envKey: "GOOGLE_SERVICES_FILE_PROD",
fallbackRelativePath: "./.secrets/google-services.prod.json",
}),
googleServiceInfoPlist: resolveSecretFile({
envKey: "GOOGLE_SERVICE_INFO_PLIST_PROD",
fallbackRelativePath: "./.secrets/GoogleService-Info.prod.plist",
}),
},
development: {
name: "Paseo Debug",
packageId: "sh.paseo.debug",
googleServicesFile: resolveSecretFile({
envKey: "GOOGLE_SERVICES_FILE_DEBUG",
fallbackRelativePath: "./.secrets/google-services.debug.json",
}),
googleServiceInfoPlist: resolveSecretFile({
envKey: "GOOGLE_SERVICE_INFO_PLIST_DEBUG",
fallbackRelativePath: "./.secrets/GoogleService-Info.debug.plist",
}),
},
};
const variant = variants[appVariant] ?? variants.production;
const nativeReleaseVersion = getNativeReleaseVersion(pkg.version);
export default {
expo: {
name: variant.name,
slug: "voice-mobile",
version: nativeReleaseVersion.appVersion,
orientation: "default",
icon: "./assets/images/icon.png",
scheme: "paseo",
userInterfaceStyle: "automatic",
newArchEnabled: true,
ios: {
supportsTablet: true,
infoPlist: {
NSMicrophoneUsageDescription: "This app needs access to the microphone for voice commands.",
ITSAppUsesNonExemptEncryption: false,
},
bundleIdentifier: variant.packageId,
...(variant.googleServiceInfoPlist
? { googleServicesFile: variant.googleServiceInfoPlist }
: {}),
buildNumber: nativeReleaseVersion.iosBuildNumber,
},
android: {
adaptiveIcon: {
backgroundColor: "#000000",
foregroundImage: "./assets/images/android-icon-foreground.png",
},
edgeToEdgeEnabled: true,
predictiveBackGestureEnabled: false,
softwareKeyboardLayoutMode: "resize",
// Allow HTTP connections for local network hosts (required for release builds)
usesCleartextTraffic: true,
permissions: buildProfile.androidPermissions,
package: variant.packageId,
versionCode: nativeReleaseVersion.androidVersionCode,
...(variant.googleServicesFile ? { googleServicesFile: variant.googleServicesFile } : {}),
},
web: {
output: "single",
favicon: "./assets/images/favicon.png",
},
autolinking: {
searchPaths: ["../../node_modules", "./node_modules"],
},
plugins: [
"expo-router",
withPasteInput,
withAndroidScroll,
[withAndroidAsyncStorageSize, 64],
...buildProfile.cameraPlugins,
withAndroidOptionalCamera,
[
"expo-splash-screen",
{
image: "./assets/images/splash-icon.png",
imageWidth: 200,
resizeMode: "contain",
backgroundColor: "#ffffff",
dark: {
backgroundColor: "#000000",
},
},
],
...buildProfile.notificationPlugins,
"expo-audio",
[
"expo-gradle-jvmargs",
{
xmx: "4096m",
maxMetaspace: "1024m",
},
],
[
"expo-build-properties",
{
ios: { enableSceneSupport: true },
android: {
minSdkVersion: 29,
kotlinVersion: "2.1.20",
// Allow HTTP connections for local network hosts in release builds
usesCleartextTraffic: true,
},
},
],
...buildProfile.fdroidPlugins,
...(isProfileBuild || isWatchdogBuild ? [withAndroidProfileable] : []),
],
experiments: {
typedRoutes: true,
reactCompiler: true,
autolinkingModuleResolution: true,
},
extra: {
fdroidBuild: isFdroidBuild,
profileBuild: isProfileBuild,
watchdogBuild: isWatchdogBuild,
diagnosticRevision: process.env.PASEO_DIAGNOSTIC_REVISION,
diagnosticBuildTime: process.env.PASEO_DIAGNOSTIC_BUILD_TIME,
router: {},
eas: {
projectId: "0e7f65ce-0367-46c8-a238-2b65963d235a",
},
},
owner: "getpaseo",
},
};