Skip to content

Commit a19e5e5

Browse files
authored
fix: use namespace instead of appId when generating MainActivity (#1830)
Changes how the fully specified MainActivity is specified. Previously, for `.`-prefixed activities, we read the `applicationId` from `build.gradle` and appended the Activity name, e.g. `build.gradle` with: ``` // ... namespace "com.example" defaultConfig { applicationId "com.example.mobile" // etc... ``` and `AndroidManifest.xml` with: ``` <activity android:name=".MainActivity" ... /> ``` would generate `com.example.mobile.MainActivity`. However, Android uses the application package's `namespace` instead of AppID when creating the Activity name ([docs](https://developer.android.com/guide/topics/manifest/activity-element)): > if the first character of the name is a period, such as ".ExtracurricularActivity", it is appended to the [namespace](https://developer.android.com/studio/build/configure-app-module#set-namespace) specified in the build.gradle file. This means, in the previous example, the actual name of the main activity would have been `com.example.MainActivity`. This hasn't historically been a problem because the activity name is only used when launching expo-dev-client apps, and Expo CNG generates an Android application package with the same `namespace` and `applicationId`, but we've run into a case of an application which bypasses Expo CNG and overrides the default `applicationId` for legacy reasons. This PR changes the approach to correctly use the namespace in this case. The implementation is based on `@expo/config-plugin`'s implementation of `getApplicationIdAsync`, which is not very resilient (it's just a regex looking for `namespace` in the `build.gradle` file), but since it's only used in projects using expo dev client at the moment, it should be good enough (without having to properly parse Groovy files). ### How Has This Been Tested: - check that apps using expo-dev-client still launch ### How Has This Change Been Documented: INTERNAL
1 parent d503776 commit a19e5e5

File tree

1 file changed

+23
-9
lines changed

1 file changed

+23
-9
lines changed

packages/vscode-extension/src/builders/buildAndroid.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from "path";
44
import semver from "semver";
55
import { AndroidConfig } from "@expo/config-plugins";
66
import loadConfig from "@react-native-community/cli-config";
7+
import { getAppBuildGradleAsync } from "@expo/config-plugins/build/android/Paths.js";
78
import { calculateAppArtifactHash, getNativeABI } from "../utilities/common";
89
import { ANDROID_HOME, findJavaHome } from "../utilities/android";
910
import { Logger } from "../Logger";
@@ -81,19 +82,28 @@ async function getApplicationManifest(
8182
return undefined;
8283
}
8384

85+
async function getNamespaceFromGradle(projectRoot: string): Promise<string | null> {
86+
const gradleBuild = await getAppBuildGradleAsync(projectRoot);
87+
if (!gradleBuild) {
88+
return null;
89+
}
90+
91+
// The namespace field in build.gradle defines the Java package name for the app's code.
92+
const matchResult = gradleBuild.contents.match(/namespace\s+['"](.*)['"]/);
93+
return matchResult?.[1] ?? null;
94+
}
95+
8496
async function resolveAppIdFromNative(projectRoot: string): Promise<string | null> {
85-
const applicationIdFromGradle = await AndroidConfig.Package.getApplicationIdAsync(
86-
projectRoot
87-
).catch(() => null);
88-
if (applicationIdFromGradle) {
89-
return applicationIdFromGradle;
97+
const namespaceFromGradle = await getNamespaceFromGradle(projectRoot);
98+
if (namespaceFromGradle) {
99+
return namespaceFromGradle;
90100
}
91101

92102
try {
93103
const filePath = await AndroidConfig.Paths.getAndroidManifestAsync(projectRoot);
94104
const androidManifest = await AndroidConfig.Manifest.readAndroidManifestAsync(filePath);
95105
// Assert MainActivity defined.
96-
await AndroidConfig.Manifest.getMainActivityOrThrow(androidManifest);
106+
AndroidConfig.Manifest.getMainActivityOrThrow(androidManifest);
97107
const appId = androidManifest.manifest?.$?.package;
98108
if (appId) {
99109
return appId;
@@ -111,10 +121,14 @@ async function getLaunchActivity(
111121
baseAppId: string
112122
): Promise<string | undefined> {
113123
const manifest = await getApplicationManifest(projectRoot);
114-
if (!manifest) return undefined;
124+
if (!manifest) {
125+
return undefined;
126+
}
115127

116-
const mainActivity = await AndroidConfig.Manifest.getRunnableActivity(manifest);
117-
if (!mainActivity) return undefined;
128+
const mainActivity = AndroidConfig.Manifest.getRunnableActivity(manifest);
129+
if (!mainActivity) {
130+
return undefined;
131+
}
118132

119133
const mainActivityName = mainActivity.$["android:name"];
120134

0 commit comments

Comments
 (0)