{"id":13472723,"url":"https://github.com/petterh/react-native-android-activity","last_synced_at":"2025-03-26T17:31:09.596Z","repository":{"id":39636338,"uuid":"87914187","full_name":"petterh/react-native-android-activity","owner":"petterh","description":"Sample: Start an Android activity or an iOS view controller from React Native JavaScript.","archived":false,"fork":false,"pushed_at":"2023-01-03T18:13:42.000Z","size":1566,"stargazers_count":276,"open_issues_count":21,"forks_count":68,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-10-30T05:26:12.536Z","etag":null,"topics":["android","ios","ios-app","java","javascript","native-modules","objective-c","react-native","react-native-android"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/petterh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-11T09:14:54.000Z","updated_at":"2024-09-23T14:33:25.000Z","dependencies_parsed_at":"2023-02-01T08:31:34.787Z","dependency_job_id":null,"html_url":"https://github.com/petterh/react-native-android-activity","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petterh%2Freact-native-android-activity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petterh%2Freact-native-android-activity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petterh%2Freact-native-android-activity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/petterh%2Freact-native-android-activity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/petterh","download_url":"https://codeload.github.com/petterh/react-native-android-activity/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245702219,"owners_count":20658565,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","ios","ios-app","java","javascript","native-modules","objective-c","react-native","react-native-android"],"created_at":"2024-07-31T16:00:57.397Z","updated_at":"2025-03-26T17:31:09.079Z","avatar_url":"https://github.com/petterh.png","language":"Java","readme":"# React Native Activity Demo\n\n## Build Status\n\n| Android | iOS |\n|---|---|\n| [![Build Status](https://dev.azure.com/petter0012/react-native-android-activity/_apis/build/status/petterh.react-native-android-activity?branchName=master)](https://dev.azure.com/petter0012/react-native-android-activity/_build/latest?definitionId=1\u0026branchName=master) | [![Build Status](https://dev.azure.com/petter0012/react-native-android-activity/_apis/build/status/petterh.react-native-android-activity%20(1)?branchName=master)](https://dev.azure.com/petter0012/react-native-android-activity/_build/latest?definitionId=2\u0026branchName=master) |\n\nThis sample, which grew out of a [question on Stack Overflow](https://stackoverflow.com/questions/42253397/call-android-activity-from-react-native-code/43675819), demonstrates the interface between React Native JavaScript and native code \u0026ndash; Java on Android, Objective-C on iOS.\n\nThe original version was Android-only; support for iOS was added in March 2019.\n\nThis project demonstrates the following:\n\n* Calling from JavaScript into native modules:\n  * ...using a custom native module called `ActivityStarter`:\n    * Navigate from React Native to a Java activity (or iOS view controller) internal to the host app;\n    * Start an external intent to dial a phone number, passing data from JavaScript;\n    * Query the host app for information.\n  * ...using the native module `Clipboard`, which [comes with React Native out of the box](https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/modules/clipboard/ClipboardModule.java):\n    * Copy information to the clipboard.\n* Calling a JavaScript method from Java or Objective-C, using an officially undocumented approach.\n* Sending events from the native platform to JavaScript. (When possible, prefer this approach to the undocumented one.)\n* Verifying that custom edit menu extensions work with React Native `TextInput`. (Android only.)\n* Adding a custom menu option to React Native debug menu.\n\nThere is no technical difference between the `ActivityStarter` and `Clipboard` native modules, except one is defined in this project while the other ships as part of React Native.\n\nThe starting point for this sample is a slightly tweaked standard React Native project as generated by a long-outdated version of `react-native init`. We add six buttons to the generated page:\n\n![Android Demo App](img/AndroidScreenShot.png)\n\nThe `TextInput` box appears only in the Android version. Since both platforms use the same JavaScript, I took the opportunity to demonstrate how to handle platform-specific tweaks \u0026ndash; look for `Platform.select` in [`index.js`](index.js).\n\n## Getting started\n\n\u003c!-- markdownlint-disable MD031 --\u003e\n\n* Install [Git](https://git-scm.com/downloads).\n* Install [Node.js](https://nodejs.org/en/download/).\n* Install [Yarn](https://yarnpkg.com/lang/en/docs/install/#windows-stable). Use a shell with Git, Node and Yarn in the path for all commands.\n* Clone this project:\\\n  `git clone https://github.com/petterh/react-native-android-activity.git`\\\n  (Alternatively, create your own fork and clone that instead.)\n* `cd react-native-android-activity`\n* Run `yarn` to download dependencies (or, if you wish, `npm install`)\n* For Android development (using Windows, Mac or Linux), install [Android Studio](https://developer.android.com/studio/install.html) (follow instructions [on this page](https://facebook.github.io/react-native/docs/getting-started.html)).\n* For iOS development (Mac only), install [Xcode](https://developer.apple.com/xcode/).\n* By default, the debug build of the app loads the JS bundle from your dev box, so start a bundler:\n  ```cmd\n  yarn start\n  ```\n\n### Android\n\n* Connect an Android device via USB, or use an emulator.\n* [Enable USB Debugging in Developer options](https://developer.android.com/studio/run/device).\n* Open the app in Android Studio and run it.\n* If this fails with the message \"Could not get BatchedBridge, make sure your bundle is packaged correctly\", your packager is likely not running.\n* If it complains about connecting to the dev server, run `adb reverse tcp:8081 tcp:8081`\n* If it crashes while opening the ReactNative controls, try to modify the following phone settings:\n**Android Settings -\u003e Apps -\u003e Settings once again (the gear) to go to Configure Apps view -\u003e Draw over other apps -\u003e Allow React Native Android Activity Demo to draw over other apps**. (The demo app *should* ask for this automatically, though.)\n* To embed the bundle in the apk (and not have to run the packager), set `bundleInDebug=true` in `android/gradle.properties`.\n\n### iOS\n\n* Open the iOS project in Xcode: `open Activity.xcworkspace`.\n* Run the Activity application.\n\n\u003c!-- markdownlint-enable MD031 --\u003e\n\n## The React Native side\n\nThe gist of the JavaScript code looks like this:\n\n```javascript\nimport { ..., NativeModules, ... } from 'react-native';\n\nexport default class ActivityDemoComponent extends Component {\n  render() {\n    return (\n      \u003cView style={styles.container}\u003e\n        \u003cText style={styles.welcome}\u003e\n          Welcome to React Native!\n        \u003c/Text\u003e\n        \u003cText style={styles.instructions}\u003e\n          To get started, edit index.js\n        \u003c/Text\u003e\n        \u003c!-- Menu buttons: https://facebook.github.io/react-native/docs/debugging --\u003e\n        \u003cText style={styles.instructions}\u003e\n          Double tap R on your keyboard to reload,{'\\n'}\n          Shake or press menu button for dev menu\n        \u003c/Text\u003e\n        \u003cView style={styles.buttonContainer}\u003e\n          \u003cButton\n            onPress={() =\u003e NativeModules.ActivityStarter.navigateToExample()}\n            title='Start example activity'\n          /\u003e\n          \u003cButton\n            onPress={() =\u003e NativeModules.ActivityStarter.dialNumber('+1 (234) 567-8910')}\n            title='Dial +1 (234) 567-8910'\n          /\u003e\n          \u003cButton\n            onPress={() =\u003e NativeModules.ActivityStarter.getName((name) =\u003e { alert(name); })}\n            title='Get activity name'\n          /\u003e\n          \u003cButton\n            onPress={() =\u003e NativeModules.Clipboard.setString(\"Hello from JavaScript!\")}\n            title='Copy to clipboard'\n          /\u003e\n        \u003c/View\u003e\n      \u003c/View\u003e\n    );\n  }\n}\n```\n\nThe first three buttons use three methods on `NativeModules.ActivityStarter`. Where does this come from?\n\n## Android: The Java module\n\n`ActivityStarter` is just a Java class that implements a React Native Java interface called `NativeModule`. The heavy lifting of this interface is already done by `BaseJavaModule`, so one normally extends either that one or `ReactContextBaseJavaModule`:\n\n```java\nclass ActivityStarterModule extends ReactContextBaseJavaModule {\n\n    ActivityStarterModule(ReactApplicationContext reactContext) {\n        super(reactContext);\n    }\n\n    @Override\n    public String getName() {\n        return \"ActivityStarter\";\n    }\n\n    @ReactMethod\n    void navigateToExample() {\n        ReactApplicationContext context = getReactApplicationContext();\n        Intent intent = new Intent(context, ExampleActivity.class);\n        context.startActivity(intent);\n    }\n\n    @ReactMethod\n    void dialNumber(@NonNull String number) {\n        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(\"tel:\" + number));\n        getReactApplicationContext().startActivity(intent);\n    }\n\n    @ReactMethod\n    void getActivityName(@NonNull Callback callback) {\n        Activity activity = getCurrentActivity();\n        if (activity != null) {\n            callback.invoke(activity.getClass().getSimpleName());\n        }\n    }\n}\n```\n\nThe name of this class doesn't matter; the `ActivityStarter` module name exposed to JavaScript comes from the `getName()` method.\n\nEach method annotated with a `@ReactMethod` attribute is accessible from JavaScript. Overloads are not allowed, though; you have to know the method signatures. (The out-of-the-box `Clipboard` module isn't usually accessed the way I do it here; React Native includes [`Clipboard.js`](https://github.com/facebook/react-native/blob/master/Libraries/Components/Clipboard/Clipboard.js), which [makes the thing more accessible from JavaScript](https://facebook.github.io/react-native/docs/clipboard.html) \u0026ndash; if you're creating modules for public consumption, consider doing something similar.)\n\nA `@ReactMethod` must be of type `void`. In the case of `getActivityName()` we want to return a string; we do this by using a callback.\n\n## Android: Connecting the dots\n\nThe default app generated by `react-native init` contains a `MainApplication` class that initializes React Native. Among other things it extends `ReactNativeHost` to override its `getPackages` method:\n\n```java\n@Override\nprotected List\u003cReactPackage\u003e getPackages() {\n    return Arrays.\u003cReactPackage\u003easList(\n            new MainReactPackage()\n    );\n}\n```\n\nThis is the point where we hook our Java code to the React Native machinery. Create a class that implements `ReactPackage` and override `createNativeModules`:\n\n```java\nclass ActivityStarterReactPackage implements ReactPackage {\n    @Override\n    public List\u003cNativeModule\u003e createNativeModules(ReactApplicationContext reactContext) {\n        List\u003cNativeModule\u003e modules = new ArrayList\u003c\u003e();\n        modules.add(new ActivityStarterModule(reactContext));\n        return modules;\n    }\n\n    @Override\n    public List\u003cClass\u003c? extends JavaScriptModule\u003e\u003e createJSModules() {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List\u003cViewManager\u003e createViewManagers(ReactApplicationContext reactContext) {\n        return Collections.emptyList();\n    }\n}\n```\n\nFinally, update `MainApplication` to include our new package:\n\n```java\npublic class MainApplication extends Application implements ReactApplication {\n\n    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {\n        @Override\n        public boolean getUseDeveloperSupport() {\n            return BuildConfig.DEBUG;\n        }\n\n        @Override\n        protected List\u003cReactPackage\u003e getPackages() {\n            return Arrays.\u003cReactPackage\u003easList(\n                    new ActivityStarterReactPackage(), // This is it!\n                    new MainReactPackage()\n            );\n        }\n    };\n\n    @Override\n    public ReactNativeHost getReactNativeHost() {\n        return mReactNativeHost;\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        SoLoader.init(this, false);\n    }\n}\n```\n\n## Android: Calling JavaScript from Java\n\nThis demo is invoked by the last button on the page:\n\n```javascript\n\u003cButton\n    onPress={() =\u003e NativeModules.ActivityStarter.callJavaScript()}\n    title='Call JavaScript from Java'\n/\u003e\n```\n\nThe Java side looks like this (in `ActivityStarterReactPackage` class):\n\n```java\n@ReactMethod\nvoid callJavaScript() {\n    Activity activity = getCurrentActivity();\n    if (activity != null) {\n        MainApplication application = (MainApplication) activity.getApplication();\n        ReactNativeHost reactNativeHost = application.getReactNativeHost();\n        ReactInstanceManager reactInstanceManager = reactNativeHost.getReactInstanceManager();\n        ReactContext reactContext = reactInstanceManager.getCurrentReactContext();\n\n        if (reactContext != null) {\n            CatalystInstance catalystInstance = reactContext.getCatalystInstance();\n            WritableNativeArray params = new WritableNativeArray();\n            params.pushString(\"Hello, JavaScript!\");\n            catalystInstance.callFunction(\"JavaScriptVisibleToJava\", \"alert\", params);\n        }\n    }\n}\n```\n\nThe JavaScript method we're calling is defined and made visible to Java as follows:\n\n```javascript\nimport BatchedBridge from \"react-native/Libraries/BatchedBridge/BatchedBridge\";\n\nexport class ExposedToJava {\n  alert(message) {\n      alert(message);\n  }\n}\n\nconst exposedToJava = new ExposedToJava();\nBatchedBridge.registerCallableModule(\"JavaScriptVisibleToJava\", exposedToJava);\n```\n\n## Android: Summary\n\n1. The main application class initializes React Native and creates a `ReactNativeHost` whose `getPackages` include our package in its list.\n1. `ActivityStarterReactPackage` includes `ActivityStarterModule` in its native modules list.\n1. `ActivityStarterModule` returns \"ActivityStarter\" from its `getName` method, and annotates three methods with the `ReactMethod` attribute.\n1. JavaScript can access `ActivityStarter.getActivityName` and friends via `NativeModules`.\n\n## iOS\n\nThe iOS Objective-C classes are parallel to the Android Java classes. There are differences:\n\n* Modules are picked up automatically.\n* There is no react application context; instead there is the react native bridge, which is initialized in the [`AppDelegate`](ios/Activity/AppDelegate.m) class.\n* Events are done somewhat differently. In Android we can just grab a `DeviceEventManagerModule.RCTDeviceEventEmitter` and fire away; in iOS it is necessary to subclass `RCTEventEmitter`.\n\nHere is a sample of an Objective-C class implementation with methods callable from JavaScript:\n\n```obj-c\n@implementation ActivityStarterModule\n\nRCT_EXPORT_MODULE(ActivityStarter);\n\nRCT_EXPORT_METHOD(navigateToExample)\n{\n  dispatch_async(dispatch_get_main_queue(), ^{\n    AppDelegate *appDelegate = (AppDelegate *) [UIApplication sharedApplication].delegate;\n    [appDelegate navigateToExampleView];\n  });\n}\n\nRCT_EXPORT_METHOD(getActivityName:(RCTResponseSenderBlock) callback)\n{\n  callback(@[@\"ActivityStarter (callback)\"]);\n}\n\n@end\n```\n\n## iOS: Calling JavaScript from Java\n\nThis requires the react native bridge, so responsibility resides with the `AppDelegate` class, for convenience.\n\n```obj-c\n- (void) callJavaScript\n{\n  [self.reactBridge enqueueJSCall:@\"JavaScriptVisibleToJava\"\n                           method:@\"alert\"\n                             args:@[@\"Hello, JavaScript!\"]\n                       completion:nil];\n}\n```\n\n## Addendum\n\nI just added a second version of `ActivityStarterModule.getActivityName` called `getActivityNameAsPromise`, with a corresponding button.\n\n## Addendum 2\n\n[I added a sample of event triggering, another way to communicate](https://github.com/petterh/react-native-android-activity/commit/e63706e2ca828d4de4db1bf7cf85fe5be28d648d). Tap **Start Example Activity**, then **Trigger event**.\n\n## Further reading\n\n* [Native Modules on Android](https://facebook.github.io/react-native/docs/native-modules-android.html)\n* [Native Modules on iOS](https://facebook.github.io/react-native/docs/native-modules-ios)\n\n## Issues\n\nThe various Android apps explicitly call `SoLoader.init` because of [this issue](https://github.com/facebook/react-native/issues/26342). I have a [PR to fix it](https://github.com/facebook/react-native/pull/26343). Once this is in (assuming Facebook accepts it) I'll remove them.\n","funding_links":[],"categories":["Java","\u003ca name=\"UI:-Native-Modules\"\u003eUI: Native Modules\u003c/a\u003e"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetterh%2Freact-native-android-activity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpetterh%2Freact-native-android-activity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpetterh%2Freact-native-android-activity/lists"}