I\'m attempting to bridge over the Android functionality of keeping the screen on to React Native. I figured I could do this with a simple module, however I don\'t know how
CustomReactPackage.java
:
public class CustomReactPackage implements ReactPackage {
private Activity mActivity = null;
public CustomReactPackage(Activity activity) {
mActivity = activity;
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
// Add native modules here
return modules;
}
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> modules = new ArrayList<>();
// Add native UI components here
modules.add(new LSPlayerManager(mActivity));
return modules;
}
}
LSPlayerManager
is my native UI component. I define a constructor so that I can pass in the activity:
public LSPlayerManager(Activity activity) {
mActivity = activity;
}
And finally in MainActivity.java
where the ReactInstanceManager
is defined, we can pass the activity to our custom React package:
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("src/index.android")
.addPackage(new MainReactPackage())
.addPackage(new CustomReactPackage(this)) // <--- LIKE THIS!
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
UPDATE FOR REACT NATIVE 0.29.0
This is no longer how you access activity in a native module. See https://github.com/facebook/react-native/releases/tag/v0.29.0 for migration instructions
It's not super clear from https://github.com/facebook/react-native/blob/master/docs/NativeModulesAndroid.md, but you can read the source code of the ToastModule in the react native distribution to see how they do it github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/modules/toast/ToastModule.java#L31-L33
The idea is that the main activity will be passed as the reactContext in the initializer for the module called from ReactInstanceManager.builder() call in github.com/facebook/react-native/blob/3b4845f93c296ed93c348733809845d587227823/local-cli/generator-android/templates/package/MainActivity.java#L28 via the instantiation of MainReactPackage github.com/facebook/react-native/blob/daba14264c9f0d29c0327440df25d81c2f58316c/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java#L49
How do you pass the activity to a package from ReactApplication::getPackages() ?
I do not understand. I cannot find any clear examples
package com.bluetoothioexample;
import android.app.Application;
import android.util.Log;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import java.util.Arrays;
import java.util.List;
import com.subsite.bluetoothio.BluetoothIOPackage;
import com.oblador.vectoricons.VectorIconsPackage;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new BluetoothIOPackage(this), //<- How pass the activity
// from ReactApplication ?
new VectorIconsPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
Answer: You do NOT pass Activity from MainApplication to the package.
You call getCurrentActivity() from within your ReactContextBaseJavaModule
I needed to modify an outdated module (react-android-360-video) and found this helpful...
In android/app/src/main/java/com/webcdpmobiledemo/MainApplication.java
, I used the new format for adding a package:
...
import com.vrvideocomponent.VrVideoViewPackage;
public class MainApplication extends Application implements ReactApplication {
...
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new VrVideoViewPackage()
);
}
...
};
...
}
And android/app/src/main/java/com/webcdpmobiledemo/MainActivity.java
is essentially empty:
package com.yourproject;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "YourProject";
}
}
Then, I modified the VrVideoViewPackage file, which needs to pass the reactContext
to the VrVideoViewManager
it calls:
...
public class VrVideoViewPackage implements ReactPackage {
...
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new VrVideoViewManager(reactContext)
);
}
}
And finally, in the VrVideoViewManager
the activity can be accessed like so:
...
import android.app.Activity;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.bridge.ReactContext;
...
public class VrVideoViewManager extends SimpleViewManager<VrVideoView> {
...
public VrVideoViewManager(ReactContext reactContext) {
// Do not store mActivity, always getCurrentActivity when needed
Activity mActivity = mContext.getCurrentActivity();
}
@Override
protected VrVideoView createViewInstance(ThemedReactContext reactContext) {
// You can also activity from ThemedReactContext
Activity mActivity = reactContext.getCurrentActivity();
VrVideoView vrView = new VrVideoView(mActivity);
vrView.setEventListener(new ActivityEventListener(vrView));
vrView.pauseVideo();
return new VrVideoView(mActivity);
}
...
}
I guess getCurrentActivity()
method of ReactContextBaseJavaModule might be used like the following code which has been copied from React-Native
original code;
import android.app.Activity;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
public class AwesomeModule extends ReactContextBaseJavaModule {
public AwesomeModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "AwesomeAndroid";
}
private static final String ERROR_NO_ACTIVITY = "E_NO_ACTIVITY";
private static final String ERROR_NO_ACTIVITY_MESSAGE = "Tried to do the something while not attached to an Activity";
@ReactMethod
public void doSomething(successCallback, errorCallback) {
final Activity activity = getCurrentActivity();
if (activity == null) {
errorCallback(ERROR_NO_ACTIVITY, ERROR_NO_ACTIVITY_MESSAGE);
return;
}
}
}
In your class file imports include...
import android.view.WindowManager;
import android.view.Window;
import android.view.View;
In your class constructor...
private static ReactApplicationContext reactContext;
MyModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
Then in for class methods...
@ReactMethod
public void doSomethingRequiringWindow() {
// get access to current UI thread first
reactContext.getCurrentActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Window window = reactContext
.getCurrentActivity().getWindow();
View decorView = reactContext
.getCurrentActivity().getWindow().getDecorView();
// you have access to main ui thread so you can effect
// immediate behavior on window and decorView
}
});
}