Skip to content

Commit

Permalink
feat: add api to change status bar and navigation bar colors
Browse files Browse the repository at this point in the history
  • Loading branch information
jpudysz committed Mar 27, 2024
1 parent 1a2c965 commit 3332861
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"@typescript-eslint/no-empty-interface": 0,
"no-duplicate-imports": 0,
"functional/immutable-data": 0,
"no-underscore-dangle": 0
"no-underscore-dangle": 0,
"max-lines": 0
}
}
20 changes: 20 additions & 0 deletions android/src/main/cxx/cpp-adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,26 @@ Java_com_unistyles_UnistylesModule_nativeInstall(
env->DeleteLocalRef(cls);
});

unistylesRuntime->onSetNavigationBarColor([=](const std::string &color) {
jstring colorStr = env->NewStringUTF(color.c_str());
jclass cls = env->GetObjectClass(unistylesModule);
jmethodID methodId = env->GetMethodID(cls, "onSetNavigationBarColor", "(Ljava/lang/String;)V");

env->CallVoidMethod(unistylesModule, methodId, colorStr);
env->DeleteLocalRef(colorStr);
env->DeleteLocalRef(cls);
});

unistylesRuntime->onSetStatusBarColor([=](const std::string &color) {
jstring colorStr = env->NewStringUTF(color.c_str());
jclass cls = env->GetObjectClass(unistylesModule);
jmethodID methodId = env->GetMethodID(cls, "onSetStatusBarColor", "(Ljava/lang/String;)V");

env->CallVoidMethod(unistylesModule, methodId, colorStr);
env->DeleteLocalRef(colorStr);
env->DeleteLocalRef(cls);
});

jsi::Object hostObject = jsi::Object::createFromHostObject(*runtime, unistylesRuntime);

runtime->global().setProperty(*runtime, "__UNISTYLES__", std::move(hostObject));
Expand Down
3 changes: 3 additions & 0 deletions android/src/main/java/com/unistyles/Platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import com.facebook.react.bridge.ReactApplicationContext
class Platform(reactApplicationContext: ReactApplicationContext) {
private val config: UnistylesConfig = UnistylesConfig(reactApplicationContext)

var defaultNavigationBarColor: Int? = null
var defaultStatusBarColor: Int? = null

fun hasNewLayoutConfig(): Boolean {
return this.config.hasNewLayoutConfig()
}
Expand Down
21 changes: 21 additions & 0 deletions android/src/main/java/com/unistyles/UnistylesModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.os.Handler
import android.os.Looper
import android.util.Log
Expand Down Expand Up @@ -223,6 +224,26 @@ class UnistylesModule(reactContext: ReactApplicationContext) : ReactContextBaseJ
.emit("__unistylesOnChange", body)
}

private fun onSetNavigationBarColor(color: String) {
val activity = currentActivity ?: return

if (platform.defaultNavigationBarColor == null) {
platform.defaultNavigationBarColor = activity.window.navigationBarColor
}

activity.window.navigationBarColor = if (color == "") platform.defaultNavigationBarColor!! else Color.parseColor(color)
}

private fun onSetStatusBarColor(color: String) {
val activity = currentActivity ?: return

if (platform.defaultStatusBarColor == null) {
platform.defaultStatusBarColor = activity.window.statusBarColor
}

activity.window.statusBarColor = if (color == "") platform.defaultStatusBarColor!! else Color.parseColor(color)
}

@ReactMethod
fun addListener(eventName: String?) = Unit

