Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PlatformColor Support #7216

Merged
merged 15 commits into from
Aug 29, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.reactnativenavigation.options.params

import com.facebook.react.bridge.ColorPropConverter
import com.facebook.react.bridge.ReadableMap
import com.reactnativenavigation.NavigationApplication

private fun parsePlatformColor(paths: ReadableMap) =
ColorPropConverter.getColor(paths, NavigationApplication.instance)

class ReactPlatformColor(private val paths: ReadableMap) :
Colour(parsePlatformColor(paths)) {
override fun get(): Int {
return parsePlatformColor(paths)
}

override fun get(defaultValue: Int?): Int? {
return try {
parsePlatformColor(paths)
}catch (e:Exception){
defaultValue
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class NullThemeColour() : ThemeColour(NullColor(), NullColor()) {

open class ThemeColour(private var lightColor: Colour, private var darkColor: Colour) {

constructor(color:Colour):this(color,color)
constructor(color: Colour) : this(color, color)

private fun selectedColor() = if (isDarkMode()) darkColor else lightColor

Expand All @@ -30,18 +30,23 @@ open class ThemeColour(private var lightColor: Colour, private var darkColor: Co
fun hasTransparency() = selectedColor().hasTransparency()
fun canApplyValue() = selectedColor().canApplyValue()

companion object{
companion object {
@JvmStatic
fun of(color:Int) = ThemeColour(Colour(color), Colour(color))
fun of(color: Int) = ThemeColour(Colour(color), Colour(color))

@JvmStatic
fun of(light:Int,dark:Int) = ThemeColour(Colour(light),Colour(dark))
fun of(light: Int, dark: Int) = ThemeColour(Colour(light), Colour(dark))

@JvmStatic
fun parse(context: Context, json: JSONObject?): ThemeColour {
return json?.let {
ThemeColour(ColorParser.parse(context, json, LIGHT_COLOR_KEY), ColorParser.parse(context, json, DARK_COLOR_KEY))
ThemeColour(
ColorParser.parse(context, json, LIGHT_COLOR_KEY),
ColorParser.parse(context, json, DARK_COLOR_KEY)
)
} ?: NullThemeColour()
}

@JvmStatic
fun transparent() = of(Color.TRANSPARENT)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.reactnativenavigation.options.parsers;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
Expand Down Expand Up @@ -98,7 +99,7 @@ private static Object parseNumber(ReadableArray arr, int index) {
}

public static WritableMap convert(JSONObject jsonObject) {
WritableMap map = new WritableNativeMap();
WritableMap map = Arguments.createMap();

Iterator<String> iterator = jsonObject.keys();
while (iterator.hasNext()) {
Expand All @@ -125,7 +126,7 @@ public static WritableMap convert(JSONObject jsonObject) {
}

public static WritableArray convert(JSONArray jsonArray) {
WritableArray array = new WritableNativeArray();
WritableArray array = Arguments.createArray();

for (int i = 0; i < jsonArray.length(); i++) {
Object value = jsonArray.opt(i);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.reactnativenavigation.options.parsers

import android.content.Context
import com.facebook.react.bridge.ColorPropConverter
import com.reactnativenavigation.options.params.Colour
import com.reactnativenavigation.options.params.DontApplyColour
import com.reactnativenavigation.options.params.NullColor
import com.reactnativenavigation.options.params.ReactPlatformColor
import org.json.JSONObject

object ColorParser {
private const val KEY_RESOURCE_PATHS = "resource_paths"
private const val VAL_NO_COLOR = "NoColor"

@JvmStatic
fun parse(context: Context?, json: JSONObject, colorName: String?): Colour {
if (json.has(KEY_RESOURCE_PATHS)) {
return ReactPlatformColor(JSONParser.convert(json))
}
return when (val color = json.opt(colorName)) {
null, VAL_NO_COLOR -> {
DontApplyColour()
}
is Int -> {
Colour(json.optInt(colorName))
}
is JSONObject -> {
ColorPropConverter.getColor(color, context)?.let {
Colour(it)
} ?: NullColor()
}
else -> {
NullColor()
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
Expand All @@ -20,6 +21,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
Expand Down Expand Up @@ -58,6 +60,8 @@ public void beforeEach() {
mockConfiguration.uiMode = Configuration.UI_MODE_NIGHT_NO;
when(res.getConfiguration()).thenReturn(mockConfiguration);
when(NavigationApplication.instance.getResources()).thenReturn(res);
when(res.getColor(ArgumentMatchers.anyInt())).thenReturn(0x00000);
when(res.getColor(ArgumentMatchers.anyInt(),any())).thenReturn(0x00000);
}

public void mockStatusBarUtils(int statusBarHeight,int statusBarHeightDp, Functions.Func block) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,53 @@
package com.reactnativenavigation.options.parsers;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JavaOnlyArray;
import com.facebook.react.bridge.JavaOnlyMap;
import com.reactnativenavigation.BaseTest;
import com.reactnativenavigation.options.params.DontApplyColour;
import com.reactnativenavigation.options.params.ReactPlatformColor;
import com.reactnativenavigation.utils.StatusBarUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;

import android.app.Activity;

public class ColorParseTest extends BaseTest {

Activity activity;
@Override
public void beforeEach() {
super.beforeEach();
activity = newActivity();
}

@Test
public void nullIsParsedAsNoColor() throws JSONException {
JSONObject json = new JSONObject();
json.put("color", "NoColor");
assertThat(ColorParser.parse(null, json, "color")).isInstanceOf(DontApplyColour.class);
}

@Test
public void shouldParsePlatformColors() throws JSONException {
JSONObject json = new JSONObject();
JSONObject color = new JSONObject();
final JSONArray jsonArray = new JSONArray();
jsonArray.put("@color/colorPrimary");
color.put("resource_paths",
jsonArray);
try (MockedStatic<Arguments> theMock = Mockito.mockStatic(Arguments.class)) {
theMock.when(Arguments::createMap).thenReturn(new JavaOnlyMap());
theMock.when(Arguments::createArray).thenReturn(new JavaOnlyArray());
assertThat(ColorParser.parse(activity, color, "color")).isInstanceOf(ReactPlatformColor.class);
}
}
}
17 changes: 17 additions & 0 deletions lib/src/commands/OptionsProcessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,23 @@ describe('navigation options', () => {
Platform.OS = 'android';
});

it('PlatformColor should be passed to native as is', () => {
const options: Options = {
topBar: {
background: {
color: {
// @ts-ignore
resource_paths: ['@color/textColor'],
},
},
},
};
uut.processOptions(options, CommandName.SetRoot);
expect(options).toEqual({
topBar: { background: { color: { resource_paths: ['@color/textColor'] } } },
});
});

it('processes color keys', () => {
const options: Options = {
statusBar: { backgroundColor: 'red' },
Expand Down
11 changes: 8 additions & 3 deletions lib/src/commands/OptionsProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,15 @@ export class OptionsProcessor {
if (value === null) {
options[key] = newColorObj;
} else if (value instanceof Object) {
for (let keyColor in value) {
newColorObj[keyColor] = this.colorService.toNativeColor(value[keyColor]);
if ('semantic' in value || 'resource_paths' in value) {
options[key] = value;
return;
} else {
for (let keyColor in value) {
newColorObj[keyColor] = this.colorService.toNativeColor(value[keyColor]);
}
options[key] = newColorObj;
}
options[key] = newColorObj;
} else {
let parsedColor = this.colorService.toNativeColor(value);
newColorObj.light = parsedColor;
Expand Down
4 changes: 2 additions & 2 deletions lib/src/interfaces/Options.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// tslint:disable jsdoc-format
import { ImageRequireSource, ImageSourcePropType, Insets } from 'react-native';
import { ImageRequireSource, ImageSourcePropType, Insets, OpaqueColorValue } from 'react-native';

// TODO: Import ColorValue instead when upgrading @types/react-native to 0.63+
// Only assign PlatformColor or DynamicColorIOS as a Color symbol!
export declare type Color = string | symbol | ThemeColor | null;
export declare type Color = string | symbol | ThemeColor | OpaqueColorValue | null;
type FontFamily = string;
type FontStyle = 'normal' | 'italic';
type FontWeightIOS =
Expand Down
5 changes: 5 additions & 0 deletions playground/android/app/src/main/res/values-night/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="textColor" type="color">#BA292E</item>
<item name="backgroundColor" type="color">#282528</item>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="textColor" type="color">#000000</item>
<item name="backgroundColor" type="color">#e8e8e8</item>
</resources>
5 changes: 5 additions & 0 deletions playground/android/app/src/main/res/values/colors.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="textColor" type="color">#000000</item>
<item name="backgroundColor" type="color">#e8e8e8</item>
</resources>
6 changes: 0 additions & 6 deletions playground/android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,4 @@
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@color/backgroundColor</item>
</style>

<!--This is your application's default background color.
It will be visible when the app is first opened (while the splash layout is visible)
and when transitioning between a destination drawn under the StatusBar to
a destination drawn behind it-->
<item name="backgroundColor" type="color">#e8e8e8</item>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"color" : {
"platform" : "ios",
"reference" : "darkTextColor"
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.180",
"green" : "0.160",
"red" : "0.730"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading