Cross platform status bar API

Summary:
I started working on improving the `StatusBar` API and make it work on Android. I added support for `setColor`, `setTranslucent` (the status bar is still visible but the app can draw under) and `setHidden` on Android. Looking for feedback on how to improve the API before I put more time on this :).

Right now I went for a cross platform API and functions that don't exist on a platform are just a no-op but I'm not sure it is the best choice since at the moment what is supported is very different between both platforms. I was wondering what you guys think and if it would be better off as 2 different modules.

It is also possible to port some of the features I added for Android to iOS even if there is no 'standard' way to do it. Like `setColor` could be implemented by drawing a colored view under the status bar and translucent by adding/removing some padding.
Closes https://github.com/facebook/react-native/pull/5360

Reviewed By: svcscm

Differential Revision: D2840417

Pulled By: nicklockwood

fb-gh-sync-id: 5c8d988bccf8035341f0efe27e54dd8402c18d24
This commit is contained in:
Janic Duplessis
2016-02-03 06:40:39 -08:00
committed by facebook-github-bot-7
parent 0c91931adf
commit b979128c54
14 changed files with 588 additions and 17 deletions

View File

@@ -9,6 +9,8 @@
package com.facebook.react.bridge;
import javax.annotation.Nullable;
/**
* Interface that represents a JavaScript Promise which can be passed to the native module as a
* method parameter.
@@ -21,7 +23,7 @@ public interface Promise {
/**
* Successfully resolve the Promise.
*/
void resolve(Object value);
void resolve(@Nullable Object value);
/**
* Report an error which wasn't caused by an exception.

View File

@@ -0,0 +1,20 @@
include_defs('//ReactAndroid/DEFS')
android_library(
name = 'statusbar',
srcs = glob(['**/*.java']),
deps = [
react_native_target('java/com/facebook/react/bridge:bridge'),
react_native_target('java/com/facebook/react/common:common'),
react_native_dep('third-party/android/support/v4:lib-support-v4'),
react_native_dep('third-party/java/infer-annotations:infer-annotations'),
react_native_dep('third-party/java/jsr-305:jsr-305'),
],
visibility = [
'PUBLIC',
],
)
project_config(
src_target = ':statusbar',
)

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* <p/>
* 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.modules.statusbar;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.view.View;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.UiThreadUtil;
public class StatusBarModule extends ReactContextBaseJavaModule {
private static final String ERROR_NO_ACTIVITY =
"Tried to change the status bar while not attached to an Activity";
private int mWindowFlags = 0;
public StatusBarModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "StatusBarManager";
}
@ReactMethod
public void setColor(final int color, final boolean animated, final Promise res) {
final Activity activity = getCurrentActivity();
if (activity == null) {
res.reject(ERROR_NO_ACTIVITY);
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
if (animated) {
int curColor = activity.getWindow().getStatusBarColor();
ValueAnimator colorAnimation = ValueAnimator.ofObject(
new ArgbEvaluator(), curColor, color);
colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
activity.getWindow().setStatusBarColor((Integer) animator.getAnimatedValue());
}
});
colorAnimation
.setDuration(300)
.setStartDelay(0);
colorAnimation.start();
} else {
activity.getWindow().setStatusBarColor(color);
}
res.resolve(null);
}
}
);
} else {
res.resolve(null);
}
}
@ReactMethod
public void setTranslucent(final boolean translucent, final Promise res) {
final Activity activity = getCurrentActivity();
if (activity == null) {
res.reject(ERROR_NO_ACTIVITY);
return;
}
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (translucent) {
mWindowFlags |=
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
} else {
mWindowFlags &=
~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
activity.getWindow().getDecorView().setSystemUiVisibility(mWindowFlags);
ViewCompat.requestApplyInsets(activity.getWindow().getDecorView());
res.resolve(null);
}
}
);
}
@ReactMethod
public void setHidden(final boolean hidden, final Promise res) {
final Activity activity = getCurrentActivity();
if (activity == null) {
res.reject(ERROR_NO_ACTIVITY);
return;
}
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (hidden) {
mWindowFlags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
} else {
mWindowFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
}
activity.getWindow().getDecorView().setSystemUiVisibility(mWindowFlags);
res.resolve(null);
}
}
);
}
}

View File

@@ -36,6 +36,7 @@ android_library(
react_native_target('java/com/facebook/react/modules/location:location'),
react_native_target('java/com/facebook/react/modules/netinfo:netinfo'),
react_native_target('java/com/facebook/react/modules/network:network'),
react_native_target('java/com/facebook/react/modules/statusbar:statusbar'),
react_native_target('java/com/facebook/react/modules/storage:storage'),
react_native_target('java/com/facebook/react/modules/timepicker:timepicker'),
react_native_target('java/com/facebook/react/modules/toast:toast'),

View File

@@ -29,6 +29,7 @@ import com.facebook.react.modules.intent.IntentModule;
import com.facebook.react.modules.location.LocationModule;
import com.facebook.react.modules.netinfo.NetInfoModule;
import com.facebook.react.modules.network.NetworkingModule;
import com.facebook.react.modules.statusbar.StatusBarModule;
import com.facebook.react.modules.storage.AsyncStorageModule;
import com.facebook.react.modules.timepicker.TimePickerDialogModule;
import com.facebook.react.modules.toast.ToastModule;
@@ -77,9 +78,11 @@ public class MainReactPackage implements ReactPackage {
new LocationModule(reactContext),
new NetworkingModule(reactContext),
new NetInfoModule(reactContext),
new StatusBarModule(reactContext),
new TimePickerDialogModule(reactContext),
new ToastModule(reactContext),
new WebSocketModule(reactContext));
new WebSocketModule(reactContext)
);
}
@Override