pythonnative 0.36.0__py3-none-any.whl → 0.38.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 +14 -4
- pythonnative/_ios_log.py +1 -1
- pythonnative/alerts.py +24 -86
- 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/cli/pn.py +96 -10
- pythonnative/components/_base.py +31 -0
- pythonnative/components/controls.py +19 -16
- pythonnative/components/layout.py +70 -11
- pythonnative/components/lists.py +6 -6
- pythonnative/gestures.py +40 -16
- 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 +49 -29
- pythonnative/native_modules/app_state.py +10 -6
- pythonnative/native_modules/battery.py +22 -87
- pythonnative/native_modules/biometrics.py +15 -130
- pythonnative/native_modules/camera.py +24 -296
- pythonnative/native_modules/clipboard.py +14 -111
- pythonnative/native_modules/desktop.py +322 -0
- pythonnative/native_modules/file_system.py +90 -187
- pythonnative/native_modules/haptics.py +29 -135
- pythonnative/native_modules/linking.py +29 -141
- pythonnative/native_modules/location.py +32 -186
- pythonnative/native_modules/net_info.py +22 -135
- pythonnative/native_modules/notifications.py +37 -265
- pythonnative/native_modules/permissions.py +78 -194
- pythonnative/native_modules/registry.py +513 -0
- pythonnative/native_modules/secure_store.py +23 -164
- pythonnative/native_modules/share.py +9 -114
- pythonnative/native_views/__init__.py +67 -86
- pythonnative/native_views/bridge_backend.py +264 -0
- pythonnative/navigation/__init__.py +2 -1
- pythonnative/navigation/hooks.py +46 -7
- pythonnative/navigation/state.py +30 -9
- pythonnative/platform.py +19 -15
- pythonnative/platform_metrics.py +3 -3
- pythonnative/project/android.py +23 -3
- pythonnative/project/builder.py +99 -48
- pythonnative/project/config.py +96 -26
- pythonnative/project/deps.py +703 -0
- pythonnative/project/doctor.py +53 -4
- pythonnative/project/plugins.py +366 -0
- pythonnative/project/runtime_assets.py +13 -14
- 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/style.py +94 -44
- pythonnative/templates/android_template/app/build.gradle +11 -11
- 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/build.gradle +7 -4
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +1 -1
- 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 +99 -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 +266 -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 +41 -48
- 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 +82 -10
- pythonnative/utils.py +26 -109
- pythonnative/virtual_rows.py +13 -23
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/METADATA +10 -12
- pythonnative-0.38.0.dist-info/RECORD +264 -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.38.0.dist-info}/WHEEL +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/entry_points.txt +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/licenses/LICENSE +0 -0
- {pythonnative-0.36.0.dist-info → pythonnative-0.38.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
"""Battery level and charging state.
|
|
2
2
|
|
|
3
3
|
[`Battery`][pythonnative.Battery] reports the current charge fraction
|
|
4
|
-
(``0.0
|
|
5
|
-
lets you subscribe to changes
|
|
4
|
+
(``0.0`` to ``1.0``, or ``-1.0`` when the platform doesn't know) and
|
|
5
|
+
charging state, and lets you subscribe to changes. Both getters read a
|
|
6
|
+
value the OS already holds, so they are synchronous. The native
|
|
7
|
+
``Battery`` module pushes a ``change`` event with ``{"level", "state"}``;
|
|
8
|
+
off device, tests drive the same path through
|
|
6
9
|
[`dispatch_battery`][pythonnative.native_modules.battery.dispatch_battery].
|
|
7
10
|
"""
|
|
8
11
|
|
|
9
12
|
from __future__ import annotations
|
|
10
13
|
|
|
11
|
-
from typing import Callable, Dict, List
|
|
14
|
+
from typing import Any, Callable, Dict, List
|
|
12
15
|
|
|
13
16
|
from .. import diagnostics
|
|
14
|
-
from
|
|
17
|
+
from .registry import native_module, on_event
|
|
15
18
|
|
|
16
19
|
BatteryState = str # "unknown" | "unplugged" | "charging" | "full"
|
|
17
20
|
|
|
@@ -19,25 +22,23 @@ _listeners: List[Callable[[Dict[str, object]], None]] = []
|
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
class Battery:
|
|
22
|
-
"""Battery interface (synchronous getters + change listener).
|
|
25
|
+
"""Battery interface (synchronous getters + change listener).
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
NativeModuleError: If the native module fails.
|
|
29
|
+
"""
|
|
23
30
|
|
|
24
31
|
@staticmethod
|
|
25
32
|
def get_level() -> float:
|
|
26
|
-
"""Return the charge fraction in ``[0, 1]
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if IS_ANDROID:
|
|
30
|
-
return _android_level()
|
|
31
|
-
return -1.0
|
|
33
|
+
"""Return the charge fraction in ``[0, 1]``, or ``-1.0`` when the platform can't report it."""
|
|
34
|
+
level = float(native_module("Battery").call("get_level"))
|
|
35
|
+
return level if 0.0 <= level <= 1.0 else -1.0
|
|
32
36
|
|
|
33
37
|
@staticmethod
|
|
34
38
|
def get_state() -> BatteryState:
|
|
35
39
|
"""Return ``"charging"`` / ``"full"`` / ``"unplugged"`` / ``"unknown"``."""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if IS_ANDROID:
|
|
39
|
-
return _android_state()
|
|
40
|
-
return "unknown"
|
|
40
|
+
state = str(native_module("Battery").call("get_state") or "unknown")
|
|
41
|
+
return state if state in ("unknown", "unplugged", "charging", "full") else "unknown"
|
|
41
42
|
|
|
42
43
|
@staticmethod
|
|
43
44
|
def add_listener(callback: Callable[[Dict[str, object]], None]) -> Callable[[], None]:
|
|
@@ -57,7 +58,7 @@ class Battery:
|
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
def dispatch_battery(level: float, state: BatteryState) -> None:
|
|
60
|
-
"""Notify listeners of a battery change
|
|
61
|
+
"""Notify listeners of a battery change."""
|
|
61
62
|
payload: Dict[str, object] = {"level": level, "state": state}
|
|
62
63
|
for listener in list(_listeners):
|
|
63
64
|
try:
|
|
@@ -66,75 +67,9 @@ def dispatch_battery(level: float, state: BatteryState) -> None:
|
|
|
66
67
|
diagnostics.swallowed("battery.dispatch_battery")
|
|
67
68
|
|
|
68
69
|
|
|
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
|
|
70
|
+
def _on_native_change(payload: Any) -> None:
|
|
71
|
+
if isinstance(payload, dict):
|
|
72
|
+
dispatch_battery(float(payload.get("level", -1.0)), str(payload.get("state", "unknown")))
|
|
125
73
|
|
|
126
|
-
from ..utils import get_android_context
|
|
127
74
|
|
|
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"
|
|
75
|
+
on_event("Battery", "change", _on_native_change)
|
|
@@ -2,11 +2,12 @@
|
|
|
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
|
-
``is_available`` is synchronous; ``authenticate``
|
|
8
|
-
presents the system prompt and resolves to ``True``
|
|
9
|
-
``False``
|
|
8
|
+
``is_available`` is synchronous (a capability lookup); ``authenticate``
|
|
9
|
+
is a coroutine that presents the system prompt and resolves to ``True``
|
|
10
|
+
on success or ``False`` when the user fails or cancels the prompt.
|
|
10
11
|
|
|
11
12
|
Example:
|
|
12
13
|
```python
|
|
@@ -20,138 +21,22 @@ 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:
|
|
34
|
-
"""Biometric authentication interface.
|
|
28
|
+
"""Biometric authentication interface.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
NativeModuleError: If the native module fails.
|
|
32
|
+
"""
|
|
35
33
|
|
|
36
34
|
@staticmethod
|
|
37
35
|
def is_available() -> bool:
|
|
38
|
-
"""Return ``True`` when biometric auth can be attempted."""
|
|
39
|
-
|
|
40
|
-
return _ios_available()
|
|
41
|
-
if IS_ANDROID:
|
|
42
|
-
return _android_available()
|
|
43
|
-
return False
|
|
36
|
+
"""Return ``True`` when biometric auth can be attempted (enrolled hardware; ``False`` on desktop)."""
|
|
37
|
+
return bool(native_module("Biometrics").call("is_available"))
|
|
44
38
|
|
|
45
39
|
@staticmethod
|
|
46
40
|
async def authenticate(reason: str = "Authenticate") -> bool:
|
|
47
|
-
"""Present the biometric prompt; resolve ``True`` on success."""
|
|
48
|
-
|
|
49
|
-
future: asyncio.Future[bool] = loop.create_future()
|
|
50
|
-
|
|
51
|
-
def _done(ok: bool) -> None:
|
|
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
|
+
"""Present the biometric prompt; resolve ``True`` on success, ``False`` on failure or cancel."""
|
|
42
|
+
return bool(await native_module("Biometrics").call_async("authenticate", reason=reason))
|