pythonnative 0.36.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.
- pythonnative/__init__.py +6 -4
- pythonnative/_ios_log.py +1 -1
- pythonnative/alerts.py +34 -88
- pythonnative/bootstrap.py +90 -0
- pythonnative/bridge/__init__.py +306 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +164 -0
- pythonnative/bridge/fake.py +236 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/components/layout.py +65 -3
- pythonnative/gestures.py +13 -10
- pythonnative/hooks.py +41 -26
- pythonnative/hosts/__init__.py +13 -36
- pythonnative/hosts/native.py +348 -0
- pythonnative/images.py +6 -5
- pythonnative/native_modules/__init__.py +30 -29
- pythonnative/native_modules/app_state.py +10 -6
- pythonnative/native_modules/battery.py +20 -84
- pythonnative/native_modules/biometrics.py +11 -124
- pythonnative/native_modules/camera.py +15 -291
- pythonnative/native_modules/clipboard.py +14 -109
- pythonnative/native_modules/desktop.py +322 -0
- pythonnative/native_modules/file_system.py +15 -21
- pythonnative/native_modules/haptics.py +19 -129
- pythonnative/native_modules/linking.py +24 -137
- pythonnative/native_modules/location.py +28 -180
- pythonnative/native_modules/net_info.py +25 -135
- pythonnative/native_modules/notifications.py +36 -257
- pythonnative/native_modules/permissions.py +20 -184
- pythonnative/native_modules/registry.py +513 -0
- pythonnative/native_modules/secure_store.py +18 -155
- pythonnative/native_modules/share.py +8 -113
- pythonnative/native_views/__init__.py +67 -86
- pythonnative/native_views/bridge_backend.py +264 -0
- pythonnative/platform.py +19 -15
- pythonnative/platform_metrics.py +3 -3
- pythonnative/project/android.py +0 -3
- pythonnative/project/builder.py +38 -16
- pythonnative/project/config.py +12 -2
- pythonnative/project/plugins.py +366 -0
- pythonnative/py.typed +0 -0
- pythonnative/runtime.py +35 -203
- pythonnative/sdk/__init__.py +48 -33
- pythonnative/sdk/_components.py +57 -76
- pythonnative/storage.py +18 -243
- pythonnative/templates/android_template/app/build.gradle +6 -9
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +34 -166
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -151
- pythonnative/templates/android_template/pythonnative/build.gradle +48 -0
- pythonnative/templates/android_template/pythonnative/src/main/AndroidManifest.xml +11 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PNBridge.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +249 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +32 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +115 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +35 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +33 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +156 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +87 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +245 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +179 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +97 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +135 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +88 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/Navigator.kt +116 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +223 -0
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNAccessibilityDelegate.kt +5 -4
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNBorderDrawable.kt +12 -3
- pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNFrameLayout.kt +9 -10
- pythonnative/templates/android_template/{app/src/main/java/com/pythonnative/android_template → pythonnative/src/main/java/com/pythonnative/runtime/views}/PNVirtualListView.java +9 -12
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/templates/android_template/pythonnative/src/test/java/com/pythonnative/runtime/PromiseTest.kt +75 -0
- pythonnative/templates/android_template/settings.gradle +1 -0
- pythonnative/templates/ios_template/PythonNativeKit/Package.swift +21 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Animation/PNAnimator.swift +301 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNBridge.swift +191 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNRegistry.swift +140 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNTransaction.swift +165 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNButtonManagers.swift +206 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNImageManager.swift +221 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextInputManager.swift +289 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +257 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/HostModule.swift +158 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PNNativeModule.swift +125 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PermissionModules.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Screens/PNViewController.swift +251 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNManagerTests.swift +124 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNModuleTests.swift +98 -0
- pythonnative/templates/ios_template/PythonNativeKit/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +8 -50
- pythonnative/templates/ios_template/ios_template/Info.plist +4 -4
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +38 -46
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +11 -16
- pythonnative/templates/ios_template/ios_template/ViewController.swift +12 -142
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +47 -1
- pythonnative/utils.py +4 -82
- pythonnative/virtual_rows.py +3 -3
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/METADATA +3 -5
- pythonnative-0.37.0.dist-info/RECORD +263 -0
- pythonnative/hosts/android.py +0 -269
- pythonnative/hosts/ios.py +0 -404
- pythonnative/native_views/android.py +0 -3702
- pythonnative/native_views/ios.py +0 -5253
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -43
- pythonnative-0.36.0.dist-info/RECORD +0 -155
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.37.0.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
"""Native API modules for device capabilities.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
Cross-platform Python interfaces to common device APIs. Every module
|
|
4
|
+
here is a thin facade over a *native module*: a Swift class in
|
|
5
|
+
``PythonNativeKit`` and a Kotlin class in the ``pythonnative`` Gradle
|
|
6
|
+
module registered under the same name (``"Camera"``, ``"Haptics"``,
|
|
7
|
+
...). Facades reach them through
|
|
8
|
+
[`native_module`][pythonnative.native_modules.registry.native_module]
|
|
9
|
+
and the bridge described in ``docs/concepts/bridge.md``; there is no
|
|
10
|
+
Python-side Objective-C or JNI anywhere in this package.
|
|
11
|
+
|
|
12
|
+
On a desktop machine (``pn preview``, unit tests) the same names
|
|
13
|
+
resolve to plain Python implementations in
|
|
14
|
+
[`pythonnative.native_modules.desktop`][pythonnative.native_modules.desktop]
|
|
15
|
+
with safe defaults (in-memory buffers, ``"unknown"`` states, no-op
|
|
16
|
+
feedback), so the same code stays runnable off device. Third-party
|
|
17
|
+
packages ship their own native modules the same way; see
|
|
18
|
+
``docs/guides/native-modules.md``.
|
|
9
19
|
|
|
10
20
|
Hardware / media:
|
|
11
21
|
|
|
@@ -43,8 +53,6 @@ Reactive state (with hooks):
|
|
|
43
53
|
[`use_net_info`][pythonnative.use_net_info]: connectivity.
|
|
44
54
|
"""
|
|
45
55
|
|
|
46
|
-
from typing import Any, Sequence
|
|
47
|
-
|
|
48
56
|
from .app_state import AppState, use_app_state
|
|
49
57
|
from .battery import Battery
|
|
50
58
|
from .biometrics import Biometrics
|
|
@@ -57,46 +65,39 @@ from .location import Location
|
|
|
57
65
|
from .net_info import NetInfo, use_net_info
|
|
58
66
|
from .notifications import Notifications
|
|
59
67
|
from .permissions import Permissions
|
|
68
|
+
from .registry import (
|
|
69
|
+
BridgeModule,
|
|
70
|
+
NativeModule,
|
|
71
|
+
NativeModuleError,
|
|
72
|
+
PythonModule,
|
|
73
|
+
native_module,
|
|
74
|
+
register_python_module,
|
|
75
|
+
)
|
|
60
76
|
from .secure_store import SecureStore
|
|
61
77
|
from .share import Share
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
def dispatch_activity_result(request_code: int, result_code: int, data: Any) -> bool:
|
|
65
|
-
"""Route an Android ``onActivityResult`` to whichever module is waiting on it.
|
|
66
|
-
|
|
67
|
-
Called by the screen host. Returns ``True`` when a pending request
|
|
68
|
-
consumed the result.
|
|
69
|
-
"""
|
|
70
|
-
from .camera import deliver_android_activity_result
|
|
71
|
-
|
|
72
|
-
return deliver_android_activity_result(request_code, result_code, data)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def dispatch_permissions_result(request_code: int, permissions: Sequence[str], grant_results: Sequence[int]) -> bool:
|
|
76
|
-
"""Route an Android ``onRequestPermissionsResult`` to the pending ``Permissions.request``."""
|
|
77
|
-
from .permissions import deliver_android_permission_result
|
|
78
|
-
|
|
79
|
-
return deliver_android_permission_result(request_code, permissions, grant_results)
|
|
80
|
-
|
|
81
|
-
|
|
82
79
|
__all__ = [
|
|
83
80
|
"AppState",
|
|
84
81
|
"Battery",
|
|
85
82
|
"Biometrics",
|
|
83
|
+
"BridgeModule",
|
|
86
84
|
"Camera",
|
|
87
85
|
"Clipboard",
|
|
88
86
|
"FileSystem",
|
|
89
87
|
"Haptics",
|
|
90
88
|
"Linking",
|
|
91
89
|
"Location",
|
|
90
|
+
"NativeModule",
|
|
91
|
+
"NativeModuleError",
|
|
92
92
|
"NetInfo",
|
|
93
93
|
"Notifications",
|
|
94
94
|
"Permissions",
|
|
95
|
+
"PythonModule",
|
|
95
96
|
"SecureStore",
|
|
96
97
|
"Share",
|
|
97
98
|
"Vibration",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
99
|
+
"native_module",
|
|
100
|
+
"register_python_module",
|
|
100
101
|
"use_app_state",
|
|
101
102
|
"use_net_info",
|
|
102
103
|
]
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[`AppState`][pythonnative.AppState] exposes the current lifecycle phase
|
|
4
4
|
(``"active"``, ``"inactive"``, or ``"background"``) and lets you
|
|
5
|
-
subscribe to transitions. The native
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
subscribe to transitions. The native ``AppState`` module pushes a
|
|
6
|
+
``change`` event on every transition; off device, tests drive the same
|
|
7
|
+
path through
|
|
8
|
+
[`dispatch_app_state`][pythonnative.native_modules.app_state.dispatch_app_state].
|
|
9
9
|
|
|
10
10
|
Prefer the [`use_app_state`][pythonnative.use_app_state] hook inside
|
|
11
11
|
components; use the imperative API for non-UI code.
|
|
@@ -27,6 +27,7 @@ from typing import Callable, List
|
|
|
27
27
|
|
|
28
28
|
from .. import diagnostics
|
|
29
29
|
from ..hooks import use_effect, use_state
|
|
30
|
+
from .registry import on_event
|
|
30
31
|
|
|
31
32
|
AppStateStatus = str # "active" | "inactive" | "background"
|
|
32
33
|
|
|
@@ -64,8 +65,8 @@ class AppState:
|
|
|
64
65
|
def dispatch_app_state(state: AppStateStatus) -> None:
|
|
65
66
|
"""Update the current state and notify every listener.
|
|
66
67
|
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
Unknown values are ignored so a misbehaving host can't push garbage
|
|
69
|
+
into the tree.
|
|
69
70
|
"""
|
|
70
71
|
global _current
|
|
71
72
|
if state not in _VALID or state == _current:
|
|
@@ -78,6 +79,9 @@ def dispatch_app_state(state: AppStateStatus) -> None:
|
|
|
78
79
|
diagnostics.swallowed("app_state.dispatch_app_state")
|
|
79
80
|
|
|
80
81
|
|
|
82
|
+
on_event("AppState", "change", lambda payload: dispatch_app_state(str(payload)))
|
|
83
|
+
|
|
84
|
+
|
|
81
85
|
def use_app_state() -> AppStateStatus:
|
|
82
86
|
"""Subscribe a component to [`AppState`][pythonnative.AppState].
|
|
83
87
|
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
[`Battery`][pythonnative.Battery] reports the current charge fraction
|
|
4
4
|
(``0.0``–``1.0``, or ``-1.0`` when unknown) and charging state, and
|
|
5
|
-
lets you subscribe to changes
|
|
5
|
+
lets you subscribe to changes. The native ``Battery`` module pushes a
|
|
6
|
+
``change`` event with ``{"level", "state"}``; off device, tests drive
|
|
7
|
+
the same path through
|
|
6
8
|
[`dispatch_battery`][pythonnative.native_modules.battery.dispatch_battery].
|
|
7
9
|
"""
|
|
8
10
|
|
|
9
11
|
from __future__ import annotations
|
|
10
12
|
|
|
11
|
-
from typing import Callable, Dict, List
|
|
13
|
+
from typing import Any, Callable, Dict, List
|
|
12
14
|
|
|
13
15
|
from .. import diagnostics
|
|
14
|
-
from
|
|
16
|
+
from .registry import native_module, on_event
|
|
15
17
|
|
|
16
18
|
BatteryState = str # "unknown" | "unplugged" | "charging" | "full"
|
|
17
19
|
|
|
@@ -24,20 +26,20 @@ class Battery:
|
|
|
24
26
|
@staticmethod
|
|
25
27
|
def get_level() -> float:
|
|
26
28
|
"""Return the charge fraction in ``[0, 1]`` (``-1.0`` if unknown)."""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
31
|
-
return -1.0
|
|
29
|
+
try:
|
|
30
|
+
level = float(native_module("Battery").call("get_level"))
|
|
31
|
+
except Exception:
|
|
32
|
+
return -1.0
|
|
33
|
+
return level if 0.0 <= level <= 1.0 else -1.0
|
|
32
34
|
|
|
33
35
|
@staticmethod
|
|
34
36
|
def get_state() -> BatteryState:
|
|
35
37
|
"""Return ``"charging"`` / ``"full"`` / ``"unplugged"`` / ``"unknown"``."""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
40
|
-
return "unknown"
|
|
38
|
+
try:
|
|
39
|
+
state = str(native_module("Battery").call("get_state") or "unknown")
|
|
40
|
+
except Exception:
|
|
41
|
+
return "unknown"
|
|
42
|
+
return state if state in ("unknown", "unplugged", "charging", "full") else "unknown"
|
|
41
43
|
|
|
42
44
|
@staticmethod
|
|
43
45
|
def add_listener(callback: Callable[[Dict[str, object]], None]) -> Callable[[], None]:
|
|
@@ -57,7 +59,7 @@ class Battery:
|
|
|
57
59
|
|
|
58
60
|
|
|
59
61
|
def dispatch_battery(level: float, state: BatteryState) -> None:
|
|
60
|
-
"""Notify listeners of a battery change
|
|
62
|
+
"""Notify listeners of a battery change."""
|
|
61
63
|
payload: Dict[str, object] = {"level": level, "state": state}
|
|
62
64
|
for listener in list(_listeners):
|
|
63
65
|
try:
|
|
@@ -66,75 +68,9 @@ def dispatch_battery(level: float, state: BatteryState) -> None:
|
|
|
66
68
|
diagnostics.swallowed("battery.dispatch_battery")
|
|
67
69
|
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _ios_device() -> object:
|
|
75
|
-
from rubicon.objc import ObjCClass
|
|
76
|
-
|
|
77
|
-
device = ObjCClass("UIDevice").currentDevice
|
|
78
|
-
try:
|
|
79
|
-
device.setBatteryMonitoringEnabled_(True)
|
|
80
|
-
except Exception:
|
|
81
|
-
diagnostics.swallowed("battery._ios_device")
|
|
82
|
-
return device
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _ios_level() -> float:
|
|
86
|
-
try:
|
|
87
|
-
level = float(_ios_device().batteryLevel)
|
|
88
|
-
return level if level >= 0 else -1.0
|
|
89
|
-
except Exception:
|
|
90
|
-
return -1.0
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def _ios_state() -> BatteryState:
|
|
94
|
-
try:
|
|
95
|
-
# UIDeviceBatteryState: 0 unknown, 1 unplugged, 2 charging, 3 full
|
|
96
|
-
return {0: "unknown", 1: "unplugged", 2: "charging", 3: "full"}.get(int(_ios_device().batteryState), "unknown")
|
|
97
|
-
except Exception:
|
|
98
|
-
return "unknown"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
# ======================================================================
|
|
102
|
-
# Android: BatteryManager
|
|
103
|
-
# ======================================================================
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def _android_level() -> float:
|
|
107
|
-
try:
|
|
108
|
-
from java import jclass
|
|
109
|
-
|
|
110
|
-
from ..utils import get_android_context
|
|
111
|
-
|
|
112
|
-
ctx = get_android_context()
|
|
113
|
-
Context = jclass("android.content.Context")
|
|
114
|
-
bm = ctx.getSystemService(Context.BATTERY_SERVICE)
|
|
115
|
-
BatteryManager = jclass("android.os.BatteryManager")
|
|
116
|
-
pct = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
|
|
117
|
-
return (pct / 100.0) if pct is not None and pct >= 0 else -1.0
|
|
118
|
-
except Exception:
|
|
119
|
-
return -1.0
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def _android_state() -> BatteryState:
|
|
123
|
-
try:
|
|
124
|
-
from java import jclass
|
|
71
|
+
def _on_native_change(payload: Any) -> None:
|
|
72
|
+
if isinstance(payload, dict):
|
|
73
|
+
dispatch_battery(float(payload.get("level", -1.0)), str(payload.get("state", "unknown")))
|
|
125
74
|
|
|
126
|
-
from ..utils import get_android_context
|
|
127
75
|
|
|
128
|
-
|
|
129
|
-
Context = jclass("android.content.Context")
|
|
130
|
-
bm = ctx.getSystemService(Context.BATTERY_SERVICE)
|
|
131
|
-
BatteryManager = jclass("android.os.BatteryManager")
|
|
132
|
-
status = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_STATUS)
|
|
133
|
-
return {
|
|
134
|
-
BatteryManager.BATTERY_STATUS_CHARGING: "charging",
|
|
135
|
-
BatteryManager.BATTERY_STATUS_FULL: "full",
|
|
136
|
-
BatteryManager.BATTERY_STATUS_DISCHARGING: "unplugged",
|
|
137
|
-
BatteryManager.BATTERY_STATUS_NOT_CHARGING: "unplugged",
|
|
138
|
-
}.get(status, "unknown")
|
|
139
|
-
except Exception:
|
|
140
|
-
return "unknown"
|
|
76
|
+
on_event("Battery", "change", _on_native_change)
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[`Biometrics`][pythonnative.Biometrics] gates an action behind the
|
|
4
4
|
device's biometric hardware via ``LAContext`` (iOS) and
|
|
5
|
-
``BiometricPrompt`` (Android)
|
|
5
|
+
``BiometricPrompt`` (Android), both implemented in the native
|
|
6
|
+
``Biometrics`` module.
|
|
6
7
|
|
|
7
8
|
``is_available`` is synchronous; ``authenticate`` is a coroutine that
|
|
8
9
|
presents the system prompt and resolves to ``True`` on success or
|
|
@@ -20,14 +21,7 @@ Example:
|
|
|
20
21
|
|
|
21
22
|
from __future__ import annotations
|
|
22
23
|
|
|
23
|
-
import
|
|
24
|
-
from typing import Any, Callable, Dict
|
|
25
|
-
|
|
26
|
-
from .. import diagnostics
|
|
27
|
-
from ..runtime import resolve_future
|
|
28
|
-
from ..utils import IS_ANDROID, IS_IOS
|
|
29
|
-
|
|
30
|
-
_pending: Dict[int, Any] = {}
|
|
24
|
+
from .registry import native_module
|
|
31
25
|
|
|
32
26
|
|
|
33
27
|
class Biometrics:
|
|
@@ -36,122 +30,15 @@ class Biometrics:
|
|
|
36
30
|
@staticmethod
|
|
37
31
|
def is_available() -> bool:
|
|
38
32
|
"""Return ``True`` when biometric auth can be attempted."""
|
|
39
|
-
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
return False
|
|
33
|
+
try:
|
|
34
|
+
return bool(native_module("Biometrics").call("is_available"))
|
|
35
|
+
except Exception:
|
|
36
|
+
return False
|
|
44
37
|
|
|
45
38
|
@staticmethod
|
|
46
39
|
async def authenticate(reason: str = "Authenticate") -> bool:
|
|
47
40
|
"""Present the biometric prompt; resolve ``True`` on success."""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
resolve_future(future, ok)
|
|
53
|
-
|
|
54
|
-
if IS_IOS:
|
|
55
|
-
_ios_authenticate(reason, _done)
|
|
56
|
-
elif IS_ANDROID:
|
|
57
|
-
_android_authenticate(reason, _done)
|
|
58
|
-
else:
|
|
59
|
-
_done(False)
|
|
60
|
-
|
|
61
|
-
return await future
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# ======================================================================
|
|
65
|
-
# iOS: LAContext
|
|
66
|
-
# ======================================================================
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def _ios_context() -> Any:
|
|
70
|
-
from rubicon.objc import ObjCClass
|
|
71
|
-
|
|
72
|
-
return ObjCClass("LAContext").alloc().init()
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def _ios_available() -> bool:
|
|
76
|
-
try:
|
|
77
|
-
# LAPolicyDeviceOwnerAuthenticationWithBiometrics == 1
|
|
78
|
-
return bool(_ios_context().canEvaluatePolicy_error_(1, None))
|
|
79
|
-
except Exception:
|
|
80
|
-
return False
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def _ios_authenticate(reason: str, on_done: Callable[[bool], None]) -> None:
|
|
84
|
-
try:
|
|
85
|
-
from rubicon.objc import Block, ObjCClass
|
|
86
|
-
|
|
87
|
-
context = _ios_context()
|
|
88
|
-
token = id(context)
|
|
89
|
-
_pending[token] = context
|
|
90
|
-
|
|
91
|
-
def _reply(success: bool, error: Any) -> None:
|
|
92
|
-
del error
|
|
93
|
-
_pending.pop(token, None)
|
|
94
|
-
try:
|
|
95
|
-
on_done(bool(success))
|
|
96
|
-
except Exception:
|
|
97
|
-
diagnostics.swallowed("biometrics._ios_authenticate._reply")
|
|
98
|
-
|
|
99
|
-
context.evaluatePolicy_localizedReason_reply_(1, reason, Block(_reply, None, bool, ObjCClass("NSError")))
|
|
100
|
-
except Exception:
|
|
101
|
-
on_done(False)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# ======================================================================
|
|
105
|
-
# Android: BiometricPrompt
|
|
106
|
-
# ======================================================================
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def _android_available() -> bool:
|
|
110
|
-
try:
|
|
111
|
-
from java import jclass
|
|
112
|
-
|
|
113
|
-
from ..utils import get_android_context
|
|
114
|
-
|
|
115
|
-
BiometricManager = jclass("androidx.biometric.BiometricManager")
|
|
116
|
-
manager = BiometricManager.from_(get_android_context())
|
|
117
|
-
Authenticators = jclass("androidx.biometric.BiometricManager$Authenticators")
|
|
118
|
-
result = manager.canAuthenticate(Authenticators.BIOMETRIC_WEAK)
|
|
119
|
-
return result == BiometricManager.BIOMETRIC_SUCCESS
|
|
120
|
-
except Exception:
|
|
121
|
-
return False
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def _android_authenticate(reason: str, on_done: Callable[[bool], None]) -> None:
|
|
125
|
-
try:
|
|
126
|
-
from java import dynamic_proxy, jclass
|
|
127
|
-
|
|
128
|
-
from ..utils import get_android_context
|
|
129
|
-
|
|
130
|
-
ctx = get_android_context()
|
|
131
|
-
FragmentActivity = jclass("androidx.fragment.app.FragmentActivity")
|
|
132
|
-
if not FragmentActivity.isInstance(ctx):
|
|
133
|
-
on_done(False)
|
|
134
|
-
return
|
|
135
|
-
|
|
136
|
-
BiometricPrompt = jclass("androidx.biometric.BiometricPrompt")
|
|
137
|
-
ContextCompat = jclass("androidx.core.content.ContextCompat")
|
|
138
|
-
executor = ContextCompat.getMainExecutor(ctx)
|
|
139
|
-
|
|
140
|
-
class _Callback(dynamic_proxy(BiometricPrompt.AuthenticationCallback)): # type: ignore[misc]
|
|
141
|
-
def onAuthenticationSucceeded(self, result: Any) -> None: # noqa: N802, ARG002
|
|
142
|
-
on_done(True)
|
|
143
|
-
|
|
144
|
-
def onAuthenticationError(self, code: int, message: Any) -> None: # noqa: N802, ARG002
|
|
145
|
-
on_done(False)
|
|
146
|
-
|
|
147
|
-
def onAuthenticationFailed(self) -> None: # noqa: N802
|
|
148
|
-
# Fired on a non-matching attempt; the prompt stays up,
|
|
149
|
-
# so don't resolve yet.
|
|
150
|
-
pass
|
|
151
|
-
|
|
152
|
-
prompt = BiometricPrompt(ctx, executor, _Callback())
|
|
153
|
-
Builder = jclass("androidx.biometric.BiometricPrompt$PromptInfo$Builder")
|
|
154
|
-
info = Builder().setTitle(reason).setNegativeButtonText("Cancel").build()
|
|
155
|
-
prompt.authenticate(info)
|
|
156
|
-
except Exception:
|
|
157
|
-
on_done(False)
|
|
41
|
+
try:
|
|
42
|
+
return bool(await native_module("Biometrics").call_async("authenticate", reason=reason))
|
|
43
|
+
except Exception:
|
|
44
|
+
return False
|