Skip to content
Permalink
Browse files

Add ReactInstanceHolder

Reviewed By: astreet

Differential Revision: D3328342

fbshipit-source-id: af4e825d0b7c2d3d4490094a939e97cc527dd242
  • Loading branch information...
foghina authored and Facebook Github Bot 8 committed Jun 17, 2016
1 parent d78602b commit 49f20f41546e3ba8e7fe43c84c4c701684d0434d
@@ -40,38 +40,8 @@
private @Nullable PermissionListener mPermissionListener;
private @Nullable ReactInstanceManager mReactInstanceManager;
private @Nullable ReactRootView mReactRootView;
private LifecycleState mLifecycleState = LifecycleState.BEFORE_RESUME;
private DoubleTapReloadRecognizer mDoubleTapReloadRecognizer;

/**
* Returns the name of the bundle in assets. If this is null, and no file path is specified for
* the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will
* always try to load the JS bundle from the packager server.
* e.g. "index.android.bundle"
*/
protected @Nullable String getBundleAssetName() {
return "index.android.bundle";
};

/**
* Returns a custom path of the bundle file. This is used in cases the bundle should be loaded
* from a custom path. By default it is loaded from Android assets, from a path specified
* by {@link getBundleAssetName}.
* e.g. "file://sdcard/myapp_cache/index.android.bundle"
*/
protected @Nullable String getJSBundleFile() {
return null;
}

/**
* Returns the name of the main module. Determines the URL used to fetch the JS bundle
* from the packager server. It is only used when dev support is enabled.
* This is the first file to be executed once the {@link ReactInstanceManager} is created.
* e.g. "index.android"
*/
protected String getJSMainModuleName() {
return "index.android";
}
private boolean mDoRefresh = false;

/**
* Returns the launchOptions which will be passed to the {@link ReactInstanceManager}
@@ -92,48 +62,31 @@ protected String getJSMainModuleName() {
protected abstract String getMainComponentName();

/**
* Returns whether dev mode should be enabled. This enables e.g. the dev menu.
*/
protected abstract boolean getUseDeveloperSupport();

/**
* Returns a list of {@link ReactPackage} used by the app.
* You'll most likely want to return at least the {@code MainReactPackage}.
* If your app uses additional views or modules besides the default ones,
* you'll want to include more packages here.
* A subclass may override this method if it needs to use a custom {@link ReactRootView}.
*/
protected abstract List<ReactPackage> getPackages();
protected ReactRootView createRootView() {
return new ReactRootView(this);
}

/**
* A subclass may override this method if it needs to use a custom instance.
* Get the {@link ReactNativeHost} used by this app. By default, assumes {@link #getApplication()}
* is an instance of {@link ReactApplication} and calls
* {@link ReactApplication#getReactNativeHost()}. Override this method if your application class
* does not implement {@code ReactApplication} or you simply have a different mechanism for
* storing a {@code ReactNativeHost}, e.g. as a static field somewhere.
*/
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(getApplication())
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setInitialLifecycleState(mLifecycleState);

for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}

String jsBundleFile = getJSBundleFile();

if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(getBundleAssetName());
}

return builder.build();
protected ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getApplication()).getReactNativeHost();
}

/**
* A subclass may override this method if it needs to use a custom {@link ReactRootView}.
* Get whether developer support should be enabled or not. By default this delegates to
* {@link ReactNativeHost#getUseDeveloperSupport()}. Override this method if your application
* class does not implement {@code ReactApplication} or you simply have a different logic for
* determining this (default just checks {@code BuildConfig}).
*/
protected ReactRootView createRootView() {
return new ReactRootView(this);
protected boolean getUseDeveloperSupport() {
return ((ReactApplication) getApplication()).getReactNativeHost().getUseDeveloperSupport();

This comment has been minimized.

Copy link
@baoti

baoti Jun 18, 2016

Call this.getReactNativeHost() instead.

This comment has been minimized.

Copy link
@baoti

baoti Jun 29, 2016

@foghina , If it call getReactNativeHost(), I just need override one getReactNativeHost(), no need for getUseDeveloperSupport().

}

@Override
@@ -150,9 +103,11 @@ protected void onCreate(Bundle savedInstanceState) {
}
}

