Repository: react-native-community/cli (TypeScript)

Issue:
 On Android, app isn't opened automatically if applicationId is changed
### Description

I change `applicationId` but keep package ID. I run `yarn android --appId new.app.id` but it throws `Error type 3` and app isn't opened.

It doesn't happen on older RN versions.

Log on v0.72.7, notice `Starting: Intent` line and compare it to error log below

```
yarn run v1.22.21
$ react-native run-android --appId com.foo.barr
info JS server already running.
info Installing the app...

> Task :app:installDebug
Installing APK 'app-debug.apk' on 'Pixel_5_API_30(AVD) - 11' for :app:debug
Installed on 1 device.

BUILD SUCCESSFUL in 2s
38 actionable tasks: 2 executed, 36 up-to-date
info Connecting to the development server...
info Starting the app on "emulator-5554"...
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.foo.bar/com.foobar.MainActivity }
```

### Steps to reproduce

1. Run `npx react-native init foobar`
2. Open `android/app/build.gradle`, change `applicationId "com.foobar"` to `applicationId "com.foo.bar"`
3. Run `yarn android --appId com.foo.bar`
4. It throws `Error: Activity class {com.foo.bar/com.foo.bar.MainActivity} does not exist.` and doesn't start app

### React Native Version

0.73.0

### Affected Platforms

Runtime - Android, Build - MacOS

### Output of `npx react-native info`

```text
System:
  OS: macOS 14.1.2
  CPU: (8) arm64 Apple M1
  Memory: 110.70 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 18.18.2
    path: /usr/local/bin/node
  Yarn:
    version: 1.22.21
    path: /opt/homebrew/bin/yarn
  npm:
    version: 9.8.1
    path: /usr/local/bin/npm
  Watchman: Not Found
Managers:
  CocoaPods:
    version: 1.14.3
    path: /Users/pnthach/.rvm/rubies/ruby-2.7.5/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.0
      - iOS 17.0
      - macOS 14.0
      - tvOS 17.0
      - watchOS 10.0
  Android SDK:
    API Levels:
      - "30"
      - "31"
      - "33"
      - "34"
    Build Tools:
      - 28.0.3
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 32.0.0
      - 33.0.0
      - 33.0.1
      - 33.0.2
      - 34.0.0
    System Images:
      - android-26 | Google APIs ARM 64 v8a
      - android-30 | Google APIs ARM 64 v8a
      - android-33 | Google APIs ARM 64 v8a
      - android-33 | Google Play ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2023.1 AI-231.9392.1.2311.11076708
  Xcode:
    version: 15.0.1/15A507
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.9
    path: /usr/bin/javac
  Ruby:
    version: 2.7.5
    path: /Users/pnthach/.rvm/rubies/ruby-2.7.5/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.73.0
    wanted: 0.73.0
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: Not found
  newArchEnabled: false
```


### Stacktrace or Logs

```text
yarn run v1.22.21
$ react-native run-android
info Installing the app...

> Task :app:installDebug
Installing APK 'app-debug.apk' on 'Pixel_5_API_30(AVD) - 11' for :app:debug
Installed on 1 device.

BUILD SUCCESSFUL in 5s
43 actionable tasks: 14 executed, 29 up-to-date
info Connecting to the development server...
info Starting the app on "emulator-5554"...
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.foo.bar/.MainActivity }
Error type 3
Error: Activity class {com.foo.bar/com.foo.bar.MainActivity} does not exist.
✨  Done in 6.71s.
```


### Reproducer

https://github.com/pnthach95/applicationIdError



Discussion:
Yeah I can repro that. Seems related to #1951 and https://github.com/react-native-community/cli/issues/1884. Would you like to help us pin-point the issue and fix it?
Hi @thymikee 

`cli-platform-android@11.3.10` in `react-native@0.72.7`