Expand Down
28 changes: 28 additions & 0 deletions cxx/UnistylesRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,18 +250,46 @@ jsi::Value UnistylesRuntime::get(jsi::Runtime& runtime, const jsi::PropNameID& p

if (propName == "statusBar") {
auto statusBar = jsi::Object(runtime);
auto setStatusBarColorFunction = jsi::Function::createFromHostFunction(runtime,
jsi::PropNameID::forAscii(runtime, "setColor"),
1,
[this](jsi::Runtime &runtime, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) -> jsi::Value {
std::string color = arguments[0].asString(runtime).utf8(runtime);

if (this->onSetStatusBarColorCallback.has_value()) {
this->onSetStatusBarColorCallback.value()(color);
}

return jsi::Value::undefined();
}
);

statusBar.setProperty(runtime, "width", this->statusBar.width);
statusBar.setProperty(runtime, "height", this->statusBar.height);
statusBar.setProperty(runtime, "setColor", setStatusBarColorFunction);

return statusBar;
}

if (propName == "navigationBar") {
auto navigationBarValue = jsi::Object(runtime);
auto setNavigationBarColorFunction = jsi::Function::createFromHostFunction(runtime,
jsi::PropNameID::forAscii(runtime, "setColor"),
1,
[this](jsi::Runtime &runtime, const jsi::Value &thisVal, const jsi::Value *arguments, size_t count) -> jsi::Value {
std::string color = arguments[0].asString(runtime).utf8(runtime);

if (this->onSetStatusBarColorCallback.has_value()) {
this->onSetNavigationBarColorCallback.value()(color);
}

return jsi::Value::undefined();
}
);

navigationBarValue.setProperty(runtime, "width", this->navigationBar.width);
navigationBarValue.setProperty(runtime, "height", this->navigationBar.height);
navigationBarValue.setProperty(runtime, "setColor", setNavigationBarColorFunction);

return navigationBarValue;
}
Expand Down
25 changes: 18 additions & 7 deletions cxx/UnistylesRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <jsi/jsi.h>
#include <vector>
#include <map>
#include <optional>

using namespace facebook;

Expand Down Expand Up @@ -35,14 +36,16 @@ class JSI_EXPORT UnistylesRuntime : public jsi::HostObject {
std::function<void(std::string breakpoint, std::string orientation, Dimensions& screen, Dimensions& statusBar, Insets& insets, Dimensions& navigationBar)> onLayoutChangeCallback;
std::function<void(std::string)> onContentSizeCategoryChangeCallback;
std::function<void()> onPluginChangeCallback;

std::optional<std::function<void(std::string)>> onSetStatusBarColorCallback;
std::optional<std::function<void(std::string)>> onSetNavigationBarColorCallback;

Dimensions screen;
Dimensions statusBar;
Dimensions navigationBar;
Insets insets;
std::string colorScheme;
std::string contentSizeCategory;

public:
UnistylesRuntime(
Dimensions screen,
Expand All @@ -60,28 +63,36 @@ class JSI_EXPORT UnistylesRuntime : public jsi::HostObject {

bool hasAdaptiveThemes;
bool supportsAutomaticColorScheme;

std::string themeName;
std::string breakpoint;
std::vector<std::string> pluginNames;
std::vector<std::string> themes;
std::vector<std::pair<std::string, double>> sortedBreakpointPairs;

void onThemeChange(std::function<void(std::string)> callback) {
this->onThemeChangeCallback = callback;
}

void onLayoutChange(std::function<void(std::string breakpoint, std::string orientation, Dimensions& screen, Dimensions& statusBar, Insets& insets, Dimensions& navigationBar)> callback) {
this->onLayoutChangeCallback = callback;
}

void onPluginChange(std::function<void()> callback) {
this->onPluginChangeCallback = callback;
}

void onContentSizeCategoryChange(std::function<void(std::string)> callback) {
this->onContentSizeCategoryChangeCallback = callback;
}

void onSetStatusBarColor(std::function<void(std::string color)> callback) {
this->onSetStatusBarColorCallback = callback;
}

void onSetNavigationBarColor(std::function<void(std::string color)> callback) {
this->onSetNavigationBarColorCallback = callback;
}

jsi::Value get(jsi::Runtime&, const jsi::PropNameID& name) override;
void set(jsi::Runtime& runtime, const jsi::PropNameID& propNameId, const jsi::Value& value) override;
Expand Down
1 change: 1 addition & 0 deletions examples/expo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const App: React.FunctionComponent = () => (
<Stack.Screen name={DemoNames.ContentSizeCategoryScreen} component={Screens.ContentSizeCategoryScreen} />
<Stack.Screen name={DemoNames.BooleanVariants} component={Screens.BooleanVariantsScreen} />
<Stack.Screen name={DemoNames.UpdateTheme} component={Screens.UpdateThemeScreen} />
<Stack.Screen name={DemoNames.AndroidStatusBarNavigationBar} component={Screens.AndroidStatusBarNavigationBarScreen} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
Expand Down
6 changes: 4 additions & 2 deletions examples/expo/src/common/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export enum DemoNames {
WebMediaQueriesScreen = 'WebMediaQueriesScreen',
ContentSizeCategoryScreen = 'ContentSizeCategoryScreen',
BooleanVariants = 'BooleanVariantsScreen',
UpdateTheme = 'UpdateThemeScreen'
UpdateTheme = 'UpdateThemeScreen',
AndroidStatusBarNavigationBar = 'AndroidStatusBarNavigationBarScreen'
}

export type DemoStackParams = {
Expand Down Expand Up @@ -59,7 +60,8 @@ export type DemoStackParams = {
[DemoNames.WebMediaQueriesScreen]: undefined,
[DemoNames.ContentSizeCategoryScreen]: undefined,
[DemoNames.BooleanVariants]: undefined,
[DemoNames.UpdateTheme]: undefined
[DemoNames.UpdateTheme]: undefined,
[DemoNames.AndroidStatusBarNavigationBar]: undefined
}

export type NavigationProps<S extends DemoNames = DemoNames.Home> = NavigationProp<DemoStackParams, S>
57 changes: 57 additions & 0 deletions examples/expo/src/examples/AndroidStatusBarNavigationBarScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useEffect } from 'react'
import { Platform, Text, View } from 'react-native'
import { UnistylesRuntime, createStyleSheet, useStyles } from 'react-native-unistyles'
import { Button, DemoScreen } from '../components'

export const AndroidStatusBarNavigationBarScreen: React.FunctionComponent = () => {
const { styles, theme } = useStyles(stylesheet)

useEffect(() => () => {
UnistylesRuntime.statusBar.setColor()
UnistylesRuntime.navigationBar.setColor()
}, [])

return (
<DemoScreen>
<View style={styles.container}>
<Text style={styles.text}>
This screen presents how to change status bar / navigation bar colors with UnistylesRuntime
</Text>
{Platform.OS !== 'android' && (
<Text style={styles.bold}>
Please open Android emulator to see the effect
</Text>
)}
<Button
color={theme.colors.accent}
title="Set status bar color"
onPress={() => UnistylesRuntime.statusBar.setColor(theme.colors.accent)}
/>
<Button
color={theme.colors.accent}
title="Set navigation bar color"
onPress={() => UnistylesRuntime.navigationBar.setColor(theme.colors.accent)}
/>
</View>
</DemoScreen>
)
}

const stylesheet = createStyleSheet(theme => ({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 20,
backgroundColor: theme.colors.backgroundColor,
rowGap: 20
},
text: {
textAlign: 'center',
color: theme.colors.typography,
fontSize: 14
},
bold: {
fontWeight: 'bold'
}
}))
17 changes: 17 additions & 0 deletions examples/expo/src/examples/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,23 @@ export const HomeScreen = () => {
navigation.navigate(DemoNames.NoStyleSheetScreen)
}}
/>
<DemoLink
description="Change status/navigation bar color"
onPress={() => {
UnistylesRegistry
.addThemes({
light: lightTheme,
dark: darkTheme,
premium: premiumTheme
})
.addBreakpoints(breakpoints)
.addConfig({
initialTheme: 'light'
})

navigation.navigate(DemoNames.AndroidStatusBarNavigationBar)
}}
/>
</DemoGroup>
<DemoGroup title="Benchmark">
<DemoLink
Expand Down
1 change: 1 addition & 0 deletions examples/expo/src/examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export { WebMediaQueriesScreen } from './WebMediaQueriesScreen'
export { ContentSizeCategoryScreen } from './ContentSizeCategoryScreen'
export { BooleanVariantsScreen } from './BooleanVariantsScreen'
export { UpdateThemeScreen } from './UpdateThemeScreen'
export { AndroidStatusBarNavigationBarScreen } from './AndroidStatusBarNavigationBarScreen'
16 changes: 12 additions & 4 deletions src/core/UnistylesRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,26 @@ export class UnistylesRuntime {

/**
* Get the status bar info
* @returns - The status bar size { width, height }
* @returns - The status bar api { width, height, setColor }
*/
public get statusBar() {
return this.unistylesBridge.statusBar
return {
width: this.unistylesBridge.statusBar.width,
height: this.unistylesBridge.statusBar.height,
setColor: (color?: string) => this.unistylesBridge.statusBar.setColor(color ?? '')
}
}

/**
* Get the navigation bar info (Android)
* @returns - The navigation bar size { width, height }
* @returns - The navigation bar api { width, height, setColor }
*/
public get navigationBar() {
return this.unistylesBridge.navigationBar
return {
width: this.unistylesBridge.navigationBar.width,
height: this.unistylesBridge.navigationBar.height,
setColor: (color?: string) => this.unistylesBridge.navigationBar.setColor(color ?? '')
}
}

/**
Expand Down
12 changes: 10 additions & 2 deletions src/types/unistyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ export type ScreenDimensions = {
width: number
}

interface StatusBar extends ScreenDimensions {
setColor(color?: string): void
}

interface NavigationBar extends ScreenDimensions {
setColor(color?: string): void
}

export type UnistylesConfig = {
adaptiveThemes?: boolean,
initialTheme?: keyof UnistylesThemes,
Expand All @@ -36,8 +44,8 @@ export type UnistylesBridge = {
contentSizeCategory: IOSContentSizeCategory | AndroidContentSizeCategory,
sortedBreakpointPairs: Array<[keyof UnistylesBreakpoints, UnistylesBreakpoints[keyof UnistylesBreakpoints]]>,
insets: ScreenInsets,
statusBar: ScreenDimensions,
navigationBar: ScreenDimensions
statusBar: StatusBar,
navigationBar: NavigationBar

// setters
themes: Array<keyof UnistylesThemes>,
Expand Down

0 comments on commit 3332861

Please sign in to comment.