/**
* 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.content.Context;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import com.facebook.common.logging.FLog;
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.UiThreadUtil;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.common.ReactConstants;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.uimanager.PixelUtil;
import java.util.Map;
import javax.annotation.Nullable;
/**
* {@link NativeModule} that allows changing the appearance of the status bar.
*/
@ReactModule(name = "StatusBarManager")
public class StatusBarModule extends ReactContextBaseJavaModule {
private static final String HEIGHT_KEY = "HEIGHT";
public StatusBarModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "StatusBarManager";
}
@Override
public @Nullable Map<String, Object> getConstants() {
final Context context = getReactApplicationContext();
final int heightResId = context.getResources()
.getIdentifier("status_bar_height", "dimen", "android");
final float height = heightResId > 0 ?
PixelUtil.toDIPFromPixel(context.getResources().getDimensionPixelSize(heightResId)) :
0;
return MapBuilder.<String, Object>of(
HEIGHT_KEY, height);
}
@ReactMethod
public void setColor(final int color, final boolean animated) {
final Activity activity = getCurrentActivity();
if (activity == null) {
FLog.w(ReactConstants.TAG, "StatusBarModule: Ignored status bar change, current activity is null.");
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);
}
}
});
}
}
@ReactMethod
public void setTranslucent(final boolean translucent) {
final Activity activity = getCurrentActivity();
if (activity == null) {
FLog.w(ReactConstants.TAG, "StatusBarModule: Ignored status bar change, current activity is null.");
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void run() {
// If the status bar is translucent hook into the window insets calculations
// and consume all the top insets so no padding will be added under the status bar.
View decorView = activity.getWindow().getDecorView();
if (translucent) {
decorView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
WindowInsets defaultInsets = v.onApplyWindowInsets(insets);
return defaultInsets.replaceSystemWindowInsets(
defaultInsets.getSystemWindowInsetLeft(),
0,
defaultInsets.getSystemWindowInsetRight(),
defaultInsets.getSystemWindowInsetBottom());
}
});
} else {
decorView.setOnApplyWindowInsetsListener(null);
}
ViewCompat.requestApplyInsets(decorView);
}
});
}
}
@ReactMethod
public void setHidden(final boolean hidden) {
final Activity activity = getCurrentActivity();
if (activity == null) {
FLog.w(ReactConstants.TAG, "StatusBarModule: Ignored status bar change, current activity is null.");
return;
}
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
if (hidden) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
} else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
});
}
@ReactMethod
public void setStyle(final String style) {
final Activity activity = getCurrentActivity();
if (activity == null) {
FLog.w(ReactConstants.TAG, "StatusBarModule: Ignored status bar change, current activity is null.");
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@TargetApi(Build.VERSION_CODES.M)
@Override
public void run() {
View decorView = activity.getWindow().getDecorView();
decorView.setSystemUiVisibility(
style.equals("dark-content") ? View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0);
}
}
);
}
}
}