45

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 to get access to the current Android Activity from said module.

I need the Activity reference so I can call .getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); on it

I tried to get the activity via casting like so ((Activity)getReactApplicationContext().getBaseContext()), but this throws a "cannot be cast to Android.app.Activity" error

1
  • For examples related to communication between android and native components. Repo Link Commented Apr 4, 2017 at 9:26

11 Answers 11

43

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

Sign up to request clarification or add additional context in comments.

5 Comments

This is definitely the best answer and exactly what I'm doing in my own project. In my case I'm using the embedded ZXing QR code scanner inside of React Native and needed to both launch the scan and read the result (onActivityResult). Here's the gist of my code in case it helps anyone with the similar problem - gist.github.com/mdp/a501edd93632f13fd9f8
how can I pass the current activity to the new way to add packages. return Arrays.<ReactPackage>asList( new MainReactPackage(), new ThePackageClass(this) <-- this here doesnt work ); the this doesnt work here.
This answer is no longer valid from react-native 29.0 and onwards where we declare the packages within MainApplication.java
@SudoPlz you're right. Now you can just extend ReactContextBaseJavaModule and call getCurrentActivity(). However, this only works for Native Modules, not Native UI Components. I still have not found a way to get the activity in a SimpleViewManager class...
How do you pass the activity to a package from ReactApplication::getPackages() ? I do not understand. I cannot find any clear examples.
42

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;
    }

  }

}

2 Comments

The link I'd wrote in answer specifies the master repo of react-native and it seems alive. Also I tried to find anything about its removing however I could not. Could you please share more information about it?
Of course ReactContextBaseJavaModule.getCurrentActivity is working in latest RN (0.56) and returns the only one activity on RN apps: MainActivity, unless you are using react-native-navigation.
9

Editted:

The issue is that getReactApplicationContext() returns the context of the Application and not the Activity. You cannot typecast an Application context to an Activity.

This is a simple workaround

Since usually, there is only one activity (Main Activity) in react-native, We can write a static function in MainActivity to return the activity

private static Activity mCurrentActivity = null;

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mCurrentActivity = this;
    ...
}
...
public static Activity getActivity(){
    Activity activity = new Activity();
    activity = mCurrentActivity;
    return activity;
}

then call MainActivity.getActivity() from the bridge modules

4 Comments

this works, but I'm assuming you saw the comment on GitHub: github.com/facebook/react-native/issues/…. I'm using that method and it's working great. If you edit your answer to describe that method I'll accept it.
I am yet to look into creating my own implemenatation of ReactPackage. Since you've already figured it out, it will be a great help to the community if you can pass your knowledge
just posted my own answer. Check it out and give it a try.
I am newbie to android, could you let me know how can i call getCurrentActivity() in a react native package? I am looking to integrate github.com/msmakhlouf/react-native-android-sms plugin, but it doesn't seems to work because of activity this.
2

Get Activity / Window / Decor View with UI Thread context

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
    }
  });
}

Comments

2

If you want to get current instance of the foreground activity in a ReactContextBaseJavaModule subclass, use:

// class MyReactModule extends ReactContextBaseJavaModule
Activity activity = getCurrentActivity();

It comes from ReactContextBaseJavaModule#getCurrentActivity which uses reactContext.getCurrentActivity()

Note: ReactApplicationContext#getCurrentActivity is package private. So you can not use reactContext.getCurrentActivity().

  • ReactContextBaseJavaModule's is protected. So deriving it makes it achievable:
protected @Nullable final Activity getCurrentActivity() {
    return mReactApplicationContext.getCurrentActivity();
  }
  • ReactApplicationContext's is package private (default access). So you can't get it:
/* package */ @Nullable Activity getCurrentActivity() {
    return mCurrentActivity;
  }

Bunos

Since it's @Nullable, make sure you check it's availability:

if (reactContext.hasCurrentActivity()) {
   // There is. So get it
   Activity theActivity = getCurrentActivity();
}

2 Comments

Interesting, how do you about adding existing native code/ui? stackoverflow.com/questions/69562692/…
Hmmm. I'll post an answer
1

For custom UI component, you can get the context in the constructor, and later you can get the activity by context.getCurrentActivity()

3 Comments

bro I am a little confused. I am trying to use "GoogleSignIn.getAccountForExtension" and I am not able to link the context to it. please check this out stackoverflow.com/questions/65948333/…
@grooot bro the link is dead
0

For 0.28 and before, you can get activity from ReactInstanceManagerImpl 's private @Nullable Activity mCurrentActivity;, 0.29+ 's ReactContextBaseJavaModule using the same field.

Comments

0

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

1 Comment

Never mind. ReactContextBaseJavaModule class has a getCurrentActivity() method.
0

my code like this final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext()); and not found error for running

Comments

-1

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

1 Comment

Does not explain how to get the Activity.
-1

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);
    }

    ...
}

2 Comments

Upto Package is ok, after that? i have to get DeviceToken from Module 'public class DeviceModule extends ReactContextBaseJavaModule' a string Value as "PackageReference.ReactOverridemethod.StringValue" from .js????
@Ramaraju I'm not sure I understand your question, could you rephrase? The solution I provided is for extracting activity within a ReactPackage class and a SimpleViewManager class. If you wish to extract from ReactContextBaseJavaModule class you might check out efkan's answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.