mReactInstanceManager = createReactInstanceManager();
mReactRootView = createRootView();
mReactRootView.startReactApplication(mReactInstanceManager, getMainComponentName(), getLaunchOptions());
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
getMainComponentName(),
getLaunchOptions());
setContentView(mReactRootView);
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}
@@ -161,62 +116,57 @@ protected void onCreate(Bundle savedInstanceState) {
protected void onPause() {
super.onPause();

mLifecycleState = LifecycleState.BEFORE_RESUME;

if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause();
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onHostPause();
}
}

@Override
protected void onResume() {
super.onResume();

mLifecycleState = LifecycleState.RESUMED;

if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onHostResume(this, this);
}
}

@Override
protected void onDestroy() {
super.onDestroy();

mReactRootView.unmountReactApplication();
mReactRootView = null;

if (mReactInstanceManager != null) {
mReactInstanceManager.destroy();
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
mReactRootView = null;
}
getReactNativeHost().clear();
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mReactInstanceManager != null) {
mReactInstanceManager.onActivityResult(requestCode, resultCode, data);
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager()
.onActivityResult(requestCode, resultCode, data);
}
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (mReactInstanceManager != null &&
mReactInstanceManager.getDevSupportManager().getDevSupportEnabled()) {
if (getReactNativeHost().hasInstance() && getUseDeveloperSupport()) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
mReactInstanceManager.showDevOptionsDialog();
getReactNativeHost().getReactInstanceManager().showDevOptionsDialog();
return true;
}
if (mDoubleTapReloadRecognizer.didDoubleTapR(keyCode, getCurrentFocus())) {
mReactInstanceManager.getDevSupportManager().handleReloadJS();
getReactNativeHost().getReactInstanceManager().getDevSupportManager().handleReloadJS();
}
}
return super.onKeyUp(keyCode, event);
}

@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onBackPressed();
} else {
super.onBackPressed();
}
@@ -229,8 +179,8 @@ public void invokeDefaultOnBackPressed() {

@Override
public void onNewIntent(Intent intent) {
if (mReactInstanceManager != null) {
mReactInstanceManager.onNewIntent(intent);
if (getReactNativeHost().hasInstance()) {
getReactNativeHost().getReactInstanceManager().onNewIntent(intent);
} else {
super.onNewIntent(intent);
}
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

package com.facebook.react;

public interface ReactApplication {

/**
* Get the default {@link ReactNativeHost} for this app.
*/
ReactNativeHost getReactNativeHost();
}
@@ -0,0 +1,125 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

package com.facebook.react;

import javax.annotation.Nullable;

import java.util.List;

import android.app.Application;

import com.facebook.infer.annotation.Assertions;

/**
* Simple class that holds an instance of {@link ReactInstanceManager}. This can be used in your
* {@link Application class} (see {@link ReactApplication}), or as a static field.
*/
public abstract class ReactNativeHost {

private final Application mApplication;
private @Nullable ReactInstanceManager mReactInstanceManager;

protected ReactNativeHost(Application application) {
mApplication = application;
}

/**
* Get the current {@link ReactInstanceManager} instance, or create one.
*/
public ReactInstanceManager getReactInstanceManager() {
if (mReactInstanceManager == null) {
mReactInstanceManager = createReactInstanceManager();
}
return mReactInstanceManager;
}

/**
* Get whether this holder contains a {@link ReactInstanceManager} instance, or not. I.e. if
* {@link #getReactInstanceManager()} has been called at least once since this object was created
* or {@link #clear()} was called.
*/
public boolean hasInstance() {
return mReactInstanceManager != null;
}

/**
* Destroy the current instance and release the internal reference to it, allowing it to be GCed.
*/
public void clear() {
if (mReactInstanceManager != null) {
mReactInstanceManager.destroy();
mReactInstanceManager = null;
}
}

protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
.setApplication(mApplication)
.setJSMainModuleName(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);

for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}

String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}

return builder.build();
}

/**
* Returns the name of the main module. Determines the URL used to fetch the JS bundle
* from the packager server. It is only used when dev support is enabled.
* This is the first file to be executed once the {@link ReactInstanceManager} is created.
* e.g. "index.android"
*/
protected String getJSMainModuleName() {
return "index.android";
}

/**
* Returns a custom path of the bundle file. This is used in cases the bundle should be loaded
* from a custom path. By default it is loaded from Android assets, from a path specified
* by {@link getBundleAssetName}.
* e.g. "file://sdcard/myapp_cache/index.android.bundle"
*/
protected @Nullable String getJSBundleFile() {
return null;
}

/**
* Returns the name of the bundle in assets. If this is null, and no file path is specified for
* the bundle, the app will only work with {@code getUseDeveloperSupport} enabled and will
* always try to load the JS bundle from the packager server.
* e.g. "index.android.bundle"
*/
protected @Nullable String getBundleAssetName() {
return "index.android.bundle";
}

/**
* Returns whether dev mode should be enabled. This enables e.g. the dev menu.
*/
protected abstract boolean getUseDeveloperSupport();

/**
* Returns a list of {@link ReactPackage} used by the app.
* You'll most likely want to return at least the {@code MainReactPackage}.
* If your app uses additional views or modules besides the default ones,
* you'll want to include more packages here.
*/
protected abstract List<ReactPackage> getPackages();
}
@@ -1,11 +1,6 @@
package <%= package %>;

