react-native-unistyles 2.4.0-rc.1 → 2.4.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +5 -2
  2. package/android/src/main/cxx/cpp-adapter.cpp +28 -13
  3. package/android/src/main/cxx/helpers.cpp +21 -56
  4. package/android/src/main/cxx/helpers.h +3 -3
  5. package/android/src/main/java/com/unistyles/Config.kt +116 -0
  6. package/android/src/main/java/com/unistyles/Insets.kt +138 -0
  7. package/android/src/main/java/com/unistyles/Models.kt +85 -0
  8. package/android/src/main/java/com/unistyles/Platform.kt +10 -79
  9. package/android/src/main/java/com/unistyles/UnistylesModule.kt +85 -25
  10. package/cxx/UnistylesRuntime.cpp +57 -42
  11. package/cxx/UnistylesRuntime.h +30 -19
  12. package/ios/UnistylesModule.mm +20 -6
  13. package/ios/platform/Platform_iOS.h +5 -4
  14. package/ios/platform/Platform_iOS.mm +35 -22
  15. package/ios/platform/Platform_macOS.h +5 -4
  16. package/ios/platform/Platform_macOS.mm +18 -25
  17. package/lib/commonjs/common.js +7 -0
  18. package/lib/commonjs/common.js.map +1 -1
  19. package/lib/commonjs/core/UnistylesModule.js +6 -0
  20. package/lib/commonjs/core/UnistylesModule.js.map +1 -1
  21. package/lib/commonjs/core/UnistylesRuntime.js +8 -0
  22. package/lib/commonjs/core/UnistylesRuntime.js.map +1 -1
  23. package/lib/commonjs/hooks/useUnistyles.js +18 -1
  24. package/lib/commonjs/hooks/useUnistyles.js.map +1 -1
  25. package/lib/module/common.js +7 -0
  26. package/lib/module/common.js.map +1 -1
  27. package/lib/module/core/UnistylesModule.js +6 -0
  28. package/lib/module/core/UnistylesModule.js.map +1 -1
  29. package/lib/module/core/UnistylesRuntime.js +8 -0
  30. package/lib/module/core/UnistylesRuntime.js.map +1 -1
  31. package/lib/module/hooks/useUnistyles.js +18 -1
  32. package/lib/module/hooks/useUnistyles.js.map +1 -1
  33. package/lib/typescript/src/common.d.ts +8 -1
  34. package/lib/typescript/src/common.d.ts.map +1 -1
  35. package/lib/typescript/src/core/UnistylesModule.d.ts.map +1 -1
  36. package/lib/typescript/src/core/UnistylesRuntime.d.ts +6 -1
  37. package/lib/typescript/src/core/UnistylesRuntime.d.ts.map +1 -1
  38. package/lib/typescript/src/hooks/useUnistyles.d.ts +14 -0
  39. package/lib/typescript/src/hooks/useUnistyles.d.ts.map +1 -1
  40. package/lib/typescript/src/types/unistyles.d.ts +6 -2
  41. package/lib/typescript/src/types/unistyles.d.ts.map +1 -1
  42. package/package.json +1 -1
  43. package/src/common.ts +8 -1
  44. package/src/core/UnistylesModule.ts +8 -2
  45. package/src/core/UnistylesRuntime.ts +8 -0
  46. package/src/hooks/useUnistyles.ts +18 -1
  47. package/src/types/unistyles.ts +6 -2
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
- <img src="https://avatars.githubusercontent.com/u/51229884?s=200&v=4" height="70px" width="70px" />
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
- jint screenWidth,
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
- screenWidth,
45
- screenHeight,
44
+ jobjectToDimensions(env, screen),
46
45
  colorSchemeStr,
47
46
  contentSizeCategoryStr,
48
- jobjectToStdMap(env, insets),
49
- jobjectToStdMap(env, statusBar)
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, int width, int height) {
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
- jmethodID methodId = env->GetMethodID(cls, "onLayoutChange", "(Ljava/lang/String;Ljava/lang/String;II)V");
67
-
68
- env->CallVoidMethod(unistylesModule, methodId, breakpointStr, orientationStr, width, height);
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, jint width, jint height, jobject insets, jobject statusBar) {
116
+ Java_com_unistyles_UnistylesModule_nativeOnOrientationChange(JNIEnv *env, jobject thiz, jobject screen, jobject insets, jobject statusBar, jobject navigationBar) {
107
117
  if (unistylesRuntime != nullptr) {
108
- unistylesRuntime->handleScreenSizeChange(width, height, jobjectToStdMap(env, insets), jobjectToStdMap(env, statusBar));
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
- int jobjectToInt(JNIEnv *env, jobject integer) {
4
- jclass integerClass = env->FindClass("java/lang/Integer");
5
- jmethodID intValueMethod = env->GetMethodID(integerClass, "intValue", "()I");
6
- int value = env->CallIntMethod(integer, intValueMethod);
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->DeleteLocalRef(integerClass);
9
+ int width = env->GetIntField(dimensionObj, widthFieldID);
10
+ int height = env->GetIntField(dimensionObj, heightFieldID);
9
11
 
10
- return value;
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
- env->DeleteLocalRef(stringJbytes);
29
- env->DeleteLocalRef(stringClass);
30
-
31
- return ret;
14
+ return Dimensions{width, height};
32
15
  }
33
16
 
34
- std::map<std::string, int> jobjectToStdMap(JNIEnv *env, jobject map) {
35
- std::map<std::string, int> result;
36
-
37
- jclass setClass = env->FindClass("java/util/Set");
38
- jclass iteratorClass = env->FindClass("java/util/Iterator");
39
- jmethodID entrySetMethod = env->GetMethodID(env->GetObjectClass(map), "entrySet", "()Ljava/util/Set;");
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
- jmethodID getKeyMethod = env->GetMethodID(env->GetObjectClass(entry), "getKey", "()Ljava/lang/Object;");
50
- jmethodID getValueMethod = env->GetMethodID(env->GetObjectClass(entry), "getValue", "()Ljava/lang/Object;");
51
- jstring key = (jstring) env->CallObjectMethod(entry, getKeyMethod);
52
- jobject value = env->CallObjectMethod(entry, getValueMethod);
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(set);
62
- env->DeleteLocalRef(iterator);
63
- env->DeleteLocalRef(setClass);
64
- env->DeleteLocalRef(iteratorClass);
29
+ env->DeleteLocalRef(insetsClass);
65
30
 
66
- return result;
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
- int jobjectToInt(JNIEnv *env, jobject integer);
6
- std::string jstringToStdString(JNIEnv *env, jstring jStr);
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(private val reactApplicationContext: ReactApplicationContext) {
10
- @SuppressLint("InternalInsetResource", "DiscouragedApi", "ObsoleteSdkInt")
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
- return mapOf(
18
- "width" to screenWidth,
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
- private fun getContentSizeCategory(fontScale: Float): String {
31
- val contentSizeCategory = when {
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
- @Suppress("DEPRECATION")
53
- private fun getScreenInsets(density: Float): Map<String, Int> {
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
- @SuppressLint("InternalInsetResource", "DiscouragedApi")
83
- private fun getStatusBarHeight(density: Float): Int {
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
  }