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,10 +1,10 @@
|
|
|
1
1
|
"""Encrypted key/value storage for secrets (tokens, credentials).
|
|
2
2
|
|
|
3
3
|
[`SecureStore`][pythonnative.SecureStore] persists small string values
|
|
4
|
-
in the iOS Keychain and Android ``EncryptedSharedPreferences
|
|
5
|
-
right place for auth tokens and
|
|
6
|
-
[`AsyncStorage`][pythonnative.AsyncStorage] (plain,
|
|
7
|
-
never hold.
|
|
4
|
+
in the iOS Keychain and Android ``EncryptedSharedPreferences`` (the
|
|
5
|
+
native ``SecureStore`` module), the right place for auth tokens and
|
|
6
|
+
other secrets that [`AsyncStorage`][pythonnative.AsyncStorage] (plain,
|
|
7
|
+
unencrypted) should never hold.
|
|
8
8
|
|
|
9
9
|
All methods are synchronous and return a ``bool`` (writes/deletes) or
|
|
10
10
|
``Optional[str]`` (reads). On desktop the module falls back to an
|
|
@@ -22,12 +22,9 @@ Example:
|
|
|
22
22
|
|
|
23
23
|
from __future__ import annotations
|
|
24
24
|
|
|
25
|
-
from typing import
|
|
25
|
+
from typing import Optional
|
|
26
26
|
|
|
27
|
-
from
|
|
28
|
-
|
|
29
|
-
_SERVICE = "com.pythonnative.securestore"
|
|
30
|
-
_desktop_store: Dict[str, str] = {}
|
|
27
|
+
from .registry import native_module
|
|
31
28
|
|
|
32
29
|
|
|
33
30
|
class SecureStore:
|
|
@@ -36,158 +33,24 @@ class SecureStore:
|
|
|
36
33
|
@staticmethod
|
|
37
34
|
def set_item(key: str, value: str) -> bool:
|
|
38
35
|
"""Store ``value`` under ``key``. Returns ``True`` on success."""
|
|
39
|
-
|
|
40
|
-
return
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
_desktop_store[key] = value
|
|
44
|
-
return True
|
|
36
|
+
try:
|
|
37
|
+
return bool(native_module("SecureStore").call("set_item", key=key, value=value))
|
|
38
|
+
except Exception:
|
|
39
|
+
return False
|
|
45
40
|
|
|
46
41
|
@staticmethod
|
|
47
42
|
def get_item(key: str) -> Optional[str]:
|
|
48
43
|
"""Return the value for ``key``, or ``None`` if absent."""
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return
|
|
53
|
-
return
|
|
44
|
+
try:
|
|
45
|
+
value = native_module("SecureStore").call("get_item", key=key)
|
|
46
|
+
except Exception:
|
|
47
|
+
return None
|
|
48
|
+
return None if value is None else str(value)
|
|
54
49
|
|
|
55
50
|
@staticmethod
|
|
56
51
|
def delete_item(key: str) -> bool:
|
|
57
52
|
"""Delete ``key``. Returns ``True`` if it existed and was removed."""
|
|
58
|
-
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
return _android_delete(key)
|
|
62
|
-
return _desktop_store.pop(key, None) is not None
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# ======================================================================
|
|
66
|
-
# iOS: Security framework (Keychain)
|
|
67
|
-
# ======================================================================
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def _ios_query(key: str) -> Dict[str, object]:
|
|
71
|
-
from rubicon.objc import ObjCClass
|
|
72
|
-
|
|
73
|
-
NSDictionary = ObjCClass("NSMutableDictionary")
|
|
74
|
-
query = NSDictionary.alloc().init()
|
|
75
|
-
query.setObject_forKey_("genp", "class") # kSecClassGenericPassword shorthand
|
|
76
|
-
query.setObject_forKey_(_SERVICE, "svce")
|
|
77
|
-
query.setObject_forKey_(key, "acct")
|
|
78
|
-
return query
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
def _ios_set(key: str, value: str) -> bool:
|
|
82
|
-
try:
|
|
83
|
-
from ctypes import CDLL, util
|
|
84
|
-
|
|
85
|
-
from rubicon.objc import ObjCClass
|
|
86
|
-
|
|
87
|
-
sec = CDLL(util.find_library("Security"))
|
|
88
|
-
query = _ios_query(key)
|
|
89
|
-
sec.SecItemDelete(query.ptr)
|
|
90
|
-
data = ObjCClass("NSString").stringWithString_(value).dataUsingEncoding_(4) # UTF-8
|
|
91
|
-
query.setObject_forKey_(data, "v_Data")
|
|
92
|
-
status = sec.SecItemAdd(query.ptr, None)
|
|
93
|
-
return status == 0
|
|
94
|
-
except Exception:
|
|
95
|
-
return False
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
def _ios_get(key: str) -> Optional[str]:
|
|
99
|
-
try:
|
|
100
|
-
from ctypes import CDLL, byref, c_void_p, util
|
|
101
|
-
|
|
102
|
-
from rubicon.objc import ObjCClass
|
|
103
|
-
|
|
104
|
-
sec = CDLL(util.find_library("Security"))
|
|
105
|
-
query = _ios_query(key)
|
|
106
|
-
query.setObject_forKey_(True, "r_Data")
|
|
107
|
-
query.setObject_forKey_("m_Limit_One", "m_Limit")
|
|
108
|
-
result = c_void_p(0)
|
|
109
|
-
status = sec.SecItemCopyMatching(query.ptr, byref(result))
|
|
110
|
-
if status != 0 or not result.value:
|
|
111
|
-
return None
|
|
112
|
-
data = ObjCClass("NSData").alloc() # placeholder to keep import used
|
|
113
|
-
del data
|
|
114
|
-
ns = ObjCClass("NSString").alloc().initWithData_encoding_(result, 4)
|
|
115
|
-
return str(ns) if ns is not None else None
|
|
116
|
-
except Exception:
|
|
117
|
-
return None
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
def _ios_delete(key: str) -> bool:
|
|
121
|
-
try:
|
|
122
|
-
from ctypes import CDLL, util
|
|
123
|
-
|
|
124
|
-
sec = CDLL(util.find_library("Security"))
|
|
125
|
-
return sec.SecItemDelete(_ios_query(key).ptr) == 0
|
|
126
|
-
except Exception:
|
|
127
|
-
return False
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
# ======================================================================
|
|
131
|
-
# Android: EncryptedSharedPreferences
|
|
132
|
-
# ======================================================================
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def _android_prefs() -> Optional[object]:
|
|
136
|
-
try:
|
|
137
|
-
from java import jclass
|
|
138
|
-
|
|
139
|
-
from ..utils import get_android_context
|
|
140
|
-
|
|
141
|
-
ctx = get_android_context()
|
|
142
|
-
MasterKey = jclass("androidx.security.crypto.MasterKey")
|
|
143
|
-
Builder = jclass("androidx.security.crypto.MasterKey$Builder")
|
|
144
|
-
EncryptedSharedPreferences = jclass("androidx.security.crypto.EncryptedSharedPreferences")
|
|
145
|
-
KeyScheme = jclass("androidx.security.crypto.EncryptedSharedPreferences$PrefKeyEncryptionScheme")
|
|
146
|
-
ValueScheme = jclass("androidx.security.crypto.EncryptedSharedPreferences$PrefValueEncryptionScheme")
|
|
147
|
-
master = Builder(ctx).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build()
|
|
148
|
-
return EncryptedSharedPreferences.create(
|
|
149
|
-
ctx,
|
|
150
|
-
_SERVICE,
|
|
151
|
-
master,
|
|
152
|
-
KeyScheme.AES256_SIV,
|
|
153
|
-
ValueScheme.AES256_GCM,
|
|
154
|
-
)
|
|
155
|
-
except Exception:
|
|
156
|
-
return None
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
def _android_set(key: str, value: str) -> bool:
|
|
160
|
-
prefs = _android_prefs()
|
|
161
|
-
if prefs is None:
|
|
162
|
-
return False
|
|
163
|
-
try:
|
|
164
|
-
editor = prefs.edit()
|
|
165
|
-
editor.putString(key, value)
|
|
166
|
-
return bool(editor.commit())
|
|
167
|
-
except Exception:
|
|
168
|
-
return False
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def _android_get(key: str) -> Optional[str]:
|
|
172
|
-
prefs = _android_prefs()
|
|
173
|
-
if prefs is None:
|
|
174
|
-
return None
|
|
175
|
-
try:
|
|
176
|
-
value = prefs.getString(key, None)
|
|
177
|
-
return str(value) if value is not None else None
|
|
178
|
-
except Exception:
|
|
179
|
-
return None
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def _android_delete(key: str) -> bool:
|
|
183
|
-
prefs = _android_prefs()
|
|
184
|
-
if prefs is None:
|
|
185
|
-
return False
|
|
186
|
-
try:
|
|
187
|
-
if not prefs.contains(key):
|
|
53
|
+
try:
|
|
54
|
+
return bool(native_module("SecureStore").call("delete_item", key=key))
|
|
55
|
+
except Exception:
|
|
188
56
|
return False
|
|
189
|
-
editor = prefs.edit()
|
|
190
|
-
editor.remove(key)
|
|
191
|
-
return bool(editor.commit())
|
|
192
|
-
except Exception:
|
|
193
|
-
return False
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[`Share.share`][pythonnative.Share] is a coroutine that opens
|
|
4
4
|
``UIActivityViewController`` (iOS) or an ``ACTION_SEND`` chooser
|
|
5
|
-
(Android)
|
|
6
|
-
``False`` if they dismiss it.
|
|
5
|
+
(Android) through the native ``Share`` module and resolves to ``True``
|
|
6
|
+
once the user completes a share or ``False`` if they dismiss it.
|
|
7
7
|
|
|
8
8
|
Example:
|
|
9
9
|
```python
|
|
@@ -19,15 +19,9 @@ Example:
|
|
|
19
19
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
|
|
22
|
-
import
|
|
23
|
-
from typing import Any, Callable, Dict, Optional
|
|
22
|
+
from typing import Optional
|
|
24
23
|
|
|
25
|
-
from
|
|
26
|
-
from ..runtime import resolve_future
|
|
27
|
-
from ..utils import IS_ANDROID, IS_IOS
|
|
28
|
-
|
|
29
|
-
# Retain pool so iOS completion handlers aren't collected early.
|
|
30
|
-
_pending: Dict[int, Any] = {}
|
|
24
|
+
from .registry import native_module
|
|
31
25
|
|
|
32
26
|
|
|
33
27
|
class Share:
|
|
@@ -51,106 +45,7 @@ class Share:
|
|
|
51
45
|
``True`` if the user completed a share, ``False`` if they
|
|
52
46
|
cancelled or no share UI is available.
|
|
53
47
|
"""
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
resolve_future(future, ok)
|
|
59
|
-
|
|
60
|
-
if IS_IOS:
|
|
61
|
-
_ios_share(_done, message=message, url=url, title=title)
|
|
62
|
-
elif IS_ANDROID:
|
|
63
|
-
_android_share(_done, message=message, url=url, title=title)
|
|
64
|
-
else:
|
|
65
|
-
_done(False)
|
|
66
|
-
|
|
67
|
-
return await future
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
# ======================================================================
|
|
71
|
-
# iOS: UIActivityViewController
|
|
72
|
-
# ======================================================================
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def _ios_share(
|
|
76
|
-
on_done: Callable[[bool], None],
|
|
77
|
-
message: Optional[str],
|
|
78
|
-
url: Optional[str],
|
|
79
|
-
title: Optional[str],
|
|
80
|
-
) -> None:
|
|
81
|
-
del title
|
|
82
|
-
try:
|
|
83
|
-
from rubicon.objc import Block, ObjCClass
|
|
84
|
-
|
|
85
|
-
items = ObjCClass("NSMutableArray").alloc().init()
|
|
86
|
-
if message:
|
|
87
|
-
items.addObject_(message)
|
|
88
|
-
if url:
|
|
89
|
-
nsurl = ObjCClass("NSURL").URLWithString_(url)
|
|
90
|
-
if nsurl is not None:
|
|
91
|
-
items.addObject_(nsurl)
|
|
92
|
-
|
|
93
|
-
controller = (
|
|
94
|
-
ObjCClass("UIActivityViewController").alloc().initWithActivityItems_applicationActivities_(items, None)
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
token = id(controller)
|
|
98
|
-
_pending[token] = controller
|
|
99
|
-
|
|
100
|
-
def _completion(activity: Any, completed: bool, items_: Any, error: Any) -> None:
|
|
101
|
-
del activity, items_, error
|
|
102
|
-
_pending.pop(token, None)
|
|
103
|
-
try:
|
|
104
|
-
on_done(bool(completed))
|
|
105
|
-
except Exception:
|
|
106
|
-
diagnostics.swallowed("share._ios_share._completion")
|
|
107
|
-
|
|
108
|
-
controller.setCompletionWithItemsHandler_(
|
|
109
|
-
Block(_completion, None, ObjCClass("NSString"), Block, ObjCClass("NSArray"), ObjCClass("NSError"))
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
app = ObjCClass("UIApplication").sharedApplication
|
|
113
|
-
top = app.keyWindow.rootViewController
|
|
114
|
-
while top is not None and top.presentedViewController is not None:
|
|
115
|
-
top = top.presentedViewController
|
|
116
|
-
if top is not None:
|
|
117
|
-
top.presentViewController_animated_completion_(controller, True, None)
|
|
118
|
-
else:
|
|
119
|
-
_pending.pop(token, None)
|
|
120
|
-
on_done(False)
|
|
121
|
-
except Exception:
|
|
122
|
-
on_done(False)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# ======================================================================
|
|
126
|
-
# Android: ACTION_SEND chooser
|
|
127
|
-
# ======================================================================
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def _android_share(
|
|
131
|
-
on_done: Callable[[bool], None],
|
|
132
|
-
message: Optional[str],
|
|
133
|
-
url: Optional[str],
|
|
134
|
-
title: Optional[str],
|
|
135
|
-
) -> None:
|
|
136
|
-
try:
|
|
137
|
-
from java import jclass
|
|
138
|
-
|
|
139
|
-
from ..utils import get_android_context
|
|
140
|
-
|
|
141
|
-
Intent = jclass("android.content.Intent")
|
|
142
|
-
intent = Intent(Intent.ACTION_SEND)
|
|
143
|
-
intent.setType("text/plain")
|
|
144
|
-
body = "\n".join(part for part in (message, url) if part)
|
|
145
|
-
intent.putExtra(Intent.EXTRA_TEXT, body)
|
|
146
|
-
if title:
|
|
147
|
-
intent.putExtra(Intent.EXTRA_SUBJECT, title)
|
|
148
|
-
|
|
149
|
-
chooser = Intent.createChooser(intent, title or "Share")
|
|
150
|
-
chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
151
|
-
get_android_context().startActivity(chooser)
|
|
152
|
-
# ACTION_SEND chooser gives no completion callback; report
|
|
153
|
-
# success once the chooser has been launched.
|
|
154
|
-
on_done(True)
|
|
155
|
-
except Exception:
|
|
156
|
-
on_done(False)
|
|
48
|
+
try:
|
|
49
|
+
return bool(await native_module("Share").call_async("share", message=message, url=url, title=title))
|
|
50
|
+
except Exception:
|
|
51
|
+
return False
|
|
@@ -1,34 +1,25 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
[`
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
(Chaquopy / Java bridge).
|
|
24
|
-
- `pythonnative.native_views.ios`: iOS handlers (rubicon-objc).
|
|
25
|
-
- `pythonnative.native_views.desktop`: Tkinter preview handlers.
|
|
26
|
-
|
|
27
|
-
All platform-branching is handled at registration time via lazy
|
|
28
|
-
imports, so this package can be imported on any platform for testing.
|
|
29
|
-
A mock registry can be installed via
|
|
30
|
-
[`set_registry`][pythonnative.native_views.set_registry] to drive the
|
|
31
|
-
reconciler with no real native views.
|
|
1
|
+
"""The view backend the reconciler commits to.
|
|
2
|
+
|
|
3
|
+
The reconciler talks to exactly one object, obtained from
|
|
4
|
+
[`get_registry`][pythonnative.native_views.get_registry], through a
|
|
5
|
+
small protocol: ``apply_mutations`` (one ordered batch of
|
|
6
|
+
create/update/insert/destroy/frame ops per commit, see
|
|
7
|
+
`pythonnative.mutations`), ``resolve_view``, ``measure_intrinsic``,
|
|
8
|
+
``command``, and the animation hooks. Two implementations exist:
|
|
9
|
+
|
|
10
|
+
- [`BridgeBackend`][pythonnative.native_views.bridge_backend.BridgeBackend]
|
|
11
|
+
(iOS and Android): serializes each commit and hands it to the native
|
|
12
|
+
runtime through the bridge; Swift and Kotlin component managers own
|
|
13
|
+
every platform view. Python holds no native objects.
|
|
14
|
+
- [`NativeViewRegistry`][pythonnative.native_views.NativeViewRegistry]
|
|
15
|
+
(desktop preview and tests): maps element type names to Python
|
|
16
|
+
[`ViewHandler`][pythonnative.native_views.base.ViewHandler]
|
|
17
|
+
implementations (`pythonnative.native_views.desktop` renders with
|
|
18
|
+
Tkinter) and owns the tag table itself.
|
|
19
|
+
|
|
20
|
+
Platform selection happens lazily on first use, so this package
|
|
21
|
+
imports on any platform. Tests install a mock with
|
|
22
|
+
[`set_registry`][pythonnative.native_views.set_registry].
|
|
32
23
|
"""
|
|
33
24
|
|
|
34
25
|
import math
|
|
@@ -292,98 +283,88 @@ class NativeViewRegistry:
|
|
|
292
283
|
# Singleton registry
|
|
293
284
|
# ======================================================================
|
|
294
285
|
|
|
295
|
-
_registry:
|
|
286
|
+
_registry: Any = None
|
|
296
287
|
|
|
297
288
|
|
|
298
|
-
def
|
|
299
|
-
"""Return ``"android"``, ``"desktop"``, or ``"ios"`` for the active runtime."""
|
|
300
|
-
from ..utils import IS_ANDROID, IS_DESKTOP
|
|
301
|
-
|
|
302
|
-
if IS_ANDROID:
|
|
303
|
-
return "android"
|
|
304
|
-
if IS_DESKTOP:
|
|
305
|
-
return "desktop"
|
|
306
|
-
return "ios"
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
def _register_builtin_handlers(registry: NativeViewRegistry) -> None:
|
|
310
|
-
"""Register every built-in handler for the active platform.
|
|
311
|
-
|
|
312
|
-
The desktop (Tkinter) backend is selected when ``pn preview`` sets
|
|
313
|
-
``PN_PLATFORM=desktop``; otherwise this picks Android (on device) or
|
|
314
|
-
iOS (the default off-device path, exercised by the iOS templates and
|
|
315
|
-
by tests that install the ``[ios]`` extra). Off-device unit tests
|
|
316
|
-
typically inject a mock registry via ``set_registry`` instead.
|
|
317
|
-
"""
|
|
318
|
-
from ..utils import IS_ANDROID, IS_DESKTOP
|
|
319
|
-
|
|
320
|
-
if IS_ANDROID:
|
|
321
|
-
from .android import register_handlers
|
|
322
|
-
elif IS_DESKTOP:
|
|
323
|
-
from .desktop import register_handlers
|
|
324
|
-
else:
|
|
325
|
-
from .ios import register_handlers
|
|
326
|
-
register_handlers(registry)
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
def _install_sdk_handlers(registry: NativeViewRegistry) -> None:
|
|
289
|
+
def _install_sdk_handlers(registry: Any) -> None:
|
|
330
290
|
"""Copy decorator-registered SDK handlers + entry-point plugins.
|
|
331
291
|
|
|
332
292
|
Imported lazily so unit tests that never touch the SDK don't pay the
|
|
333
|
-
entry-point discovery cost.
|
|
293
|
+
entry-point discovery cost. Only meaningful for the Python-handler
|
|
294
|
+
registry (desktop / tests); on device the native runtime owns
|
|
295
|
+
component managers and Python handlers are recorded but never run.
|
|
334
296
|
"""
|
|
335
297
|
try:
|
|
336
298
|
from ..sdk._components import install_into_registry as _sdk_install
|
|
337
299
|
except Exception:
|
|
338
300
|
return
|
|
339
301
|
try:
|
|
340
|
-
_sdk_install(registry
|
|
302
|
+
_sdk_install(registry)
|
|
341
303
|
except Exception:
|
|
342
304
|
# A misbehaving plugin must not break PythonNative's startup.
|
|
343
305
|
pass
|
|
344
306
|
|
|
345
307
|
|
|
346
|
-
def
|
|
347
|
-
"""
|
|
308
|
+
def _create_backend() -> Any:
|
|
309
|
+
"""Build the backend for the active platform.
|
|
310
|
+
|
|
311
|
+
iOS and Android get the bridge backend. ``pn preview``
|
|
312
|
+
(``PN_PLATFORM=desktop``) gets the Tkinter handler registry.
|
|
313
|
+
Anywhere else (plain unit tests) there is no renderer at all, so
|
|
314
|
+
the registry is empty: tests inject a
|
|
315
|
+
``pythonnative.testing.FakeBackend`` with ``set_registry`` and a
|
|
316
|
+
stray commit raises a clear "unknown element type" rather than
|
|
317
|
+
silently importing a platform module.
|
|
318
|
+
"""
|
|
319
|
+
from ..utils import IS_ANDROID, IS_DESKTOP, IS_IOS
|
|
320
|
+
|
|
321
|
+
if IS_IOS or IS_ANDROID:
|
|
322
|
+
from .bridge_backend import BridgeBackend
|
|
348
323
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
324
|
+
return BridgeBackend()
|
|
325
|
+
registry = NativeViewRegistry()
|
|
326
|
+
if IS_DESKTOP:
|
|
327
|
+
from .desktop import register_handlers
|
|
328
|
+
|
|
329
|
+
register_handlers(registry)
|
|
330
|
+
_install_sdk_handlers(registry)
|
|
331
|
+
return registry
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def get_registry() -> Any:
|
|
335
|
+
"""Return the process-wide view backend, creating it on first use.
|
|
355
336
|
|
|
356
337
|
Returns:
|
|
357
|
-
|
|
338
|
+
A [`BridgeBackend`][pythonnative.native_views.bridge_backend.BridgeBackend]
|
|
339
|
+
on device, otherwise a `NativeViewRegistry` (Tkinter handlers
|
|
340
|
+
under ``pn preview``, plus every decorator-registered SDK
|
|
341
|
+
handler and any handlers exposed by third-party packages via
|
|
342
|
+
the [`pythonnative.handlers`][pythonnative.sdk.ENTRY_POINT_GROUP]
|
|
343
|
+
entry point group).
|
|
358
344
|
"""
|
|
359
345
|
global _registry
|
|
360
346
|
if _registry is not None:
|
|
361
347
|
return _registry
|
|
362
|
-
_registry =
|
|
363
|
-
_register_builtin_handlers(_registry)
|
|
364
|
-
_install_sdk_handlers(_registry)
|
|
348
|
+
_registry = _create_backend()
|
|
365
349
|
return _registry
|
|
366
350
|
|
|
367
351
|
|
|
368
|
-
def refresh_registry() ->
|
|
369
|
-
"""Re-run SDK handler installation against the existing
|
|
352
|
+
def refresh_registry() -> Any:
|
|
353
|
+
"""Re-run SDK handler installation against the existing backend.
|
|
370
354
|
|
|
371
355
|
Call this after registering a new component at runtime if the
|
|
372
356
|
registry has already been instantiated. This is mostly useful in
|
|
373
357
|
REPL sessions and tests; the normal flow is "register, then call
|
|
374
358
|
[`get_registry`][pythonnative.native_views.get_registry]" and the
|
|
375
359
|
handlers come along automatically.
|
|
376
|
-
|
|
377
|
-
Returns:
|
|
378
|
-
The active `NativeViewRegistry`.
|
|
379
360
|
"""
|
|
380
361
|
registry = get_registry()
|
|
381
362
|
_install_sdk_handlers(registry)
|
|
382
363
|
return registry
|
|
383
364
|
|
|
384
365
|
|
|
385
|
-
def set_registry(registry:
|
|
386
|
-
"""Install a custom
|
|
366
|
+
def set_registry(registry: Any) -> None:
|
|
367
|
+
"""Install a custom backend (primarily for testing).
|
|
387
368
|
|
|
388
369
|
Replaces the lazy singleton so subsequent
|
|
389
370
|
[`get_registry`][pythonnative.native_views.get_registry] calls
|
|
@@ -393,7 +374,7 @@ def set_registry(registry: Optional[NativeViewRegistry]) -> None:
|
|
|
393
374
|
rebuild it from scratch.
|
|
394
375
|
|
|
395
376
|
Args:
|
|
396
|
-
registry: The replacement
|
|
377
|
+
registry: The replacement backend, or ``None`` to clear.
|
|
397
378
|
"""
|
|
398
379
|
global _registry
|
|
399
380
|
_registry = registry
|