import com.facebook.react.ReactActivity;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

public class MainActivity extends ReactActivity {

@@ -17,24 +12,4 @@
protected String getMainComponentName() {
return "<%= name %>";
}

/**
* Returns whether dev mode should be enabled.
* This enables e.g. the dev menu.
*/
@Override
protected boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

/**
* A list of packages used by the app. If the app uses additional views
* or modules besides the default ones, add more packages here.
*/
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
}

37 comments on commit 49f20f4

@foghina

This comment has been minimized.

Copy link
Contributor Author

replied Jun 17, 2016

@grabbou heads-up: this is a breaking change. I had to change the template to add some new functionality that will be used for background JS. New apps will not have any issues, but upgrading might be difficult. In theory (not tested) old apps should continue to work without updating the template, but react-native upgrade might be problematic.

Let me know if you need more info!

@grabbou

This comment has been minimized.

Copy link
Collaborator

replied Jun 17, 2016

Thanks! Just wanted to say this is a good way for providing extra explanation and informing about breaking changes. I am travelling right now, but saved that and will answer tomorrow :)

CC: @mkonicek @bestander

@baoti

This comment has been minimized.

Copy link

replied Jun 17, 2016

@foghina Examples (Movies and UIExplorer) are broken.

@leeight

This comment has been minimized.

Copy link
Contributor

replied Jun 20, 2016

Fixed

@orhan

This comment has been minimized.

Copy link

replied Jun 21, 2016

As @chirag04 already pointed out, there is no easy way to access a reference to the MainActivity anymore when calling getPackages, so this breaks a lot of third-party modules which require an activity to be passed in their constructor.

I haven't fully understood why we are going to the route of using MainApplication for creating a ReactInstanceManager, so I can't come up with a proper solution to this.

Is there any way to handle this gracefully?

@baoti

This comment has been minimized.

Copy link

replied Jun 22, 2016

@chirag04 @orhan When public the ReactContext.getCurrentActivity, you can use it from third-party modules.

@foghina

This comment has been minimized.

Copy link
Contributor Author

replied Jun 22, 2016

@orhan that is by design. We persist React instances across Activities, so packages & modules shouldn't take activities in constructors and should never hold long-lived references to them, as that will cause leaks. Instead, modules can extend ReactContextBaseJavaModule and use getCurrentActivity() when they need it. This may make it harder to integrate 3rd party libraries, but it enforces certain assumptions that let us decouple the React instance from the Activity.

