react-native-unistyles 2.4.0-rc.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +5 -2
- package/android/src/main/cxx/cpp-adapter.cpp +28 -13
- package/android/src/main/cxx/helpers.cpp +21 -56
- package/android/src/main/cxx/helpers.h +3 -3
- package/android/src/main/java/com/unistyles/Config.kt +116 -0
- package/android/src/main/java/com/unistyles/Insets.kt +138 -0
- package/android/src/main/java/com/unistyles/Models.kt +85 -0
- package/android/src/main/java/com/unistyles/Platform.kt +10 -79
- package/android/src/main/java/com/unistyles/UnistylesModule.kt +85 -25
- package/cxx/UnistylesRuntime.cpp +57 -42
- package/cxx/UnistylesRuntime.h +30 -19
- package/ios/UnistylesModule.mm +20 -6
- package/ios/platform/Platform_iOS.h +5 -4
- package/ios/platform/Platform_iOS.mm +35 -22
- package/ios/platform/Platform_macOS.h +5 -4
- package/ios/platform/Platform_macOS.mm +18 -25
- package/lib/commonjs/common.js +7 -0
- package/lib/commonjs/common.js.map +1 -1
- package/lib/commonjs/core/UnistylesModule.js +6 -0
- package/lib/commonjs/core/UnistylesModule.js.map +1 -1
- package/lib/commonjs/core/UnistylesRuntime.js +8 -0
- package/lib/commonjs/core/UnistylesRuntime.js.map +1 -1
- package/lib/commonjs/hooks/useUnistyles.js +18 -1
- package/lib/commonjs/hooks/useUnistyles.js.map +1 -1
- package/lib/module/common.js +7 -0
- package/lib/module/common.js.map +1 -1
- package/lib/module/core/UnistylesModule.js +6 -0
- package/lib/module/core/UnistylesModule.js.map +1 -1
- package/lib/module/core/UnistylesRuntime.js +8 -0
- package/lib/module/core/UnistylesRuntime.js.map +1 -1
- package/lib/module/hooks/useUnistyles.js +18 -1
- package/lib/module/hooks/useUnistyles.js.map +1 -1
- package/lib/typescript/src/common.d.ts +8 -1
- package/lib/typescript/src/common.d.ts.map +1 -1
- package/lib/typescript/src/core/UnistylesModule.d.ts.map +1 -1
- package/lib/typescript/src/core/UnistylesRuntime.d.ts +6 -1
- package/lib/typescript/src/core/UnistylesRuntime.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useUnistyles.d.ts +14 -0
- package/lib/typescript/src/hooks/useUnistyles.d.ts.map +1 -1
- package/lib/typescript/src/types/unistyles.d.ts +6 -2
- package/lib/typescript/src/types/unistyles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common.ts +8 -1
- package/src/core/UnistylesModule.ts +8 -2
- package/src/core/UnistylesRuntime.ts +8 -0
- package/src/hooks/useUnistyles.ts +18 -1
- package/src/types/unistyles.ts +6 -2
- package/windows/ReactNativeUnistyles/ReactNativeUnistyles.h +33 -27
package/README.md
CHANGED
@@ -48,10 +48,13 @@
|
|
48
48
|
[How to become a sponsor?](https://reactnativeunistyles.vercel.app/other/for-sponsors/)
|
49
49
|
|
50
50
|
<a href="https://codemask.com">
|
51
|
-
|
51
|
+
<img src="https://avatars.githubusercontent.com/u/51229884?s=200&v=4" height="70px" width="70px" alt="codemask" />
|
52
52
|
</a>
|
53
53
|
<a href="https://galaxies.dev">
|
54
|
-
<img src="https://avatars.githubusercontent.com/u/118431096?s=200&v=4" height="70px" width="70px" />
|
54
|
+
<img src="https://avatars.githubusercontent.com/u/118431096?s=200&v=4" height="70px" width="70px" alt="galaxies-dev" />
|
55
|
+
</a>
|
56
|
+
<a href="https://github.com/kmartinezmedia">
|
57
|
+
<img src="https://avatars.githubusercontent.com/u/6308123?s=200&v=4" height="70px" width="70px" alt="kmartinezmedia" />
|
55
58
|
</a>
|
56
59
|
|
57
60
|
|
@@ -15,12 +15,12 @@ Java_com_unistyles_UnistylesModule_nativeInstall(
|
|
15
15
|
JNIEnv *env,
|
16
16
|
jobject thiz,
|
17
17
|
jlong jsi,
|
18
|
-
|
19
|
-
jint screenHeight,
|
18
|
+
jobject screen,
|
20
19
|
jstring colorScheme,
|
21
20
|
jstring contentSizeCategory,
|
22
21
|
jobject insets,
|
23
|
-
jobject statusBar
|
22
|
+
jobject statusBar,
|
23
|
+
jobject navigationBar
|
24
24
|
) {
|
25
25
|
auto runtime = reinterpret_cast<facebook::jsi::Runtime *>(jsi);
|
26
26
|
|
@@ -41,12 +41,12 @@ Java_com_unistyles_UnistylesModule_nativeInstall(
|
|
41
41
|
env->ReleaseStringUTFChars(contentSizeCategory, contentSizeCategoryChars);
|
42
42
|
|
43
43
|
unistylesRuntime = std::make_shared<UnistylesRuntime>(
|
44
|
-
|
45
|
-
screenHeight,
|
44
|
+
jobjectToDimensions(env, screen),
|
46
45
|
colorSchemeStr,
|
47
46
|
contentSizeCategoryStr,
|
48
|
-
|
49
|
-
|
47
|
+
jobjectToInsets(env, insets),
|
48
|
+
jobjectToDimensions(env, statusBar),
|
49
|
+
jobjectToDimensions(env, navigationBar)
|
50
50
|
);
|
51
51
|
|
52
52
|
unistylesRuntime->onThemeChange([=](const std::string &theme) {
|
@@ -59,13 +59,23 @@ Java_com_unistyles_UnistylesModule_nativeInstall(
|
|
59
59
|
env->DeleteLocalRef(cls);
|
60
60
|
});
|
61
61
|
|
62
|
-
unistylesRuntime->onLayoutChange([=](const std::string &breakpoint, const std::string &orientation,
|
62
|
+
unistylesRuntime->onLayoutChange([=](const std::string &breakpoint, const std::string &orientation, Dimensions& screen, Dimensions& statusBar, Insets& insets, Dimensions& navigationBar) {
|
63
63
|
jstring breakpointStr = env->NewStringUTF(breakpoint.c_str());
|
64
64
|
jstring orientationStr = env->NewStringUTF(orientation.c_str());
|
65
65
|
jclass cls = env->GetObjectClass(unistylesModule);
|
66
|
-
|
67
|
-
|
68
|
-
env->
|
66
|
+
jclass dimensionsClass = env->FindClass("com/unistyles/Dimensions");
|
67
|
+
jmethodID dimensionsConstructor = env->GetMethodID(dimensionsClass, "<init>", "(II)V");
|
68
|
+
jobject screenObj = env->NewObject(dimensionsClass, dimensionsConstructor, screen.width, screen.height);
|
69
|
+
jobject statusBarObj = env->NewObject(dimensionsClass, dimensionsConstructor, statusBar.width, statusBar.height);
|
70
|
+
jobject navigationBarObj = env->NewObject(dimensionsClass, dimensionsConstructor, navigationBar.width, navigationBar.height);
|
71
|
+
|
72
|
+
jclass insetsClass = env->FindClass("com/unistyles/Insets");
|
73
|
+
jmethodID insetsConstructor = env->GetMethodID(insetsClass, "<init>", "(IIII)V");
|
74
|
+
jobject insetsObj = env->NewObject(insetsClass, insetsConstructor, insets.left, insets.top, insets.right, insets.bottom);
|
75
|
+
jmethodID methodId = env->GetMethodID(cls, "onLayoutChange",
|
76
|
+
"(Ljava/lang/String;Ljava/lang/String;Lcom/unistyles/Dimensions;Lcom/unistyles/Dimensions;Lcom/unistyles/Insets;Lcom/unistyles/Dimensions;)V");
|
77
|
+
|
78
|
+
env->CallVoidMethod(unistylesModule, methodId, breakpointStr, orientationStr, screenObj, statusBarObj, insetsObj, navigationBarObj);
|
69
79
|
env->DeleteLocalRef(breakpointStr);
|
70
80
|
env->DeleteLocalRef(orientationStr);
|
71
81
|
env->DeleteLocalRef(cls);
|
@@ -103,9 +113,14 @@ Java_com_unistyles_UnistylesModule_nativeDestroy(JNIEnv *env, jobject thiz) {
|
|
103
113
|
|
104
114
|
extern "C"
|
105
115
|
JNIEXPORT void JNICALL
|
106
|
-
Java_com_unistyles_UnistylesModule_nativeOnOrientationChange(JNIEnv *env, jobject thiz,
|
116
|
+
Java_com_unistyles_UnistylesModule_nativeOnOrientationChange(JNIEnv *env, jobject thiz, jobject screen, jobject insets, jobject statusBar, jobject navigationBar) {
|
107
117
|
if (unistylesRuntime != nullptr) {
|
108
|
-
|
118
|
+
Dimensions screenDimensions = jobjectToDimensions(env, screen);
|
119
|
+
Dimensions statusBarDimensions = jobjectToDimensions(env, statusBar);
|
120
|
+
Insets screenInsets = jobjectToInsets(env, insets);
|
121
|
+
Dimensions navigationBarDimensions = jobjectToDimensions(env, navigationBar);
|
122
|
+
|
123
|
+
unistylesRuntime->handleScreenSizeChange(screenDimensions, screenInsets, statusBarDimensions, navigationBarDimensions);
|
109
124
|
}
|
110
125
|
}
|
111
126
|
|
@@ -1,69 +1,34 @@
|
|
1
1
|
#include "helpers.h"
|
2
|
+
#include "UnistylesRuntime.h"
|
2
3
|
|
3
|
-
|
4
|
-
jclass
|
5
|
-
|
6
|
-
|
4
|
+
Dimensions jobjectToDimensions(JNIEnv *env, jobject dimensionObj) {
|
5
|
+
jclass dimensionClass = env->FindClass("com/unistyles/Dimensions");
|
6
|
+
jfieldID widthFieldID = env->GetFieldID(dimensionClass, "width", "I");
|
7
|
+
jfieldID heightFieldID = env->GetFieldID(dimensionClass, "height", "I");
|
7
8
|
|
8
|
-
env->
|
9
|
+
int width = env->GetIntField(dimensionObj, widthFieldID);
|
10
|
+
int height = env->GetIntField(dimensionObj, heightFieldID);
|
9
11
|
|
10
|
-
|
11
|
-
}
|
12
|
-
|
13
|
-
std::string jstringToStdString(JNIEnv *env, jstring jStr) {
|
14
|
-
if (!jStr) {
|
15
|
-
return "";
|
16
|
-
}
|
17
|
-
|
18
|
-
const jclass stringClass = env->GetObjectClass(jStr);
|
19
|
-
const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
|
20
|
-
const jbyteArray stringJbytes = (jbyteArray) env->CallObjectMethod(jStr, getBytes, env->NewStringUTF("UTF-8"));
|
21
|
-
|
22
|
-
size_t length = (size_t) env->GetArrayLength(stringJbytes);
|
23
|
-
jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL);
|
24
|
-
|
25
|
-
std::string ret = std::string((char *)pBytes, length);
|
26
|
-
env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT);
|
12
|
+
env->DeleteLocalRef(dimensionClass);
|
27
13
|
|
28
|
-
|
29
|
-
env->DeleteLocalRef(stringClass);
|
30
|
-
|
31
|
-
return ret;
|
14
|
+
return Dimensions{width, height};
|
32
15
|
}
|
33
16
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
jmethodID iteratorMethod = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
|
41
|
-
jobject set = env->CallObjectMethod(map, entrySetMethod);
|
42
|
-
jobject iterator = env->CallObjectMethod(set, iteratorMethod);
|
43
|
-
jmethodID hasNextMethod = env->GetMethodID(iteratorClass, "hasNext", "()Z");
|
44
|
-
jmethodID nextMethod = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
|
45
|
-
|
46
|
-
while (env->CallBooleanMethod(iterator, hasNextMethod)) {
|
47
|
-
jobject entry = env->CallObjectMethod(iterator, nextMethod);
|
17
|
+
Insets jobjectToInsets(JNIEnv *env, jobject insetsObj) {
|
18
|
+
jclass insetsClass = env->FindClass("com/unistyles/Insets");
|
19
|
+
jfieldID leftFieldID = env->GetFieldID(insetsClass, "left", "I");
|
20
|
+
jfieldID topFieldID = env->GetFieldID(insetsClass, "top", "I");
|
21
|
+
jfieldID rightFieldID = env->GetFieldID(insetsClass, "right", "I");
|
22
|
+
jfieldID bottomFieldID = env->GetFieldID(insetsClass, "bottom", "I");
|
48
23
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
result[jstringToStdString(env, key)] = jobjectToInt(env, value);
|
55
|
-
|
56
|
-
env->DeleteLocalRef(entry);
|
57
|
-
env->DeleteLocalRef(key);
|
58
|
-
env->DeleteLocalRef(value);
|
59
|
-
}
|
24
|
+
int left = env->GetIntField(insetsObj, leftFieldID);
|
25
|
+
int top = env->GetIntField(insetsObj, topFieldID);
|
26
|
+
int right = env->GetIntField(insetsObj, rightFieldID);
|
27
|
+
int bottom = env->GetIntField(insetsObj, bottomFieldID);
|
60
28
|
|
61
|
-
env->DeleteLocalRef(
|
62
|
-
env->DeleteLocalRef(iterator);
|
63
|
-
env->DeleteLocalRef(setClass);
|
64
|
-
env->DeleteLocalRef(iteratorClass);
|
29
|
+
env->DeleteLocalRef(insetsClass);
|
65
30
|
|
66
|
-
return
|
31
|
+
return Insets{top, bottom, left, right};
|
67
32
|
}
|
68
33
|
|
69
34
|
void throwKotlinException(
|
@@ -1,9 +1,9 @@
|
|
1
1
|
#include <jni.h>
|
2
2
|
#include <string>
|
3
3
|
#include <map>
|
4
|
+
#include <UnistylesRuntime.h>
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
std::map<std::string, int> jobjectToStdMap(JNIEnv *env, jobject map);
|
6
|
+
Dimensions jobjectToDimensions(JNIEnv *env, jobject dimensionObj);
|
7
|
+
Insets jobjectToInsets(JNIEnv *env, jobject insetsObj);
|
8
8
|
|
9
9
|
void throwKotlinException(JNIEnv *env, const char *message);
|
@@ -0,0 +1,116 @@
|
|
1
|
+
package com.unistyles
|
2
|
+
|
3
|
+
import android.annotation.SuppressLint
|
4
|
+
import android.content.res.Configuration
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
6
|
+
|
7
|
+
class UnistylesConfig(private val reactApplicationContext: ReactApplicationContext) {
|
8
|
+
private val insets: UnistylesInsets = UnistylesInsets(reactApplicationContext)
|
9
|
+
private val density: Float = reactApplicationContext.resources.displayMetrics.density
|
10
|
+
private var lastConfig: Config = this.getAppConfig()
|
11
|
+
private var lastLayoutConfig: LayoutConfig = this.getAppLayoutConfig()
|
12
|
+
|
13
|
+
fun hasNewConfig(): Boolean {
|
14
|
+
val newConfig = this.getAppConfig()
|
15
|
+
val newContentSizeCategory = newConfig.contentSizeCategory != lastConfig.contentSizeCategory
|
16
|
+
val newColorScheme = newConfig.colorScheme != lastConfig.colorScheme
|
17
|
+
|
18
|
+
if (!newContentSizeCategory && !newColorScheme) {
|
19
|
+
return false
|
20
|
+
}
|
21
|
+
|
22
|
+
lastConfig = newConfig
|
23
|
+
lastConfig.hasNewContentSizeCategory = newContentSizeCategory
|
24
|
+
lastConfig.hasNewColorScheme = newColorScheme
|
25
|
+
|
26
|
+
return true
|
27
|
+
}
|
28
|
+
|
29
|
+
fun hasNewLayoutConfig(): Boolean {
|
30
|
+
val newConfig = this.getAppLayoutConfig()
|
31
|
+
|
32
|
+
if (newConfig.isEqual(lastLayoutConfig)) {
|
33
|
+
return false
|
34
|
+
}
|
35
|
+
|
36
|
+
lastLayoutConfig = newConfig
|
37
|
+
|
38
|
+
return true
|
39
|
+
}
|
40
|
+
|
41
|
+
fun getConfig(): Config {
|
42
|
+
return this.lastConfig
|
43
|
+
}
|
44
|
+
|
45
|
+
fun getLayoutConfig(): LayoutConfig {
|
46
|
+
return this.lastLayoutConfig
|
47
|
+
}
|
48
|
+
|
49
|
+
private fun getAppConfig(): Config {
|
50
|
+
val fontScale = reactApplicationContext.resources.configuration.fontScale
|
51
|
+
|
52
|
+
return Config(
|
53
|
+
this.getColorScheme(),
|
54
|
+
this.getContentSizeCategory(fontScale),
|
55
|
+
)
|
56
|
+
}
|
57
|
+
|
58
|
+
private fun getAppLayoutConfig(): LayoutConfig {
|
59
|
+
val displayMetrics = reactApplicationContext.resources.displayMetrics
|
60
|
+
val screenWidth = (displayMetrics.widthPixels / density).toInt()
|
61
|
+
val screenHeight = (displayMetrics.heightPixels / density).toInt()
|
62
|
+
|
63
|
+
return LayoutConfig(
|
64
|
+
Dimensions(screenWidth, screenHeight),
|
65
|
+
this.insets.get(),
|
66
|
+
Dimensions(screenWidth, getStatusBarHeight()),
|
67
|
+
Dimensions(screenWidth, getNavigationBarHeight())
|
68
|
+
)
|
69
|
+
}
|
70
|
+
|
71
|
+
private fun getContentSizeCategory(fontScale: Float): String {
|
72
|
+
val contentSizeCategory = when {
|
73
|
+
fontScale <= 0.85f -> "Small"
|
74
|
+
fontScale <= 1.0f -> "Default"
|
75
|
+
fontScale <= 1.15f -> "Large"
|
76
|
+
fontScale <= 1.3f -> "ExtraLarge"
|
77
|
+
fontScale <= 1.5f -> "Huge"
|
78
|
+
fontScale <= 1.8 -> "ExtraHuge"
|
79
|
+
else -> "ExtraExtraHuge"
|
80
|
+
}
|
81
|
+
|
82
|
+
return contentSizeCategory
|
83
|
+
}
|
84
|
+
|
85
|
+
private fun getColorScheme(): String {
|
86
|
+
val colorScheme = when (reactApplicationContext.resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
|
87
|
+
Configuration.UI_MODE_NIGHT_YES -> "dark"
|
88
|
+
Configuration.UI_MODE_NIGHT_NO -> "light"
|
89
|
+
else -> "unspecified"
|
90
|
+
}
|
91
|
+
|
92
|
+
return colorScheme
|
93
|
+
}
|
94
|
+
|
95
|
+
@SuppressLint("InternalInsetResource", "DiscouragedApi")
|
96
|
+
private fun getStatusBarHeight(): Int {
|
97
|
+
val heightResId = reactApplicationContext.resources.getIdentifier("status_bar_height", "dimen", "android")
|
98
|
+
|
99
|
+
if (heightResId > 0) {
|
100
|
+
return (reactApplicationContext.resources.getDimensionPixelSize(heightResId) / density).toInt()
|
101
|
+
}
|
102
|
+
|
103
|
+
return 0
|
104
|
+
}
|
105
|
+
|
106
|
+
@SuppressLint("InternalInsetResource", "DiscouragedApi")
|
107
|
+
private fun getNavigationBarHeight(): Int {
|
108
|
+
val heightResId = reactApplicationContext.resources.getIdentifier("navigation_bar_height", "dimen", "android")
|
109
|
+
|
110
|
+
if (heightResId > 0) {
|
111
|
+
return (reactApplicationContext.resources.getDimensionPixelSize(heightResId) / density).toInt()
|
112
|
+
}
|
113
|
+
|
114
|
+
return 0
|
115
|
+
}
|
116
|
+
}
|
@@ -0,0 +1,138 @@
|
|
1
|
+
package com.unistyles
|
2
|
+
|
3
|
+
import android.content.Context
|
4
|
+
import android.graphics.Rect
|
5
|
+
import android.os.Build
|
6
|
+
import android.view.View
|
7
|
+
import android.view.Window
|
8
|
+
import android.view.WindowInsets
|
9
|
+
import android.view.WindowManager
|
10
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
11
|
+
import kotlin.math.roundToInt
|
12
|
+
|
13
|
+
class UnistylesInsets(private val reactApplicationContext: ReactApplicationContext) {
|
14
|
+
private val density: Float = reactApplicationContext.resources.displayMetrics.density
|
15
|
+
|
16
|
+
fun get(): Insets {
|
17
|
+
return this.getCurrentInsets()
|
18
|
+
}
|
19
|
+
|
20
|
+
private fun getCurrentInsets(): Insets {
|
21
|
+
val baseInsets = this.getBaseInsets()
|
22
|
+
val statusBarTranslucent = this.hasTranslucentStatusBar(baseInsets) ?: return baseInsets
|
23
|
+
val window = reactApplicationContext.currentActivity?.window ?: return baseInsets
|
24
|
+
val shouldModifyLandscapeInsets = this.forceLandscapeInsets(baseInsets, window)
|
25
|
+
|
26
|
+
val topInset = this.getTopInset(baseInsets, statusBarTranslucent, window)
|
27
|
+
val bottomInset = this.getBottomInset(baseInsets, window)
|
28
|
+
val leftInset = if (shouldModifyLandscapeInsets) 0 else baseInsets.left
|
29
|
+
val rightInset = if (shouldModifyLandscapeInsets) 0 else baseInsets.right
|
30
|
+
|
31
|
+
return Insets(topInset, bottomInset, leftInset, rightInset)
|
32
|
+
}
|
33
|
+
|
34
|
+
@Suppress("DEPRECATION")
|
35
|
+
private fun getBaseInsets(): Insets {
|
36
|
+
// this is the best API, but it's available from Android 11
|
37
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
38
|
+
val windowManager = reactApplicationContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
39
|
+
val systemBarsInsets = windowManager.currentWindowMetrics.windowInsets.getInsets(WindowInsets.Type.systemBars())
|
40
|
+
|
41
|
+
val top = (systemBarsInsets.top / density).roundToInt()
|
42
|
+
val bottom = (systemBarsInsets.bottom / density).roundToInt()
|
43
|
+
val left = (systemBarsInsets.left / density).roundToInt()
|
44
|
+
val right = (systemBarsInsets.right / density).roundToInt()
|
45
|
+
|
46
|
+
return Insets(top, bottom, left, right)
|
47
|
+
}
|
48
|
+
|
49
|
+
// available from Android 6.0
|
50
|
+
val window = reactApplicationContext.currentActivity?.window ?: return Insets(0, 0, 0, 0)
|
51
|
+
val systemInsets = window.decorView.rootWindowInsets
|
52
|
+
|
53
|
+
val top = (systemInsets.systemWindowInsetTop / density).roundToInt()
|
54
|
+
val bottom = (systemInsets.systemWindowInsetBottom / density).roundToInt()
|
55
|
+
val left = (systemInsets.systemWindowInsetLeft / density).roundToInt()
|
56
|
+
val right = (systemInsets.systemWindowInsetRight / density).roundToInt()
|
57
|
+
|
58
|
+
return Insets(top, bottom, left, right)
|
59
|
+
}
|
60
|
+
|
61
|
+
// this function helps to detect statusBar translucent, as React Native is using weird API instead of windows flags
|
62
|
+
private fun hasTranslucentStatusBar(baseInsets: Insets): Boolean? {
|
63
|
+
val window = reactApplicationContext.currentActivity?.window ?: return null
|
64
|
+
val contentView = window.decorView.findViewById<View>(android.R.id.content) ?: return null
|
65
|
+
val decorViewLocation = IntArray(2)
|
66
|
+
val contentViewLocation = IntArray(2)
|
67
|
+
|
68
|
+
// decor view is a top level view with navigationBar and statusBar
|
69
|
+
window.decorView.getLocationOnScreen(decorViewLocation)
|
70
|
+
// content view is view without navigationBar and statusBar
|
71
|
+
contentView.getLocationOnScreen(contentViewLocation)
|
72
|
+
|
73
|
+
val statusBarHeight = contentViewLocation[1] - decorViewLocation[1]
|
74
|
+
|
75
|
+
// if positions are the same, then the status bar is translucent
|
76
|
+
return statusBarHeight == 0
|
77
|
+
}
|
78
|
+
|
79
|
+
private fun getTopInset(baseInsets: Insets, statusBarTranslucent: Boolean, window: Window): Int {
|
80
|
+
if (!statusBarTranslucent) {
|
81
|
+
return 0
|
82
|
+
}
|
83
|
+
|
84
|
+
return baseInsets.top
|
85
|
+
}
|
86
|
+
|
87
|
+
@Suppress("DEPRECATION")
|
88
|
+
private fun getBottomInset(baseInsets: Insets, window: Window): Int {
|
89
|
+
val translucentNavigation = hasTranslucentNavigation(window)
|
90
|
+
|
91
|
+
// Android 11 has inconsistent FLAG_LAYOUT_NO_LIMITS, which alone does nothing
|
92
|
+
// https://stackoverflow.com/questions/64153785/android-11-window-setdecorfitssystemwindow-doesnt-show-screen-behind-status-a
|
93
|
+
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) {
|
94
|
+
if ((hasFullScreenMode(window) && translucentNavigation) || translucentNavigation) {
|
95
|
+
return baseInsets.bottom
|
96
|
+
}
|
97
|
+
|
98
|
+
return 0
|
99
|
+
}
|
100
|
+
|
101
|
+
return if (hasTranslucentNavigation(window) || hasFullScreenMode(window)) baseInsets.bottom else 0
|
102
|
+
}
|
103
|
+
|
104
|
+
private fun forceLandscapeInsets(baseInsets: Insets, window: Window): Boolean {
|
105
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
106
|
+
return false
|
107
|
+
}
|
108
|
+
|
109
|
+
val displayMetrics = reactApplicationContext.resources.displayMetrics
|
110
|
+
val isLandscape = displayMetrics.widthPixels > displayMetrics.heightPixels
|
111
|
+
|
112
|
+
if (!isLandscape) {
|
113
|
+
return false
|
114
|
+
}
|
115
|
+
|
116
|
+
val contentView = window.decorView.findViewById<View>(android.R.id.content) ?: return false
|
117
|
+
val visibleRect = Rect()
|
118
|
+
val drawingRect = Rect()
|
119
|
+
|
120
|
+
window.decorView.getGlobalVisibleRect(visibleRect)
|
121
|
+
contentView.getDrawingRect(drawingRect)
|
122
|
+
|
123
|
+
// width is equal to navigationBarHeight + statusBarHeight (in landscape)
|
124
|
+
val width = ((visibleRect.width() - contentView.width) / density).roundToInt()
|
125
|
+
|
126
|
+
// we should correct landscape if insets are equal to width
|
127
|
+
return (baseInsets.left + baseInsets.right) == width
|
128
|
+
}
|
129
|
+
|
130
|
+
@Suppress("DEPRECATION")
|
131
|
+
private fun hasTranslucentNavigation(window: Window): Boolean {
|
132
|
+
return (window.attributes.flags and WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
|
133
|
+
}
|
134
|
+
|
135
|
+
private fun hasFullScreenMode(window: Window): Boolean {
|
136
|
+
return (window.attributes.flags and WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) == WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
|
137
|
+
}
|
138
|
+
}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
package com.unistyles
|
2
|
+
|
3
|
+
class Dimensions(var width: Int, var height: Int) {
|
4
|
+
fun isEqual(dimensions: Dimensions): Boolean {
|
5
|
+
if (this.width != dimensions.width) {
|
6
|
+
return false
|
7
|
+
}
|
8
|
+
|
9
|
+
return this.height == dimensions.height
|
10
|
+
}
|
11
|
+
|
12
|
+
override fun toString(): String {
|
13
|
+
return "${width}x${height}"
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
class Insets(var top: Int, var bottom: Int, var left: Int, var right: Int) {
|
18
|
+
fun isEqual(insets: Insets): Boolean {
|
19
|
+
if (this.top != insets.top) {
|
20
|
+
return false
|
21
|
+
}
|
22
|
+
|
23
|
+
if (this.bottom != insets.bottom) {
|
24
|
+
return false
|
25
|
+
}
|
26
|
+
|
27
|
+
if (this.left != insets.left) {
|
28
|
+
return false
|
29
|
+
}
|
30
|
+
|
31
|
+
return this.right == insets.right
|
32
|
+
}
|
33
|
+
|
34
|
+
override fun toString(): String {
|
35
|
+
return "T:${top}B:${bottom}L:${left}R:${right}"
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
class LayoutConfig(
|
40
|
+
val screen: Dimensions,
|
41
|
+
val insets: Insets,
|
42
|
+
val statusBar: Dimensions,
|
43
|
+
val navigationBar: Dimensions
|
44
|
+
) {
|
45
|
+
fun isEqual(config: LayoutConfig): Boolean {
|
46
|
+
if (!this.screen.isEqual(config.screen)) {
|
47
|
+
return false
|
48
|
+
}
|
49
|
+
|
50
|
+
if (!this.insets.isEqual(config.insets)) {
|
51
|
+
return false
|
52
|
+
}
|
53
|
+
|
54
|
+
if (!this.statusBar.isEqual(config.statusBar)) {
|
55
|
+
return false
|
56
|
+
}
|
57
|
+
|
58
|
+
return this.navigationBar.isEqual(config.navigationBar)
|
59
|
+
}
|
60
|
+
|
61
|
+
override fun toString(): String {
|
62
|
+
return buildString {
|
63
|
+
append("screen=")
|
64
|
+
append(screen)
|
65
|
+
append(" insets=")
|
66
|
+
append(insets)
|
67
|
+
append(" statusBar=")
|
68
|
+
append(statusBar)
|
69
|
+
append(" navigationBar=")
|
70
|
+
append(navigationBar)
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
class Config(
|
76
|
+
val colorScheme: String,
|
77
|
+
val contentSizeCategory: String,
|
78
|
+
) {
|
79
|
+
var hasNewColorScheme: Boolean = false
|
80
|
+
var hasNewContentSizeCategory: Boolean = false
|
81
|
+
|
82
|
+
override fun toString(): String {
|
83
|
+
return "colorScheme=${colorScheme} contentSizeCategory:${contentSizeCategory}"
|
84
|
+
}
|
85
|
+
}
|
@@ -1,92 +1,23 @@
|
|
1
1
|
package com.unistyles
|
2
2
|
|
3
|
-
import android.annotation.SuppressLint
|
4
|
-
import android.content.res.Configuration
|
5
|
-
import android.os.Build
|
6
|
-
import android.view.WindowInsets
|
7
3
|
import com.facebook.react.bridge.ReactApplicationContext
|
8
4
|
|
9
|
-
class Platform(
|
10
|
-
|
11
|
-
fun getConfig(): Map<String, Any> {
|
12
|
-
val displayMetrics = reactApplicationContext.resources.displayMetrics
|
13
|
-
val fontScale = reactApplicationContext.resources.configuration.fontScale
|
14
|
-
val screenWidth = (displayMetrics.widthPixels / displayMetrics.density).toInt()
|
15
|
-
val screenHeight = (displayMetrics.heightPixels / displayMetrics.density).toInt()
|
5
|
+
class Platform(reactApplicationContext: ReactApplicationContext) {
|
6
|
+
private val config: UnistylesConfig = UnistylesConfig(reactApplicationContext)
|
16
7
|
|
17
|
-
|
18
|
-
|
19
|
-
"height" to screenHeight,
|
20
|
-
"colorScheme" to getColorScheme(),
|
21
|
-
"contentSizeCategory" to getContentSizeCategory(fontScale),
|
22
|
-
"insets" to getScreenInsets(displayMetrics.density),
|
23
|
-
"statusBar" to mapOf(
|
24
|
-
"height" to getStatusBarHeight(displayMetrics.density),
|
25
|
-
"width" to screenWidth
|
26
|
-
)
|
27
|
-
)
|
8
|
+
fun hasNewLayoutConfig(): Boolean {
|
9
|
+
return this.config.hasNewLayoutConfig()
|
28
10
|
}
|
29
11
|
|
30
|
-
|
31
|
-
|
32
|
-
fontScale <= 0.85f -> "Small"
|
33
|
-
fontScale <= 1.0f -> "Default"
|
34
|
-
fontScale <= 1.15f -> "Large"
|
35
|
-
fontScale <= 1.3f -> "ExtraLarge"
|
36
|
-
else -> "Huge"
|
37
|
-
}
|
38
|
-
|
39
|
-
return contentSizeCategory
|
40
|
-
}
|
41
|
-
|
42
|
-
private fun getColorScheme(): String {
|
43
|
-
val colorScheme = when (reactApplicationContext.resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)) {
|
44
|
-
Configuration.UI_MODE_NIGHT_YES -> "dark"
|
45
|
-
Configuration.UI_MODE_NIGHT_NO -> "light"
|
46
|
-
else -> "unspecified"
|
47
|
-
}
|
48
|
-
|
49
|
-
return colorScheme
|
12
|
+
fun hasNewConfig(): Boolean {
|
13
|
+
return this.config.hasNewConfig()
|
50
14
|
}
|
51
15
|
|
52
|
-
|
53
|
-
|
54
|
-
val insets = mutableMapOf(
|
55
|
-
"top" to 0,
|
56
|
-
"bottom" to 0,
|
57
|
-
"left" to 0,
|
58
|
-
"right" to 0
|
59
|
-
)
|
60
|
-
|
61
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
62
|
-
val systemInsets = reactApplicationContext.currentActivity?.window?.decorView?.rootWindowInsets?.getInsets(WindowInsets.Type.displayCutout())
|
63
|
-
|
64
|
-
insets["top"] = ((systemInsets?.top ?: 0) / density).toInt()
|
65
|
-
insets["bottom"] = ((systemInsets?.bottom ?: 0) / density).toInt()
|
66
|
-
insets["left"] = ((systemInsets?.left ?: 0) / density).toInt()
|
67
|
-
insets["right"] = ((systemInsets?.right ?: 0) / density).toInt()
|
68
|
-
|
69
|
-
return insets
|
70
|
-
}
|
71
|
-
|
72
|
-
val systemInsets = reactApplicationContext.currentActivity?.window?.decorView?.rootWindowInsets
|
73
|
-
|
74
|
-
insets["top"] = ((systemInsets?.systemWindowInsetTop ?: 0) / density).toInt()
|
75
|
-
insets["bottom"] = ((systemInsets?.systemWindowInsetBottom ?: 0) / density).toInt()
|
76
|
-
insets["left"] = ((systemInsets?.systemWindowInsetLeft ?: 0) / density).toInt()
|
77
|
-
insets["right"] = ((systemInsets?.systemWindowInsetRight ?: 0) / density).toInt()
|
78
|
-
|
79
|
-
return insets
|
16
|
+
fun getConfig(): Config {
|
17
|
+
return this.config.getConfig()
|
80
18
|
}
|
81
19
|
|
82
|
-
|
83
|
-
|
84
|
-
val heightResId = reactApplicationContext.resources.getIdentifier("status_bar_height", "dimen", "android")
|
85
|
-
|
86
|
-
if (heightResId > 0) {
|
87
|
-
return (reactApplicationContext.resources.getDimensionPixelSize(heightResId) / density).toInt()
|
88
|
-
}
|
89
|
-
|
90
|
-
return 0
|
20
|
+
fun getLayoutConfig(): LayoutConfig {
|
21
|
+
return this.config.getLayoutConfig()
|
91
22
|
}
|
92
23
|
}
|