Skip to content

Commit

Permalink
feat: read main activity from AndroidManifest.xml automatically (#1967
Browse files Browse the repository at this point in the history
)

* feat: read main activity from `AndroidManifest.xml` automatically

* test: update e2e snaphosts

* test: add test scenario for main activity as class name

* chore: types cleanup

* chore: code cleaning

* fix: tests
  • Loading branch information
szymonrybczak authored Aug 17, 2023
1 parent bd496c4 commit 7342221
Show file tree
Hide file tree
Showing 18 changed files with 276 additions and 18 deletions.
3 changes: 2 additions & 1 deletion __e2e__/__snapshots__/config.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ exports[`shows up current config without unnecessary output 1`] = `
"android": {
"sourceDir": "<<REPLACED_ROOT>>/TestProject/android",
"appName": "app",
"packageName": "com.testproject"
"packageName": "com.testproject",
"mainActivity": ".MainActivity"
}
}
}
Expand Down
1 change: 1 addition & 0 deletions docs/platforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ type AndroidProjectConfig = {
sourceDir: string;
appName: string;
packageName: string;
mainActivity: string;
dependencyConfiguration?: string;
watchModeCommandParams: string;
};
Expand Down
16 changes: 16 additions & 0 deletions packages/cli-config/src/__tests__/index-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,12 +406,28 @@ test('should convert project sourceDir relative path to absolute', () => {
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.coinbase.android">
<application android:name=".MainApplication">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
`,
'android2/AndroidManifest.xml': `
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.coinbase.android">
<application android:name=".MainApplication">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
`,
});
Expand Down
1 change: 1 addition & 0 deletions packages/cli-platform-android/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@react-native-community/cli-tools": "12.0.0-alpha.10",
"chalk": "^4.1.2",
"execa": "^5.0.0",
"fast-xml-parser": "^4.2.4",
"glob": "^7.1.3",
"logkitty": "^0.7.1"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ describe('--appFolder', () => {
appName: 'app',
packageName: 'com.test',
sourceDir: '/android',
mainActivity: '.MainActivity',
};
beforeEach(() => {
jest.clearAllMocks();
Expand Down
15 changes: 7 additions & 8 deletions packages/cli-platform-android/src/commands/runAndroid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ async function runAndroid(_argv: Array<string>, config: Config, args: Flags) {
}
}

const androidProject = getAndroidProject(config);
let androidProject = getAndroidProject(config);

if (args.mainActivity) {
androidProject.mainActivity = args.mainActivity;
}

return buildAndRun(args, androidProject);
}
Expand Down Expand Up @@ -294,12 +298,8 @@ function installAndLaunchOnDevice(
androidProject,
selectedTask,
);
tryLaunchAppOnDevice(
selectedDevice,
androidProject.packageName,
adbPath,
args,
);

tryLaunchAppOnDevice(selectedDevice, androidProject, adbPath, args);
}

export default {
Expand Down Expand Up @@ -338,7 +338,6 @@ export default {
{
name: '--main-activity <string>',
description: 'Name of the activity to start',
default: 'MainActivity',
},
{
name: '--deviceId <string>',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ async function runOnAllDevices(
if (args.binaryPath && device) {
tryInstallAppOnDevice(args, adbPath, device, androidProject);
}
tryLaunchAppOnDevice(device, androidProject.packageName, adbPath, args);
tryLaunchAppOnDevice(device, androidProject, adbPath, args);
},
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,25 @@
*/

import execa from 'execa';
import {Flags} from '.';
import {AndroidProject, Flags} from '.';
import {logger, CLIError} from '@react-native-community/cli-tools';

function tryLaunchAppOnDevice(
device: string | void,
packageName: string,
androidProject: AndroidProject,
adbPath: string,
args: Flags,
) {
const {appId, appIdSuffix} = args;
const {packageName, mainActivity} = androidProject;

const packageNameWithSuffix = [appId || packageName, appIdSuffix]
.filter(Boolean)
.join('.');

const activityToLaunch = args.mainActivity.includes('.')
? args.mainActivity
: [packageName, args.mainActivity].filter(Boolean).join('.');
const activityToLaunch = mainActivity.includes('.')
? mainActivity
: [packageName, mainActivity].filter(Boolean).join('.');

try {
// Here we're using the same flags as Android Studio to launch the app
Expand Down
20 changes: 20 additions & 0 deletions packages/cli-platform-android/src/config/__fixtures__/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ const appBuildGradle = fs.readFileSync(
path.join(__dirname, './files/appbuild.gradle'),
);

const fewActivitiesManifest = fs.readFileSync(
path.join(__dirname, './files/AndroidManifest-few-activities.xml'),
);

const classNameManifest = fs.readFileSync(
path.join(__dirname, './files/AndroidManifest-className.xml'),
);

function generateValidFileStructureForLib(classFileName: string) {
return {
'build.gradle': buildGradle,
Expand Down Expand Up @@ -278,3 +286,15 @@ export const findPackagesClassNameJavaNotValid = [
}
`,
];

export const fewActivities = {
src: {
'AndroidManifest.xml': fewActivitiesManifest,
},
};

export const className = {
src: {
'AndroidManifest.xml': classNameManifest,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.some.example">
<uses-permission android:name="android.permission.INTERNET" />

<application android:name=".MainApplication">
<activity android:name="com.example.ExampleAppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<application android:name=".ExampleApplication">
<activity android:name=".ExampleAppActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="rntester" android:host="example" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
</application>
</queries>
</manifest>
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.some.example">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.some.example">
<uses-permission android:name="android.permission.INTERNET" />

<application android:name=".MainApplication">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ exports[`android::getProjectConfig returns an object with android project config
Object {
"appName": "",
"dependencyConfiguration": undefined,
"mainActivity": ".MainActivity",
"packageName": "com.some.example",
"sourceDir": "/flat/android",
"unstable_reactLegacyComponentNames": undefined,
Expand All @@ -15,6 +16,7 @@ exports[`android::getProjectConfig returns an object with android project config
Object {
"appName": "",
"dependencyConfiguration": undefined,
"mainActivity": ".MainActivity",
"packageName": "com.some.example",
"sourceDir": "/multiple/android",
"unstable_reactLegacyComponentNames": undefined,
Expand All @@ -26,6 +28,7 @@ exports[`android::getProjectConfig returns an object with android project config
Object {
"appName": "app",
"dependencyConfiguration": undefined,
"mainActivity": ".MainActivity",
"packageName": "com.some.example",
"sourceDir": "/nested/android",
"unstable_reactLegacyComponentNames": undefined,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import findManifest from '../findManifest';
import getMainActivity from '../getMainActivity';
import * as mocks from '../__fixtures__/android';

jest.mock('path');
jest.mock('fs');

const fs = require('fs');

describe('android::getMainActivity', () => {
beforeAll(() => {
fs.__setMockFilesystem({
empty: {},
valid: {
android: {
app: mocks.valid,
},
},
className: {
android: {
app: mocks.className,
},
},
few: {
android: {
app: mocks.fewActivities,
},
},
});
});

it('returns main activity if file exists in the folder', () => {
const manifestPath = findManifest('/valid');
const manifest = getMainActivity(manifestPath || '');
expect(manifest).not.toBeNull();
expect(typeof manifest).toBe('string');
expect(manifest).toBe('.MainActivity');
});

it('returns main activity if there is few activities', () => {
const manifestPath = findManifest('/few');
const mainActivity = getMainActivity(manifestPath || '');
expect(mainActivity).not.toBeNull();
expect(typeof mainActivity).toBe('string');
expect(mainActivity).toBe('.ExampleAppActivity');
});

it('returns main activity if it is class name', () => {
const manifestPath = findManifest('/className');
const mainActivity = getMainActivity(manifestPath || '');
expect(mainActivity).not.toBeNull();
expect(typeof mainActivity).toBe('string');
expect(mainActivity).toBe('com.example.ExampleAppActivity');
});

it('returns null if file do not exist', () => {
const fakeManifestPath = findManifest('/empty');
expect(getMainActivity(fakeManifestPath || '')).toBeNull();
});
});
Loading

0 comments on commit 7342221

Please sign in to comment.