@nihgwu

This comment has been minimized.

Copy link
Contributor

replied Jun 24, 2016

i've upgrade to 0.29, then how can i get those third-party packages to work witch need to pass the Activity to the constructor such as react-native-code-push

@satya164

This comment has been minimized.

Copy link
Collaborator

replied Jun 24, 2016

@nihgwu Those packages need to be updated so that they don't need the activity to be passed in the constructor. The correct way is to extend ReactContextBaseJavaModule and use the getCurrentActivity() method.

@lostintangent

This comment has been minimized.

Copy link

replied Jun 24, 2016

@nihgwu We're making the necessary changes to the CodePush plugin right now in order to support RN 0.29. We should have a release out by early next week.

@nihgwu

This comment has been minimized.

Copy link
Contributor

replied Jun 24, 2016

@lostintangent that's great, then i'll disabled it temporarily

@kraffslol

This comment has been minimized.

Copy link
Contributor

replied Jun 27, 2016

How would something like onActivityResult be used now?

@foghina

This comment has been minimized.

Copy link
Contributor Author

replied Jun 27, 2016

@kraffslol same way as before, just need to go through the host.

@kraffslol

This comment has been minimized.

Copy link
Contributor

replied Jun 27, 2016

@foghina Could you give an example on how this is done? Placing the onActivityResult method inside of the ReactNativeHost constructor in MainApplication.java results in a compilation error. When placing it in MainActivity it compiles, but from what I can see there's no way for me to pass the data on to my module.

@kraffslol

This comment has been minimized.

Copy link
Contributor

replied Jun 27, 2016

@satya164 Thank you!

@nihgwu

This comment has been minimized.

Copy link
Contributor

replied Jun 29, 2016

I'm using the react-native-splashscreen package in my app, and I've tried to using the getCurrentActivity method, but it's always null, I don't know why because I know nothing about native android development, I've also checked the new version of react-native-code-push which already add the support for RN0.29, but find no clue.
https://github.com/remobile/react-native-splashscreen/blob/master/android/src/main/java/com/remobile/splashscreen/RCTSplashScreen.java#L83

@satya164

This comment has been minimized.

Copy link
Collaborator

replied Jun 29, 2016

@nihgwu It's set in onResume, so it'll be null when you try to access it inside constructor.

@nihgwu

This comment has been minimized.

Copy link
Contributor

replied Jun 29, 2016

@satya164 Thank you so much, sorry to bother you but how can I achieve it in the new structure, now i move the SplashScreen.show to the index.js and it works but there is still white screen between the launch and RN initialization

@satya164

This comment has been minimized.

Copy link
Collaborator

replied Jun 29, 2016

@nihgwu I think your module can subscribe to lifecycle events - http://facebook.github.io/react-native/docs/native-modules-android.html#listening-to-lifecycle-event and then you can call the function in onHostResume

@nihgwu

This comment has been minimized.

Copy link
Contributor

replied Jun 29, 2016

@satya164 It works, thanks

@avishayil

This comment has been minimized.

Copy link

replied Jul 1, 2016

Hi @satya164, i'm trying to get the activity from the module but it gets null:

public class MyPackage extends ReactContextBaseJavaModule implements LifecycleEventListener {

    private ReactContext mReactContext;

    public MyPackage(ReactApplicationContext reactContext) {
        super(reactContext);

        mReactContext = reactContext;
        Log.d("ACTIVITY MYPACKAGE", this.getCurrentActivity().getLocalClassName());   
   }
}

Log produces Attempted to invoke virtual method ... getLocalClassName() on a null object reference.

If i'm moving the code to the onHostResume, it never happens.

Any ideas?

@satya164

This comment has been minimized.

Copy link
Collaborator

replied Jul 1, 2016

@avishayil as mentioned earlier, you can't use getCurrentActivity in the module constructor. Also seems you're not adding the listener itself, that's why onHostResume never gets called. Check the docs on how to add listener - http://facebook.github.io/react-native/docs/native-modules-android.html#listening-to-lifecycle-event

@nihgwu

This comment has been minimized.

Copy link
Contributor

replied Jul 1, 2016

@avishayil #8509 (comment) you can check the diff I've pasted there from the original one, I'm sure you will find the solution

@avishayil

This comment has been minimized.

Copy link

replied Jul 2, 2016

Nailed it, thanks!

@varungupta85

This comment has been minimized.

Copy link
Contributor

replied Jul 6, 2016

What is the motivation behind this change? I wanted to have some context before I start to make the suggested changes in my application. I don't have any experience in native android development. So, I am sorry if it is obvious. Thanks!

@marcshilling

This comment has been minimized.

Copy link

replied Jul 8, 2016

@foghina @satya164 have you put any thought into how Native UI Components can access the activity? It's easy for Native Modules to extend ReactContextBaseJavaModule, but Native UI Components are already extending SimpleViewManager, so I'm not sure how to proceed...

@foghina

This comment has been minimized.

Copy link
Contributor Author

replied Jul 8, 2016

@marcshilling view managers shouldn't be tied to activities either. Why do you need to access the Activity from a view manager? In methods that take a view as a parameter (such as prop setters), you may be able to cast View#getContext() to an Activity, though I'm not 100% sure. Check out the answer here as well:

http://stackoverflow.com/questions/8276634/android-get-hosting-activity-from-a-view

@marcshilling

This comment has been minimized.

Copy link

replied Jul 8, 2016

@foghina I'm bridging an Android library that vends a Fragment. In order to add a fragment to a view you need a FragmentManager, and in order to get a FragmentManager you need an Activity. This was my SimpleViewManager's constructor prior to 0.29:

    public LSPlayerManager(Activity activity) {
        mActivity = activity;
        mFragmentManager = mActivity.getFragmentManager();
    }

Then later on, I:

        mPlayerFragment = VRLSPlayerFragment.newInstance();
        mFragmentManager.beginTransaction().add(mPlayerFragment, mPlayerFragment.FRAGMENT_TAG).commit();
        mFragmentManager.executePendingTransactions();
        mFrameLayout.addView(mPlayerFragment.getView());

FWIW, I tried to pass reactContext into the constructor and cast it to activity, but this crashes:
mActivity = (Activity)((Context)reactContext);

@foghina

This comment has been minimized.

Copy link
Contributor Author

replied Jul 8, 2016

Yeah, ReactContext is not an instance of Activity. But the context that we pass to view managers from the UIManager is different, so check the context passed to createViewInstance, it might be an instance of Activity.

If not, feel free to send a PR that adds getCurrentActivity() to view managers :).

@marcshilling

This comment has been minimized.

Copy link

replied Jul 8, 2016

I don't even think it would be possible to add getCurrentActivity() to SimpleViewManager because those have no context. It seems like we'd have to completely reverse course on the decisions that 0.29.0 made (moving things in MainApplication instead of MainActivity).

@foghina

This comment has been minimized.

Copy link
Contributor Author

replied Jul 8, 2016

@stan229

This comment has been minimized.

Copy link

replied Jul 13, 2016

Another use case where Activity is necessary for a class that extends SimpleViewManager is when rendering YouTube videos using the Android YouTube API. To instantiate a YouTubePlayerView you need to pass the activity. I'm also curious if there are any short-term workarounds for this that we can implement, as I do not really want to regress versions.

@marcshilling

This comment has been minimized.

Copy link

replied Jul 13, 2016

@stan229 I've yet to find a workaround. As far as I can tell this change is a regression that needs to be addressed. I've opened an issue here: #8661

@darmie

This comment has been minimized.

Copy link

replied Jul 17, 2016

@satya164 I have the latest version of react-native 0.29.1 , and I am trying to update a native module.