`console.log(args.mainActivity)` in `tryLaunchAppOnDevice`, result is `MainActivity`
https://github.com/react-native-community/cli/blob/59e4dac7e56fb05f33508ff804c0eac7448c16a8/packages/cli-platform-android/src/commands/runAndroid/tryLaunchAppOnDevice.ts#L23-L27

`cli-platform-android@12.1.1` in `react-native@0.73.0`

`console.log(mainActivity)` in `tryLaunchAppOnDevice`, result is `.MainActivity`
https://github.com/react-native-community/cli/blob/a45a2d92c3d53dcb0cf7ba8be4860ad75ff06702/packages/cli-platform-android/src/commands/runAndroid/tryLaunchAppOnDevice.ts#L26-L30

So, `mainActivity` always includes `.` in `12.1.1`, it doesn't in older versions
This was addressed in #2195 and will hopefully land with RN 73.1
Hi Mike! 
Could you please provide some info on when the resolution for this issue get landed?
Because I tried on RN 0.73.2 but the app still didn't get launched automatically 🥲.
@thymikee need help when applicationId is diff from namespace, or applicationId must match namespace?
> @thymikee need help when applicationId is diff from namespace, or applicationId must match namespace?

I have the same problem, I have an application in version 0.64.4, and now I'm updating it to version 0.73.4, with this new "namespace" variable, if I leave the "applicationId" different I get the error below:

CommandError: Couldn't open Android app with activity "br.com.integralservices.development.mob/.MainActivity" on device "Pixel_4_API_34".
The app might not be installed, try installing it with: npx expo run:android -d Pixel_4_API_34

Code in current version 0.73.4:

   namespace "com.integralservices"
     defaultConfig {
         applicationId "br.com.integralservices.development.mob"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         versionCode 1
         versionName "1.0"

         buildConfigField("boolean", "REACT_NATIVE_UNSTABLE_USE_RUNTIME_SCHEDULER_ALWAYS", (findProperty("reactNative.unstable_useRuntimeSchedulerAlways") ?: true).toString())
     }

     flavorDimensions "env"
     productFlavors {
         production {
             dimension "env"
             applicationId "br.com.integralservices.production.mob"
         }
         development {
             dimension "env"
             applicationId "br.com.integralservices.development.mob"
         }
         staging {
             dimension "env"
             applicationId "br.com.integralservices.staging.mob"
         }
     }


Code in old version 0.64.4:

     defaultConfig {
         applicationId "br.com.integralservices.development.mob"
         minSdkVersion rootProject.ext.minSdkVersion
         targetSdkVersion rootProject.ext.targetSdkVersion
         versionCode 1
         versionName "1.0"
     }

     flavorDimensions "env"
     productFlavors {
         production {
             dimension "env"
             applicationId 'br.com.integralservices.production.mob'
         }
         development {
             dimension "env"
             applicationId 'br.com.integralservices.development.mob'
         }
         staging {
             dimension "env"
             applicationId 'br.com.integralservices.staging.mob'
         }
     }

In the old version it runs without problems in the emulator, but in this one I couldn't solve this, did you find any solution?
> @thymikee need help when applicationId is diff from namespace, or applicationId must match namespace?

Could @thymikee  answer this question for us?
@woowalker @geydson  `applicationId` doesn't have to match `namespace` . You can have different `applicationId`s for multiple versions of your app as shown on the snippet above. Make sure the `namespace` matches the package name in `MainActivity.kt`.

When using product flavors, you can try using `--mode` flag to indicate the flavor, e.g. `yarn android --mode developmentDebug`. You can also list all available tasks by using `--interactive` flag.

@geydson 
> CommandError: Couldn't open Android app with activity "br.com.integralservices.development.mob/.MainActivity" on device "Pixel_4_API_34".
The app might not be installed, try installing it with: npx expo run:android -d Pixel_4_API_34

your output indicates you are using Expo, if the problem still occurs, I'd suggest to open an issue in Expo repository.
Hi @TMisiukiewicz  at what version this fix landed?
Upgrading my app to 0.73.7 doesn't solve this issue 🥲
> Upgrading my app to 0.73.7 doesn't solve this issue 🥲

If it doesn't work on yours, show your error log
My error log is similar like yours @pnthach95 :

```bash
Error: Activity class {com.foo.bar/com.foo.bar.MainActivity} does not exist.
```
> My error log is similar like yours @pnthach95 :
> 
> ```shell
> Error: Activity class {com.foo.bar/com.foo.bar.MainActivity} does not exist.
> ```

I upgrade my reproducer to 0.73.7 and it can launch app without any problems

https://github.com/react-native-community/cli/assets/31266357/81d10c18-c5ac-407b-af80-18268c3412da

If you believe this issue wasn't resolved, show your reproducer as evidence, don't copy my log



Patch:
diff --git a/packages/cli-platform-android/src/commands/runAndroid/index.ts b/packages/cli-platform-android/src/commands/runAndroid/index.ts
index 63636d64b..d207641b4 100644
--- a/packages/cli-platform-android/src/commands/runAndroid/index.ts
+++ b/packages/cli-platform-android/src/commands/runAndroid/index.ts
@@ -34,7 +34,7 @@ import {checkUsers, promptForUser} from './listAndroidUsers';
 export interface Flags extends BuildFlags {
   appId: string;
   appIdSuffix: string;
-  mainActivity: string;
+  mainActivity?: string;
   port: number;
   terminal?: string;
   packager?: boolean;
diff --git a/packages/cli-platform-android/src/commands/runAndroid/tryLaunchAppOnDevice.ts b/packages/cli-platform-android/src/commands/runAndroid/tryLaunchAppOnDevice.ts
index bef77a345..2a0dda4a0 100644
--- a/packages/cli-platform-android/src/commands/runAndroid/tryLaunchAppOnDevice.ts
+++ b/packages/cli-platform-android/src/commands/runAndroid/tryLaunchAppOnDevice.ts
@@ -25,7 +25,7 @@ function tryLaunchAppOnDevice(
     .join('.');
 
   const activityToLaunch = mainActivity.includes('.')
-    ? mainActivity
+    ? [packageName, mainActivity].join('')
     : [packageName, mainActivity].filter(Boolean).join('.');
 
   try {


Test patch:
diff --git a/packages/cli-platform-android/src/commands/runAndroid/__tests__/runOnAllDevices.test.ts b/packages/cli-platform-android/src/commands/runAndroid/__tests__/runOnAllDevices.test.ts
index 935137307..1e54de80d 100644
--- a/packages/cli-platform-android/src/commands/runAndroid/__tests__/runOnAllDevices.test.ts
+++ b/packages/cli-platform-android/src/commands/runAndroid/__tests__/runOnAllDevices.test.ts
@@ -66,6 +66,7 @@ describe('--appFolder', () => {
   const androidProject: AndroidProjectConfig = {
     appName: 'app',
     packageName: 'com.test',
+    applicationId: 'com.test',
     sourceDir: '/android',
     mainActivity: '.MainActivity',
   };
diff --git a/packages/cli-platform-android/src/commands/runAndroid/__tests__/tryLaunchAppOnDevice.test.ts b/packages/cli-platform-android/src/commands/runAndroid/__tests__/tryLaunchAppOnDevice.test.ts
new file mode 100644
index 000000000..39630bcdf
--- /dev/null
+++ b/packages/cli-platform-android/src/commands/runAndroid/__tests__/tryLaunchAppOnDevice.test.ts
@@ -0,0 +1,133 @@
+import {AndroidProjectConfig} from '@react-native-community/cli-types';
+import tryLaunchAppOnDevice from '../tryLaunchAppOnDevice';
+import {Flags} from '..';
+import execa from 'execa';
+
+jest.mock('execa');
+jest.mock('../getAdbPath');
+jest.mock('../tryLaunchEmulator');
+
+const adbPath = 'path/to/adb';
+const device = 'emulator-5554';
+let args: Flags = {
+  activeArchOnly: false,
+  packager: true,
+  port: 8081,
+  terminal: 'iTerm.app',
+  appId: '',
+  appIdSuffix: '',
+  listDevices: false,
+};
+
+let androidProject: AndroidProjectConfig = {
+  sourceDir: '/Users/thymikee/Developer/tmp/App73/android',
+  appName: 'app',
+  packageName: 'com.myapp',
+  applicationId: 'com.myapp.custom',
+  mainActivity: '.MainActivity',
+  dependencyConfiguration: undefined,
+  watchModeCommandParams: undefined,
+  unstable_reactLegacyComponentNames: undefined,
+};
+
+const shellStartCommand = ['shell', 'am', 'start'];
+const actionCategoryFlags = [
+  '-a',
+  'android.intent.action.MAIN',
+  '-c',
+  'android.intent.category.LAUNCHER',
+];
+
+beforeEach(() => {
+  jest.clearAllMocks();
+});
+
+test('launches adb shell with intent to launch com.myapp.MainActivity with different appId than packageName on a simulator', () => {
+  tryLaunchAppOnDevice(device, androidProject, adbPath, args);
+
+  expect(execa.sync).toHaveBeenCalledWith(
+    'path/to/adb',
+    [
+      '-s',
+      'emulator-5554',
+      ...shellStartCommand,
+      '-n',
+      'com.myapp.custom/com.myapp.MainActivity',
+      ...actionCategoryFlags,
+    ],
+    {stdio: 'inherit'},
+  );
+});
+
+test('launches adb shell with intent to launch com.myapp.MainActivity with same appId as packageName on a simulator', () => {
+  tryLaunchAppOnDevice(
+    device,
+    {...androidProject, applicationId: 'com.myapp'},
+    adbPath,
+    args,
+  );
+
+  expect(execa.sync).toHaveBeenCalledWith(
+    'path/to/adb',
+    [
+      '-s',
+      'emulator-5554',
+      ...shellStartCommand,
+      '-n',
+      'com.myapp/com.myapp.MainActivity',
+      ...actionCategoryFlags,
+    ],
+    {stdio: 'inherit'},
+  );
+});
+
+test('launches adb shell with intent to launch com.myapp.MainActivity with different appId than packageName on a device (without calling simulator)', () => {
+  tryLaunchAppOnDevice(undefined, androidProject, adbPath, args);
+
+  expect(execa.sync).toHaveBeenCalledWith(
+    'path/to/adb',
+    [
+      ...shellStartCommand,
+      '-n',
+      'com.myapp.custom/com.myapp.MainActivity',
+      ...actionCategoryFlags,
+    ],
+    {stdio: 'inherit'},
+  );
+});
+
+test('--appId flag overwrites applicationId setting in androidProject', () => {
+  tryLaunchAppOnDevice(undefined, androidProject, adbPath, {
+    ...args,
+    appId: 'my.app.id',
+  });
+
+  expect(execa.sync).toHaveBeenCalledWith(
+    'path/to/adb',
+    [
+      ...shellStartCommand,
+      '-n',
+      'my.app.id/com.myapp.MainActivity',
+      ...actionCategoryFlags,
+    ],
+    {stdio: 'inherit'},
+  );
+});
+
+test('appIdSuffix Staging is appended to applicationId', () => {
+  tryLaunchAppOnDevice(undefined, androidProject, adbPath, {
+    ...args,
+    appIdSuffix: 'Staging',
+  });
+
+  expect(execa.sync).toHaveBeenCalledWith(
+    'path/to/adb',
+    [
+      ...shellStartCommand,
+      '-n',
+      'com.myapp.custom.Staging/com.myapp.MainActivity',
+      ...actionCategoryFlags,
+    ],
+    {stdio: 'inherit'},
+  );
+});