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
pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package com.pythonnative.runtime.components
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
import androidx.core.view.ViewCompat
|
|
6
|
+
import androidx.core.view.WindowInsetsCompat
|
|
7
|
+
import com.pythonnative.runtime.PNBridge
|
|
8
|
+
import com.pythonnative.runtime.modules.ModuleEvents
|
|
9
|
+
import com.pythonnative.runtime.views.PNFrameLayout
|
|
10
|
+
import org.json.JSONObject
|
|
11
|
+
import kotlin.math.max
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Flex container (shared by `View`, `Column`, and `Row`): a bare
|
|
15
|
+
* [PNFrameLayout]. All flex semantics are computed by the Python layout
|
|
16
|
+
* engine and applied through `setFrame`; the container is just a
|
|
17
|
+
* positioning surface with `pointer_events` interception.
|
|
18
|
+
*/
|
|
19
|
+
open class ViewManager : ComponentManager() {
|
|
20
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View = PNFrameLayout(context)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Empty layout placeholder used as a flexible gap. */
|
|
24
|
+
class SpacerManager : ComponentManager() {
|
|
25
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View = View(context)
|
|
26
|
+
|
|
27
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
28
|
+
// Spacers carry no visual props of their own.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Safe-area container: a flex container with `fitsSystemWindows`. */
|
|
33
|
+
class SafeAreaViewManager : ViewManager() {
|
|
34
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View {
|
|
35
|
+
val view = PNFrameLayout(context)
|
|
36
|
+
view.fitsSystemWindows = true
|
|
37
|
+
return view
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Vanilla container that publishes the keyboard height (from window
|
|
43
|
+
* insets) as a `Host` module `keyboard` event so the Python-side
|
|
44
|
+
* component can compute its offset.
|
|
45
|
+
*/
|
|
46
|
+
class KeyboardAvoidingViewManager : ViewManager() {
|
|
47
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View {
|
|
48
|
+
val view = PNFrameLayout(context)
|
|
49
|
+
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
|
|
50
|
+
publishKeyboard(v, insets)
|
|
51
|
+
insets
|
|
52
|
+
}
|
|
53
|
+
view.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
|
|
54
|
+
ViewCompat.getRootWindowInsets(v)?.let { publishKeyboard(v, it) }
|
|
55
|
+
}
|
|
56
|
+
return view
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private fun publishKeyboard(view: View, insets: WindowInsetsCompat) {
|
|
60
|
+
val density = PNBridge.density()
|
|
61
|
+
val ime = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
|
|
62
|
+
val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
|
|
63
|
+
val height = max(0, ime - bars) / density
|
|
64
|
+
val last = stateOf(view)["keyboard_height"] as? Double
|
|
65
|
+
if (last != null && last == height.toDouble()) return
|
|
66
|
+
stateOf(view)["keyboard_height"] = height.toDouble()
|
|
67
|
+
ModuleEvents.emit("Host", "keyboard", JSONObject().put("height", height.toDouble()))
|
|
68
|
+
}
|
|
69
|
+
}
|
pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
package com.pythonnative.runtime.components
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.res.ColorStateList
|
|
5
|
+
import android.view.View
|
|
6
|
+
import android.widget.CheckBox
|
|
7
|
+
import android.widget.CompoundButton
|
|
8
|
+
import android.widget.ProgressBar
|
|
9
|
+
import android.widget.SeekBar
|
|
10
|
+
import android.widget.Switch
|
|
11
|
+
import com.pythonnative.runtime.bridge.JsonUtil
|
|
12
|
+
import com.pythonnative.runtime.bridge.num
|
|
13
|
+
import com.pythonnative.runtime.bridge.str
|
|
14
|
+
import com.pythonnative.runtime.bridge.value
|
|
15
|
+
import org.json.JSONObject
|
|
16
|
+
|
|
17
|
+
/** Base for `CompoundButton` widgets: `on_change` wiring with a suppress guard. */
|
|
18
|
+
abstract class CheckedManager : ComponentManager() {
|
|
19
|
+
protected fun bindChecked(button: CompoundButton) {
|
|
20
|
+
button.setOnCheckedChangeListener { b, checked ->
|
|
21
|
+
if (stateOf(b)["suppress"] == true) return@setOnCheckedChangeListener
|
|
22
|
+
fire(b, "on_change", checked)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** `Switch` element. */
|
|
28
|
+
class SwitchManager : CheckedManager() {
|
|
29
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View {
|
|
30
|
+
val sw = Switch(context)
|
|
31
|
+
bindChecked(sw)
|
|
32
|
+
return sw
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
36
|
+
val sw = view as Switch
|
|
37
|
+
if (props.has("value")) {
|
|
38
|
+
val state = stateOf(sw)
|
|
39
|
+
state["suppress"] = true
|
|
40
|
+
try {
|
|
41
|
+
sw.isChecked = JsonUtil.truthy(props.value("value"))
|
|
42
|
+
} finally {
|
|
43
|
+
state["suppress"] = false
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (props.has("disabled")) sw.isEnabled = !JsonUtil.truthy(props.value("disabled"))
|
|
47
|
+
if (props.has("enabled")) sw.isEnabled = JsonUtil.truthy(props.value("enabled"))
|
|
48
|
+
PNColor.parse(props.value("on_tint_color") ?: props.value("tint_color"))?.let {
|
|
49
|
+
sw.thumbTintList = ColorStateList(
|
|
50
|
+
arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
|
|
51
|
+
intArrayOf(it, 0xFFFAFAFA.toInt()),
|
|
52
|
+
)
|
|
53
|
+
sw.trackTintList = ColorStateList(
|
|
54
|
+
arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf()),
|
|
55
|
+
intArrayOf(PNColor.withAlpha(it, 0.5), 0x61000000),
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
PNColor.parse(props.value("thumb_color"))?.let { sw.thumbTintList = ColorStateList.valueOf(it) }
|
|
59
|
+
ViewStyler.applyAccessibility(sw, props)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** `Checkbox` element with an optional inline label. */
|
|
64
|
+
class CheckboxManager : CheckedManager() {
|
|
65
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View {
|
|
66
|
+
val cb = CheckBox(context)
|
|
67
|
+
bindChecked(cb)
|
|
68
|
+
return cb
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
72
|
+
val cb = view as CheckBox
|
|
73
|
+
if (props.has("label")) cb.text = props.str("label") ?: ""
|
|
74
|
+
if (props.has("value")) {
|
|
75
|
+
val state = stateOf(cb)
|
|
76
|
+
state["suppress"] = true
|
|
77
|
+
try {
|
|
78
|
+
cb.isChecked = JsonUtil.truthy(props.value("value"))
|
|
79
|
+
} finally {
|
|
80
|
+
state["suppress"] = false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (props.has("disabled")) cb.isEnabled = !JsonUtil.truthy(props.value("disabled"))
|
|
84
|
+
PNColor.parse(props.value("color"))?.let { cb.buttonTintList = ColorStateList.valueOf(it) }
|
|
85
|
+
ViewStyler.applyAccessibility(cb, props)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** `ProgressBar` element: a horizontal determinate bar with a 0..1 `value`. */
|
|
90
|
+
class ProgressBarManager : ComponentManager() {
|
|
91
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View {
|
|
92
|
+
val pb = ProgressBar(context, null, android.R.attr.progressBarStyleHorizontal)
|
|
93
|
+
pb.max = 1000
|
|
94
|
+
return pb
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
98
|
+
val pb = view as ProgressBar
|
|
99
|
+
props.num("value")?.let { pb.progress = (it.coerceIn(0.0, 1.0) * 1000).toInt() }
|
|
100
|
+
PNColor.parse(props.value("color"))?.let { pb.progressTintList = ColorStateList.valueOf(it) }
|
|
101
|
+
PNColor.parse(props.value("track_color"))?.let {
|
|
102
|
+
val track = ColorStateList.valueOf(it)
|
|
103
|
+
pb.progressBackgroundTintList = track
|
|
104
|
+
pb.secondaryProgressTintList = track
|
|
105
|
+
}
|
|
106
|
+
if (props.has("indeterminate")) pb.isIndeterminate = JsonUtil.truthy(props.value("indeterminate"))
|
|
107
|
+
ViewStyler.apply(pb, props)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** `ActivityIndicator` element: an indeterminate spinner. */
|
|
112
|
+
class ActivityIndicatorManager : ComponentManager() {
|
|
113
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View = ProgressBar(context)
|
|
114
|
+
|
|
115
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
116
|
+
val pb = view as ProgressBar
|
|
117
|
+
if (props.has("animating")) {
|
|
118
|
+
pb.visibility = if (JsonUtil.truthy(props.value("animating"))) View.VISIBLE else View.GONE
|
|
119
|
+
}
|
|
120
|
+
if (props.has("hides_when_stopped") && !JsonUtil.truthy(props.value("hides_when_stopped"))) {
|
|
121
|
+
pb.visibility = View.VISIBLE
|
|
122
|
+
}
|
|
123
|
+
PNColor.parse(props.value("color"))?.let { pb.indeterminateTintList = ColorStateList.valueOf(it) }
|
|
124
|
+
props.str("size")?.let {
|
|
125
|
+
// The framework ProgressBar has no runtime size switch; scale "large".
|
|
126
|
+
val scale = if (it == "large") 1.5f else 1f
|
|
127
|
+
pb.scaleX = scale
|
|
128
|
+
pb.scaleY = scale
|
|
129
|
+
}
|
|
130
|
+
ViewStyler.apply(pb, props)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** `Slider` element: a `SeekBar` mapped onto `min_value..max_value`. */
|
|
135
|
+
class SliderManager : ComponentManager() {
|
|
136
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View {
|
|
137
|
+
val sb = SeekBar(context)
|
|
138
|
+
sb.max = 1000
|
|
139
|
+
sb.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
|
140
|
+
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
|
141
|
+
if (!fromUser) return
|
|
142
|
+
fire(seekBar, "on_change", valueFor(seekBar, progress))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
|
146
|
+
fire(seekBar, "on_sliding_start", valueFor(seekBar, seekBar.progress))
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
|
150
|
+
fire(seekBar, "on_sliding_complete", valueFor(seekBar, seekBar.progress))
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
return sb
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private fun range(view: View): Pair<Double, Double> {
|
|
157
|
+
val merged = propsOf(view)
|
|
158
|
+
val mn = merged.num("min_value") ?: merged.num("minimum_value") ?: 0.0
|
|
159
|
+
val mx = merged.num("max_value") ?: merged.num("maximum_value") ?: 1.0
|
|
160
|
+
return Pair(mn, mx)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private fun valueFor(view: View, progress: Int): Double {
|
|
164
|
+
val (mn, mx) = range(view)
|
|
165
|
+
val span = if (mx != mn) mx - mn else 1.0
|
|
166
|
+
var value = mn + (progress / 1000.0) * span
|
|
167
|
+
propsOf(view).num("step")?.takeIf { it > 0 }?.let { step ->
|
|
168
|
+
value = mn + Math.round((value - mn) / step) * step
|
|
169
|
+
}
|
|
170
|
+
return value
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
174
|
+
val sb = view as SeekBar
|
|
175
|
+
val (mn, mx) = range(sb)
|
|
176
|
+
val span = if (mx != mn) mx - mn else 1.0
|
|
177
|
+
props.num("value")?.let { sb.progress = (((it - mn) / span) * 1000).toInt().coerceIn(0, 1000) }
|
|
178
|
+
if (props.has("disabled")) sb.isEnabled = !JsonUtil.truthy(props.value("disabled"))
|
|
179
|
+
PNColor.parse(props.value("minimum_track_tint_color") ?: props.value("tint_color"))?.let {
|
|
180
|
+
sb.progressTintList = ColorStateList.valueOf(it)
|
|
181
|
+
}
|
|
182
|
+
PNColor.parse(props.value("maximum_track_tint_color"))?.let { sb.progressBackgroundTintList = ColorStateList.valueOf(it) }
|
|
183
|
+
PNColor.parse(props.value("thumb_tint_color"))?.let { sb.thumbTintList = ColorStateList.valueOf(it) }
|
|
184
|
+
ViewStyler.applyAccessibility(sb, props)
|
|
185
|
+
}
|
|
186
|
+
}
|
pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
package com.pythonnative.runtime.components
|
|
2
|
+
|
|
3
|
+
import android.app.DatePickerDialog
|
|
4
|
+
import android.app.TimePickerDialog
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.view.View
|
|
7
|
+
import android.widget.Button
|
|
8
|
+
import com.pythonnative.runtime.bridge.PNLog
|
|
9
|
+
import com.pythonnative.runtime.bridge.str
|
|
10
|
+
import com.pythonnative.runtime.bridge.value
|
|
11
|
+
import org.json.JSONObject
|
|
12
|
+
import java.text.SimpleDateFormat
|
|
13
|
+
import java.util.Calendar
|
|
14
|
+
import java.util.Locale
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* `DatePicker` element: a trigger `Button` opening the native
|
|
18
|
+
* `DatePickerDialog` (`mode` `"date"`), `TimePickerDialog` (`"time"`),
|
|
19
|
+
* or a chained date-then-time flow (`"datetime"`). Values are ISO
|
|
20
|
+
* strings; the confirmed value is reported through `on_change`.
|
|
21
|
+
*/
|
|
22
|
+
class DatePickerManager : ComponentManager() {
|
|
23
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View {
|
|
24
|
+
val btn = Button(context)
|
|
25
|
+
btn.isAllCaps = false
|
|
26
|
+
btn.setOnClickListener { v ->
|
|
27
|
+
if (propsOf(v).value("disabled") == true) return@setOnClickListener
|
|
28
|
+
openDialog(v as Button)
|
|
29
|
+
}
|
|
30
|
+
return btn
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
34
|
+
val btn = view as Button
|
|
35
|
+
if (props.has("disabled")) btn.isEnabled = props.value("disabled") != true
|
|
36
|
+
if (props.has("value") || props.has("mode") || initial) refreshLabel(btn)
|
|
37
|
+
ViewStyler.applyAccessibility(btn, props)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private fun mode(btn: Button): String = propsOf(btn).str("mode") ?: "date"
|
|
41
|
+
|
|
42
|
+
private fun refreshLabel(btn: Button) {
|
|
43
|
+
val value = propsOf(btn).str("value")
|
|
44
|
+
btn.text = if (!value.isNullOrEmpty()) value else PLACEHOLDERS[mode(btn)] ?: "Select"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private fun openDialog(btn: Button) {
|
|
48
|
+
val mode = mode(btn)
|
|
49
|
+
val cal = parse(propsOf(btn).str("value"), mode)
|
|
50
|
+
when (mode) {
|
|
51
|
+
"time" -> openTime(btn, cal)
|
|
52
|
+
"datetime" -> openDate(btn, cal, thenTime = true)
|
|
53
|
+
else -> openDate(btn, cal, thenTime = false)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private fun openDate(btn: Button, cal: Calendar, thenTime: Boolean) {
|
|
58
|
+
val dialog = DatePickerDialog(
|
|
59
|
+
btn.context,
|
|
60
|
+
{ _, year, month, day ->
|
|
61
|
+
cal.set(Calendar.YEAR, year)
|
|
62
|
+
cal.set(Calendar.MONTH, month)
|
|
63
|
+
cal.set(Calendar.DAY_OF_MONTH, day)
|
|
64
|
+
if (thenTime) openTime(btn, cal) else commit(btn, cal)
|
|
65
|
+
},
|
|
66
|
+
cal.get(Calendar.YEAR),
|
|
67
|
+
cal.get(Calendar.MONTH),
|
|
68
|
+
cal.get(Calendar.DAY_OF_MONTH),
|
|
69
|
+
)
|
|
70
|
+
try {
|
|
71
|
+
val merged = propsOf(btn)
|
|
72
|
+
merged.str("minimum")?.takeIf { it.isNotEmpty() }?.let { dialog.datePicker.minDate = parse(it, mode(btn)).timeInMillis }
|
|
73
|
+
merged.str("maximum")?.takeIf { it.isNotEmpty() }?.let { dialog.datePicker.maxDate = parse(it, mode(btn)).timeInMillis }
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
PNLog.swallowed("DatePickerManager.minMax", e)
|
|
76
|
+
}
|
|
77
|
+
dialog.show()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun openTime(btn: Button, cal: Calendar) {
|
|
81
|
+
TimePickerDialog(
|
|
82
|
+
btn.context,
|
|
83
|
+
{ _, hour, minute ->
|
|
84
|
+
cal.set(Calendar.HOUR_OF_DAY, hour)
|
|
85
|
+
cal.set(Calendar.MINUTE, minute)
|
|
86
|
+
commit(btn, cal)
|
|
87
|
+
},
|
|
88
|
+
cal.get(Calendar.HOUR_OF_DAY),
|
|
89
|
+
cal.get(Calendar.MINUTE),
|
|
90
|
+
true,
|
|
91
|
+
).show()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private fun format(mode: String): SimpleDateFormat = SimpleDateFormat(PATTERNS[mode] ?: PATTERNS["date"]!!, Locale.US)
|
|
95
|
+
|
|
96
|
+
private fun parse(value: String?, mode: String): Calendar {
|
|
97
|
+
val cal = Calendar.getInstance()
|
|
98
|
+
if (!value.isNullOrEmpty()) {
|
|
99
|
+
try {
|
|
100
|
+
format(mode).parse(value)?.let { cal.time = it }
|
|
101
|
+
} catch (e: Exception) {
|
|
102
|
+
PNLog.swallowed("DatePickerManager.parse", e)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return cal
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private fun commit(btn: Button, cal: Calendar) {
|
|
109
|
+
val iso = try {
|
|
110
|
+
format(mode(btn)).format(cal.time)
|
|
111
|
+
} catch (e: Exception) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
propsOf(btn).put("value", iso)
|
|
115
|
+
btn.text = iso
|
|
116
|
+
fire(btn, "on_change", iso)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private companion object {
|
|
120
|
+
val PATTERNS = mapOf("date" to "yyyy-MM-dd", "time" to "HH:mm", "datetime" to "yyyy-MM-dd'T'HH:mm")
|
|
121
|
+
val PLACEHOLDERS = mapOf("date" to "Select date", "time" to "Select time", "datetime" to "Select date & time")
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
package com.pythonnative.runtime.components
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Bitmap
|
|
5
|
+
import android.graphics.BitmapFactory
|
|
6
|
+
import android.util.LruCache
|
|
7
|
+
import com.pythonnative.runtime.bridge.MainThread
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.io.FileOutputStream
|
|
10
|
+
import java.net.HttpURLConnection
|
|
11
|
+
import java.net.URL
|
|
12
|
+
import java.security.MessageDigest
|
|
13
|
+
import java.util.concurrent.Executors
|
|
14
|
+
import kotlin.math.max
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Image fetching and decoding for the `Image` manager.
|
|
18
|
+
*
|
|
19
|
+
* Remote images are downloaded on a small background pool into a disk
|
|
20
|
+
* cache under `cacheDir/pn_images` (keyed by the SHA-256 of the URL),
|
|
21
|
+
* decoded with `inSampleSize` downsampling to the target size, and kept
|
|
22
|
+
* in a memory `LruCache`. Callbacks run on the main thread.
|
|
23
|
+
*/
|
|
24
|
+
object ImageLoader {
|
|
25
|
+
private val executor = Executors.newFixedThreadPool(3) { runnable ->
|
|
26
|
+
Thread(runnable, "pn-image").apply { isDaemon = true }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private val memory: LruCache<String, Bitmap> by lazy {
|
|
30
|
+
val maxKb = (Runtime.getRuntime().maxMemory() / 1024L).toInt()
|
|
31
|
+
object : LruCache<String, Bitmap>(max(4 * 1024, maxKb / 8)) {
|
|
32
|
+
override fun sizeOf(key: String, value: Bitmap): Int = max(1, value.byteCount / 1024)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Result callback: exactly one of `bitmap` or `error` is non-null. */
|
|
37
|
+
fun interface Callback {
|
|
38
|
+
fun onResult(bitmap: Bitmap?, error: String?)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Load `url` (http/https) into a bitmap sized for `targetW` x `targetH` pixels. */
|
|
42
|
+
fun loadRemote(context: Context, url: String, targetW: Int, targetH: Int, callback: Callback) {
|
|
43
|
+
val key = "$url@$targetW"
|
|
44
|
+
memory.get(key)?.let {
|
|
45
|
+
callback.onResult(it, null)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
val cacheDir = File(context.cacheDir, "pn_images")
|
|
49
|
+
executor.execute {
|
|
50
|
+
try {
|
|
51
|
+
val file = cachedFile(cacheDir, url)
|
|
52
|
+
if (!file.exists() || file.length() == 0L) download(url, file)
|
|
53
|
+
val bitmap = decodeDownsampled(file.absolutePath, targetW, targetH)
|
|
54
|
+
if (bitmap == null) {
|
|
55
|
+
file.delete()
|
|
56
|
+
deliver(callback, null, "decode failed")
|
|
57
|
+
} else {
|
|
58
|
+
memory.put(key, bitmap)
|
|
59
|
+
deliver(callback, bitmap, null)
|
|
60
|
+
}
|
|
61
|
+
} catch (e: Exception) {
|
|
62
|
+
deliver(callback, null, e.message ?: e.javaClass.simpleName)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Decode a local file on a background thread. */
|
|
68
|
+
fun loadFile(path: String, targetW: Int, targetH: Int, callback: Callback) {
|
|
69
|
+
executor.execute {
|
|
70
|
+
val bitmap = try {
|
|
71
|
+
decodeDownsampled(path, targetW, targetH)
|
|
72
|
+
} catch (e: Exception) {
|
|
73
|
+
null
|
|
74
|
+
}
|
|
75
|
+
deliver(callback, bitmap, if (bitmap == null) "decode failed" else null)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Decode `path` with `inSampleSize` chosen so the result still covers the target. */
|
|
80
|
+
fun decodeDownsampled(path: String, targetW: Int, targetH: Int): Bitmap? {
|
|
81
|
+
val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
|
82
|
+
BitmapFactory.decodeFile(path, bounds)
|
|
83
|
+
val srcW = bounds.outWidth
|
|
84
|
+
val srcH = bounds.outHeight
|
|
85
|
+
if (srcW <= 0 || srcH <= 0) return null
|
|
86
|
+
var tw = if (targetW > 0) targetW else srcW
|
|
87
|
+
var th = if (targetH > 0) targetH else tw
|
|
88
|
+
if (tw <= 0) tw = srcW
|
|
89
|
+
if (th <= 0) th = srcH
|
|
90
|
+
var sample = 1
|
|
91
|
+
while (srcW / (sample * 2) >= tw && srcH / (sample * 2) >= th) sample *= 2
|
|
92
|
+
val opts = BitmapFactory.Options().apply { inSampleSize = sample }
|
|
93
|
+
return BitmapFactory.decodeFile(path, opts)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private fun cachedFile(dir: File, url: String): File {
|
|
97
|
+
if (!dir.exists()) dir.mkdirs()
|
|
98
|
+
val digest = MessageDigest.getInstance("SHA-256").digest(url.toByteArray(Charsets.UTF_8))
|
|
99
|
+
val hex = digest.joinToString("") { String.format("%02x", it) }
|
|
100
|
+
val path = url.substringBefore('?').substringBefore('#')
|
|
101
|
+
val ext = path.substringAfterLast('.', "").takeIf { it.length in 1..5 && it.all { c -> c.isLetterOrDigit() } }
|
|
102
|
+
return File(dir, if (ext != null) "$hex.$ext" else hex)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private fun download(url: String, target: File) {
|
|
106
|
+
var current = url
|
|
107
|
+
var redirects = 0
|
|
108
|
+
while (true) {
|
|
109
|
+
val connection = URL(current).openConnection() as HttpURLConnection
|
|
110
|
+
connection.connectTimeout = 15_000
|
|
111
|
+
connection.readTimeout = 30_000
|
|
112
|
+
connection.instanceFollowRedirects = true
|
|
113
|
+
connection.setRequestProperty("User-Agent", "PythonNative/Android")
|
|
114
|
+
try {
|
|
115
|
+
val code = connection.responseCode
|
|
116
|
+
if (code in 300..399 && redirects < 5) {
|
|
117
|
+
val location = connection.getHeaderField("Location") ?: throw IllegalStateException("redirect without Location")
|
|
118
|
+
current = URL(URL(current), location).toString()
|
|
119
|
+
redirects++
|
|
120
|
+
continue
|
|
121
|
+
}
|
|
122
|
+
if (code !in 200..299) throw IllegalStateException("HTTP $code")
|
|
123
|
+
val tmp = File(target.parentFile, target.name + ".part")
|
|
124
|
+
connection.inputStream.use { input ->
|
|
125
|
+
FileOutputStream(tmp).use { output -> input.copyTo(output) }
|
|
126
|
+
}
|
|
127
|
+
if (!tmp.renameTo(target)) {
|
|
128
|
+
tmp.copyTo(target, overwrite = true)
|
|
129
|
+
tmp.delete()
|
|
130
|
+
}
|
|
131
|
+
return
|
|
132
|
+
} finally {
|
|
133
|
+
connection.disconnect()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private fun deliver(callback: Callback, bitmap: Bitmap?, error: String?) {
|
|
139
|
+
MainThread.post { callback.onResult(bitmap, error) }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
package com.pythonnative.runtime.components
|
|
2
|
+
|
|
3
|
+
import com.pythonnative.runtime.PNBridge
|
|
4
|
+
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.content.res.ColorStateList
|
|
7
|
+
import android.graphics.BitmapFactory
|
|
8
|
+
import android.util.Base64
|
|
9
|
+
import android.view.View
|
|
10
|
+
import android.widget.ImageView
|
|
11
|
+
import com.pythonnative.runtime.bridge.PNLog
|
|
12
|
+
import com.pythonnative.runtime.bridge.str
|
|
13
|
+
import com.pythonnative.runtime.bridge.value
|
|
14
|
+
import org.json.JSONObject
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* `Image` element. Sources: `http(s)` URLs (downloaded and cached by
|
|
18
|
+
* [ImageLoader]), absolute file paths, drawable resource names, and
|
|
19
|
+
* base64 `data:` URIs. Fires `on_load` / `on_error`.
|
|
20
|
+
*/
|
|
21
|
+
class ImageManager : ComponentManager() {
|
|
22
|
+
override fun createView(context: Context, tag: Long, props: JSONObject): View = ImageView(context)
|
|
23
|
+
|
|
24
|
+
override fun applyProps(view: View, props: JSONObject, initial: Boolean) {
|
|
25
|
+
val iv = view as ImageView
|
|
26
|
+
PNColor.parse(props.value("tint_color"))?.let { iv.imageTintList = ColorStateList.valueOf(it) }
|
|
27
|
+
if (props.has("tint_color") && props.value("tint_color") == null) iv.imageTintList = null
|
|
28
|
+
PNColor.parse(props.value("placeholder_color"))?.let { iv.setBackgroundColor(it) }
|
|
29
|
+
if (props.has("source")) {
|
|
30
|
+
val source = sourceString(props.value("source"))
|
|
31
|
+
if (source != null) loadSource(iv, source) else iv.setImageDrawable(null)
|
|
32
|
+
}
|
|
33
|
+
val mode = props.str("resize_mode") ?: props.str("scale_type")
|
|
34
|
+
if (mode != null) {
|
|
35
|
+
iv.scaleType = when (mode) {
|
|
36
|
+
"cover" -> ImageView.ScaleType.CENTER_CROP
|
|
37
|
+
"contain" -> ImageView.ScaleType.FIT_CENTER
|
|
38
|
+
"stretch" -> ImageView.ScaleType.FIT_XY
|
|
39
|
+
"center", "repeat" -> ImageView.ScaleType.CENTER
|
|
40
|
+
else -> iv.scaleType
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
ViewStyler.apply(iv, props)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private fun sourceString(value: Any?): String? {
|
|
47
|
+
return when (value) {
|
|
48
|
+
null -> null
|
|
49
|
+
is JSONObject -> value.str("uri") ?: value.str("url") ?: value.str("path")
|
|
50
|
+
else -> value.toString().takeIf { it.isNotEmpty() }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private fun loadSource(iv: ImageView, source: String) {
|
|
55
|
+
val state = stateOf(iv)
|
|
56
|
+
state["pending_uri"] = source
|
|
57
|
+
try {
|
|
58
|
+
when {
|
|
59
|
+
source.startsWith("data:") -> loadDataUri(iv, source)
|
|
60
|
+
source.startsWith("http://") || source.startsWith("https://") -> {
|
|
61
|
+
val (tw, th) = targetSize(iv)
|
|
62
|
+
ImageLoader.loadRemote(iv.context, source, tw, th) { bitmap, error ->
|
|
63
|
+
if (stateOf(iv)["pending_uri"] != source) return@loadRemote
|
|
64
|
+
if (bitmap != null) {
|
|
65
|
+
iv.setImageBitmap(bitmap)
|
|
66
|
+
fire(iv, "on_load")
|
|
67
|
+
PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
|
|
68
|
+
} else {
|
|
69
|
+
fire(iv, "on_error", error ?: "load failed")
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
source.startsWith("/") || source.startsWith("file://") -> {
|
|
74
|
+
val path = source.removePrefix("file://")
|
|
75
|
+
val (tw, th) = targetSize(iv)
|
|
76
|
+
ImageLoader.loadFile(path, tw, th) { bitmap, error ->
|
|
77
|
+
if (stateOf(iv)["pending_uri"] != source) return@loadFile
|
|
78
|
+
if (bitmap != null) {
|
|
79
|
+
iv.setImageBitmap(bitmap)
|
|
80
|
+
fire(iv, "on_load")
|
|
81
|
+
PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
|
|
82
|
+
} else {
|
|
83
|
+
fire(iv, "on_error", error ?: "decode failed")
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else -> {
|
|
88
|
+
val ctx = iv.context
|
|
89
|
+
val name = source.substringBeforeLast('.', source)
|
|
90
|
+
val resId = ctx.resources.getIdentifier(name, "drawable", ctx.packageName)
|
|
91
|
+
.takeIf { it != 0 }
|
|
92
|
+
?: ctx.resources.getIdentifier(name, "mipmap", ctx.packageName)
|
|
93
|
+
if (resId != 0) {
|
|
94
|
+
iv.setImageResource(resId)
|
|
95
|
+
fire(iv, "on_load")
|
|
96
|
+
PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
|
|
97
|
+
} else {
|
|
98
|
+
fire(iv, "on_error", "drawable '$name' not found")
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch (e: Exception) {
|
|
103
|
+
PNLog.swallowed("ImageManager.loadSource", e)
|
|
104
|
+
fire(iv, "on_error", e.message ?: "load failed")
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private fun loadDataUri(iv: ImageView, source: String) {
|
|
109
|
+
try {
|
|
110
|
+
val payload = source.substringAfter(',', "")
|
|
111
|
+
val raw = Base64.decode(payload, Base64.DEFAULT)
|
|
112
|
+
val bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.size)
|
|
113
|
+
if (bitmap != null) {
|
|
114
|
+
iv.setImageBitmap(bitmap)
|
|
115
|
+
fire(iv, "on_load")
|
|
116
|
+
PNBridge.registry.tagOf(iv)?.let { com.pythonnative.runtime.layout.NativeLayout.invalidate(it) }
|
|
117
|
+
} else {
|
|
118
|
+
fire(iv, "on_error", "data URI decode failed")
|
|
119
|
+
}
|
|
120
|
+
} catch (e: Exception) {
|
|
121
|
+
fire(iv, "on_error", "data URI decode failed")
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private fun targetSize(iv: ImageView): Pair<Int, Int> {
|
|
126
|
+
var w = iv.width
|
|
127
|
+
var h = iv.height
|
|
128
|
+
val frame = recordOf(iv)?.frame
|
|
129
|
+
if (w <= 0 && frame != null) {
|
|
130
|
+
w = px(frame[2])
|
|
131
|
+
h = px(frame[3])
|
|
132
|
+
}
|
|
133
|
+
if (w <= 0) {
|
|
134
|
+
val metrics = iv.context.resources.displayMetrics
|
|
135
|
+
w = metrics.widthPixels
|
|
136
|
+
h = metrics.heightPixels
|
|
137
|
+
}
|
|
138
|
+
if (h <= 0) h = w
|
|
139
|
+
return Pair(w, h)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override fun measure(view: View, maxWidth: Double, maxHeight: Double): FloatArray {
|
|
143
|
+
val iv = view as ImageView
|
|
144
|
+
val drawable = iv.drawable ?: return floatArrayOf(0f, 0f)
|
|
145
|
+
val density = view.context.resources.displayMetrics.density
|
|
146
|
+
// Bitmaps decode at device density, so their pixel size is already a dp-ish measure.
|
|
147
|
+
var w = drawable.intrinsicWidth / density
|
|
148
|
+
var h = drawable.intrinsicHeight / density
|
|
149
|
+
if (w <= 0f || h <= 0f) return floatArrayOf(0f, 0f)
|
|
150
|
+
val maxW = if (maxWidth.isFinite() && maxWidth < 1e6) maxWidth.toFloat() else Float.MAX_VALUE
|
|
151
|
+
val maxH = if (maxHeight.isFinite() && maxHeight < 1e6) maxHeight.toFloat() else Float.MAX_VALUE
|
|
152
|
+
if (w > maxW) {
|
|
153
|
+
h *= maxW / w
|
|
154
|
+
w = maxW
|
|
155
|
+
}
|
|
156
|
+
if (h > maxH) {
|
|
157
|
+
w *= maxH / h
|
|
158
|
+
h = maxH
|
|
159
|
+
}
|
|
160
|
+
return floatArrayOf(w, h)
|
|
161
|
+
}
|
|
162
|
+
}
|