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

Sign up to get free protection for your applications and to get access to all the features.
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
  }