pythonnative 0.40.0__cp313-cp313-win_amd64.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 +411 -0
- pythonnative/_ios_log.py +92 -0
- pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
- pythonnative/alerts.py +236 -0
- pythonnative/animated.py +1687 -0
- pythonnative/animation_graph.py +78 -0
- pythonnative/appearance.py +124 -0
- pythonnative/bootstrap.py +100 -0
- pythonnative/bridge/__init__.py +302 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +170 -0
- pythonnative/bridge/commits.py +114 -0
- pythonnative/bridge/fake.py +243 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/bridge/web.py +489 -0
- pythonnative/cli/__init__.py +7 -0
- pythonnative/cli/pn.py +1214 -0
- pythonnative/component.py +271 -0
- pythonnative/components/__init__.py +111 -0
- pythonnative/components/_base.py +166 -0
- pythonnative/components/controls.py +521 -0
- pythonnative/components/layout.py +518 -0
- pythonnative/components/lists.py +785 -0
- pythonnative/components/media.py +204 -0
- pythonnative/components/overlays.py +107 -0
- pythonnative/components/pressable.py +252 -0
- pythonnative/components/structural.py +167 -0
- pythonnative/components/text.py +291 -0
- pythonnative/devclient.py +824 -0
- pythonnative/devserver/__init__.py +34 -0
- pythonnative/devserver/server.py +718 -0
- pythonnative/devserver/static/animation_graph.js +82 -0
- pythonnative/devserver/static/bridge.js +147 -0
- pythonnative/devserver/static/colors.js +70 -0
- pythonnative/devserver/static/host.js +552 -0
- pythonnative/devserver/static/index.html +60 -0
- pythonnative/devserver/static/layout.js +85 -0
- pythonnative/devserver/static/preview.css +415 -0
- pythonnative/devserver/static/renderer.js +1994 -0
- pythonnative/devserver/static/shell.js +345 -0
- pythonnative/devserver/static/yoga/LICENSE +21 -0
- pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
- pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
- pythonnative/devserver/static/yoga/src/index.js +16 -0
- pythonnative/devserver/static/yoga/src/index.js.map +1 -0
- pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
- pythonnative/devserver/static/yoga/src/load.js +17 -0
- pythonnative/devserver/static/yoga/src/load.js.map +1 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
- pythonnative/devserver/watcher.py +267 -0
- pythonnative/devserver/ws.py +421 -0
- pythonnative/diagnostics.py +241 -0
- pythonnative/element.py +159 -0
- pythonnative/equality.py +19 -0
- pythonnative/events.py +231 -0
- pythonnative/gestures.py +1333 -0
- pythonnative/hooks.py +1621 -0
- pythonnative/hosts/__init__.py +63 -0
- pythonnative/hosts/base.py +596 -0
- pythonnative/hosts/native.py +302 -0
- pythonnative/hot_reload.py +585 -0
- pythonnative/layout.py +328 -0
- pythonnative/mutations.py +142 -0
- pythonnative/native/android/build.gradle +52 -0
- pythonnative/native/android/gradle.properties +3 -0
- pythonnative/native/android/settings.gradle +12 -0
- pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
- pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
- pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
- pythonnative/native/ios/Package.swift +23 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/native/yoga/LICENSE +21 -0
- pythonnative/native/yoga/Package.swift +9 -0
- pythonnative/native/yoga/VENDORED.md +2 -0
- pythonnative/native/yoga/include/PNStyle.h +10 -0
- pythonnative/native/yoga/include/module.modulemap +5 -0
- pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/python.cpp +16 -0
- pythonnative/native/yoga/style.cpp +272 -0
- pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
- pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
- pythonnative/native/yoga/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
- pythonnative/native/yoga/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
- pythonnative/native/yoga/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
- pythonnative/native/yoga/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
- pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
- pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
- pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
- pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
- pythonnative/native/yoga/yoga/config/Config.h +92 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
- pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
- pythonnative/native/yoga/yoga/debug/Log.h +34 -0
- pythonnative/native/yoga/yoga/enums/Align.h +47 -0
- pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
- pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
- pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
- pythonnative/native/yoga/yoga/enums/Display.h +41 -0
- pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
- pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
- pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
- pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
- pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
- pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
- pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
- pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
- pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
- pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
- pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
- pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
- pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
- pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
- pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
- pythonnative/native/yoga/yoga/event/event.cpp +87 -0
- pythonnative/native/yoga/yoga/event/event.h +130 -0
- pythonnative/native/yoga/yoga/module.modulemap +21 -0
- pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
- pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
- pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
- pythonnative/native/yoga/yoga/node/Node.h +337 -0
- pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
- pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
- pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
- pythonnative/native/yoga/yoga/style/Style.h +757 -0
- pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
- pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
- pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
- pythonnative/native_modules/__init__.py +124 -0
- pythonnative/native_modules/app_state.py +99 -0
- pythonnative/native_modules/battery.py +75 -0
- pythonnative/native_modules/biometrics.py +42 -0
- pythonnative/native_modules/camera.py +65 -0
- pythonnative/native_modules/clipboard.py +46 -0
- pythonnative/native_modules/fallback.py +324 -0
- pythonnative/native_modules/file_system.py +145 -0
- pythonnative/native_modules/haptics.py +75 -0
- pythonnative/native_modules/linking.py +123 -0
- pythonnative/native_modules/location.py +75 -0
- pythonnative/native_modules/net_info.py +112 -0
- pythonnative/native_modules/notifications.py +129 -0
- pythonnative/native_modules/permissions.py +108 -0
- pythonnative/native_modules/registry.py +526 -0
- pythonnative/native_modules/secure_store.py +52 -0
- pythonnative/native_modules/share.py +51 -0
- pythonnative/native_views/__init__.py +375 -0
- pythonnative/native_views/base.py +316 -0
- pythonnative/native_views/bridge_backend.py +277 -0
- pythonnative/navigation/__init__.py +103 -0
- pythonnative/navigation/container.py +154 -0
- pythonnative/navigation/handle.py +540 -0
- pythonnative/navigation/hooks.py +133 -0
- pythonnative/navigation/host.py +58 -0
- pythonnative/navigation/linking.py +200 -0
- pythonnative/navigation/navigators.py +659 -0
- pythonnative/navigation/screen.py +149 -0
- pythonnative/navigation/state.py +269 -0
- pythonnative/net.py +248 -0
- pythonnative/platform.py +166 -0
- pythonnative/platform_metrics.py +252 -0
- pythonnative/preview.py +287 -0
- pythonnative/profiling.py +101 -0
- pythonnative/project/__init__.py +68 -0
- pythonnative/project/android.py +557 -0
- pythonnative/project/builder.py +817 -0
- pythonnative/project/config.py +773 -0
- pythonnative/project/deps.py +719 -0
- pythonnative/project/devices.py +304 -0
- pythonnative/project/doctor.py +283 -0
- pythonnative/project/fingerprint.py +171 -0
- pythonnative/project/icons.py +247 -0
- pythonnative/project/ios.py +325 -0
- pythonnative/project/lockfile.py +100 -0
- pythonnative/project/permissions.py +361 -0
- pythonnative/project/plugins.py +519 -0
- pythonnative/project/runtime_assets.py +183 -0
- pythonnative/py.typed +0 -0
- pythonnative/query.py +153 -0
- pythonnative/reconciler/__init__.py +29 -0
- pythonnative/reconciler/boundaries.py +375 -0
- pythonnative/reconciler/children.py +88 -0
- pythonnative/reconciler/core.py +1165 -0
- pythonnative/reconciler/layout_pass.py +393 -0
- pythonnative/reconciler/vnode.py +266 -0
- pythonnative/refresh.py +55 -0
- pythonnative/runtime.py +306 -0
- pythonnative/scheduler.py +159 -0
- pythonnative/sdk/__init__.py +153 -0
- pythonnative/sdk/_components.py +427 -0
- pythonnative/sdk/builtins.py +91 -0
- pythonnative/sdk/codegen.py +149 -0
- pythonnative/sdk/module_codegen.py +184 -0
- pythonnative/sdk/schema.py +224 -0
- pythonnative/sdk/templates/contracts.kt +46 -0
- pythonnative/sdk/templates/contracts.swift +46 -0
- pythonnative/storage.py +184 -0
- pythonnative/style.py +841 -0
- pythonnative/suspense.py +183 -0
- pythonnative/templates/android_template/app/build.gradle +75 -0
- pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
- pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
- pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
- pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
- pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
- pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
- pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
- pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
- pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
- pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
- pythonnative/templates/android_template/build.gradle +10 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
- pythonnative/templates/android_template/gradle.properties +23 -0
- pythonnative/templates/android_template/gradlew +185 -0
- pythonnative/templates/android_template/gradlew.bat +89 -0
- pythonnative/templates/android_template/settings.gradle +17 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
- pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
- pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
- pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
- pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
- pythonnative/testing/__init__.py +53 -0
- pythonnative/testing/backend.py +276 -0
- pythonnative/testing/harness.py +369 -0
- pythonnative/utils.py +145 -0
- pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
- pythonnative-0.40.0.dist-info/METADATA +150 -0
- pythonnative-0.40.0.dist-info/RECORD +418 -0
- pythonnative-0.40.0.dist-info/WHEEL +5 -0
- pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
- pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
- pythonnative-0.40.0.dist-info/top_level.txt +1 -0
- pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
package com.pythonnative.runtime
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import com.pythonnative.runtime.animation.PNAnimator
|
|
7
|
+
import com.pythonnative.runtime.bridge.JsonUtil
|
|
8
|
+
import com.pythonnative.runtime.bridge.MainThread
|
|
9
|
+
import com.pythonnative.runtime.bridge.PNRegistry
|
|
10
|
+
import com.pythonnative.runtime.bridge.TransactionApplier
|
|
11
|
+
import com.pythonnative.runtime.bridge.ViewRegistry
|
|
12
|
+
import com.pythonnative.runtime.modules.BuiltinModules
|
|
13
|
+
import com.pythonnative.runtime.modules.ModuleEnvelope
|
|
14
|
+
import com.pythonnative.runtime.modules.Promise
|
|
15
|
+
import org.json.JSONObject
|
|
16
|
+
import java.util.Locale
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The single Java class Python touches.
|
|
20
|
+
*
|
|
21
|
+
* Every entry point mirrors one C symbol of the iOS bridge and is
|
|
22
|
+
* expected to run on the main thread (Python's asyncio loop lives
|
|
23
|
+
* there). Calls from other threads are logged but still executed so a
|
|
24
|
+
* misbehaving caller fails loudly in logcat instead of silently.
|
|
25
|
+
*/
|
|
26
|
+
object PNBridge {
|
|
27
|
+
/** Logcat tag used by the whole runtime. */
|
|
28
|
+
const val TAG = "PythonNative"
|
|
29
|
+
|
|
30
|
+
/** Protocol version compiled into this library. */
|
|
31
|
+
const val PROTOCOL_VERSION = 2
|
|
32
|
+
|
|
33
|
+
private val pythonQueue = java.util.concurrent.Executors.newSingleThreadExecutor { runnable ->
|
|
34
|
+
Thread(runnable, "PythonNative-events").apply { isDaemon = true }
|
|
35
|
+
}
|
|
36
|
+
@Volatile private var host: PythonHost? = null
|
|
37
|
+
private var activity: Activity? = null
|
|
38
|
+
private val hostReadyListeners = ArrayList<Runnable>()
|
|
39
|
+
private val modulePromises = java.util.concurrent.ConcurrentHashMap<Long, Promise>()
|
|
40
|
+
|
|
41
|
+
/** Tag to view bookkeeping shared by the applier and managers. */
|
|
42
|
+
val registry = ViewRegistry()
|
|
43
|
+
|
|
44
|
+
private data class Message(val kind: String, val tag: Long, val name: String, val payload: String) {
|
|
45
|
+
val continuous get() = kind == "event" && (name in setOf("on_scroll", "on_selection_change", "on_gesture_update") ||
|
|
46
|
+
name.startsWith("gesture:") && JSONObject(payload).optJSONArray("args")?.optJSONObject(0)?.optString("state") == "changed")
|
|
47
|
+
}
|
|
48
|
+
private val mailbox = java.util.ArrayDeque<Message>()
|
|
49
|
+
private var draining = false
|
|
50
|
+
|
|
51
|
+
private val commits = com.pythonnative.runtime.bridge.CommitState()
|
|
52
|
+
private val applier by lazy { TransactionApplier(registry) }
|
|
53
|
+
|
|
54
|
+
/** The protocol version Python must match before starting. */
|
|
55
|
+
@JvmStatic
|
|
56
|
+
fun protocolVersion(): Int = PROTOCOL_VERSION
|
|
57
|
+
|
|
58
|
+
/** Install the Python callback target; flushes callbacks queued before it existed. */
|
|
59
|
+
@JvmStatic
|
|
60
|
+
fun setHost(host: PythonHost) {
|
|
61
|
+
this.host = host
|
|
62
|
+
val pending = ArrayList(hostReadyListeners)
|
|
63
|
+
hostReadyListeners.clear()
|
|
64
|
+
for (listener in pending) {
|
|
65
|
+
try {
|
|
66
|
+
listener.run()
|
|
67
|
+
} catch (e: Exception) {
|
|
68
|
+
Log.e(TAG, "host-ready listener failed", e)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Whether a [PythonHost] has been installed. */
|
|
74
|
+
fun hasHost(): Boolean = host != null
|
|
75
|
+
|
|
76
|
+
/** Run `runnable` now if a host is installed, otherwise once [setHost] is called. */
|
|
77
|
+
fun runWhenHostReady(runnable: Runnable) {
|
|
78
|
+
if (host != null) runnable.run() else hostReadyListeners.add(runnable)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Record the current activity; modules and managers derive their `Context` from it. */
|
|
82
|
+
@JvmStatic
|
|
83
|
+
fun setContext(activity: Activity) {
|
|
84
|
+
this.activity = activity
|
|
85
|
+
PNRegistry.ensureBuiltins()
|
|
86
|
+
BuiltinModules.onContextAttached(activity)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Forget `activity` if it is the current one (called from `Activity.onDestroy`). */
|
|
90
|
+
@JvmStatic
|
|
91
|
+
fun clearContext(activity: Activity) {
|
|
92
|
+
if (this.activity === activity) this.activity = null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The current activity, or `null` before [setContext]. */
|
|
96
|
+
fun activity(): Activity? = activity
|
|
97
|
+
|
|
98
|
+
/** The context used to create views; throws before [setContext]. */
|
|
99
|
+
fun context(): Context =
|
|
100
|
+
activity ?: throw IllegalStateException("PNBridge.setContext(activity) has not been called")
|
|
101
|
+
|
|
102
|
+
/** Display density of the current context (1.0 when no context is attached). */
|
|
103
|
+
fun density(): Float {
|
|
104
|
+
val ctx = activity ?: return 1f
|
|
105
|
+
val d = ctx.resources.displayMetrics.density
|
|
106
|
+
return if (d > 0f) d else 1f
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ------------------------------------------------------------------
|
|
110
|
+
// Python -> native
|
|
111
|
+
// ------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
/** Apply one serialized transaction (a JSON array of ops). */
|
|
114
|
+
@JvmStatic
|
|
115
|
+
fun apply(transactionJson: String): String {
|
|
116
|
+
if (!MainThread.isMain()) return MainThread.call { apply(transactionJson) }
|
|
117
|
+
return commits.apply(transactionJson, applier)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Measure the view with `tag` under the given constraints (dp; `1e6`
|
|
122
|
+
* means unconstrained) and return `"w,h"` in dp.
|
|
123
|
+
*/
|
|
124
|
+
@JvmStatic
|
|
125
|
+
fun measure(tag: Long, maxWidth: Double, maxHeight: Double): String {
|
|
126
|
+
if (!MainThread.isMain()) return MainThread.call { measure(tag, maxWidth, maxHeight) }
|
|
127
|
+
val record = registry.get(tag)
|
|
128
|
+
if (record == null) {
|
|
129
|
+
Log.w(TAG, "measure: unknown tag $tag")
|
|
130
|
+
return "0,0"
|
|
131
|
+
}
|
|
132
|
+
val size = try {
|
|
133
|
+
record.manager.measure(record.view, maxWidth, maxHeight)
|
|
134
|
+
} catch (e: Exception) {
|
|
135
|
+
Log.e(TAG, "measure failed for ${record.typeName}#$tag", e)
|
|
136
|
+
floatArrayOf(0f, 0f)
|
|
137
|
+
}
|
|
138
|
+
return String.format(Locale.US, "%.3f,%.3f", size[0], size[1])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Run an imperative command on one view and return its JSON result. */
|
|
142
|
+
@JvmStatic
|
|
143
|
+
fun command(tag: Long, name: String, argsJson: String): String? {
|
|
144
|
+
if (!MainThread.isMain()) return MainThread.call { command(tag, name, argsJson) }
|
|
145
|
+
val record = registry.get(tag) ?: return null
|
|
146
|
+
return try {
|
|
147
|
+
val args = if (argsJson.isBlank()) JSONObject() else JSONObject(argsJson)
|
|
148
|
+
JsonUtil.encode(record.manager.command(record.view, name, args))
|
|
149
|
+
} catch (e: Exception) {
|
|
150
|
+
Log.e(TAG, "command '$name' failed for ${record.typeName}#$tag", e)
|
|
151
|
+
null
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Handle an animation request (`set`, `start`, or `cancel`) for one view. */
|
|
156
|
+
@JvmStatic
|
|
157
|
+
fun animate(tag: Long, requestJson: String): String? {
|
|
158
|
+
if (!MainThread.isMain()) return MainThread.call { animate(tag, requestJson) }
|
|
159
|
+
val record = registry.get(tag) ?: return null
|
|
160
|
+
return try {
|
|
161
|
+
PNAnimator.handle(record, JSONObject(requestJson))
|
|
162
|
+
} catch (e: Exception) {
|
|
163
|
+
Log.e(TAG, "animate failed for ${record.typeName}#$tag: $requestJson", e)
|
|
164
|
+
null
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Call a native module method; `argsJson` is `{"call_id": n, "args": {...}}`. */
|
|
169
|
+
@JvmStatic
|
|
170
|
+
fun call(module: String, method: String, argsJson: String): String? {
|
|
171
|
+
if (!MainThread.isMain()) return MainThread.call { call(module, method, argsJson) }
|
|
172
|
+
val (callId, args) = try {
|
|
173
|
+
ModuleEnvelope.decodeCall(argsJson)
|
|
174
|
+
} catch (e: Exception) {
|
|
175
|
+
return ModuleEnvelope.error("malformed call envelope: ${e.message}", "bad_request")
|
|
176
|
+
}
|
|
177
|
+
if (method == "_pn_cancel") {
|
|
178
|
+
modulePromises.remove(args.optLong("call_id"))?.takeIf { it.module == module }?.cancel()
|
|
179
|
+
return ModuleEnvelope.ok(null)
|
|
180
|
+
}
|
|
181
|
+
if (module == "Runtime") return JSONObject().put("ok", true).put("value", JSONObject()
|
|
182
|
+
.put("protocol", 2).put("yoga", "3.2.1").put("schema", com.pythonnative.generated.PNContracts.fingerprint)
|
|
183
|
+
.put("animation_graph", true).put("logical_lists", true).put("native_layout", true)).toString()
|
|
184
|
+
if (module == "Layout") return JSONObject().put("ok", true).put("value",
|
|
185
|
+
com.pythonnative.runtime.layout.NativeLayout.compute(args)).toString()
|
|
186
|
+
val target = PNRegistry.module(module)
|
|
187
|
+
?: return ModuleEnvelope.error("unknown module '$module'", "unknown_module")
|
|
188
|
+
val promise = Promise(callId, module)
|
|
189
|
+
if (callId > 0) {
|
|
190
|
+
modulePromises[callId] = promise
|
|
191
|
+
promise.onFinished = { modulePromises.remove(callId) }
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
target.call(method, args, promise)
|
|
195
|
+
} catch (e: Exception) {
|
|
196
|
+
Log.e(TAG, "module $module.$method threw", e)
|
|
197
|
+
if (!promise.isSettled) promise.reject(e.message ?: e.toString(), "exception")
|
|
198
|
+
}
|
|
199
|
+
return promise.markReturned()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ------------------------------------------------------------------
|
|
203
|
+
// native -> Python
|
|
204
|
+
// ------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Deliver one callback to Python. Returns `null` when no host is
|
|
208
|
+
* installed or the Python handler raised (the error is logged).
|
|
209
|
+
*/
|
|
210
|
+
fun callPython(kind: String, tag: Long, name: String, payloadJson: String): String? {
|
|
211
|
+
val target = host
|
|
212
|
+
if (target == null) {
|
|
213
|
+
Log.w(TAG, "callPython($kind, $tag, $name) dropped: no PythonHost installed")
|
|
214
|
+
return null
|
|
215
|
+
}
|
|
216
|
+
if (kind == "event") com.pythonnative.runtime.animation.AnimationGraph.event(tag, name, org.json.JSONArray(payloadJson))
|
|
217
|
+
var editRevision = 0L
|
|
218
|
+
if (kind == "event" && name == "on_change") registry.get(tag)?.let { record ->
|
|
219
|
+
if (record.typeName == "TextInput") {
|
|
220
|
+
editRevision = ((record.state["edit_revision"] as? Number)?.toLong() ?: 0L) + 1L
|
|
221
|
+
record.state["edit_revision"] = editRevision
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
val payload = if (kind == "event") commits.event(org.json.JSONArray(payloadJson), editRevision) else payloadJson
|
|
225
|
+
val message = Message(kind, tag, name, payload)
|
|
226
|
+
val schedule = synchronized(mailbox) {
|
|
227
|
+
val last = mailbox.peekLast()
|
|
228
|
+
if (message.continuous && last?.continuous == true && last.tag == tag && last.name == name) mailbox.removeLast()
|
|
229
|
+
mailbox.addLast(message)
|
|
230
|
+
val needed = !draining
|
|
231
|
+
draining = true
|
|
232
|
+
needed
|
|
233
|
+
}
|
|
234
|
+
if (schedule) pythonQueue.execute {
|
|
235
|
+
while (true) {
|
|
236
|
+
val next = synchronized(mailbox) {
|
|
237
|
+
mailbox.pollFirst().also { if (it == null) draining = false }
|
|
238
|
+
} ?: break
|
|
239
|
+
try { target.callback(next.kind, next.tag, next.name, next.payload) }
|
|
240
|
+
catch (error: Exception) { Log.e(TAG, "Python callback ${next.kind}/${next.name} raised", error) }
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return null
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ------------------------------------------------------------------
|
|
247
|
+
// Threading helpers
|
|
248
|
+
// ------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
/** Whether the caller is on the main thread. */
|
|
251
|
+
fun isMainThread(): Boolean = MainThread.isMain()
|
|
252
|
+
|
|
253
|
+
/** Post `runnable` to the main thread. */
|
|
254
|
+
fun post(runnable: Runnable) = MainThread.post(runnable)
|
|
255
|
+
|
|
256
|
+
private fun assertMain(entry: String) {
|
|
257
|
+
if (!MainThread.isMain()) {
|
|
258
|
+
Log.w(TAG, "PNBridge.$entry called off the main thread (${Thread.currentThread().name})")
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package com.pythonnative.runtime
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The reverse direction of the bridge: native to Python.
|
|
5
|
+
*
|
|
6
|
+
* The app template installs an implementation that forwards to
|
|
7
|
+
* `pythonnative.bridge.native_callback` through Chaquopy. The runtime
|
|
8
|
+
* library itself never touches Chaquopy, which keeps it unit-testable
|
|
9
|
+
* with plain JUnit.
|
|
10
|
+
*
|
|
11
|
+
* `kind` is one of `event`, `module`, `host`, `animation`, or `pump`;
|
|
12
|
+
* see the bridge protocol documentation for the payload of each.
|
|
13
|
+
*/
|
|
14
|
+
interface PythonHost {
|
|
15
|
+
/**
|
|
16
|
+
* Deliver one callback to Python and return its (JSON-encoded)
|
|
17
|
+
* result, or `null` when the handler produced nothing.
|
|
18
|
+
*/
|
|
19
|
+
fun callback(kind: String, tag: Long, name: String, payloadJson: String): String?
|
|
20
|
+
}
|
pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
package com.pythonnative.runtime.animation
|
|
2
|
+
|
|
3
|
+
import com.pythonnative.runtime.PNBridge
|
|
4
|
+
import org.json.JSONArray
|
|
5
|
+
import org.json.JSONObject
|
|
6
|
+
import kotlin.math.*
|
|
7
|
+
|
|
8
|
+
/** Expressions and input bindings live on the UI thread, independent of Python. */
|
|
9
|
+
object AnimationGraph {
|
|
10
|
+
private data class Graph(val nodes: List<JSONObject>, val bindings: JSONArray)
|
|
11
|
+
private val graphs = HashMap<Long, Graph>()
|
|
12
|
+
private val values = HashMap<Long, Double>()
|
|
13
|
+
private val previous = HashMap<Long, Double>()
|
|
14
|
+
private val output = HashMap<Long, Any>()
|
|
15
|
+
|
|
16
|
+
fun install(spec: JSONObject) {
|
|
17
|
+
val id = spec.getLong("id")
|
|
18
|
+
val bindings = spec.getJSONArray("bindings")
|
|
19
|
+
val raw = spec.getJSONArray("nodes")
|
|
20
|
+
val nodes = (0 until raw.length()).map { raw.getJSONObject(it) }
|
|
21
|
+
val ids = nodes.map { it.getLong("id") }.toSet()
|
|
22
|
+
graphs.entries.removeAll { (_, graph) -> graph.nodes.any { it.getLong("id") in ids } }
|
|
23
|
+
if (bindings.length() == 0) { collect(); return }
|
|
24
|
+
for (node in nodes) {
|
|
25
|
+
val key = node.getLong("id")
|
|
26
|
+
values.putIfAbsent(key, node.optDouble("value", 0.0))
|
|
27
|
+
previous.putIfAbsent(key, node.optDouble("previous", 0.0))
|
|
28
|
+
}
|
|
29
|
+
graphs[id] = Graph(nodes, bindings)
|
|
30
|
+
collect()
|
|
31
|
+
evaluate()
|
|
32
|
+
}
|
|
33
|
+
fun value(id: Long): Double = values[id] ?: 0.0
|
|
34
|
+
fun set(id: Long, value: Double) { if (value.isFinite()) { values[id] = value; evaluate() } }
|
|
35
|
+
fun event(tag: Long, name: String, args: JSONArray) {
|
|
36
|
+
val payload = args.optJSONObject(0) ?: return
|
|
37
|
+
val props = PNBridge.registry.get(tag)?.props ?: return
|
|
38
|
+
val fields = if (name.startsWith("gesture:")) {
|
|
39
|
+
val index = name.removePrefix("gesture:").toIntOrNull() ?: return
|
|
40
|
+
props.optJSONArray("gestures")?.optJSONObject(index)?.optJSONObject("animated_events")?.optJSONObject(payload.optString("state"))
|
|
41
|
+
} else props.optJSONObject("_pn_animated_events")?.optJSONObject(name)
|
|
42
|
+
if (fields == null) return
|
|
43
|
+
for (field in fields.keys()) if (payload.opt(field) is Number) values[fields.getLong(field)] = payload.getDouble(field)
|
|
44
|
+
evaluate()
|
|
45
|
+
}
|
|
46
|
+
fun forget(tag: Long) {
|
|
47
|
+
for ((id, graph) in graphs.toMap()) {
|
|
48
|
+
val kept = JSONArray()
|
|
49
|
+
for (i in 0 until graph.bindings.length()) if (graph.bindings.getJSONArray(i).getLong(0) != tag) kept.put(graph.bindings.getJSONArray(i))
|
|
50
|
+
if (kept.length() == 0) graphs.remove(id) else graphs[id] = Graph(graph.nodes, kept)
|
|
51
|
+
}
|
|
52
|
+
collect()
|
|
53
|
+
}
|
|
54
|
+
private fun collect() {
|
|
55
|
+
val live = graphs.values.flatMap { it.nodes }.map { it.getLong("id") }.toSet()
|
|
56
|
+
values.keys.retainAll(live); previous.keys.retainAll(live); output.keys.retainAll(live)
|
|
57
|
+
}
|
|
58
|
+
private fun input(node: JSONObject): Double = if (node.has("node")) values[node.getLong("node")] ?: 0.0 else node.optDouble("constant", 0.0)
|
|
59
|
+
private fun evaluate() {
|
|
60
|
+
for (graph in graphs.values) {
|
|
61
|
+
for (node in graph.nodes) {
|
|
62
|
+
val id = node.getLong("id")
|
|
63
|
+
val raw = node.optJSONArray("inputs") ?: JSONArray()
|
|
64
|
+
val inputs = (0 until raw.length()).map { input(raw.getJSONObject(it)) }
|
|
65
|
+
val a = inputs.getOrElse(0) { 0.0 }; val b = inputs.getOrElse(1) { 0.0 }
|
|
66
|
+
val value = when (node.getString("kind")) {
|
|
67
|
+
"value" -> values[id] ?: 0.0
|
|
68
|
+
"add" -> a + b; "subtract" -> a - b; "multiply" -> a * b
|
|
69
|
+
"divide" -> if (b == 0.0) 0.0 else a / b
|
|
70
|
+
"modulo" -> if (b == 0.0) 0.0 else a % b
|
|
71
|
+
"negate" -> -a
|
|
72
|
+
"diff_clamp" -> {
|
|
73
|
+
val next = ((values[id] ?: 0.0) + a - (previous[id] ?: a)).coerceIn(node.getDouble("minimum"), node.getDouble("maximum"))
|
|
74
|
+
previous[id] = a; next
|
|
75
|
+
}
|
|
76
|
+
"interpolate" -> interpolate(node, a, id)
|
|
77
|
+
else -> 0.0
|
|
78
|
+
}
|
|
79
|
+
values[id] = if (value.isFinite()) value else 0.0
|
|
80
|
+
if (!node.optBoolean("color")) output[id] = values[id]!!
|
|
81
|
+
}
|
|
82
|
+
for (i in 0 until graph.bindings.length()) {
|
|
83
|
+
val binding = graph.bindings.getJSONArray(i)
|
|
84
|
+
val record = PNBridge.registry.get(binding.getLong(0)) ?: continue
|
|
85
|
+
record.manager.setAnimatedProperty(record.view, binding.getString(1), output[binding.getLong(2)])
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
private fun interpolate(node: JSONObject, input: Double, id: Long): Double {
|
|
90
|
+
val ranges = node.getJSONArray("ranges"); val outputs = node.getJSONArray("outputs")
|
|
91
|
+
val first = ranges.getDouble(0); val last = ranges.getDouble(ranges.length() - 1)
|
|
92
|
+
var x = input
|
|
93
|
+
val mode = if (x < first) node.getString("left") else if (x > last) node.getString("right") else "extend"
|
|
94
|
+
if (mode == "identity") return x
|
|
95
|
+
if (mode == "clamp") x = x.coerceIn(first, last)
|
|
96
|
+
var index = 0
|
|
97
|
+
while (index < ranges.length() - 2 && x >= ranges.getDouble(index + 1)) index++
|
|
98
|
+
val span = ranges.getDouble(index + 1) - ranges.getDouble(index)
|
|
99
|
+
val t = if (span == 0.0) 0.0 else (x - ranges.getDouble(index)) / span
|
|
100
|
+
if (node.optBoolean("color")) {
|
|
101
|
+
val from = outputs.getJSONArray(index); val to = outputs.getJSONArray(index + 1)
|
|
102
|
+
val channels = (0..3).map { (from.getDouble(it) + (to.getDouble(it) - from.getDouble(it)) * t.coerceIn(0.0, 1.0)).roundToInt().coerceIn(0, 255) }
|
|
103
|
+
output[id] = "#" + channels.joinToString("") { "%02X".format(it) }
|
|
104
|
+
return 0.0
|
|
105
|
+
}
|
|
106
|
+
return outputs.getDouble(index) + (outputs.getDouble(index + 1) - outputs.getDouble(index)) * t
|
|
107
|
+
}
|
|
108
|
+
}
|
pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
package com.pythonnative.runtime.animation
|
|
2
|
+
|
|
3
|
+
import org.json.JSONArray
|
|
4
|
+
import org.json.JSONObject
|
|
5
|
+
import kotlin.math.max
|
|
6
|
+
import kotlin.math.sqrt
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Pure-Kotlin decoding of animation specs (`pn.Animated.timing`,
|
|
10
|
+
* `spring`, `decay`) and their mapping to Android animator parameters.
|
|
11
|
+
* Kept free of Android types so the math is unit-testable.
|
|
12
|
+
*/
|
|
13
|
+
object AnimationSpecs {
|
|
14
|
+
/** `FlingAnimation` scales `friction` by this constant internally (`DragForce`). */
|
|
15
|
+
const val FLING_FRICTION_SCALE = 4.2
|
|
16
|
+
|
|
17
|
+
/** Easing curves identical to the Python ticker's `_EASINGS`. */
|
|
18
|
+
val EASINGS: Map<String, (Float) -> Float> = mapOf(
|
|
19
|
+
"linear" to { t -> t },
|
|
20
|
+
"ease" to { t -> 3f * t * t - 2f * t * t * t },
|
|
21
|
+
"ease_in" to { t -> t * t },
|
|
22
|
+
"ease_out" to { t -> 1f - (1f - t) * (1f - t) },
|
|
23
|
+
"ease_in_out" to { t -> 3f * t * t - 2f * t * t * t },
|
|
24
|
+
"ease_in_quad" to { t -> t * t },
|
|
25
|
+
"ease_out_quad" to { t -> 1f - (1f - t) * (1f - t) },
|
|
26
|
+
"bounce" to { t -> bounceOut(t) },
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
/** Decoded `timing` parameters. */
|
|
30
|
+
data class Timing(val from: Double, val to: Double, val durationMs: Long, val easing: Any?)
|
|
31
|
+
|
|
32
|
+
/** Decoded `spring` parameters mapped to `SpringForce` terms. */
|
|
33
|
+
data class Spring(
|
|
34
|
+
val from: Double,
|
|
35
|
+
val to: Double,
|
|
36
|
+
/** `stiffness / mass`, so the natural frequency matches the Python integrator. */
|
|
37
|
+
val stiffness: Double,
|
|
38
|
+
/** `damping / (2 * sqrt(stiffness * mass))`. */
|
|
39
|
+
val dampingRatio: Double,
|
|
40
|
+
/** Units per second. */
|
|
41
|
+
val initialVelocity: Double,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
/** Decoded `decay` parameters mapped to `FlingAnimation` terms. */
|
|
45
|
+
data class Decay(
|
|
46
|
+
val from: Double,
|
|
47
|
+
/** Units per second (Python specs use units per millisecond). */
|
|
48
|
+
val startVelocity: Double,
|
|
49
|
+
/** `FlingAnimation.setFriction` value reproducing `v(t) = v0 * exp(-k * 1000 * t)`. */
|
|
50
|
+
val friction: Double,
|
|
51
|
+
/** Where the value settles: `from + v0 / (k * 1000)`. */
|
|
52
|
+
val projectedFinal: Double,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
fun kind(spec: JSONObject): String = spec.optString("kind", "")
|
|
56
|
+
|
|
57
|
+
fun timing(spec: JSONObject): Timing = Timing(
|
|
58
|
+
from = spec.optDouble("from", 0.0),
|
|
59
|
+
to = spec.optDouble("to", 0.0),
|
|
60
|
+
durationMs = max(0L, spec.optDouble("duration_ms", 300.0).toLong()),
|
|
61
|
+
easing = spec.opt("easing"),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
fun spring(spec: JSONObject): Spring {
|
|
65
|
+
val stiffness = max(1e-6, spec.optDouble("stiffness", 100.0))
|
|
66
|
+
val damping = max(0.0, spec.optDouble("damping", 10.0))
|
|
67
|
+
val mass = max(1e-6, spec.optDouble("mass", 1.0))
|
|
68
|
+
return Spring(
|
|
69
|
+
from = spec.optDouble("from", 0.0),
|
|
70
|
+
to = spec.optDouble("to", 0.0),
|
|
71
|
+
stiffness = stiffness / mass,
|
|
72
|
+
dampingRatio = dampingRatio(stiffness, damping, mass),
|
|
73
|
+
initialVelocity = spec.optDouble("initial_velocity", 0.0),
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `damping / (2 * sqrt(stiffness * mass))`. */
|
|
78
|
+
fun dampingRatio(stiffness: Double, damping: Double, mass: Double): Double =
|
|
79
|
+
damping / (2.0 * sqrt(max(1e-12, stiffness * mass)))
|
|
80
|
+
|
|
81
|
+
fun decay(spec: JSONObject): Decay {
|
|
82
|
+
val v0PerMs = spec.optDouble("velocity", 0.0)
|
|
83
|
+
val k = max(1e-6, spec.optDouble("deceleration", 0.997))
|
|
84
|
+
val from = spec.optDouble("from", 0.0)
|
|
85
|
+
return Decay(
|
|
86
|
+
from = from,
|
|
87
|
+
startVelocity = v0PerMs * 1000.0,
|
|
88
|
+
friction = decayFriction(k),
|
|
89
|
+
projectedFinal = from + v0PerMs / (k * 1000.0),
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The Python ticker decays velocity as `exp(-k * dt_ms)`, that is
|
|
95
|
+
* `exp(-k * 1000 * t_s)`. `FlingAnimation` decays as
|
|
96
|
+
* `exp(-4.2 * friction * t_s)`, so `friction = k * 1000 / 4.2`.
|
|
97
|
+
*/
|
|
98
|
+
fun decayFriction(deceleration: Double): Double = deceleration * 1000.0 / FLING_FRICTION_SCALE
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve an easing value. Names map to [EASINGS]; a four-number
|
|
102
|
+
* array is returned as cubic-bezier control points (`DoubleArray`)
|
|
103
|
+
* for the caller to build a `PathInterpolator`. Unknown names fall
|
|
104
|
+
* back to `ease_in_out` like the Python ticker.
|
|
105
|
+
*/
|
|
106
|
+
fun resolveEasing(easing: Any?): Any {
|
|
107
|
+
if (easing is JSONArray && easing.length() == 4) {
|
|
108
|
+
return DoubleArray(4) { easing.optDouble(it, 0.0) }
|
|
109
|
+
}
|
|
110
|
+
if (easing is String) {
|
|
111
|
+
val trimmed = easing.trim()
|
|
112
|
+
if (trimmed.startsWith("cubic-bezier(") || trimmed.startsWith("cubic_bezier(")) {
|
|
113
|
+
val inner = trimmed.substringAfter('(').substringBeforeLast(')')
|
|
114
|
+
val parts = inner.split(',').mapNotNull { it.trim().toDoubleOrNull() }
|
|
115
|
+
if (parts.size == 4) return parts.toDoubleArray()
|
|
116
|
+
}
|
|
117
|
+
return EASINGS[trimmed] ?: EASINGS.getValue("ease_in_out")
|
|
118
|
+
}
|
|
119
|
+
return EASINGS.getValue("ease_in_out")
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private fun bounceOut(t: Float): Float {
|
|
123
|
+
val n1 = 7.5625f
|
|
124
|
+
val d1 = 2.75f
|
|
125
|
+
return when {
|
|
126
|
+
t < 1f / d1 -> n1 * t * t
|
|
127
|
+
t < 2f / d1 -> {
|
|
128
|
+
val u = t - 1.5f / d1
|
|
129
|
+
n1 * u * u + 0.75f
|
|
130
|
+
}
|
|
131
|
+
t < 2.5f / d1 -> {
|
|
132
|
+
val u = t - 2.25f / d1
|
|
133
|
+
n1 * u * u + 0.9375f
|
|
134
|
+
}
|
|
135
|
+
else -> {
|
|
136
|
+
val u = t - 2.625f / d1
|
|
137
|
+
n1 * u * u + 0.984375f
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|