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,557 @@
|
|
|
1
|
+
"""Config-driven Android project configurator.
|
|
2
|
+
|
|
3
|
+
Turns the bundled ``android_template`` into a concrete, buildable Gradle
|
|
4
|
+
project for a specific [`AppConfig`][pythonnative.project.config.AppConfig]:
|
|
5
|
+
|
|
6
|
+
- **Package relocation.** The template lives under
|
|
7
|
+
``com.pythonnative.android_template``; this module rewrites and moves
|
|
8
|
+
it to the app's own ``application_id`` so each app ships a distinct
|
|
9
|
+
package. The PythonNative Android runtime resolves its helper classes
|
|
10
|
+
(``Navigator``) via ``getPackageName()``, so the relocation needs no
|
|
11
|
+
runtime configuration.
|
|
12
|
+
- **Identity & SDKs.** ``applicationId``, ``versionCode``/``versionName``,
|
|
13
|
+
``minSdk``/``targetSdk``/``compileSdk``, ABI filters, and the embedded
|
|
14
|
+
CPython version are written into ``app/build.gradle``.
|
|
15
|
+
- **Permissions.** ``<uses-permission>`` entries (derived from
|
|
16
|
+
``[permissions]``) and the launch orientation are written into
|
|
17
|
+
``AndroidManifest.xml``.
|
|
18
|
+
- **Signing.** A release ``signingConfig`` is injected when a keystore is
|
|
19
|
+
configured (passwords are read from the environment at build time).
|
|
20
|
+
- **Branding.** Launcher icons and an Android 12+ splash screen are
|
|
21
|
+
generated from the configured assets.
|
|
22
|
+
- **Python sources.** The user's ``app/`` and (in a dev checkout) the
|
|
23
|
+
in-repo ``pythonnative`` package are staged into Chaquopy's source set,
|
|
24
|
+
and ``requirements.txt`` is generated from ``[requirements].packages``.
|
|
25
|
+
|
|
26
|
+
Everything here is plain file manipulation, which keeps it fully unit
|
|
27
|
+
testable without an Android toolchain.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import os
|
|
33
|
+
import re
|
|
34
|
+
import shutil
|
|
35
|
+
from dataclasses import dataclass
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Callable, List, Optional
|
|
38
|
+
|
|
39
|
+
from . import icons
|
|
40
|
+
from .config import AppConfig
|
|
41
|
+
|
|
42
|
+
TEMPLATE_PACKAGE = "com.pythonnative.android_template"
|
|
43
|
+
"""The fixed package the bundled Android template ships under."""
|
|
44
|
+
|
|
45
|
+
TEXT_SUFFIXES = {".kt", ".java", ".gradle", ".xml", ".pro", ".properties", ".cfg"}
|
|
46
|
+
|
|
47
|
+
_SOURCE_ROOTS = (
|
|
48
|
+
("app", "src", "main", "java"),
|
|
49
|
+
("app", "src", "test", "java"),
|
|
50
|
+
("app", "src", "androidTest", "java"),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
Logger = Callable[[str], None]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class AndroidLayout:
|
|
58
|
+
"""Resolved paths within a configured Android project.
|
|
59
|
+
|
|
60
|
+
Attributes:
|
|
61
|
+
project_dir: The Gradle project root (``.../android_template``).
|
|
62
|
+
application_id: The final Android application id.
|
|
63
|
+
python_root: Chaquopy Python source root
|
|
64
|
+
(``app/src/main/python``).
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
project_dir: Path
|
|
68
|
+
application_id: str
|
|
69
|
+
python_root: Path
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def configure(
|
|
73
|
+
project_dir: Path,
|
|
74
|
+
config: AppConfig,
|
|
75
|
+
*,
|
|
76
|
+
dev_lib_root: Optional[Path] = None,
|
|
77
|
+
release: bool = False,
|
|
78
|
+
log: Optional[Logger] = None,
|
|
79
|
+
) -> AndroidLayout:
|
|
80
|
+
"""Fully configure a staged Android template for ``config``.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
project_dir: The staged ``android_template`` directory.
|
|
84
|
+
config: The validated app configuration.
|
|
85
|
+
dev_lib_root: Path to an in-repo ``pythonnative`` package to
|
|
86
|
+
bundle (dev checkout); ``None`` to rely on the PyPI install.
|
|
87
|
+
release: Ship bytecode only (Chaquopy ``pyc.src``). Debug builds
|
|
88
|
+
keep the ``.py`` sources in the APK so tracebacks show code
|
|
89
|
+
and the dev client can tell what the app already runs.
|
|
90
|
+
log: Optional progress logger.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
An [`AndroidLayout`][pythonnative.project.android.AndroidLayout]
|
|
94
|
+
describing the configured project.
|
|
95
|
+
"""
|
|
96
|
+
emit: Logger = log or (lambda _message: None)
|
|
97
|
+
project_dir = Path(project_dir)
|
|
98
|
+
|
|
99
|
+
relocate_package(project_dir, TEMPLATE_PACKAGE, config.application_id)
|
|
100
|
+
configure_gradle(project_dir, config, release=release)
|
|
101
|
+
configure_settings_gradle(project_dir, config)
|
|
102
|
+
configure_strings(project_dir, config)
|
|
103
|
+
configure_manifest(project_dir, config)
|
|
104
|
+
write_requirements(project_dir, config)
|
|
105
|
+
|
|
106
|
+
_apply_branding(project_dir, config, emit)
|
|
107
|
+
|
|
108
|
+
python_root = stage_python_sources(project_dir, config, dev_lib_root=dev_lib_root)
|
|
109
|
+
emit(f"Configured Android project ({config.application_id}).")
|
|
110
|
+
return AndroidLayout(project_dir=project_dir, application_id=config.application_id, python_root=python_root)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ======================================================================
|
|
114
|
+
# Package relocation
|
|
115
|
+
# ======================================================================
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def relocate_package(project_dir: Path, old_package: str, new_package: str) -> None:
|
|
119
|
+
"""Rewrite and move the template's Java/Kotlin package.
|
|
120
|
+
|
|
121
|
+
Replaces every occurrence of ``old_package`` with ``new_package`` in
|
|
122
|
+
all text sources, then moves the source directories from the old
|
|
123
|
+
package path to the new one. A no-op when the packages are equal.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
project_dir: The staged Android project root.
|
|
127
|
+
old_package: The dotted package currently in the template.
|
|
128
|
+
new_package: The desired dotted package (the app's id).
|
|
129
|
+
"""
|
|
130
|
+
if old_package == new_package:
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
_replace_in_tree(project_dir, old_package, new_package)
|
|
134
|
+
|
|
135
|
+
old_rel = old_package.replace(".", os.sep)
|
|
136
|
+
new_rel = new_package.replace(".", os.sep)
|
|
137
|
+
for parts in _SOURCE_ROOTS:
|
|
138
|
+
source_root = project_dir.joinpath(*parts)
|
|
139
|
+
old_dir = source_root / old_rel
|
|
140
|
+
if not old_dir.is_dir():
|
|
141
|
+
continue
|
|
142
|
+
new_dir = source_root / new_rel
|
|
143
|
+
new_dir.mkdir(parents=True, exist_ok=True)
|
|
144
|
+
for entry in old_dir.iterdir():
|
|
145
|
+
shutil.move(str(entry), str(new_dir / entry.name))
|
|
146
|
+
_prune_empty_dirs(source_root, old_rel)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _replace_in_tree(root: Path, needle: str, replacement: str) -> None:
|
|
150
|
+
for path in root.rglob("*"):
|
|
151
|
+
if not path.is_file() or path.suffix not in TEXT_SUFFIXES:
|
|
152
|
+
continue
|
|
153
|
+
try:
|
|
154
|
+
text = path.read_text(encoding="utf-8")
|
|
155
|
+
except (UnicodeDecodeError, OSError):
|
|
156
|
+
continue
|
|
157
|
+
if needle in text:
|
|
158
|
+
path.write_text(text.replace(needle, replacement), encoding="utf-8")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _prune_empty_dirs(source_root: Path, relative: str) -> None:
|
|
162
|
+
current = source_root / relative
|
|
163
|
+
while current != source_root and current.is_dir() and not any(current.iterdir()):
|
|
164
|
+
current.rmdir()
|
|
165
|
+
current = current.parent
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# ======================================================================
|
|
169
|
+
# Gradle / manifest / resources
|
|
170
|
+
# ======================================================================
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def configure_gradle(project_dir: Path, config: AppConfig, *, release: bool = False) -> None:
|
|
174
|
+
"""Write identity, SDK levels, ABIs, Python version, bytecode policy, and signing.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
project_dir: The staged Android project root.
|
|
178
|
+
config: The validated app configuration.
|
|
179
|
+
release: Compile app sources to bytecode and drop the ``.py``
|
|
180
|
+
files from the APK (Chaquopy's default). Debug builds keep
|
|
181
|
+
them, for tracebacks with source lines and for the dev
|
|
182
|
+
client's overlay seeding.
|
|
183
|
+
"""
|
|
184
|
+
gradle_path = project_dir / "app" / "build.gradle"
|
|
185
|
+
content = gradle_path.read_text(encoding="utf-8")
|
|
186
|
+
|
|
187
|
+
content = re.sub(r"src\s+(true|false)", f"src {'true' if release else 'false'}", content, count=1)
|
|
188
|
+
|
|
189
|
+
content = re.sub(r"versionCode\s+\d+", f"versionCode {config.build}", content)
|
|
190
|
+
content = re.sub(r'versionName\s+"[^"]*"', f'versionName "{config.version}"', content)
|
|
191
|
+
content = re.sub(r"minSdk\s+\d+", f"minSdk {config.android.min_sdk}", content)
|
|
192
|
+
content = re.sub(r"targetSdk\s+\d+", f"targetSdk {config.android.target_sdk}", content)
|
|
193
|
+
content = re.sub(r"compileSdk\s+\d+", f"compileSdk {config.android.compile_sdk}", content)
|
|
194
|
+
content = re.sub(r'version\s+"3\.\d+"', f'version "{config.python_version}"', content)
|
|
195
|
+
|
|
196
|
+
abi_csv = ", ".join(f'"{abi}"' for abi in config.android.abi_filters)
|
|
197
|
+
content = re.sub(r"abiFilters[^\n]*", f"abiFilters {abi_csv}", content)
|
|
198
|
+
|
|
199
|
+
if config.extra_index_urls:
|
|
200
|
+
content = _inject_pip_options(content, config)
|
|
201
|
+
|
|
202
|
+
if config.android.signing.is_configured:
|
|
203
|
+
content = _inject_signing(content, config)
|
|
204
|
+
|
|
205
|
+
gradle_path.write_text(content, encoding="utf-8")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _inject_pip_options(content: str, config: AppConfig) -> str:
|
|
209
|
+
"""Add ``--extra-index-url`` pip options for ``[requirements].extra_index_urls``.
|
|
210
|
+
|
|
211
|
+
Chaquopy runs pip at Gradle build time; ``options`` lines inside the
|
|
212
|
+
``pip { }`` block are passed straight through, so a private index
|
|
213
|
+
declared once in ``pythonnative.toml`` applies to Android too.
|
|
214
|
+
"""
|
|
215
|
+
marker = ' install "-r", "requirements.txt"'
|
|
216
|
+
if marker not in content:
|
|
217
|
+
return content
|
|
218
|
+
lines = "".join(
|
|
219
|
+
f' options "--extra-index-url", "{_gradle_escape(url)}"\n' for url in config.extra_index_urls
|
|
220
|
+
)
|
|
221
|
+
return content.replace(marker, lines + marker, 1)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _gradle_escape(value: str) -> str:
|
|
225
|
+
return value.replace("\\", "\\\\").replace('"', '\\"')
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _inject_signing(content: str, config: AppConfig) -> str:
|
|
229
|
+
signing = config.android.signing
|
|
230
|
+
assert signing.keystore is not None and signing.key_alias is not None
|
|
231
|
+
keystore_path = config.resolve_path(signing.keystore).as_posix()
|
|
232
|
+
block = (
|
|
233
|
+
" signingConfigs {\n"
|
|
234
|
+
" release {\n"
|
|
235
|
+
f" storeFile file('{keystore_path}')\n"
|
|
236
|
+
f' storePassword System.getenv("{signing.store_password_env}")\n'
|
|
237
|
+
f" keyAlias '{signing.key_alias}'\n"
|
|
238
|
+
f' keyPassword System.getenv("{signing.key_password_env}")\n'
|
|
239
|
+
" }\n"
|
|
240
|
+
" }\n"
|
|
241
|
+
)
|
|
242
|
+
if "signingConfigs {" not in content:
|
|
243
|
+
content = content.replace(" buildTypes {", block + " buildTypes {", 1)
|
|
244
|
+
if "signingConfig signingConfigs.release" not in content:
|
|
245
|
+
content = content.replace(
|
|
246
|
+
" release {\n minifyEnabled false",
|
|
247
|
+
" release {\n signingConfig signingConfigs.release\n minifyEnabled false",
|
|
248
|
+
1,
|
|
249
|
+
)
|
|
250
|
+
return content
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def configure_settings_gradle(project_dir: Path, config: AppConfig) -> None:
|
|
254
|
+
"""Update ``rootProject.name`` to the configured project name.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
project_dir: The staged Android project root.
|
|
258
|
+
config: The validated app configuration.
|
|
259
|
+
"""
|
|
260
|
+
settings_path = project_dir / "settings.gradle"
|
|
261
|
+
if not settings_path.is_file():
|
|
262
|
+
return
|
|
263
|
+
content = settings_path.read_text(encoding="utf-8")
|
|
264
|
+
safe_name = re.sub(r"[^A-Za-z0-9_]", "_", config.name) or "app"
|
|
265
|
+
content = re.sub(r'rootProject\.name\s*=\s*"[^"]*"', f'rootProject.name = "{safe_name}"', content)
|
|
266
|
+
settings_path.write_text(content, encoding="utf-8")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def configure_strings(project_dir: Path, config: AppConfig) -> None:
|
|
270
|
+
"""Set the ``app_name`` and ``pn_entry_module`` string resources.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
project_dir: The staged Android project root.
|
|
274
|
+
config: The validated app configuration.
|
|
275
|
+
"""
|
|
276
|
+
strings_path = project_dir / "app" / "src" / "main" / "res" / "values" / "strings.xml"
|
|
277
|
+
if not strings_path.is_file():
|
|
278
|
+
return
|
|
279
|
+
content = strings_path.read_text(encoding="utf-8")
|
|
280
|
+
escaped = _xml_escape(config.display_name)
|
|
281
|
+
content = re.sub(
|
|
282
|
+
r'(<string name="app_name">)(.*?)(</string>)',
|
|
283
|
+
rf"\g<1>{escaped}\g<3>",
|
|
284
|
+
content,
|
|
285
|
+
flags=re.DOTALL,
|
|
286
|
+
)
|
|
287
|
+
content = re.sub(
|
|
288
|
+
r'(<string name="pn_entry_module"[^>]*>)(.*?)(</string>)',
|
|
289
|
+
rf"\g<1>{_xml_escape(config.entry_module)}\g<3>",
|
|
290
|
+
content,
|
|
291
|
+
flags=re.DOTALL,
|
|
292
|
+
)
|
|
293
|
+
strings_path.write_text(content, encoding="utf-8")
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def configure_manifest(project_dir: Path, config: AppConfig) -> None:
|
|
297
|
+
"""Inject permissions, the launch orientation, and deep-link filters.
|
|
298
|
+
|
|
299
|
+
Args:
|
|
300
|
+
project_dir: The staged Android project root.
|
|
301
|
+
config: The validated app configuration.
|
|
302
|
+
"""
|
|
303
|
+
manifest_path = project_dir / "app" / "src" / "main" / "AndroidManifest.xml"
|
|
304
|
+
content = manifest_path.read_text(encoding="utf-8")
|
|
305
|
+
|
|
306
|
+
permissions = config.resolved_permissions().android_permissions
|
|
307
|
+
if permissions:
|
|
308
|
+
lines = "".join(f' <uses-permission android:name="{name}" />\n' for name in permissions)
|
|
309
|
+
content = content.replace(" <application", f"{lines}\n <application", 1)
|
|
310
|
+
|
|
311
|
+
orientation_attr = _ANDROID_ORIENTATION.get(config.orientation)
|
|
312
|
+
if orientation_attr:
|
|
313
|
+
content = content.replace(
|
|
314
|
+
' android:exported="true"',
|
|
315
|
+
f' android:exported="true"\n android:screenOrientation="{orientation_attr}"',
|
|
316
|
+
1,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
if config.url_schemes:
|
|
320
|
+
schemes = "".join(f' <data android:scheme="{scheme}" />\n' for scheme in config.url_schemes)
|
|
321
|
+
deep_link_filter = (
|
|
322
|
+
" <intent-filter>\n"
|
|
323
|
+
' <action android:name="android.intent.action.VIEW" />\n'
|
|
324
|
+
' <category android:name="android.intent.category.DEFAULT" />\n'
|
|
325
|
+
' <category android:name="android.intent.category.BROWSABLE" />\n'
|
|
326
|
+
f"{schemes}"
|
|
327
|
+
" </intent-filter>\n"
|
|
328
|
+
)
|
|
329
|
+
content = content.replace(" </activity>", f"{deep_link_filter} </activity>", 1)
|
|
330
|
+
|
|
331
|
+
manifest_path.write_text(content, encoding="utf-8")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
_ANDROID_ORIENTATION = {
|
|
335
|
+
"portrait": "portrait",
|
|
336
|
+
"landscape": "sensorLandscape",
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def write_requirements(project_dir: Path, config: AppConfig) -> None:
|
|
341
|
+
"""Generate ``app/requirements.txt`` from ``[requirements].packages``.
|
|
342
|
+
|
|
343
|
+
Chaquopy installs from this file (referenced by ``build.gradle``).
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
project_dir: The staged Android project root.
|
|
347
|
+
config: The validated app configuration.
|
|
348
|
+
"""
|
|
349
|
+
requirements_path = project_dir / "app" / "requirements.txt"
|
|
350
|
+
from .deps import android_targets
|
|
351
|
+
from .lockfile import requirements
|
|
352
|
+
|
|
353
|
+
body = requirements(config, android_targets(config))
|
|
354
|
+
if body is None:
|
|
355
|
+
body = "\n".join(config.requirements)
|
|
356
|
+
requirements_path.write_text(body + ("\n" if body else ""), encoding="utf-8")
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
# ======================================================================
|
|
360
|
+
# Branding (icons + splash)
|
|
361
|
+
# ======================================================================
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _apply_branding(project_dir: Path, config: AppConfig, emit: Logger) -> None:
|
|
365
|
+
res_dir = project_dir / "app" / "src" / "main" / "res"
|
|
366
|
+
icon_path = config.resolve_path(config.icon) if config.icon else None
|
|
367
|
+
splash_path = config.resolve_path(config.splash) if config.splash else None
|
|
368
|
+
|
|
369
|
+
if icon_path and icons.has_source(icon_path):
|
|
370
|
+
if icons.generate_android_icons(icon_path, res_dir):
|
|
371
|
+
emit("Generated Android launcher icons.")
|
|
372
|
+
else:
|
|
373
|
+
emit("Skipping Android icons: Pillow not installed (pip install 'pythonnative[build]').")
|
|
374
|
+
|
|
375
|
+
if splash_path and icons.has_source(splash_path):
|
|
376
|
+
if icons.pillow_available():
|
|
377
|
+
configure_splash(project_dir, config, splash_path)
|
|
378
|
+
emit("Configured Android splash screen.")
|
|
379
|
+
else:
|
|
380
|
+
emit("Skipping Android splash: Pillow not installed (pip install 'pythonnative[build]').")
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def configure_splash(project_dir: Path, config: AppConfig, splash_path: Path) -> None:
|
|
384
|
+
"""Wire up an Android 12+ splash screen from the splash asset.
|
|
385
|
+
|
|
386
|
+
Adds the ``androidx.core:core-splashscreen`` dependency, a
|
|
387
|
+
``Theme.App.Starting`` splash theme (with the splash background color
|
|
388
|
+
and centered icon), installs the splash in ``MainActivity``, and
|
|
389
|
+
points the launcher activity at the splash theme.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
project_dir: The staged Android project root.
|
|
393
|
+
config: The validated app configuration.
|
|
394
|
+
splash_path: Path to the source splash image.
|
|
395
|
+
"""
|
|
396
|
+
res_dir = project_dir / "app" / "src" / "main" / "res"
|
|
397
|
+
background = icons.dominant_background_color(splash_path) or "#FFFFFF"
|
|
398
|
+
|
|
399
|
+
icons.generate_android_splash_icon(splash_path, res_dir / "drawable-xxxhdpi" / "pn_splash_icon.png")
|
|
400
|
+
|
|
401
|
+
_upsert_color(res_dir / "values" / "colors.xml", "pn_splash_background", background)
|
|
402
|
+
|
|
403
|
+
splash_style = (
|
|
404
|
+
' <style name="Theme.App.Starting" parent="Theme.SplashScreen">\n'
|
|
405
|
+
' <item name="windowSplashScreenBackground">@color/pn_splash_background</item>\n'
|
|
406
|
+
' <item name="windowSplashScreenAnimatedIcon">@drawable/pn_splash_icon</item>\n'
|
|
407
|
+
' <item name="postSplashScreenTheme">@style/Theme.Android_template</item>\n'
|
|
408
|
+
" </style>\n"
|
|
409
|
+
)
|
|
410
|
+
for themes in (res_dir / "values" / "themes.xml", res_dir / "values-night" / "themes.xml"):
|
|
411
|
+
_insert_style(themes, splash_style)
|
|
412
|
+
|
|
413
|
+
_add_gradle_dependency(
|
|
414
|
+
project_dir / "app" / "build.gradle",
|
|
415
|
+
"implementation 'androidx.core:core-splashscreen:1.0.1'",
|
|
416
|
+
)
|
|
417
|
+
_install_splash_in_activity(project_dir, config)
|
|
418
|
+
|
|
419
|
+
# Point the app theme at the splash theme; ``installSplashScreen()`` swaps
|
|
420
|
+
# to ``postSplashScreenTheme`` (the original theme) right after launch.
|
|
421
|
+
manifest_path = project_dir / "app" / "src" / "main" / "AndroidManifest.xml"
|
|
422
|
+
manifest = manifest_path.read_text(encoding="utf-8")
|
|
423
|
+
manifest = manifest.replace(
|
|
424
|
+
'android:theme="@style/Theme.Android_template"',
|
|
425
|
+
'android:theme="@style/Theme.App.Starting"',
|
|
426
|
+
1,
|
|
427
|
+
)
|
|
428
|
+
manifest_path.write_text(manifest, encoding="utf-8")
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def _install_splash_in_activity(project_dir: Path, config: AppConfig) -> None:
|
|
432
|
+
activity = project_dir / "app" / "src" / "main" / "java" / config.android_package_path / "MainActivity.kt"
|
|
433
|
+
if not activity.is_file():
|
|
434
|
+
return
|
|
435
|
+
content = activity.read_text(encoding="utf-8")
|
|
436
|
+
if "installSplashScreen" in content:
|
|
437
|
+
return
|
|
438
|
+
content = content.replace(
|
|
439
|
+
"import androidx.appcompat.app.AppCompatActivity",
|
|
440
|
+
"import androidx.appcompat.app.AppCompatActivity\n"
|
|
441
|
+
"import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen",
|
|
442
|
+
1,
|
|
443
|
+
)
|
|
444
|
+
content = content.replace(
|
|
445
|
+
" super.onCreate(savedInstanceState)",
|
|
446
|
+
" installSplashScreen()\n super.onCreate(savedInstanceState)",
|
|
447
|
+
1,
|
|
448
|
+
)
|
|
449
|
+
activity.write_text(content, encoding="utf-8")
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
# ======================================================================
|
|
453
|
+
# Python source staging
|
|
454
|
+
# ======================================================================
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def stage_python_sources(
|
|
458
|
+
project_dir: Path,
|
|
459
|
+
config: AppConfig,
|
|
460
|
+
*,
|
|
461
|
+
dev_lib_root: Optional[Path] = None,
|
|
462
|
+
) -> Path:
|
|
463
|
+
"""Copy the user's ``app/`` and (optionally) the in-repo library.
|
|
464
|
+
|
|
465
|
+
Args:
|
|
466
|
+
project_dir: The staged Android project root.
|
|
467
|
+
config: The validated app configuration.
|
|
468
|
+
dev_lib_root: Path to an in-repo ``pythonnative`` package to
|
|
469
|
+
bundle, or ``None``.
|
|
470
|
+
|
|
471
|
+
Returns:
|
|
472
|
+
The Chaquopy Python source root (``app/src/main/python``).
|
|
473
|
+
"""
|
|
474
|
+
python_root = project_dir / "app" / "src" / "main" / "python"
|
|
475
|
+
python_root.mkdir(parents=True, exist_ok=True)
|
|
476
|
+
|
|
477
|
+
app_src = config.project_root / "app"
|
|
478
|
+
if app_src.is_dir():
|
|
479
|
+
shutil.copytree(app_src, python_root / "app", dirs_exist_ok=True)
|
|
480
|
+
|
|
481
|
+
if dev_lib_root and dev_lib_root.is_dir():
|
|
482
|
+
shutil.copytree(
|
|
483
|
+
dev_lib_root,
|
|
484
|
+
python_root / "pythonnative",
|
|
485
|
+
dirs_exist_ok=True,
|
|
486
|
+
ignore=LIB_IGNORE,
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
return python_root
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
LIB_IGNORE = shutil.ignore_patterns("templates", "native", "*.so", "__pycache__", "*.pyc", "*.pyo")
|
|
493
|
+
"""Ignore rules for bundling the ``pythonnative`` package (skips templates)."""
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
# ======================================================================
|
|
497
|
+
# Small XML/text helpers
|
|
498
|
+
# ======================================================================
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def _xml_escape(value: str) -> str:
|
|
502
|
+
return value.replace("&", "&").replace("<", "<").replace(">", ">")
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _upsert_color(colors_path: Path, name: str, value: str) -> None:
|
|
506
|
+
if not colors_path.is_file():
|
|
507
|
+
colors_path.parent.mkdir(parents=True, exist_ok=True)
|
|
508
|
+
colors_path.write_text(
|
|
509
|
+
'<?xml version="1.0" encoding="utf-8"?>\n<resources>\n</resources>\n',
|
|
510
|
+
encoding="utf-8",
|
|
511
|
+
)
|
|
512
|
+
content = colors_path.read_text(encoding="utf-8")
|
|
513
|
+
entry = f' <color name="{name}">{value}</color>\n'
|
|
514
|
+
if f'name="{name}"' in content:
|
|
515
|
+
content = re.sub(
|
|
516
|
+
rf'(<color name="{re.escape(name)}">)(.*?)(</color>)',
|
|
517
|
+
rf"\g<1>{value}\g<3>",
|
|
518
|
+
content,
|
|
519
|
+
)
|
|
520
|
+
else:
|
|
521
|
+
content = content.replace("</resources>", f"{entry}</resources>", 1)
|
|
522
|
+
colors_path.write_text(content, encoding="utf-8")
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def _insert_style(themes_path: Path, style_block: str) -> None:
|
|
526
|
+
if not themes_path.is_file():
|
|
527
|
+
return
|
|
528
|
+
content = themes_path.read_text(encoding="utf-8")
|
|
529
|
+
if "Theme.App.Starting" in content:
|
|
530
|
+
return
|
|
531
|
+
content = content.replace("</resources>", f"{style_block}</resources>", 1)
|
|
532
|
+
themes_path.write_text(content, encoding="utf-8")
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _add_gradle_dependency(gradle_path: Path, dependency: str) -> None:
|
|
536
|
+
content = gradle_path.read_text(encoding="utf-8")
|
|
537
|
+
if dependency in content:
|
|
538
|
+
return
|
|
539
|
+
content = content.replace("dependencies {\n", f"dependencies {{\n {dependency}\n", 1)
|
|
540
|
+
gradle_path.write_text(content, encoding="utf-8")
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def collect_logcat_filters() -> List[str]:
|
|
544
|
+
"""Return the logcat tag filters used when streaming device logs.
|
|
545
|
+
|
|
546
|
+
Returns:
|
|
547
|
+
A list of ``tag:level`` filter specs ending with ``*:S`` to
|
|
548
|
+
silence everything else.
|
|
549
|
+
"""
|
|
550
|
+
return [
|
|
551
|
+
"python.stdout:V",
|
|
552
|
+
"python.stderr:V",
|
|
553
|
+
"PythonNative:V",
|
|
554
|
+
"AndroidRuntime:E",
|
|
555
|
+
"System.err:W",
|
|
556
|
+
"*:S",
|
|
557
|
+
]
|