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,527 @@
1
+ package com.pythonnative.runtime.gestures
2
+
3
+ import kotlin.math.abs
4
+ import kotlin.math.atan2
5
+ import kotlin.math.hypot
6
+ import kotlin.math.max
7
+
8
+ /** Gesture states reported on event payloads. */
9
+ object GestureState {
10
+ const val BEGAN = "began"
11
+ const val CHANGED = "changed"
12
+ const val ENDED = "ended"
13
+ const val CANCELLED = "cancelled"
14
+
15
+ /** Internal verdict used by recognizers to tell the arbiter they cannot succeed. */
16
+ const val FAILED = "__failed"
17
+ }
18
+
19
+ /** A pointer position in the view's coordinate space, in dp. */
20
+ data class Point(val x: Double, val y: Double)
21
+
22
+ /** Output channel of recognizers and the arbiter: `(gesture_index, payload)`. */
23
+ typealias EmitFn = (Int, Map<String, Any?>) -> Unit
24
+
25
+ /** Configuration for one leaf gesture, decoded from the serialized `gestures` prop. */
26
+ data class GestureConfig(
27
+ val kind: String,
28
+ val nTaps: Int = 1,
29
+ val maxDistance: Double = 12.0,
30
+ val minDurationMs: Double = 500.0,
31
+ val minDistance: Double = 10.0,
32
+ val minPointers: Int = 1,
33
+ val direction: String = "any",
34
+ val minVelocity: Double = 300.0,
35
+ val nPointers: Int = 1,
36
+ /** Indices this gesture may be active alongside; `null` means everything. */
37
+ val simultaneous: Set<Int>? = null,
38
+ /** Indices that must fail before this gesture may activate. */
39
+ val waitFor: Set<Int> = emptySet(),
40
+ )
41
+
42
+ /** Estimate pointer velocity from recent samples (dp per second). */
43
+ class VelocityEstimator {
44
+ private data class Sample(val x: Double, val y: Double, val t: Double)
45
+
46
+ private val samples = ArrayList<Sample>()
47
+
48
+ fun add(x: Double, y: Double, t: Double) {
49
+ samples.add(Sample(x, y, t))
50
+ val cutoff = t - WINDOW_S
51
+ while (samples.size > 2 && samples[0].t < cutoff) samples.removeAt(0)
52
+ }
53
+
54
+ fun velocity(): Point {
55
+ if (samples.size < 2) return Point(0.0, 0.0)
56
+ val first = samples.first()
57
+ val last = samples.last()
58
+ val dt = last.t - first.t
59
+ if (dt <= 1e-6) return Point(0.0, 0.0)
60
+ return Point((last.x - first.x) / dt, (last.y - first.y) / dt)
61
+ }
62
+
63
+ fun reset() = samples.clear()
64
+
65
+ private companion object {
66
+ const val WINDOW_S = 0.1
67
+ }
68
+ }
69
+
70
+ internal fun centroid(pointers: Map<Int, Point>): Point {
71
+ if (pointers.isEmpty()) return Point(0.0, 0.0)
72
+ var xs = 0.0
73
+ var ys = 0.0
74
+ for (p in pointers.values) {
75
+ xs += p.x
76
+ ys += p.y
77
+ }
78
+ return Point(xs / pointers.size, ys / pointers.size)
79
+ }
80
+
81
+ /** Base class for one gesture's state machine. `pointers` maps pointer id to position. */
82
+ abstract class Recognizer(val index: Int, val config: GestureConfig, private val emitFn: EmitFn) {
83
+ protected fun emit(state: String, vararg fields: Pair<String, Any?>) {
84
+ val payload = LinkedHashMap<String, Any?>()
85
+ payload["kind"] = config.kind
86
+ payload["state"] = state
87
+ for ((k, v) in fields) payload[k] = v
88
+ emitFn(index, payload)
89
+ }
90
+
91
+ /** Report that this gesture can no longer succeed this interaction. */
92
+ protected fun fail() {
93
+ emitFn(index, mapOf("kind" to config.kind, "state" to GestureState.FAILED))
94
+ }
95
+
96
+ /** Abandon recognition without emitting anything (lost a race). */
97
+ open fun forceFail(t: Double) = cancel(t)
98
+
99
+ open fun down(pointers: Map<Int, Point>, t: Double) {}
100
+ open fun move(pointers: Map<Int, Point>, t: Double) {}
101
+ open fun up(pointers: Map<Int, Point>, t: Double, x: Double, y: Double) {}
102
+ open fun cancel(t: Double) {}
103
+
104
+ /** Next time `poll` should run, or `null`. */
105
+ open fun deadline(): Double? = null
106
+ open fun poll(t: Double) {}
107
+
108
+ companion object {
109
+ /** Build the recognizer for `config.kind`, or `null` for unknown kinds. */
110
+ fun create(index: Int, config: GestureConfig, emit: EmitFn): Recognizer? = when (config.kind) {
111
+ "tap" -> TapRecognizer(index, config, emit)
112
+ "long_press" -> LongPressRecognizer(index, config, emit)
113
+ "pan" -> PanRecognizer(index, config, emit)
114
+ "swipe", "fling" -> SwipeRecognizer(index, config, emit)
115
+ "pinch" -> PinchRecognizer(index, config, emit)
116
+ "rotation" -> RotationRecognizer(index, config, emit)
117
+ else -> null
118
+ }
119
+ }
120
+ }
121
+
122
+ class TapRecognizer(index: Int, config: GestureConfig, emit: EmitFn) : Recognizer(index, config, emit) {
123
+ private val nTaps = max(1, config.nTaps)
124
+ private val slop = config.maxDistance
125
+ private var downPos: Point? = null
126
+ private var downTime = 0.0
127
+ private var tapCount = 0
128
+ private var lastTapTime = 0.0
129
+ private var failed = false
130
+ private var gapDeadline: Double? = null
131
+
132
+ override fun down(pointers: Map<Int, Point>, t: Double) {
133
+ if (pointers.size != 1) {
134
+ if (!failed) {
135
+ failed = true
136
+ fail()
137
+ }
138
+ return
139
+ }
140
+ if (tapCount > 0 && t - lastTapTime > MULTI_TAP_GAP_S) tapCount = 0
141
+ failed = false
142
+ gapDeadline = null
143
+ downPos = centroid(pointers)
144
+ downTime = t
145
+ }
146
+
147
+ override fun move(pointers: Map<Int, Point>, t: Double) {
148
+ val origin = downPos ?: return
149
+ if (failed) return
150
+ val c = centroid(pointers)
151
+ if (hypot(c.x - origin.x, c.y - origin.y) > slop) {
152
+ failed = true
153
+ fail()
154
+ }
155
+ }
156
+
157
+ override fun up(pointers: Map<Int, Point>, t: Double, x: Double, y: Double) {
158
+ if (failed || downPos == null) {
159
+ reset()
160
+ return
161
+ }
162
+ if (t - downTime > MAX_TAP_DURATION_S) {
163
+ reset()
164
+ fail()
165
+ return
166
+ }
167
+ tapCount += 1
168
+ lastTapTime = t
169
+ if (tapCount >= nTaps) {
170
+ emit(GestureState.ENDED, "x" to x, "y" to y)
171
+ reset()
172
+ } else {
173
+ gapDeadline = t + MULTI_TAP_GAP_S
174
+ }
175
+ downPos = null
176
+ }
177
+
178
+ override fun cancel(t: Double) = reset()
179
+
180
+ override fun deadline(): Double? = gapDeadline
181
+
182
+ override fun poll(t: Double) {
183
+ val d = gapDeadline ?: return
184
+ if (t >= d) {
185
+ gapDeadline = null
186
+ tapCount = 0
187
+ fail()
188
+ }
189
+ }
190
+
191
+ private fun reset() {
192
+ downPos = null
193
+ if (tapCount >= nTaps) tapCount = 0
194
+ failed = false
195
+ gapDeadline = null
196
+ }
197
+
198
+ private companion object {
199
+ const val MAX_TAP_DURATION_S = 0.4
200
+ const val MULTI_TAP_GAP_S = 0.3
201
+ }
202
+ }
203
+
204
+ class LongPressRecognizer(index: Int, config: GestureConfig, emit: EmitFn) : Recognizer(index, config, emit) {
205
+ private val durationS = config.minDurationMs / 1000.0
206
+ private val slop = config.maxDistance
207
+ private var downPos: Point? = null
208
+ private var pressDeadline: Double? = null
209
+ private var active = false
210
+
211
+ override fun down(pointers: Map<Int, Point>, t: Double) {
212
+ downPos = centroid(pointers)
213
+ pressDeadline = t + durationS
214
+ active = false
215
+ }
216
+
217
+ override fun move(pointers: Map<Int, Point>, t: Double) {
218
+ val origin = downPos ?: return
219
+ val c = centroid(pointers)
220
+ if (hypot(c.x - origin.x, c.y - origin.y) > slop) {
221
+ if (active) emit(GestureState.CANCELLED, "x" to c.x, "y" to c.y)
222
+ reset()
223
+ fail()
224
+ }
225
+ }
226
+
227
+ override fun up(pointers: Map<Int, Point>, t: Double, x: Double, y: Double) {
228
+ if (active) {
229
+ emit(GestureState.ENDED, "x" to x, "y" to y)
230
+ } else if (downPos != null) {
231
+ fail()
232
+ }
233
+ reset()
234
+ }
235
+
236
+ override fun cancel(t: Double) {
237
+ if (active) emit(GestureState.CANCELLED)
238
+ reset()
239
+ }
240
+
241
+ override fun deadline(): Double? = pressDeadline
242
+
243
+ override fun poll(t: Double) {
244
+ val d = pressDeadline ?: return
245
+ val origin = downPos ?: return
246
+ if (active) return
247
+ if (t >= d) {
248
+ active = true
249
+ pressDeadline = null
250
+ emit(GestureState.BEGAN, "x" to origin.x, "y" to origin.y)
251
+ }
252
+ }
253
+
254
+ private fun reset() {
255
+ downPos = null
256
+ pressDeadline = null
257
+ active = false
258
+ }
259
+ }
260
+
261
+ class PanRecognizer(index: Int, config: GestureConfig, emit: EmitFn) : Recognizer(index, config, emit) {
262
+ private val minDistance = config.minDistance
263
+ private val minPointers = max(1, config.minPointers)
264
+ private var origin: Point? = null
265
+ private var anchor: Point? = null
266
+ private val velocity = VelocityEstimator()
267
+ private var lastTranslation = Point(0.0, 0.0)
268
+
269
+ /** Whether the pan has activated (hosts use this to block parent interception). */
270
+ var active = false
271
+ private set
272
+
273
+ override fun down(pointers: Map<Int, Point>, t: Double) {
274
+ if (pointers.size < minPointers) return
275
+ if (origin == null) {
276
+ val c = centroid(pointers)
277
+ origin = c
278
+ velocity.reset()
279
+ velocity.add(c.x, c.y, t)
280
+ } else {
281
+ rebase(pointers)
282
+ }
283
+ }
284
+
285
+ override fun move(pointers: Map<Int, Point>, t: Double) {
286
+ val o = origin ?: return
287
+ if (pointers.size < minPointers) return
288
+ val c = centroid(pointers)
289
+ velocity.add(c.x, c.y, t)
290
+ if (!active) {
291
+ if (hypot(c.x - o.x, c.y - o.y) < minDistance) return
292
+ active = true
293
+ anchor = c
294
+ emit(GestureState.BEGAN, "x" to c.x, "y" to c.y, "pointer_count" to pointers.size)
295
+ return
296
+ }
297
+ val a = anchor ?: return
298
+ val v = velocity.velocity()
299
+ lastTranslation = Point(c.x - a.x, c.y - a.y)
300
+ emit(
301
+ GestureState.CHANGED,
302
+ "x" to c.x,
303
+ "y" to c.y,
304
+ "translation_x" to lastTranslation.x,
305
+ "translation_y" to lastTranslation.y,
306
+ "velocity_x" to v.x,
307
+ "velocity_y" to v.y,
308
+ "pointer_count" to pointers.size,
309
+ )
310
+ }
311
+
312
+ override fun up(pointers: Map<Int, Point>, t: Double, x: Double, y: Double) {
313
+ if (active && pointers.size < minPointers) {
314
+ val v = velocity.velocity()
315
+ val a = anchor ?: Point(x, y)
316
+ emit(
317
+ GestureState.ENDED,
318
+ "x" to x,
319
+ "y" to y,
320
+ "translation_x" to x - a.x,
321
+ "translation_y" to y - a.y,
322
+ "velocity_x" to v.x,
323
+ "velocity_y" to v.y,
324
+ "pointer_count" to pointers.size,
325
+ )
326
+ reset()
327
+ } else if (pointers.isEmpty()) {
328
+ if (!active && origin != null) fail()
329
+ reset()
330
+ } else if (active) {
331
+ rebase(pointers)
332
+ }
333
+ }
334
+
335
+ override fun cancel(t: Double) {
336
+ if (active) emit(GestureState.CANCELLED)
337
+ reset()
338
+ }
339
+
340
+ private fun rebase(pointers: Map<Int, Point>) {
341
+ val a = anchor
342
+ if (!active || a == null) {
343
+ origin = centroid(pointers)
344
+ return
345
+ }
346
+ val c = centroid(pointers)
347
+ anchor = Point(c.x - lastTranslation.x, c.y - lastTranslation.y)
348
+ }
349
+
350
+ private fun reset() {
351
+ origin = null
352
+ anchor = null
353
+ active = false
354
+ velocity.reset()
355
+ lastTranslation = Point(0.0, 0.0)
356
+ }
357
+ }
358
+
359
+ /** Directional flick recognizer; also serves `fling` (adds a pointer-count requirement). */
360
+ class SwipeRecognizer(index: Int, config: GestureConfig, emit: EmitFn) : Recognizer(index, config, emit) {
361
+ private val direction = config.direction
362
+ private val minVelocity = config.minVelocity
363
+ private val nPointers = max(1, config.nPointers)
364
+ private val velocity = VelocityEstimator()
365
+ private var tracking = false
366
+ private var maxPointers = 0
367
+
368
+ override fun down(pointers: Map<Int, Point>, t: Double) {
369
+ if (!tracking) {
370
+ velocity.reset()
371
+ tracking = true
372
+ maxPointers = 0
373
+ }
374
+ maxPointers = max(maxPointers, pointers.size)
375
+ val c = centroid(pointers)
376
+ velocity.add(c.x, c.y, t)
377
+ }
378
+
379
+ override fun move(pointers: Map<Int, Point>, t: Double) {
380
+ if (!tracking) return
381
+ maxPointers = max(maxPointers, pointers.size)
382
+ val c = centroid(pointers)
383
+ velocity.add(c.x, c.y, t)
384
+ }
385
+
386
+ override fun up(pointers: Map<Int, Point>, t: Double, x: Double, y: Double) {
387
+ if (!tracking || pointers.isNotEmpty()) return
388
+ tracking = false
389
+ velocity.add(x, y, t)
390
+ val v = velocity.velocity()
391
+ val speed = hypot(v.x, v.y)
392
+ if (speed < minVelocity || maxPointers < nPointers) {
393
+ fail()
394
+ return
395
+ }
396
+ val resolved = if (abs(v.x) >= abs(v.y)) {
397
+ if (v.x > 0) "right" else "left"
398
+ } else {
399
+ if (v.y > 0) "down" else "up"
400
+ }
401
+ if (direction != "any" && direction != resolved) {
402
+ fail()
403
+ return
404
+ }
405
+ emit(
406
+ GestureState.ENDED,
407
+ "x" to x,
408
+ "y" to y,
409
+ "velocity_x" to v.x,
410
+ "velocity_y" to v.y,
411
+ "direction" to resolved,
412
+ "pointer_count" to maxPointers,
413
+ )
414
+ }
415
+
416
+ override fun cancel(t: Double) {
417
+ tracking = false
418
+ maxPointers = 0
419
+ velocity.reset()
420
+ }
421
+ }
422
+
423
+ class PinchRecognizer(index: Int, config: GestureConfig, emit: EmitFn) : Recognizer(index, config, emit) {
424
+ private var initial: Double? = null
425
+ private var active = false
426
+ private var scale = 1.0
427
+
428
+ private fun span(pointers: Map<Int, Point>): Double? {
429
+ if (pointers.size < 2) return null
430
+ val pts = pointers.values.take(2)
431
+ return hypot(pts[1].x - pts[0].x, pts[1].y - pts[0].y)
432
+ }
433
+
434
+ override fun down(pointers: Map<Int, Point>, t: Double) {
435
+ val s = span(pointers)
436
+ if (s != null && s > 0 && initial == null) {
437
+ initial = s
438
+ active = true
439
+ val c = centroid(pointers)
440
+ emit(GestureState.BEGAN, "x" to c.x, "y" to c.y, "scale" to 1.0, "pointer_count" to pointers.size)
441
+ }
442
+ }
443
+
444
+ override fun move(pointers: Map<Int, Point>, t: Double) {
445
+ val i = initial ?: return
446
+ if (!active) return
447
+ val s = span(pointers) ?: return
448
+ if (s <= 0) return
449
+ scale = s / i
450
+ val c = centroid(pointers)
451
+ emit(GestureState.CHANGED, "x" to c.x, "y" to c.y, "scale" to scale, "pointer_count" to pointers.size)
452
+ }
453
+
454
+ override fun up(pointers: Map<Int, Point>, t: Double, x: Double, y: Double) {
455
+ if (active && pointers.size < 2) {
456
+ emit(GestureState.ENDED, "x" to x, "y" to y, "scale" to scale, "pointer_count" to pointers.size)
457
+ reset()
458
+ } else if (!active && pointers.isEmpty()) {
459
+ fail()
460
+ }
461
+ }
462
+
463
+ override fun cancel(t: Double) {
464
+ if (active) emit(GestureState.CANCELLED, "scale" to scale)
465
+ reset()
466
+ }
467
+
468
+ private fun reset() {
469
+ initial = null
470
+ active = false
471
+ scale = 1.0
472
+ }
473
+ }
474
+
475
+ class RotationRecognizer(index: Int, config: GestureConfig, emit: EmitFn) : Recognizer(index, config, emit) {
476
+ private var initial: Double? = null
477
+ private var active = false
478
+ private var rotation = 0.0
479
+
480
+ private fun angle(pointers: Map<Int, Point>): Double? {
481
+ if (pointers.size < 2) return null
482
+ val pts = pointers.values.take(2)
483
+ return atan2(pts[1].y - pts[0].y, pts[1].x - pts[0].x)
484
+ }
485
+
486
+ override fun down(pointers: Map<Int, Point>, t: Double) {
487
+ val a = angle(pointers)
488
+ if (a != null && initial == null) {
489
+ initial = a
490
+ active = true
491
+ val c = centroid(pointers)
492
+ emit(GestureState.BEGAN, "x" to c.x, "y" to c.y, "rotation" to 0.0, "pointer_count" to pointers.size)
493
+ }
494
+ }
495
+
496
+ override fun move(pointers: Map<Int, Point>, t: Double) {
497
+ val i = initial ?: return
498
+ if (!active) return
499
+ val a = angle(pointers) ?: return
500
+ var delta = a - i
501
+ while (delta > Math.PI) delta -= 2 * Math.PI
502
+ while (delta < -Math.PI) delta += 2 * Math.PI
503
+ rotation = delta
504
+ val c = centroid(pointers)
505
+ emit(GestureState.CHANGED, "x" to c.x, "y" to c.y, "rotation" to rotation, "pointer_count" to pointers.size)
506
+ }
507
+
508
+ override fun up(pointers: Map<Int, Point>, t: Double, x: Double, y: Double) {
509
+ if (active && pointers.size < 2) {
510
+ emit(GestureState.ENDED, "x" to x, "y" to y, "rotation" to rotation, "pointer_count" to pointers.size)
511
+ reset()
512
+ } else if (!active && pointers.isEmpty()) {
513
+ fail()
514
+ }
515
+ }
516
+
517
+ override fun cancel(t: Double) {
518
+ if (active) emit(GestureState.CANCELLED, "rotation" to rotation)
519
+ reset()
520
+ }
521
+
522
+ private fun reset() {
523
+ initial = null
524
+ active = false
525
+ rotation = 0.0
526
+ }
527
+ }
@@ -0,0 +1,73 @@
1
+ package com.pythonnative.runtime.modules
2
+
3
+ import android.app.Activity
4
+ import android.content.Intent
5
+ import com.pythonnative.runtime.bridge.PNRegistry
6
+
7
+ /**
8
+ * Registers the built-in modules and forwards activity callbacks to
9
+ * the modules that need them. The app's `MainActivity` calls the
10
+ * `on*` hooks; nothing here touches Python directly.
11
+ */
12
+ object BuiltinModules {
13
+ val host = HostModule()
14
+ val device = DeviceModule()
15
+ val alert = AlertModule()
16
+ val storage = StorageModule()
17
+ val secureStore = SecureStoreModule()
18
+ val clipboard = ClipboardModule()
19
+ val share = ShareModule()
20
+ val linking = LinkingModule()
21
+ val haptics = HapticsModule()
22
+ val battery = BatteryModule()
23
+ val netInfo = NetInfoModule()
24
+ val appState = AppStateModule()
25
+ val permissions = PermissionsModule()
26
+ val notifications = NotificationsModule()
27
+ val camera = CameraModule()
28
+ val location = LocationModule()
29
+ val biometrics = BiometricsModule()
30
+
31
+ private val all: List<NativeModule> = listOf(
32
+ host, device, alert, storage, secureStore, clipboard, share, linking, haptics,
33
+ battery, netInfo, appState, permissions, notifications, camera, location, biometrics,
34
+ )
35
+
36
+ fun register(registry: PNRegistry) {
37
+ for (module in all) registry.registerModule { module }
38
+ }
39
+
40
+ /** Called from `PNBridge.setContext`; starts the observers that need a context. */
41
+ fun onContextAttached(activity: Activity) {
42
+ battery.attach(activity)
43
+ netInfo.attach(activity)
44
+ activity.intent?.dataString?.let { linking.onDeepLink(it) }
45
+ }
46
+
47
+ /** Forward `Activity.onActivityResult`. Returns `true` when a module consumed it. */
48
+ fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean =
49
+ camera.onActivityResult(requestCode, resultCode, data) || share.onActivityResult(requestCode)
50
+
51
+ /** Forward `Activity.onRequestPermissionsResult`. Returns `true` when a module consumed it. */
52
+ fun onRequestPermissionsResult(
53
+ requestCode: Int,
54
+ @Suppress("UNUSED_PARAMETER") permissionNames: Array<out String>,
55
+ grantResults: IntArray,
56
+ ): Boolean = permissions.onRequestPermissionsResult(requestCode, grantResults)
57
+
58
+ /** Forward `Activity.onNewIntent` (deep links). */
59
+ fun onNewIntent(intent: Intent?) {
60
+ intent?.dataString?.let { linking.onDeepLink(it) }
61
+ }
62
+
63
+ /** Forward activity lifecycle transitions to the `AppState` module. */
64
+ fun onActivityResumed() = appState.transition("active")
65
+ fun onActivityPaused() = appState.transition("inactive")
66
+ fun onActivityStopped() = appState.transition("background")
67
+
68
+ /** Release observers when the activity is destroyed. */
69
+ fun onActivityDestroyed(activity: Activity) {
70
+ battery.detach(activity)
71
+ netInfo.detach(activity)
72
+ }
73
+ }