Gradle cannot find com.facebook.react.bridge.ActivityEventListener; and getCurrentActivity();

node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:15: error: cannot find symbol
import com.facebook.react.bridge.ActivityEventListener;

                                ^
  symbol:   class ActivityEventListener
  location: package com.facebook.react.bridge
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:55: error: cannot find symbol
        Activity mActivity = this.getCurrentActivity();
                                 ^
  symbol: method getCurrentActivity()
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:60: error: local variable mActivity is accessed from within inner class; needs to be declared final
                    Window window = mActivity.getWindow();
                                    ^
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:74: error: cannot find symbol
        Activity mActivity = this.getCurrentActivity();
                                 ^
  symbol: method getCurrentActivity()
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:78: error: local variable mActivity is accessed from within inner class; needs to be declared final
                View decorView = mActivity.getWindow().getDecorView();
                                 ^
C:\Users\Damilare\trybal\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:88: error: cannot find symbol
        Activity mActivity = this.getCurrentActivity();
                                 ^
  symbol: method getCurrentActivity()
C:\Users\Damilare\myapp\node_modules\react-native-android-statusbar\src\main\java\me\neo\react\StatusBarModule.java:94: error: local variable mActivity is accessed from within inner class; needs to be declared final
                View decorView = mActivity.getWindow().getDecorView();
                                 ^
7 errors
:react-native-android-statusbar:compileReleaseJavaWithJavac FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':react-native-android-statusbar:compileReleaseJavaWithJavac'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 21.683 secs
Could not install the app on the device, read the error above for details.
Make sure you have an Android emulator running or a device connected and have
set up your Android development environment:
https://facebook.github.io/react-native/docs/android-setup.html

This is the source:

package me.neo.react;

import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.ReactContext;


/**
 * Created by Nishanth Shankar on 9/24/15.
 */
public class StatusBarModule extends ReactContextBaseJavaModule{

    public StatusBarModule(ReactApplicationContext reactContext) {
        super(reactContext);
        //reactContext.addActivityEventListener(this);
        //mActivity = 
        //mActivity = activity;
    }

    @Override
    public String getName() {
        return "StatusBarAndroid";
    }

    @ReactMethod
    public void setHexColor(String hex){
        int color = Color.parseColor(hex);
        setStatusColor(color);
    }

    @ReactMethod
    public void setRGB(int r, int g, int b){
        int color = Color.rgb(r,g,b);
        setStatusColor(color);
    }

    @ReactMethod
    public void setARGB(int a,int r, int g, int b){
        int color = Color.argb(a, r, g, b);
        setStatusColor(color);
    }

    void setStatusColor(final int color){
        Activity mActivity = this.getCurrentActivity();
        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if(Build.VERSION.SDK_INT >= 21){
                    Window window = mActivity.getWindow();
                    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                    window.setStatusBarColor(color);

                }

            }
        });

    }


    @ReactMethod
    public void hideStatusBar(){
        Activity mActivity = this.getCurrentActivity();
        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                View decorView = mActivity.getWindow().getDecorView();
                // Hide the status bar.
                int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
                decorView.setSystemUiVisibility(uiOptions);
            }
        });
    }

    @ReactMethod
    public void showStatusBar(){
        Activity mActivity = this.getCurrentActivity();
        mActivity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if(Build.VERSION.SDK_INT < 16)
                    return;
                View decorView = mActivity.getWindow().getDecorView();
                // Hide the status bar.
                int uiOptions = View.SYSTEM_UI_FLAG_VISIBLE;
                decorView.setSystemUiVisibility(uiOptions);
            }
        });
    }


}
@satya164

This comment has been minimized.

Copy link
Collaborator

replied Jul 18, 2016

Do you have a sample project somewhere?

@darmie

This comment has been minimized.

Copy link

replied Jul 18, 2016

@satya164 Nevermind, I fixed it by reinstalling react-native. I don't know how or why, but it worked.

Please sign in to comment.
You can’t perform that action at this time.