pythonnative 0.35.0__py3-none-any.whl → 0.37.0__py3-none-any.whl

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 (176) hide show
  1. pythonnative/__init__.py +6 -4
  2. pythonnative/_ios_log.py +1 -1
  3. pythonnative/alerts.py +34 -88
  4. pythonnative/bootstrap.py +90 -0
  5. pythonnative/bridge/__init__.py +306 -0
  6. pythonnative/bridge/android.py +78 -0
  7. pythonnative/bridge/codec.py +164 -0
  8. pythonnative/bridge/fake.py +236 -0
  9. pythonnative/bridge/ios.py +138 -0
  10. pythonnative/cli/pn.py +24 -6
  11. pythonnative/components/layout.py +65 -3
  12. pythonnative/gestures.py +13 -10
  13. pythonnative/hooks.py +41 -26
  14. pythonnative/hosts/__init__.py +13 -36
  15. pythonnative/hosts/native.py +348 -0
  16. pythonnative/images.py +6 -5
  17. pythonnative/mutations.py +21 -0
  18. pythonnative/native_modules/__init__.py +30 -29
  19. pythonnative/native_modules/app_state.py +10 -6
  20. pythonnative/native_modules/battery.py +20 -84
  21. pythonnative/native_modules/biometrics.py +11 -124
  22. pythonnative/native_modules/camera.py +15 -291
  23. pythonnative/native_modules/clipboard.py +14 -109
  24. pythonnative/native_modules/desktop.py +322 -0
  25. pythonnative/native_modules/file_system.py +15 -21
  26. pythonnative/native_modules/haptics.py +19 -129
  27. pythonnative/native_modules/linking.py +24 -137
  28. pythonnative/native_modules/location.py +28 -180
  29. pythonnative/native_modules/net_info.py +25 -135
  30. pythonnative/native_modules/notifications.py +36 -257
  31. pythonnative/native_modules/permissions.py +20 -184
  32. pythonnative/native_modules/registry.py +513 -0
  33. pythonnative/native_modules/secure_store.py +18 -155
  34. pythonnative/native_modules/share.py +8 -113
  35. pythonnative/native_views/__init__.py +67 -86
  36. pythonnative/native_views/bridge_backend.py +264 -0
  37. pythonnative/platform.py +19 -15
  38. pythonnative/platform_metrics.py +3 -3
  39. pythonnative/project/android.py +0 -3
  40. pythonnative/project/builder.py +38 -16
  41. pythonnative/project/config.py +12 -2
  42. pythonnative/project/plugins.py +366 -0
  43. pythonnative/py.typed +0 -0
  44. pythonnative/runtime.py +35 -203
  45. pythonnative/sdk/__init__.py +48 -33
  46. pythonnative/sdk/_components.py +57 -76
  47. pythonnative/storage.py +18 -243
  48. pythonnative/templates/android_template/app/build.gradle +6 -9
  49. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +34 -166
  50. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -151
  51. pythonnative/templates/android_template/pythonnative/build.gradle +48 -0
  52. pythonnative/templates/android_template/pythonnative/src/main/AndroidManifest.xml +11 -0
  53. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PNBridge.kt +214 -0
  54. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
  55. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
  56. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +249 -0
  57. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
  58. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +32 -0
  59. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
  60. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
  61. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
  62. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +115 -0
  63. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
  64. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +35 -0
  65. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +33 -0
  66. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
  67. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
  68. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
  69. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
  70. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
  71. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +156 -0
  72. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
  73. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
  74. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +87 -0
  75. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
  76. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
  77. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
  78. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
  79. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
  80. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
  81. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +245 -0
  82. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
  83. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
  84. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +179 -0
  85. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
  86. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
  87. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
  88. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
  89. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
  90. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
  91. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +97 -0
  92. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
  93. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +135 -0
  94. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
  95. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +88 -0
  96. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
  97. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
  98. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
  99. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/Navigator.kt +116 -0
  100. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +223 -0
  101. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
  102. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNAccessibilityDelegate.kt +5 -4
  103. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNBorderDrawable.kt +12 -3
  104. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
  105. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNFrameLayout.kt +9 -10
  106. pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNVirtualListView.java +9 -12
  107. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
  108. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
  109. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
  110. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
  111. pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PromiseTest.kt +75 -0
  112. pythonnative/templates/android_template/settings.gradle +1 -0
  113. pythonnative/templates/ios_template/PythonNativeKit/Package.swift +21 -0
  114. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Animation/PNAnimator.swift +301 -0
  115. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNBridge.swift +191 -0
  116. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNRegistry.swift +140 -0
  117. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNTransaction.swift +165 -0
  118. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
  119. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNButtonManagers.swift +206 -0
  120. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
  121. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
  122. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
  123. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
  124. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNImageManager.swift +221 -0
  125. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
  126. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
  127. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
  128. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
  129. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextInputManager.swift +289 -0
  130. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
  131. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
  132. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
  133. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
  134. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
  135. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +257 -0
  136. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
  137. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
  138. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
  139. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/HostModule.swift +158 -0
  140. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
  141. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
  142. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PNNativeModule.swift +125 -0
  143. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PermissionModules.swift +251 -0
  144. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
  145. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
  146. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
  147. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNViewController.swift +251 -0
  148. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
  149. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
  150. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
  151. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
  152. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
  153. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
  154. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNManagerTests.swift +124 -0
  155. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNModuleTests.swift +98 -0
  156. pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
  157. pythonnative/templates/ios_template/ios_template/AppDelegate.swift +8 -50
  158. pythonnative/templates/ios_template/ios_template/Info.plist +4 -4
  159. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +38 -46
  160. pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +11 -16
  161. pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -142
  162. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +47 -1
  163. pythonnative/utils.py +4 -82
  164. pythonnative/virtual_rows.py +3 -3
  165. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/METADATA +3 -5
  166. pythonnative-0.37.0.dist-info/RECORD +263 -0
  167. pythonnative/hosts/android.py +0 -269
  168. pythonnative/hosts/ios.py +0 -404
  169. pythonnative/native_views/android.py +0 -3702
  170. pythonnative/native_views/ios.py +0 -5253
  171. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -43
  172. pythonnative-0.35.0.dist-info/RECORD +0 -155
  173. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/WHEEL +0 -0
  174. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/entry_points.txt +0 -0
  175. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/licenses/LICENSE +0 -0
  176. {pythonnative-0.35.0.dist-info → pythonnative-0.37.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,87 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.view.View
5
+ import android.widget.AdapterView
6
+ import android.widget.ArrayAdapter
7
+ import android.widget.Spinner
8
+ import com.pythonnative.runtime.bridge.JsonUtil
9
+ import com.pythonnative.runtime.bridge.str
10
+ import com.pythonnative.runtime.bridge.value
11
+ import org.json.JSONArray
12
+ import org.json.JSONObject
13
+
14
+ /** `Picker` element: a native `Spinner` dropdown over `items` of `{label, value}`. */
15
+ class PickerManager : ComponentManager() {
16
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
17
+ val spinner = Spinner(context)
18
+ spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
19
+ override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
20
+ if (stateOf(parent)["suppress"] == true) return
21
+ val items = propsOf(parent).value("items") as? JSONArray ?: return
22
+ if (position < 0 || position >= items.length()) return
23
+ fire(parent, "on_change", itemValue(items.opt(position)))
24
+ }
25
+
26
+ override fun onNothingSelected(parent: AdapterView<*>) {}
27
+ }
28
+ return spinner
29
+ }
30
+
31
+ private fun itemValue(item: Any?): Any? = when (item) {
32
+ is JSONObject -> item.value("value")
33
+ JSONObject.NULL -> null
34
+ else -> item
35
+ }
36
+
37
+ private fun itemLabel(item: Any?): String = when (item) {
38
+ is JSONObject -> item.str("label") ?: item.str("value") ?: ""
39
+ null, JSONObject.NULL -> ""
40
+ else -> item.toString()
41
+ }
42
+
43
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
44
+ val spinner = view as Spinner
45
+ val state = stateOf(spinner)
46
+ val merged = propsOf(spinner)
47
+ val items = merged.value("items") as? JSONArray ?: JSONArray()
48
+ if (props.has("items") || initial) {
49
+ val labels = (0 until items.length()).map { itemLabel(items.opt(it)) }
50
+ val adapter = ArrayAdapter(spinner.context, android.R.layout.simple_spinner_item, labels)
51
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
52
+ state["suppress"] = true
53
+ try {
54
+ spinner.adapter = adapter
55
+ } finally {
56
+ state["suppress"] = false
57
+ }
58
+ }
59
+ if (props.has("value") || props.has("selected_value") || initial) {
60
+ val value = merged.value("value") ?: merged.value("selected_value")
61
+ var target = -1
62
+ for (i in 0 until items.length()) {
63
+ if (valuesEqual(itemValue(items.opt(i)), value)) {
64
+ target = i
65
+ break
66
+ }
67
+ }
68
+ if (target >= 0 && spinner.selectedItemPosition != target) {
69
+ state["suppress"] = true
70
+ try {
71
+ spinner.setSelection(target, false)
72
+ } finally {
73
+ state["suppress"] = false
74
+ }
75
+ }
76
+ }
77
+ if (props.has("enabled")) spinner.isEnabled = JsonUtil.truthy(props.value("enabled"))
78
+ if (props.has("disabled")) spinner.isEnabled = !JsonUtil.truthy(props.value("disabled"))
79
+ ViewStyler.apply(spinner, props)
80
+ }
81
+
82
+ private fun valuesEqual(a: Any?, b: Any?): Boolean {
83
+ if (a == b) return true
84
+ if (a is Number && b is Number) return a.toDouble() == b.toDouble()
85
+ return a != null && b != null && a.toString() == b.toString()
86
+ }
87
+ }
@@ -0,0 +1,55 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.view.View
5
+ import android.view.ViewGroup
6
+ import android.widget.FrameLayout
7
+ import com.pythonnative.runtime.screens.ScreenRegistry
8
+ import org.json.JSONObject
9
+
10
+ /**
11
+ * `Portal` element: floats its children over the screen in a full-size,
12
+ * non-clickable overlay added directly to the screen's fragment
13
+ * container (the same parent as the screen root, so portal coordinates
14
+ * equal viewport coordinates). The overlay attaches lazily on the first
15
+ * child insert and re-homes itself if the container changed.
16
+ */
17
+ class PortalManager : ComponentManager() {
18
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
19
+ val overlay = FrameLayout(context)
20
+ overlay.isClickable = false
21
+ return overlay
22
+ }
23
+
24
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
25
+ ViewStyler.apply(view, props)
26
+ if (!initial) ensureAttached(view)
27
+ }
28
+
29
+ private fun ensureAttached(overlay: View) {
30
+ val container = ScreenRegistry.activeContainer() ?: return
31
+ val parent = overlay.parent as? ViewGroup
32
+ if (parent === container) {
33
+ overlay.bringToFront()
34
+ return
35
+ }
36
+ parent?.removeView(overlay)
37
+ container.addView(
38
+ overlay,
39
+ ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT),
40
+ )
41
+ }
42
+
43
+ override fun insertChild(parent: View, child: View, index: Int) {
44
+ ensureAttached(parent)
45
+ ViewChildren.insert(parent as ViewGroup, child, index)
46
+ }
47
+
48
+ override fun setFrame(view: View, x: Double, y: Double, width: Double, height: Double) {
49
+ // The overlay fills its container; the engine frames only the portal's children.
50
+ }
51
+
52
+ override fun teardown(view: View) {
53
+ (view.parent as? ViewGroup)?.removeView(view)
54
+ }
55
+ }
@@ -0,0 +1,122 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.content.res.ColorStateList
6
+ import android.graphics.drawable.RippleDrawable
7
+ import android.view.MotionEvent
8
+ import android.view.View
9
+ import android.view.ViewConfiguration
10
+ import com.pythonnative.runtime.bridge.JsonUtil
11
+ import com.pythonnative.runtime.bridge.MainThread
12
+ import com.pythonnative.runtime.bridge.num
13
+ import com.pythonnative.runtime.bridge.value
14
+ import com.pythonnative.runtime.gestures.GestureCoordinator
15
+ import com.pythonnative.runtime.views.PNFrameLayout
16
+ import org.json.JSONObject
17
+ import kotlin.math.hypot
18
+
19
+ /**
20
+ * `Pressable` element: a container dispatching press events from one
21
+ * touch stream. `on_press_in` at finger-down (plus an opacity dip),
22
+ * `on_long_press` after the long-press timeout, `on_press` on a clean
23
+ * release, and `on_press_out` when the finger lifts or the touch
24
+ * cancels. The same stream feeds the gesture coordinator so press
25
+ * feedback and pan/pinch recognition coexist on one view.
26
+ */
27
+ class PressableManager : ViewManager() {
28
+ private class Press(val downX: Double, val downY: Double, val seq: Int) {
29
+ var moved = false
30
+ var longFired = false
31
+ }
32
+
33
+ @SuppressLint("ClickableViewAccessibility")
34
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
35
+ val view = PNFrameLayout(context)
36
+ view.isClickable = true
37
+ view.isFocusable = true
38
+ view.setOnTouchListener { v, event -> onTouch(v, event) }
39
+ return view
40
+ }
41
+
42
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
43
+ // Press handling owns the touch listener; gesture specs are fed from inside it.
44
+ stateOf(view)["gestures_bound"] = true
45
+ super.applyProps(view, props, initial)
46
+ if (props.has("ripple") || props.has("android_ripple")) applyRipple(view, props)
47
+ if (props.has("disabled")) view.isEnabled = !JsonUtil.truthy(props.value("disabled"))
48
+ }
49
+
50
+ private fun applyRipple(view: View, props: JSONObject) {
51
+ val spec = props.value("android_ripple") ?: props.value("ripple")
52
+ if (spec == null || spec == false) {
53
+ view.foreground = null
54
+ return
55
+ }
56
+ val color = when (spec) {
57
+ is JSONObject -> PNColor.parse(spec.value("color"))
58
+ else -> PNColor.parse(spec)
59
+ } ?: 0x33000000
60
+ val borderless = (spec as? JSONObject)?.let { JsonUtil.truthy(it.value("borderless")) } ?: false
61
+ val mask = if (borderless) null else android.graphics.drawable.ColorDrawable(0xFFFFFFFF.toInt())
62
+ view.foreground = RippleDrawable(ColorStateList.valueOf(color), null, mask)
63
+ }
64
+
65
+ private fun onTouch(view: View, event: MotionEvent): Boolean {
66
+ if (ViewStyler.pointerEventsBlocked(view)) return false
67
+ val record = recordOf(view) ?: return false
68
+ GestureCoordinator.feed(record, event)
69
+ if (!view.isEnabled) return true
70
+ val state = record.state
71
+ val merged = record.props
72
+ val density = view.resources.displayMetrics.density
73
+ val x = event.x / density
74
+ val y = event.y / density
75
+ when (event.actionMasked) {
76
+ MotionEvent.ACTION_DOWN -> {
77
+ val seq = ((state["press_seq"] as? Int) ?: 0) + 1
78
+ val press = Press(x.toDouble(), y.toDouble(), seq)
79
+ state["press"] = press
80
+ state["press_seq"] = seq
81
+ fire(view, "on_press_in")
82
+ val opacity = merged.num("pressed_opacity") ?: merged.num("active_opacity") ?: 0.6
83
+ if (opacity < 1.0) view.animate().alpha(opacity.toFloat()).setDuration(50).start()
84
+ if (hasEvent(view, "on_long_press")) {
85
+ val delay = merged.num("delay_long_press")?.toLong() ?: ViewConfiguration.getLongPressTimeout().toLong().coerceAtLeast(500L)
86
+ MainThread.postDelayed({
87
+ val live = recordOf(view)?.state?.get("press") as? Press ?: return@postDelayed
88
+ if (live.seq != seq || live.moved || live.longFired) return@postDelayed
89
+ live.longFired = true
90
+ fire(view, "on_long_press")
91
+ }, delay)
92
+ }
93
+ view.isPressed = true
94
+ return true
95
+ }
96
+ MotionEvent.ACTION_MOVE -> {
97
+ val press = state["press"] as? Press ?: return true
98
+ val slop = merged.num("tap_slop") ?: TAP_SLOP_DP
99
+ if (hypot(x - press.downX, y - press.downY) > slop) press.moved = true
100
+ return true
101
+ }
102
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
103
+ val press = state["press"] as? Press
104
+ state["press"] = null
105
+ view.isPressed = false
106
+ if (press == null) return true
107
+ fire(view, "on_press_out")
108
+ view.animate().alpha(1f).setDuration(100).start()
109
+ if (event.actionMasked == MotionEvent.ACTION_UP && !press.moved && !press.longFired) {
110
+ view.performClick()
111
+ fire(view, "on_press")
112
+ }
113
+ return true
114
+ }
115
+ }
116
+ return true
117
+ }
118
+
119
+ private companion object {
120
+ const val TAP_SLOP_DP = 12.0
121
+ }
122
+ }
@@ -0,0 +1,214 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.content.Context
5
+ import android.view.MotionEvent
6
+ import android.view.View
7
+ import android.view.ViewGroup
8
+ import android.view.inputmethod.InputMethodManager
9
+ import android.widget.HorizontalScrollView
10
+ import androidx.core.widget.NestedScrollView
11
+ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
12
+ import com.pythonnative.runtime.bridge.JsonUtil
13
+ import com.pythonnative.runtime.bridge.MainThread
14
+ import com.pythonnative.runtime.bridge.obj
15
+ import com.pythonnative.runtime.bridge.str
16
+ import com.pythonnative.runtime.bridge.value
17
+ import org.json.JSONObject
18
+ import kotlin.math.max
19
+ import kotlin.math.roundToInt
20
+
21
+ /**
22
+ * `ScrollView` element: a `NestedScrollView` (vertical, always wrapped in
23
+ * a `SwipeRefreshLayout` that is enabled only while `refresh_control`
24
+ * is set) or a `HorizontalScrollView`.
25
+ *
26
+ * `on_scroll` carries `{x, y, extent, range}` in dp. Commands:
27
+ * `scroll_to_offset`, `scroll_to_end`, `get_scroll_offset`,
28
+ * `flash_scroll_indicators`.
29
+ */
30
+ class ScrollViewManager : ComponentManager() {
31
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
32
+ val horizontal = props.str("scroll_axis") == "horizontal" || JsonUtil.truthy(props.value("horizontal"))
33
+ return if (horizontal) {
34
+ val sv = HorizontalScrollView(context)
35
+ bindScrollListener(sv, sv, true)
36
+ bindTouch(sv, sv, true)
37
+ sv
38
+ } else {
39
+ val sv = NestedScrollView(context)
40
+ val wrapper = SwipeRefreshLayout(context)
41
+ wrapper.addView(sv, ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
42
+ wrapper.isEnabled = false
43
+ wrapper.setOnRefreshListener { fire(wrapper, "on_refresh") }
44
+ bindScrollListener(wrapper, sv, false)
45
+ bindTouch(wrapper, sv, false)
46
+ wrapper
47
+ }
48
+ }
49
+
50
+ private fun inner(outer: View): ViewGroup = when (outer) {
51
+ is SwipeRefreshLayout -> outer.getChildAt(0) as ViewGroup
52
+ else -> outer as ViewGroup
53
+ }
54
+
55
+ private fun isHorizontal(outer: View): Boolean = outer is HorizontalScrollView
56
+
57
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
58
+ val sv = inner(view)
59
+ ViewStyler.apply(sv, props)
60
+ if (props.has("shows_scroll_indicator")) {
61
+ val show = props.value("shows_scroll_indicator") != false
62
+ sv.isVerticalScrollBarEnabled = show
63
+ sv.isHorizontalScrollBarEnabled = show
64
+ }
65
+ if (props.has("bounces")) {
66
+ sv.overScrollMode = if (props.value("bounces") == false) View.OVER_SCROLL_NEVER else View.OVER_SCROLL_IF_CONTENT_SCROLLS
67
+ }
68
+ if (props.has("scroll_enabled")) stateOf(view)["scroll_enabled"] = props.value("scroll_enabled") != false
69
+ if (props.has("refresh_control") && view is SwipeRefreshLayout) applyRefresh(view, props.obj("refresh_control"))
70
+ // paging_enabled and keyboard_dismiss_mode are read from merged props at touch time.
71
+ }
72
+
73
+ private fun applyRefresh(srl: SwipeRefreshLayout, spec: JSONObject?) {
74
+ if (spec == null) {
75
+ srl.isEnabled = false
76
+ srl.isRefreshing = false
77
+ return
78
+ }
79
+ srl.isEnabled = true
80
+ PNColor.parse(spec.value("tint_color"))?.let { srl.setColorSchemeColors(it) }
81
+ PNColor.parse(spec.value("background_color"))?.let { srl.setProgressBackgroundColorSchemeColor(it) }
82
+ srl.isRefreshing = JsonUtil.truthy(spec.value("refreshing"))
83
+ }
84
+
85
+ override fun insertChild(parent: View, child: View, index: Int) = ViewChildren.insert(inner(parent), child, index)
86
+
87
+ override fun removeChild(parent: View, child: View) {
88
+ inner(parent).removeView(child)
89
+ }
90
+
91
+ private fun bindScrollListener(outer: View, sv: View, horizontal: Boolean) {
92
+ sv.setOnScrollChangeListener { v, scrollX, scrollY, _, _ ->
93
+ if (!hasEvent(outer, "on_scroll")) return@setOnScrollChangeListener
94
+ fire(outer, "on_scroll", scrollPayload(v as ViewGroup, horizontal, scrollX, scrollY))
95
+ }
96
+ }
97
+
98
+ private fun scrollPayload(sv: ViewGroup, horizontal: Boolean, scrollX: Int, scrollY: Int): Map<String, Any?> {
99
+ val content = if (sv.childCount > 0) sv.getChildAt(0) else null
100
+ val extent = if (horizontal) sv.width else sv.height
101
+ val range = if (content == null) extent else if (horizontal) content.width else content.height
102
+ return mapOf(
103
+ "x" to dp(scrollX),
104
+ "y" to dp(scrollY),
105
+ "extent" to dp(extent),
106
+ "range" to dp(max(range, extent)),
107
+ "content_width" to dp(content?.width ?: sv.width),
108
+ "content_height" to dp(content?.height ?: sv.height),
109
+ )
110
+ }
111
+
112
+ @SuppressLint("ClickableViewAccessibility")
113
+ private fun bindTouch(outer: View, sv: ViewGroup, horizontal: Boolean) {
114
+ sv.setOnTouchListener { v, event ->
115
+ val merged = propsOf(outer)
116
+ if (stateOf(outer)["scroll_enabled"] == false) return@setOnTouchListener true
117
+ when (event.actionMasked) {
118
+ MotionEvent.ACTION_MOVE -> {
119
+ val mode = merged.str("keyboard_dismiss_mode")
120
+ if (mode == "on_drag" || mode == "interactive") dismissKeyboard(v)
121
+ }
122
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
123
+ if (JsonUtil.truthy(merged.value("paging_enabled"))) scheduleSnap(sv, horizontal)
124
+ }
125
+ }
126
+ false
127
+ }
128
+ }
129
+
130
+ private fun dismissKeyboard(view: View) {
131
+ val focused = view.rootView.findFocus() ?: return
132
+ focused.clearFocus()
133
+ (view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)
134
+ ?.hideSoftInputFromWindow(view.windowToken, 0)
135
+ }
136
+
137
+ /** Wait for the fling to settle (two identical samples), then snap to the nearest page. */
138
+ private fun scheduleSnap(sv: ViewGroup, horizontal: Boolean) {
139
+ val state = stateOf(sv.parent as? View ?: sv)
140
+ val token = ((state["snap_token"] as? Int) ?: 0) + 1
141
+ state["snap_token"] = token
142
+ var last = -1
143
+ val poll = object : Runnable {
144
+ override fun run() {
145
+ if (state["snap_token"] != token) return
146
+ val current = if (horizontal) sv.scrollX else sv.scrollY
147
+ if (current == last) {
148
+ val page = if (horizontal) sv.width else sv.height
149
+ if (page > 0) {
150
+ val target = (current.toDouble() / page).roundToInt() * page
151
+ if (target != current) {
152
+ when (sv) {
153
+ is NestedScrollView -> sv.smoothScrollTo(0, target)
154
+ is HorizontalScrollView -> sv.smoothScrollTo(target, 0)
155
+ }
156
+ }
157
+ }
158
+ return
159
+ }
160
+ last = current
161
+ MainThread.postDelayed(this, 50)
162
+ }
163
+ }
164
+ MainThread.postDelayed(poll, 50)
165
+ }
166
+
167
+ override fun command(view: View, name: String, args: JSONObject): Any? {
168
+ val sv = inner(view)
169
+ val horizontal = isHorizontal(view)
170
+ when (name) {
171
+ "scroll_to_offset" -> {
172
+ val x = px(JsonUtil.toDouble(args.opt("x")))
173
+ val y = px(JsonUtil.toDouble(args.opt("y")))
174
+ val animated = args.value("animated") != false
175
+ when (sv) {
176
+ is NestedScrollView -> if (animated) sv.smoothScrollTo(x, y) else sv.scrollTo(x, y)
177
+ is HorizontalScrollView -> if (animated) sv.smoothScrollTo(x, y) else sv.scrollTo(x, y)
178
+ }
179
+ }
180
+ "scroll_to_end" -> {
181
+ val child = if (sv.childCount > 0) sv.getChildAt(0) else return null
182
+ val animated = args.value("animated") != false
183
+ if (horizontal) {
184
+ val target = max(0, child.width - sv.width)
185
+ if (animated) (sv as HorizontalScrollView).smoothScrollTo(target, 0) else sv.scrollTo(target, 0)
186
+ } else {
187
+ val target = max(0, child.height - sv.height)
188
+ if (animated) (sv as NestedScrollView).smoothScrollTo(0, target) else sv.scrollTo(0, target)
189
+ }
190
+ }
191
+ "get_scroll_offset" -> return mapOf("x" to dp(sv.scrollX), "y" to dp(sv.scrollY))
192
+ "flash_scroll_indicators" -> sv.awakenScrollBarsCompat()
193
+ }
194
+ return null
195
+ }
196
+
197
+ private fun View.awakenScrollBarsCompat() {
198
+ // awakenScrollBars is protected; toggling the fade re-shows the bars briefly.
199
+ isScrollbarFadingEnabled = false
200
+ MainThread.postDelayed({ isScrollbarFadingEnabled = true }, 600)
201
+ }
202
+
203
+ override fun setFrame(view: View, x: Double, y: Double, width: Double, height: Double) {
204
+ super.setFrame(view, x, y, width, height)
205
+ // The inner scroll view fills its refresh wrapper.
206
+ if (view is SwipeRefreshLayout) {
207
+ val sv = view.getChildAt(0)
208
+ val lp = sv.layoutParams
209
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT
210
+ lp.height = ViewGroup.LayoutParams.MATCH_PARENT
211
+ sv.layoutParams = lp
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,113 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.content.Context
4
+ import android.graphics.drawable.GradientDrawable
5
+ import android.view.View
6
+ import android.widget.Button
7
+ import android.widget.LinearLayout
8
+ import com.pythonnative.runtime.bridge.JsonUtil
9
+ import com.pythonnative.runtime.bridge.arr
10
+ import com.pythonnative.runtime.bridge.value
11
+ import org.json.JSONObject
12
+ import kotlin.math.max
13
+
14
+ /**
15
+ * `SegmentedControl` element: a horizontal `LinearLayout` of equal-width
16
+ * toggle `Button`s. The selected segment is filled with `tint_color`;
17
+ * the rest are outlined. The control owns its subviews, so child
18
+ * inserts are ignored.
19
+ */
20
+ class SegmentedControlManager : ComponentManager() {
21
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
22
+ val ll = LinearLayout(context)
23
+ ll.orientation = LinearLayout.HORIZONTAL
24
+ return ll
25
+ }
26
+
27
+ override fun insertChild(parent: View, child: View, index: Int) {}
28
+
29
+ override fun removeChild(parent: View, child: View) {}
30
+
31
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
32
+ val ll = view as LinearLayout
33
+ val state = stateOf(ll)
34
+ val merged = propsOf(ll)
35
+ var segmentsChanged = false
36
+ if (props.has("segments") || initial) {
37
+ val segments = JsonUtil.toList(merged.arr("segments")).map { it?.toString() ?: "" }
38
+ if (initial || segments != state["segments"]) {
39
+ state["segments"] = segments
40
+ segmentsChanged = true
41
+ }
42
+ }
43
+ props.value("selected_index")?.let { state["selected_index"] = JsonUtil.toInt(it) }
44
+ if (segmentsChanged) rebuild(ll) else restyle(ll)
45
+ ViewStyler.applyAccessibility(ll, props)
46
+ }
47
+
48
+ private fun rebuild(ll: LinearLayout) {
49
+ val state = stateOf(ll)
50
+ ll.removeAllViews()
51
+ @Suppress("UNCHECKED_CAST")
52
+ val segments = state["segments"] as? List<String> ?: emptyList()
53
+ val enabled = propsOf(ll).value("enabled") != false
54
+ segments.forEachIndexed { index, label ->
55
+ val btn = Button(ll.context)
56
+ btn.text = label
57
+ btn.isAllCaps = false
58
+ btn.layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1f)
59
+ btn.isEnabled = enabled
60
+ btn.setOnClickListener {
61
+ if (propsOf(ll).value("enabled") == false) return@setOnClickListener
62
+ stateOf(ll)["selected_index"] = index
63
+ restyle(ll)
64
+ fire(ll, "on_change", index)
65
+ }
66
+ ll.addView(btn)
67
+ }
68
+ restyle(ll)
69
+ }
70
+
71
+ private fun restyle(ll: LinearLayout) {
72
+ val merged = propsOf(ll)
73
+ val accent = PNColor.parseOr(merged.value("tint_color"), DEFAULT_ACCENT)
74
+ val selected = (stateOf(ll)["selected_index"] as? Int) ?: JsonUtil.toInt(merged.value("selected_index"))
75
+ val enabled = merged.value("enabled") != false
76
+ for (i in 0 until ll.childCount) {
77
+ val btn = ll.getChildAt(i) as? Button ?: continue
78
+ val drawable = GradientDrawable()
79
+ drawable.cornerRadius = pxF(6.0)
80
+ drawable.setStroke(px(1.0), accent)
81
+ if (i == selected) {
82
+ drawable.setColor(accent)
83
+ btn.setTextColor(0xFFFFFFFF.toInt())
84
+ } else {
85
+ drawable.setColor(0x00FFFFFF)
86
+ btn.setTextColor(accent)
87
+ }
88
+ btn.background = drawable
89
+ btn.isEnabled = enabled
90
+ }
91
+ }
92
+
93
+ override fun measure(view: View, maxWidth: Double, maxHeight: Double): FloatArray {
94
+ // Weighted children measure to ~0 under an unspecified spec, so
95
+ // size to the sum of the segments' natural widths instead.
96
+ val ll = view as LinearLayout
97
+ if (ll.childCount == 0) return floatArrayOf(0f, 0f)
98
+ val spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
99
+ var totalW = 0
100
+ var maxH = 0
101
+ for (i in 0 until ll.childCount) {
102
+ val child = ll.getChildAt(i)
103
+ child.measure(spec, spec)
104
+ totalW += child.measuredWidth
105
+ maxH = max(maxH, child.measuredHeight)
106
+ }
107
+ return floatArrayOf(dp(totalW).toFloat(), dp(maxH).toFloat())
108
+ }
109
+
110
+ private companion object {
111
+ val DEFAULT_ACCENT = 0xFF007AFF.toInt()
112
+ }
113
+ }
@@ -0,0 +1,52 @@
1
+ package com.pythonnative.runtime.components
2
+
3
+ import android.app.Activity
4
+ import android.content.Context
5
+ import android.view.View
6
+ import androidx.core.view.WindowCompat
7
+ import androidx.core.view.WindowInsetsCompat
8
+ import androidx.core.view.WindowInsetsControllerCompat
9
+ import com.pythonnative.runtime.PNBridge
10
+ import com.pythonnative.runtime.bridge.JsonUtil
11
+ import com.pythonnative.runtime.bridge.PNLog
12
+ import com.pythonnative.runtime.bridge.str
13
+ import com.pythonnative.runtime.bridge.value
14
+ import org.json.JSONObject
15
+
16
+ /** `StatusBar` element: applies bar color, style, and visibility to the host window. */
17
+ class StatusBarManager : ComponentManager() {
18
+ override fun createView(context: Context, tag: Long, props: JSONObject): View {
19
+ val v = View(context)
20
+ v.visibility = View.GONE
21
+ return v
22
+ }
23
+
24
+ override fun setFrame(view: View, x: Double, y: Double, width: Double, height: Double) {}
25
+
26
+ override fun measure(view: View, maxWidth: Double, maxHeight: Double): FloatArray = floatArrayOf(0f, 0f)
27
+
28
+ override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
29
+ val activity = PNBridge.activity() ?: (view.context as? Activity) ?: return
30
+ val window = activity.window ?: return
31
+ try {
32
+ PNColor.parse(props.value("background_color"))?.let { window.statusBarColor = it }
33
+ val controller: WindowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.decorView)
34
+ props.str("bar_style")?.let { style ->
35
+ // "dark" / "default" mean dark icons (light backgrounds); "light" means light icons.
36
+ controller.isAppearanceLightStatusBars = style == "dark" || style == "default" || style == "dark_content"
37
+ }
38
+ if (props.has("hidden")) {
39
+ if (JsonUtil.truthy(props.value("hidden"))) {
40
+ controller.hide(WindowInsetsCompat.Type.statusBars())
41
+ } else {
42
+ controller.show(WindowInsetsCompat.Type.statusBars())
43
+ }
44
+ }
45
+ if (props.has("translucent")) {
46
+ WindowCompat.setDecorFitsSystemWindows(window, !JsonUtil.truthy(props.value("translucent")))
47
+ }
48
+ } catch (e: Exception) {
49
+ PNLog.once("statusbar", "StatusBar: could not apply props on Android: $e")
50
+ }
51
+ }
52
+ }