@apexnative/navigation 1.0.0-alpha.0 → 1.0.0-alpha.10
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.
- package/ApexNavigation.podspec +2 -1
- package/CHANGELOG.md +128 -0
- package/README.md +147 -51
- package/android/CLAUDE.md +78 -25
- package/android/CORE.md +49 -25
- package/android/RECIPES.md +13 -6
- package/android/consumer-rules.pro +6 -0
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +85 -10
- package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
- package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
- package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
- package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/options/SideMenuOptions.kt +6 -1
- package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
- package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
- package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
- package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
- package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
- package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +35 -23
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +12 -7
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +42 -4
- package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +54 -20
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -50
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +50 -81
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +135 -94
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
- package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +39 -19
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +16 -4
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
- package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
- package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +108 -34
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +109 -21
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +476 -73
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +57 -25
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +83 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +49 -14
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +71 -12
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +37 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +42 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +19 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +43 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +77 -17
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +315 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +30 -20
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +216 -18
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +149 -41
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +115 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +222 -48
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +30 -34
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +219 -54
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +9 -7
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +143 -135
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +13 -5
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +112 -4
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +56 -41
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
- package/android/src/main/java/dev/apexnative/navigation/views/sidemenu/SideMenuView.kt +12 -3
- package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +45 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +140 -1
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -17
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +73 -34
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +125 -178
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
- package/android/src/main/jni/ANNContentHeights.cpp +82 -0
- package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
- package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
- package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
- package/android/src/main/res/values/styles.xml +1 -1
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/cli/app.js +1 -1
- package/cli/doctor/checks/app.js +77 -14
- package/cli/doctor/checks/splash.js +4 -22
- package/cli/migrate/native.js +18 -0
- package/cli/project.js +10 -2
- package/cli/setup/index.js +18 -0
- package/cli/setup/source.js +2 -1
- package/cli/setup/steps/appDelegate.js +66 -11
- package/cli/setup/steps/mainApplication.js +134 -19
- package/cli/setup/steps/metro.js +46 -0
- package/cli/setup/steps/sceneDelegate.js +108 -0
- package/cli/splash/index.js +3 -1
- package/cli/upgrade/changelog.js +2 -2
- package/docs-md/architecture/native-screens.md +4 -0
- package/docs-md/architecture/theme-and-context.md +131 -12
- package/docs-md/architecture/transitions.md +58 -12
- package/docs-md/containers/modals.md +8 -4
- package/docs-md/containers/overlays.md +3 -1
- package/docs-md/containers/side-menu.md +19 -2
- package/docs-md/containers/tabs.md +5 -1
- package/docs-md/flows/bottom-sheets.md +4 -2
- package/docs-md/flows/forms-in-a-modal.md +1 -1
- package/docs-md/fundamentals/screens-and-props.md +9 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/guides/cli.md +4 -3
- package/docs-md/guides/deep-links.md +15 -33
- package/docs-md/help/glossary.md +4 -4
- package/docs-md/help/troubleshooting.md +44 -6
- package/docs-md/index.json +57 -1
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +9 -3
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +46 -2
- package/docs-md/screen-ui/bar-buttons.md +5 -5
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +17 -12
- package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/docs-md/start/installation.md +21 -3
- package/ios/Animations/ANNFrameDriver.h +55 -15
- package/ios/Animations/ANNFrameDriver.mm +216 -60
- package/ios/Animations/ANNTransitionAnimator.h +26 -5
- package/ios/Animations/ANNTransitionAnimator.mm +61 -13
- package/ios/Animations/ANNTransitionPlan.h +2 -2
- package/ios/Animations/ANNTransitionPlan.mm +10 -0
- package/ios/Animations/ANNTransitionWait.mm +6 -7
- package/ios/Animations/Elements/ANNElementFinder.h +57 -0
- package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
- package/ios/Animations/Elements/ANNElementTransition.h +10 -8
- package/ios/Animations/Elements/ANNElementTransition.mm +19 -21
- package/ios/Animations/Elements/ANNSharedElementFlight.h +16 -11
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +308 -69
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
- package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
- package/ios/CLAUDE.md +60 -24
- package/ios/CORE.md +44 -34
- package/ios/Contract/ANNCommandAnswer.h +13 -5
- package/ios/Contract/ANNCommandAnswer.mm +40 -32
- package/ios/Contract/ANNCommandContract.h +15 -0
- package/ios/Contract/ANNCommandContract.mm +78 -0
- package/ios/Contract/ANNCommandRunner.h +10 -9
- package/ios/Contract/ANNCommandRunner.mm +22 -28
- package/ios/Contract/ANNErrorTexts.h +6 -2
- package/ios/Contract/ANNErrorTexts.mm +18 -10
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNAppDelegate.mm +0 -2
- package/ios/Core/ANNAppearanceDispatcher.h +5 -4
- package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
- package/ios/Core/ANNDeepLinkQueue.h +22 -2
- package/ios/Core/ANNDeepLinkQueue.mm +121 -14
- package/ios/Core/ANNErrorHandler.h +9 -4
- package/ios/Core/ANNErrorHandler.mm +20 -2
- package/ios/Core/ANNEventContract.h +246 -0
- package/ios/Core/ANNEventContract.mm +225 -0
- package/ios/Core/ANNEventEmitter.h +6 -2
- package/ios/Core/ANNEventEmitter.mm +117 -80
- package/ios/Core/ANNHinge.h +6 -0
- package/ios/Core/ANNHinge.mm +15 -4
- package/ios/Core/ANNJavaScriptCalls.h +33 -0
- package/ios/Core/ANNJavaScriptCalls.mm +52 -0
- package/ios/Core/ANNLayoutProtocol.h +5 -5
- package/ios/Core/ANNNavigationState.h +7 -5
- package/ios/Core/ANNNavigationState.mm +8 -7
- package/ios/Core/ANNNodeHelper.h +32 -12
- package/ios/Core/ANNNodeHelper.mm +100 -3
- package/ios/Core/ANNParking.h +5 -4
- package/ios/Core/ANNReloadCoordinator.mm +5 -12
- package/ios/Core/ANNRenderWait.h +20 -6
- package/ios/Core/ANNRenderWait.mm +143 -56
- package/ios/Core/ANNSceneDelegate.mm +25 -15
- package/ios/Core/ANNSystemUI.h +3 -2
- package/ios/Core/ANNSystemUI.mm +7 -0
- package/ios/Core/ANNUtils.h +7 -0
- package/ios/Core/ANNUtils.mm +10 -0
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Core/ApexNavigation.mm +7 -6
- package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
- package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +10 -8
- package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
- package/ios/Layouts/Modal/ANNModalCoordinator.mm +57 -16
- package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
- package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
- package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
- package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +2 -8
- package/ios/Layouts/Root/ANNRootCoordinator.mm +58 -19
- package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
- package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
- package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
- package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
- package/ios/Layouts/Screen/ANNComponentViewController.h +15 -13
- package/ios/Layouts/Screen/ANNComponentViewController.mm +130 -75
- package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenPlace.h +53 -0
- package/ios/Layouts/Screen/ANNScreenPlace.mm +205 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -44
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Screen/ANNScreenSettling.h +32 -0
- package/ios/Layouts/Screen/ANNScreenSettling.mm +137 -0
- package/ios/Layouts/SideMenu/ANNSideMenuTypes.h +8 -0
- package/ios/Layouts/SideMenu/ANNSideMenuViewController.mm +83 -14
- package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
- package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
- package/ios/Layouts/SplitView/ANNSplitViewController.mm +7 -8
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
- package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
- package/ios/Layouts/Stack/ANNStackController.h +8 -0
- package/ios/Layouts/Stack/ANNStackController.mm +184 -16
- package/ios/Layouts/Stack/ANNStackQueue.h +5 -2
- package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
- package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
- package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
- package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
- package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.h +3 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +237 -57
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +6 -2
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +232 -43
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNAnimationOptions.h +2 -1
- package/ios/Options/ANNAnimationOptions.mm +1 -1
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +25 -0
- package/ios/Options/ANNOptions.mm +157 -1
- package/ios/Options/ANNOptionsCache.h +7 -6
- package/ios/Options/ANNOptionsReader.mm +108 -0
- package/ios/Options/ANNSearchBarOptions.h +4 -3
- package/ios/Options/ANNSheetOptions.h +4 -1
- package/ios/Options/ANNSideMenuOptions.h +2 -0
- package/ios/Options/ANNSideMenuOptions.mm +2 -0
- package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
- package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
- package/ios/Options/ANNUserChoice.h +57 -0
- package/ios/Options/ANNUserChoice.mm +44 -0
- package/ios/Options/Params/ANNImageSource.h +61 -0
- package/ios/Options/Params/ANNImageSource.mm +101 -0
- package/ios/Options/Params/ANNOptionImages.h +37 -0
- package/ios/Options/Params/ANNOptionImages.mm +284 -0
- package/ios/Options/Params/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/ColorParser.h +4 -3
- package/ios/Options/Params/ColorParser.mm +22 -4
- package/ios/Options/Params/Image.h +15 -2
- package/ios/Options/Params/Image.mm +35 -2
- package/ios/Options/Params/ImageParser.h +5 -5
- package/ios/Options/Params/ImageParser.mm +28 -46
- package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
- package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
- package/ios/RECIPES.md +13 -6
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +28 -17
- package/ios/React/ANNSurfaceHostingView.h +14 -0
- package/ios/React/ANNSurfaceHostingView.mm +144 -1
- package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
- package/ios/React/Slots/ANNSlotContainers.h +14 -6
- package/ios/React/Slots/ANNSlotContainers.mm +75 -2
- package/ios/React/Slots/ANNSlotHost.mm +1 -3
- package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +14 -8
- package/ios/TopBar/ANNBarButtons.mm +397 -80
- package/ios/TopBar/ANNIconButtonImage.h +1 -0
- package/ios/TopBar/ANNIconButtonImage.mm +34 -1
- package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
- package/ios/TopBar/ANNLargeTitlePresenter.mm +50 -7
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +172 -21
- package/ios/TopBar/ANNSearchResultsViewController.mm +29 -6
- package/ios/TopBar/ANNTopBarPresenter.h +8 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +155 -33
- package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
- package/ios/TurboModules/ANNTurboModule.mm +104 -33
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
- package/lib/module/ARCHITECTURE.md +40 -27
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/app/AppRegistry.js +14 -2
- package/lib/module/api/app/AppRegistry.js.map +1 -1
- package/lib/module/api/app/createNavigation.js +190 -66
- package/lib/module/api/app/createNavigation.js.map +1 -1
- package/lib/module/api/commands/commands.js +54 -6
- package/lib/module/api/commands/commands.js.map +1 -1
- package/lib/module/api/events/navigationEvents.js +6 -2
- package/lib/module/api/events/navigationEvents.js.map +1 -1
- package/lib/module/api/hooks/useNavigation.js +8 -3
- package/lib/module/api/hooks/useNavigation.js.map +1 -1
- package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/linking/dispatchedLinks.js +23 -0
- package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
- package/lib/module/api/linking/linking.js +11 -0
- package/lib/module/api/linking/linking.js.map +1 -1
- package/lib/module/api/navigation.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/api/screens/defineScreen.js +45 -3
- package/lib/module/api/screens/defineScreen.js.map +1 -1
- package/lib/module/api/screens/nativeScreens.js +31 -6
- package/lib/module/api/screens/nativeScreens.js.map +1 -1
- package/lib/module/api/screens/registerScreens.js +193 -35
- package/lib/module/api/screens/registerScreens.js.map +1 -1
- package/lib/module/api/theme/theme.js +5 -0
- package/lib/module/api/theme/theme.js.map +1 -1
- package/lib/module/engine/commands.js +51 -9
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +20 -11
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/nativeScreens.js +9 -0
- package/lib/module/engine/nativeScreens.js.map +1 -1
- package/lib/module/engine/options.js +49 -15
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/optionsChange.js +102 -0
- package/lib/module/engine/optionsChange.js.map +1 -0
- package/lib/module/engine/places.js +52 -7
- package/lib/module/engine/places.js.map +1 -1
- package/lib/module/engine/props.js +30 -1
- package/lib/module/engine/props.js.map +1 -1
- package/lib/module/engine/refresh.js +85 -0
- package/lib/module/engine/refresh.js.map +1 -0
- package/lib/module/engine/rememberedOptions.js +61 -1
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +77 -16
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/module/interfaces/contract/commands.json +101 -0
- package/lib/module/interfaces/contract/errors.json +275 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +12 -2
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +8 -3
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/commands/commands.d.ts.map +1 -1
- package/lib/typescript/api/commands/presentation.d.ts +4 -3
- package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +3 -3
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
- package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/index.d.ts +1 -1
- package/lib/typescript/api/index.d.ts.map +1 -1
- package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
- package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
- package/lib/typescript/api/linking/linking.d.ts +8 -0
- package/lib/typescript/api/linking/linking.d.ts.map +1 -1
- package/lib/typescript/api/navigation.d.ts +4 -1
- package/lib/typescript/api/navigation.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
- package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
- package/lib/typescript/api/theme/theme.d.ts +2 -0
- package/lib/typescript/api/theme/theme.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts +5 -0
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/layout.d.ts.map +1 -1
- package/lib/typescript/engine/nativeScreens.d.ts +5 -0
- package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/engine/options.d.ts +5 -2
- package/lib/typescript/engine/options.d.ts.map +1 -1
- package/lib/typescript/engine/optionsChange.d.ts +28 -0
- package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
- package/lib/typescript/engine/places.d.ts +7 -2
- package/lib/typescript/engine/places.d.ts.map +1 -1
- package/lib/typescript/engine/props.d.ts +8 -1
- package/lib/typescript/engine/props.d.ts.map +1 -1
- package/lib/typescript/engine/refresh.d.ts +35 -0
- package/lib/typescript/engine/refresh.d.ts.map +1 -0
- package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +27 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +4 -4
- package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
- package/lib/typescript/interfaces/Events.d.ts +28 -10
- package/lib/typescript/interfaces/Events.d.ts.map +1 -1
- package/lib/typescript/interfaces/Options.d.ts +1 -1
- package/lib/typescript/interfaces/Options.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +80 -22
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +21 -7
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +23 -15
- package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/modals.d.ts +34 -26
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/sideMenu.d.ts +23 -6
- package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +17 -3
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/system.d.ts +18 -11
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +68 -38
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/metro/acceptPlugin.js +57 -0
- package/metro/babelTransformer.js +45 -0
- package/metro/index.js +36 -0
- package/package.json +4 -25
- package/src/ARCHITECTURE.md +40 -27
- package/src/adapters/NativeANNTurboModule.ts +12 -2
- package/src/api/app/AppRegistry.ts +16 -2
- package/src/api/app/createNavigation.ts +203 -69
- package/src/api/commands/commands.ts +48 -7
- package/src/api/commands/presentation.ts +4 -3
- package/src/api/events/navigationEvents.ts +10 -4
- package/src/api/hooks/useNavigation.ts +9 -4
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/index.ts +1 -0
- package/src/api/linking/dispatchedLinks.ts +18 -0
- package/src/api/linking/linking.ts +22 -1
- package/src/api/navigation.ts +4 -1
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/api/screens/defineScreen.ts +49 -5
- package/src/api/screens/nativeScreens.ts +40 -4
- package/src/api/screens/registerScreens.tsx +185 -42
- package/src/api/theme/theme.ts +5 -0
- package/src/engine/commands.ts +70 -13
- package/src/engine/layout.ts +27 -11
- package/src/engine/nativeScreens.ts +10 -1
- package/src/engine/options.ts +45 -19
- package/src/engine/optionsChange.ts +107 -0
- package/src/engine/places.ts +65 -7
- package/src/engine/props.ts +30 -1
- package/src/engine/refresh.ts +97 -0
- package/src/engine/rememberedOptions.ts +74 -1
- package/src/engine/screens.tsx +76 -21
- package/src/interfaces/ComponentEvents.ts +4 -4
- package/src/interfaces/Events.ts +29 -10
- package/src/interfaces/Options.ts +1 -0
- package/src/interfaces/contract/commands.json +101 -0
- package/src/interfaces/contract/errors.json +275 -0
- package/src/interfaces/options/animations.ts +81 -22
- package/src/interfaces/options/bottomTabs.ts +21 -7
- package/src/interfaces/options/buttons.ts +23 -15
- package/src/interfaces/options/modals.ts +34 -26
- package/src/interfaces/options/sideMenu.ts +24 -6
- package/src/interfaces/options/stack.ts +17 -3
- package/src/interfaces/options/system.ts +18 -11
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +68 -38
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -41
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -69
- package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -138
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -88
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -66
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -179
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -139
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -68
- package/ios/Layouts/Tabs/ANNTabsPreloadWindow.h +0 -27
- package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +0 -55
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
package dev.apexnative.navigation.utils
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.content.res.Resources
|
|
5
|
-
import android.graphics.BitmapFactory
|
|
6
|
-
import android.graphics.drawable.BitmapDrawable
|
|
7
4
|
import android.graphics.drawable.Drawable
|
|
8
|
-
import android.net.Uri
|
|
9
|
-
import android.os.StrictMode
|
|
10
|
-
import androidx.core.content.ContextCompat
|
|
11
|
-
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper
|
|
12
|
-
import java.io.FileInputStream
|
|
13
|
-
import java.io.FileNotFoundException
|
|
14
5
|
import java.io.IOException
|
|
15
|
-
import java.io.InputStream
|
|
16
|
-
import java.net.URL
|
|
17
6
|
|
|
18
7
|
/** What an image loads into: [onComplete] with the image, or [onError] when it can't load. Main thread. */
|
|
19
8
|
open class ImageLoadingListenerAdapter {
|
|
@@ -23,90 +12,19 @@ open class ImageLoadingListenerAdapter {
|
|
|
23
12
|
}
|
|
24
13
|
|
|
25
14
|
/**
|
|
26
|
-
* Loads
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* that read only) or through the content resolver. A remote address that is not Metro's asset is never loaded. A
|
|
31
|
-
* failure never ends the app: the listener's [ImageLoadingListenerAdapter.onError], the log, and React Native's red
|
|
32
|
-
* screen in development.
|
|
15
|
+
* Loads an image or an icon of the options at once, through the shared cache of [OptionImages]: each image its own
|
|
16
|
+
* mutable copy. For the places whose first frame needs the image (the top bar's buttons, the back button, menus, search
|
|
17
|
+
* suggestions); a place that can show it when ready asks through [ImageRequests]. A failure never ends the app: the
|
|
18
|
+
* listener's [ImageLoadingListenerAdapter.onError], and one warning per address.
|
|
33
19
|
*/
|
|
34
20
|
class ImageLoader {
|
|
35
21
|
/** Loads [uri] and gives it to [listener] before returning. Main thread. */
|
|
36
22
|
fun loadIcon(context: Context, uri: String, listener: ImageLoadingListenerAdapter) {
|
|
37
|
-
val drawable =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
val reason = if (e is LoadFailure) "" else ": ${e.message ?: e.javaClass.simpleName}"
|
|
41
|
-
DevelopmentLog.error("Could not load image $uri$reason${(e as? LoadFailure)?.why.orEmpty()}", e)
|
|
42
|
-
listener.onError(e)
|
|
23
|
+
val drawable = OptionImages.load(context, uri)
|
|
24
|
+
if (drawable == null) {
|
|
25
|
+
listener.onError(IOException("Could not load image $uri"))
|
|
43
26
|
return
|
|
44
27
|
}
|
|
45
28
|
listener.onComplete(drawable)
|
|
46
29
|
}
|
|
47
|
-
|
|
48
|
-
private fun load(context: Context, uri: String): Drawable {
|
|
49
|
-
if (uri.startsWith(FILE_SCHEME)) return decode(context) { FileInputStream(Uri.parse(uri).path) }
|
|
50
|
-
resourceDrawable(context, uri)?.let { return it }
|
|
51
|
-
if (!DevelopmentLog.isDevelopment) throw LoadFailure("")
|
|
52
|
-
val address = Uri.parse(uri)
|
|
53
|
-
return when (address.scheme) {
|
|
54
|
-
"http", "https" -> {
|
|
55
|
-
if (!isMetroAsset(address)) throw LoadFailure(": remote images are not supported in options")
|
|
56
|
-
withNetworkOnMainThread { decode(context) { URL(uri).openStream() } }
|
|
57
|
-
}
|
|
58
|
-
else -> decode(context) {
|
|
59
|
-
context.contentResolver.openInputStream(address) ?: throw FileNotFoundException(uri)
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// A drawable of the app by name, its own copy; none when the app has none by that name.
|
|
65
|
-
private fun resourceDrawable(context: Context, name: String): Drawable? {
|
|
66
|
-
val id = ResourceDrawableIdHelper.getResourceDrawableId(context, name)
|
|
67
|
-
if (id <= 0) return null
|
|
68
|
-
return try {
|
|
69
|
-
ContextCompat.getDrawable(context, id)?.mutate()
|
|
70
|
-
} catch (e: Resources.NotFoundException) {
|
|
71
|
-
null
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// The image of the stream [open] gives, closed after the read, also on an error; one it can't decode fails, and so
|
|
76
|
-
// does one too large for the memory: that is a load error too, never the end of the app.
|
|
77
|
-
private fun decode(context: Context, open: () -> InputStream): Drawable {
|
|
78
|
-
val decoded = try {
|
|
79
|
-
open().use { BitmapFactory.decodeStream(it) }
|
|
80
|
-
} catch (e: OutOfMemoryError) {
|
|
81
|
-
throw LoadFailure(": it is too large to decode")
|
|
82
|
-
}
|
|
83
|
-
val bitmap = decoded ?: throw LoadFailure(": it can't be decoded")
|
|
84
|
-
return BitmapDrawable(context.resources, bitmap)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// A failure the loader names itself: `Could not load image <source>`, then [why].
|
|
88
|
-
private class LoadFailure(val why: String) : IOException("Could not load image$why")
|
|
89
|
-
|
|
90
|
-
// Metro's asset: an address of the server the JavaScript bundle came from, under `/assets/`.
|
|
91
|
-
private fun isMetroAsset(address: Uri): Boolean {
|
|
92
|
-
val bundle = DevelopmentLog.bundleUrl?.let(Uri::parse) ?: return false
|
|
93
|
-
return address.host == bundle.host && address.port == bundle.port &&
|
|
94
|
-
address.path.orEmpty().startsWith(ASSETS_PATH)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Reading from the network on the main thread for [read] only; the thread's policy comes back, also on an error.
|
|
98
|
-
private fun <T> withNetworkOnMainThread(read: () -> T): T {
|
|
99
|
-
val policy = StrictMode.getThreadPolicy()
|
|
100
|
-
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder(policy).permitNetwork().build())
|
|
101
|
-
try {
|
|
102
|
-
return read()
|
|
103
|
-
} finally {
|
|
104
|
-
StrictMode.setThreadPolicy(policy)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private companion object {
|
|
109
|
-
const val FILE_SCHEME = "file:"
|
|
110
|
-
const val ASSETS_PATH = "/assets/"
|
|
111
|
-
}
|
|
112
30
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package dev.apexnative.navigation.utils
|
|
2
|
+
|
|
3
|
+
import android.graphics.drawable.Drawable
|
|
4
|
+
|
|
5
|
+
/** Where the images of the options come from, for one context: [OptionImages.source], or a fake in the tests. */
|
|
6
|
+
internal interface ImageSource {
|
|
7
|
+
/** The key of [uri] in the context's configuration now; for a `file:` uri it reads the file's version. */
|
|
8
|
+
fun key(uri: String): ImageKey
|
|
9
|
+
|
|
10
|
+
/** A copy of the image of [key] when the cache has it, none otherwise; never loads. */
|
|
11
|
+
fun cached(key: ImageKey): Drawable?
|
|
12
|
+
|
|
13
|
+
/** A copy of the image of [key], from the cache or loaded on the caller's thread; none on a failure. */
|
|
14
|
+
fun now(key: ImageKey): Drawable?
|
|
15
|
+
|
|
16
|
+
/** Whether [uri] can load off the main thread: a file or a drawable of the app. */
|
|
17
|
+
fun loadsLater(uri: String): Boolean
|
|
18
|
+
|
|
19
|
+
/** Loads [key] off the main thread and gives a copy of it, or none on a failure, to [done] on the main thread. */
|
|
20
|
+
fun later(key: ImageKey, done: (Drawable?) -> Unit)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The images that the places of a view asked for (an item's icon, a button's icon), by place [P]: what the cache has
|
|
25
|
+
* comes at once; what it doesn't, when the place can show it later, loads off the main thread and comes when ready;
|
|
26
|
+
* everything else loads at once. The last request of a place wins: a load that arrives after the place asked again,
|
|
27
|
+
* or after [forget] or [clear], is dropped. A load that fails gives the place no image, as an option without one:
|
|
28
|
+
* the image it showed before doesn't stay. A request builds the key of its image once (a file's version is read once).
|
|
29
|
+
* Main thread only.
|
|
30
|
+
*/
|
|
31
|
+
internal class ImageRequests<P : Any>(private val source: ImageSource) {
|
|
32
|
+
// The open load of each place: its number, which a later request of the place replaces.
|
|
33
|
+
private val waiting = HashMap<P, Long>()
|
|
34
|
+
private var last = 0L
|
|
35
|
+
|
|
36
|
+
/** The key of [uri] now, for a caller that compares it before it asks ([request] with the key). */
|
|
37
|
+
fun key(uri: String): ImageKey = source.key(uri)
|
|
38
|
+
|
|
39
|
+
/** [request] for the image of [uri], its key built here. */
|
|
40
|
+
fun request(place: P, uri: String, mayWait: Boolean, show: (Drawable?) -> Unit) =
|
|
41
|
+
request(place, source.key(uri), mayWait, show)
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Gives [show] the image of [key] for [place]: now when the cache has it, or when the place can't wait for it
|
|
45
|
+
* ([mayWait] false: the first frame needs it) or the uri can't load off the main thread (Metro's address, a content
|
|
46
|
+
* uri); otherwise later, unless [place] asks again first. Null when it doesn't load: the place shows none.
|
|
47
|
+
*/
|
|
48
|
+
fun request(place: P, key: ImageKey, mayWait: Boolean, show: (Drawable?) -> Unit) {
|
|
49
|
+
waiting.remove(place)
|
|
50
|
+
source.cached(key)?.let {
|
|
51
|
+
show(it)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
if (!mayWait || !source.loadsLater(key.uri)) {
|
|
55
|
+
show(source.now(key))
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
val number = ++last
|
|
59
|
+
waiting[place] = number
|
|
60
|
+
source.later(key) { image ->
|
|
61
|
+
if (waiting[place] != number) return@later
|
|
62
|
+
waiting.remove(place)
|
|
63
|
+
show(image)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** The place shows no image of the options now: a load it asked for earlier is dropped. */
|
|
68
|
+
fun forget(place: P) {
|
|
69
|
+
waiting.remove(place)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The view goes: every open load is dropped. */
|
|
73
|
+
fun clear() {
|
|
74
|
+
waiting.clear()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -28,7 +28,7 @@ internal object MaterialTheme {
|
|
|
28
28
|
private val wrappers = WeakHashMap<Context, WeakReference<Context>>()
|
|
29
29
|
// Themes of the navigation's own views, each made once over the activity's with a style of its own.
|
|
30
30
|
private val followers = WeakHashMap<Context, Int>()
|
|
31
|
-
// The popup theme of the bar's
|
|
31
|
+
// The popup theme of the bar's "⋮" per activity: the app's theme doesn't change while the activity lives.
|
|
32
32
|
private val popupThemes = WeakHashMap<Activity, Int>()
|
|
33
33
|
|
|
34
34
|
fun context(context: Context): Context {
|
|
@@ -38,11 +38,10 @@ internal object MaterialTheme {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* The popup theme of the top bar's
|
|
42
|
-
* theme follows light and dark: the menu takes the bar's own theme, which [rebase]
|
|
43
|
-
* the app's colors. With a Material 3 theme fixed to light or to dark, a day/night
|
|
44
|
-
* current mode's colors where the theme shows the other mode.
|
|
45
|
-
* first opening, in the mode of that moment, and no public API makes it again for a later switch.
|
|
41
|
+
* The popup theme of the top bar's "⋮", over the bar's theme at each opening of its popup, in the mode of that
|
|
42
|
+
* moment. None when the app's theme follows light and dark: the menu takes the bar's own theme, which [rebase]
|
|
43
|
+
* keeps in the current mode, with the app's colors. With a Material 3 theme fixed to light or to dark, a day/night
|
|
44
|
+
* overlay that gives the menu the current mode's colors where the theme shows the other mode.
|
|
46
45
|
*/
|
|
47
46
|
fun popupTheme(context: Context): Int {
|
|
48
47
|
val activity = activityOf(context) ?: return 0
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
package dev.apexnative.navigation.utils
|
|
2
|
+
|
|
3
|
+
import android.content.ComponentCallbacks2
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.res.Configuration
|
|
6
|
+
import android.content.res.Resources
|
|
7
|
+
import android.graphics.BitmapFactory
|
|
8
|
+
import android.graphics.drawable.BitmapDrawable
|
|
9
|
+
import android.graphics.drawable.Drawable
|
|
10
|
+
import android.net.Uri
|
|
11
|
+
import android.os.StrictMode
|
|
12
|
+
import android.util.LruCache
|
|
13
|
+
import androidx.core.content.ContextCompat
|
|
14
|
+
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper
|
|
15
|
+
import java.io.FileInputStream
|
|
16
|
+
import java.io.FileNotFoundException
|
|
17
|
+
import java.io.IOException
|
|
18
|
+
import java.io.InputStream
|
|
19
|
+
import java.net.URL
|
|
20
|
+
import java.util.Collections
|
|
21
|
+
import java.util.concurrent.ExecutorService
|
|
22
|
+
import java.util.concurrent.Executors
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The images and icons of the options, every one of them (bar buttons and their menus, the back button, search
|
|
26
|
+
* suggestions, tab icons, the FAB): one cache for the app, by [ImageKey], sized by the bytes of its images and let go
|
|
27
|
+
* when the system is low on memory with the app in the background ([clearsOnTrim]), not each time the app's UI hides;
|
|
28
|
+
* each caller gets its own mutable copy. The images don't depend on the JavaScript, so a reload keeps them.
|
|
29
|
+
*
|
|
30
|
+
* A `file:` address is a file; anything else first a drawable of the app by name (as React Native's images by resource
|
|
31
|
+
* name: release builds' assets, `{ android }` icons). Those two load on the caller's thread ([load]) or on a
|
|
32
|
+
* background thread of their own ([loadLater]). Not found while the app is in development: the address is read as a
|
|
33
|
+
* stream on the main thread, Metro's asset over the network (network on the main thread allowed for that read only)
|
|
34
|
+
* or through the content resolver. A remote address that is not Metro's asset is never loaded. A failure (a missing
|
|
35
|
+
* file or drawable, data that doesn't decode, an image too large for the memory) never ends the app: no image, and one
|
|
36
|
+
* warning per address in the log (and LogBox in development).
|
|
37
|
+
*
|
|
38
|
+
* A theme attribute inside a drawable of the app is resolved against the theme of the context that loaded it first.
|
|
39
|
+
* Main thread, unless a member says otherwise.
|
|
40
|
+
*/
|
|
41
|
+
internal object OptionImages {
|
|
42
|
+
private val cache = object : LruCache<ImageKey, Entry>(maxBytes()) {
|
|
43
|
+
override fun sizeOf(key: ImageKey, value: Entry) = value.bytes
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The addresses already named in a warning: one each.
|
|
47
|
+
private val warned: MutableSet<String> = Collections.synchronizedSet(HashSet())
|
|
48
|
+
|
|
49
|
+
// The loads off the main thread under way, with who waits for each.
|
|
50
|
+
private val loading = HashMap<ImageKey, MutableList<Waiter>>()
|
|
51
|
+
|
|
52
|
+
private val background: ExecutorService by lazy {
|
|
53
|
+
Executors.newSingleThreadExecutor { task -> Thread(task, THREAD_NAME).apply { isDaemon = true } }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Volatile
|
|
57
|
+
private var listening = false
|
|
58
|
+
|
|
59
|
+
/** The cache and the loads for [context]: the source of an [ImageRequests]. */
|
|
60
|
+
fun source(context: Context): ImageSource = object : ImageSource {
|
|
61
|
+
override fun key(uri: String) = ImageKey.of(context, uri)
|
|
62
|
+
|
|
63
|
+
override fun cached(key: ImageKey) = cached(context, key)
|
|
64
|
+
|
|
65
|
+
override fun now(key: ImageKey) = load(context, key)
|
|
66
|
+
|
|
67
|
+
override fun loadsLater(uri: String) = loadsLater(context, uri)
|
|
68
|
+
|
|
69
|
+
override fun later(key: ImageKey, done: (Drawable?) -> Unit) = loadLater(context, key, done)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** A copy of the image of [key] when the cache has it. Any thread. */
|
|
73
|
+
fun cached(context: Context, key: ImageKey): Drawable? = cache.get(key)?.copy(context)
|
|
74
|
+
|
|
75
|
+
/** A copy of the image of [uri], from the cache or loaded on the caller's thread; none on a failure. */
|
|
76
|
+
fun load(context: Context, uri: String): Drawable? = load(context, ImageKey.of(context, uri))
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A copy of the image of [key] (built in the configuration of [context]), from the cache or loaded on the caller's
|
|
80
|
+
* thread; none on a failure.
|
|
81
|
+
*/
|
|
82
|
+
fun load(context: Context, key: ImageKey): Drawable? {
|
|
83
|
+
listen(context)
|
|
84
|
+
cache.get(key)?.let { return it.copy(context) }
|
|
85
|
+
val loaded = decodeOrWarn(context, key.uri) ?: return null
|
|
86
|
+
return keep(key, loaded)?.copy(context) ?: loaded
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Whether [uri] loads off the main thread: a file, or a drawable the app has by that name. Any thread. */
|
|
90
|
+
fun loadsLater(context: Context, uri: String): Boolean =
|
|
91
|
+
uri.startsWith(FILE_SCHEME) || ResourceDrawableIdHelper.getResourceDrawableId(context, uri) > 0
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Loads [key] (built in the configuration of [context]) on the background thread and gives [done] a copy of it, or
|
|
95
|
+
* none on a failure, on the main thread; one load for all who ask for the same image meanwhile. Only for what
|
|
96
|
+
* [loadsLater].
|
|
97
|
+
*/
|
|
98
|
+
fun loadLater(context: Context, key: ImageKey, done: (Drawable?) -> Unit) {
|
|
99
|
+
listen(context)
|
|
100
|
+
val waiters = loading[key]
|
|
101
|
+
if (waiters != null) {
|
|
102
|
+
waiters.add(Waiter(context, done))
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
loading[key] = mutableListOf(Waiter(context, done))
|
|
106
|
+
background.execute {
|
|
107
|
+
val loaded = decodeOrWarn(context, key.uri)
|
|
108
|
+
val entry = loaded?.let { keep(key, it) }
|
|
109
|
+
UiThread.post { deliver(key, loaded, entry) }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Lets every image of the cache go. Any thread. */
|
|
114
|
+
fun clear() {
|
|
115
|
+
cache.evictAll()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Whether the cache lets its images go at a trim of [level]: when the system is low on memory with the app in the
|
|
120
|
+
* background (`TRIM_MEMORY_BACKGROUND` and above), not at `TRIM_MEMORY_UI_HIDDEN`, which comes each time the app
|
|
121
|
+
* goes to the background: its icons are there when it comes back.
|
|
122
|
+
*/
|
|
123
|
+
fun clearsOnTrim(level: Int): Boolean = level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
|
|
124
|
+
|
|
125
|
+
// Each waiter gets a copy of its own; an image the cache can't keep (no constant state) goes to the first only,
|
|
126
|
+
// the others load it again.
|
|
127
|
+
private fun deliver(key: ImageKey, loaded: Drawable?, entry: Entry?) {
|
|
128
|
+
val waiters = loading.remove(key) ?: return
|
|
129
|
+
waiters.forEachIndexed { index, waiter ->
|
|
130
|
+
val image = when {
|
|
131
|
+
loaded == null -> null
|
|
132
|
+
entry != null -> entry.copy(waiter.context)
|
|
133
|
+
index == 0 -> loaded
|
|
134
|
+
else -> load(waiter.context, key)
|
|
135
|
+
}
|
|
136
|
+
waiter.done(image)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Into the cache, when the image can be copied; the entry it keeps. Any thread.
|
|
141
|
+
private fun keep(key: ImageKey, image: Drawable): Entry? {
|
|
142
|
+
val state = image.constantState ?: return null
|
|
143
|
+
return Entry(state, bytesOf(image)).also { cache.put(key, it) }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private fun bytesOf(image: Drawable): Int {
|
|
147
|
+
if (image is BitmapDrawable) image.bitmap?.let { return it.allocationByteCount }
|
|
148
|
+
// A drawable that draws itself (a vector) keeps a picture of its own size when it is drawn.
|
|
149
|
+
return image.intrinsicWidth.coerceAtLeast(1) * image.intrinsicHeight.coerceAtLeast(1) * BYTES_PER_PIXEL
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// The image of [uri], or none and one warning per address. Any thread.
|
|
153
|
+
private fun decodeOrWarn(context: Context, uri: String): Drawable? = try {
|
|
154
|
+
decode(context, uri)
|
|
155
|
+
} catch (e: Exception) {
|
|
156
|
+
warnOnce(uri, (e as? LoadFailure)?.why ?: ": ${e.message ?: e.javaClass.simpleName}")
|
|
157
|
+
null
|
|
158
|
+
} catch (e: OutOfMemoryError) {
|
|
159
|
+
warnOnce(uri, ": it is too large to decode")
|
|
160
|
+
null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private fun warnOnce(uri: String, why: String) {
|
|
164
|
+
if (warned.add(uri)) DevelopmentLog.warn("Could not load image $uri$why")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private fun decode(context: Context, uri: String): Drawable {
|
|
168
|
+
if (uri.startsWith(FILE_SCHEME)) return decodeStream(context) { FileInputStream(Uri.parse(uri).path) }
|
|
169
|
+
resourceDrawable(context, uri)?.let { return it }
|
|
170
|
+
if (!DevelopmentLog.isDevelopment) throw LoadFailure("")
|
|
171
|
+
val address = Uri.parse(uri)
|
|
172
|
+
return when (address.scheme) {
|
|
173
|
+
"http", "https" -> {
|
|
174
|
+
if (!isMetroAsset(address)) throw LoadFailure(": remote images are not supported in options")
|
|
175
|
+
withNetworkOnMainThread { decodeStream(context) { URL(uri).openStream() } }
|
|
176
|
+
}
|
|
177
|
+
else -> decodeStream(context) {
|
|
178
|
+
context.contentResolver.openInputStream(address) ?: throw FileNotFoundException(uri)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// A drawable of the app by name; none when the app has none by that name.
|
|
184
|
+
private fun resourceDrawable(context: Context, name: String): Drawable? {
|
|
185
|
+
val id = ResourceDrawableIdHelper.getResourceDrawableId(context, name)
|
|
186
|
+
if (id <= 0) return null
|
|
187
|
+
return try {
|
|
188
|
+
ContextCompat.getDrawable(context, id)
|
|
189
|
+
} catch (e: Resources.NotFoundException) {
|
|
190
|
+
null
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// The image of the stream [open] gives, closed after the read, also on an error; one it can't decode fails, and so
|
|
195
|
+
// does one too large for the memory.
|
|
196
|
+
private fun decodeStream(context: Context, open: () -> InputStream): Drawable {
|
|
197
|
+
val decoded = try {
|
|
198
|
+
open().use { BitmapFactory.decodeStream(it) }
|
|
199
|
+
} catch (e: OutOfMemoryError) {
|
|
200
|
+
throw LoadFailure(": it is too large to decode")
|
|
201
|
+
}
|
|
202
|
+
val bitmap = decoded ?: throw LoadFailure(": it can't be decoded")
|
|
203
|
+
return BitmapDrawable(context.resources, bitmap)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Metro's asset: an address of the server the JavaScript bundle came from, under `/assets/`.
|
|
207
|
+
private fun isMetroAsset(address: Uri): Boolean {
|
|
208
|
+
val bundle = DevelopmentLog.bundleUrl?.let(Uri::parse) ?: return false
|
|
209
|
+
return address.host == bundle.host && address.port == bundle.port &&
|
|
210
|
+
address.path.orEmpty().startsWith(ASSETS_PATH)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Reading from the network on the main thread for [read] only; the thread's policy comes back, also on an error.
|
|
214
|
+
private fun <T> withNetworkOnMainThread(read: () -> T): T {
|
|
215
|
+
val policy = StrictMode.getThreadPolicy()
|
|
216
|
+
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder(policy).permitNetwork().build())
|
|
217
|
+
try {
|
|
218
|
+
return read()
|
|
219
|
+
} finally {
|
|
220
|
+
StrictMode.setThreadPolicy(policy)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// The cache lets its images go when the system is low on memory ([clearsOnTrim]): they load again when asked.
|
|
225
|
+
private fun listen(context: Context) {
|
|
226
|
+
if (listening) return
|
|
227
|
+
synchronized(this) {
|
|
228
|
+
if (listening) return
|
|
229
|
+
listening = true
|
|
230
|
+
}
|
|
231
|
+
context.applicationContext.registerComponentCallbacks(object : ComponentCallbacks2 {
|
|
232
|
+
override fun onTrimMemory(level: Int) {
|
|
233
|
+
if (clearsOnTrim(level)) clear()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
override fun onConfigurationChanged(newConfig: Configuration) = Unit
|
|
237
|
+
|
|
238
|
+
@Deprecated("Android 14 deprecates it; onTrimMemory comes with it")
|
|
239
|
+
override fun onLowMemory() = clear()
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// A sixteenth of the app's heap, at most 16 MB: the options' icons are small.
|
|
244
|
+
private fun maxBytes(): Int = (Runtime.getRuntime().maxMemory() / HEAP_PART).coerceAtMost(MAX_BYTES).toInt()
|
|
245
|
+
|
|
246
|
+
// An image the cache keeps: what makes its copies, and its size.
|
|
247
|
+
private class Entry(val state: Drawable.ConstantState, val bytes: Int) {
|
|
248
|
+
fun copy(context: Context): Drawable = state.newDrawable(context.resources).mutate()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private class Waiter(val context: Context, val done: (Drawable?) -> Unit)
|
|
252
|
+
|
|
253
|
+
// A failure the loader names itself: `Could not load image <source>`, then [why].
|
|
254
|
+
private class LoadFailure(val why: String) : IOException("Could not load image$why")
|
|
255
|
+
|
|
256
|
+
private const val FILE_SCHEME = "file:"
|
|
257
|
+
private const val ASSETS_PATH = "/assets/"
|
|
258
|
+
private const val THREAD_NAME = "ApexNavigation images"
|
|
259
|
+
private const val BYTES_PER_PIXEL = 4
|
|
260
|
+
private const val HEAP_PART = 16L
|
|
261
|
+
private const val MAX_BYTES = 16L * 1024 * 1024
|
|
262
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package dev.apexnative.navigation.utils
|
|
2
|
+
|
|
3
|
+
import android.animation.ValueAnimator
|
|
4
|
+
import android.annotation.SuppressLint
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.provider.Settings
|
|
8
|
+
import dev.apexnative.navigation.NavigationApplication
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Whether the system plays no animations: "Remove animations" in the accessibility settings, or the animator duration
|
|
12
|
+
* scale at 0 in the developer options. Then a transition doesn't wait for its elements' targets and images and moves no
|
|
13
|
+
* element, and the side menu opens and closes at once. Any thread.
|
|
14
|
+
*/
|
|
15
|
+
internal object SystemAnimations {
|
|
16
|
+
/**
|
|
17
|
+
* The animations are off now; [context] reads the scale below Android 8, else the application's. Lint can't see
|
|
18
|
+
* that the decision calls `areAnimatorsEnabled` (API 26) only from Android 8.
|
|
19
|
+
*/
|
|
20
|
+
@SuppressLint("NewApi")
|
|
21
|
+
fun areOff(context: Context? = null): Boolean =
|
|
22
|
+
areOff(Build.VERSION.SDK_INT, { ValueAnimator.areAnimatorsEnabled() }) { durationScaleOf(context) }
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The decision: from Android 8 the animator's own answer ([animatorsEnabled]), which follows both settings;
|
|
26
|
+
* below it the duration scale ([durationScale]) at 0.
|
|
27
|
+
*/
|
|
28
|
+
fun areOff(sdk: Int, animatorsEnabled: () -> Boolean, durationScale: () -> Float): Boolean =
|
|
29
|
+
if (sdk >= Build.VERSION_CODES.O) !animatorsEnabled() else durationScale() == 0f
|
|
30
|
+
|
|
31
|
+
// Without a context and before the application started (the unit tests), the animations play.
|
|
32
|
+
private fun durationScaleOf(context: Context?): Float {
|
|
33
|
+
val resolver = (context ?: NavigationApplication.instanceOrNull)?.contentResolver ?: return 1f
|
|
34
|
+
return Settings.Global.getFloat(resolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -22,8 +22,8 @@ object UiUtils {
|
|
|
22
22
|
fun pxToDp(context: Context, px: Float): Float = px / context.resources.displayMetrics.density
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* A size of the options in dp, in whole pixels of the screen of [context]: times `densityDpi / 160`, the
|
|
26
|
-
*
|
|
25
|
+
* A size of the options in dp, in whole pixels of the screen of [context]: times `densityDpi / 160`, rounded to the
|
|
26
|
+
* nearest pixel only here, as iOS places a size in points on whole pixels; a size of 0 or less is not converted.
|
|
27
27
|
*/
|
|
28
28
|
@JvmStatic
|
|
29
29
|
fun dpToPixels(context: Context, dp: Double): Int = dpToPixels(dp, context.resources.displayMetrics.densityDpi)
|
|
@@ -31,7 +31,7 @@ object UiUtils {
|
|
|
31
31
|
/** [dp] in whole pixels at [densityDpi], as [dpToPixels] converts it. */
|
|
32
32
|
@JvmStatic
|
|
33
33
|
fun dpToPixels(dp: Double, densityDpi: Int): Int =
|
|
34
|
-
if (dp <= 0) dp.toInt() else (dp * densityDpi / BASE_DENSITY_DPI).
|
|
34
|
+
if (dp <= 0) dp.toInt() else (dp * densityDpi / BASE_DENSITY_DPI).roundToInt()
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* A font size of the options in sp, in pixels of the screen of [context]: it follows the system's text scale,
|
|
@@ -59,7 +59,9 @@ private fun ViewController<*>.hostIsShown(): Boolean {
|
|
|
59
59
|
* Runs [mounted] as the content of this screen's slot mounts into its container, and [start] on the pre-draw of that
|
|
60
60
|
* frame: after React Native applied the whole mount, before the frame is drawn. At once if the content has mounted,
|
|
61
61
|
* after [DRAW_WAIT_LIMIT] if it never does; while [waiting] holds. A screen prepared for its push was rendered at its
|
|
62
|
-
* size and insets before
|
|
62
|
+
* size and insets before, so it is whole from that frame on; its push doesn't start in that frame: [start] hands it to
|
|
63
|
+
* the stack's queue, which starts the transition on a later turn, behind the calls JavaScript made before
|
|
64
|
+
* (`PendingCalls`), so the `mergeOptions` of the mount's commit has applied.
|
|
63
65
|
*/
|
|
64
66
|
fun ViewController<*>.whenContentMounted(waiting: () -> Boolean, mounted: () -> Unit, start: () -> Unit) {
|
|
65
67
|
var done = false
|
|
@@ -26,16 +26,3 @@ val View.borderRadius: Float
|
|
|
26
26
|
*/
|
|
27
27
|
internal fun resolveBorderRadius(radius: LengthPercentage, width: Int, density: Float): Float =
|
|
28
28
|
(radius.resolve(width / density) * density).coerceAtLeast(0f)
|
|
29
|
-
|
|
30
|
-
/** The scale the parents of [view] give it, along x and along y: a view in a scaled parent shows smaller. */
|
|
31
|
-
fun computeInheritedScale(view: View): Pair<Float, Float> {
|
|
32
|
-
var scaleX = 1f
|
|
33
|
-
var scaleY = 1f
|
|
34
|
-
var parent = view.parent as? View
|
|
35
|
-
while (parent != null) {
|
|
36
|
-
scaleX *= parent.scaleX
|
|
37
|
-
scaleY *= parent.scaleY
|
|
38
|
-
parent = parent.parent as? View
|
|
39
|
-
}
|
|
40
|
-
return scaleX to scaleY
|
|
41
|
-
}
|
|
@@ -9,7 +9,8 @@ import androidx.window.layout.WindowMetricsCalculator
|
|
|
9
9
|
import dev.apexnative.navigation.NavigationActivity
|
|
10
10
|
import dev.apexnative.navigation.options.DefaultOptions
|
|
11
11
|
import dev.apexnative.navigation.options.Options
|
|
12
|
-
import dev.apexnative.navigation.react.
|
|
12
|
+
import dev.apexnative.navigation.react.ScreenEnvironment
|
|
13
|
+
import dev.apexnative.navigation.react.events.EventEmitter
|
|
13
14
|
import dev.apexnative.navigation.utils.SystemUiUtils
|
|
14
15
|
import dev.apexnative.navigation.utils.WindowFolds
|
|
15
16
|
import dev.apexnative.navigation.viewcontrollers.child.ChildController
|
|
@@ -145,12 +146,14 @@ class ArrangementController internal constructor(
|
|
|
145
146
|
val overlay = plan.layout == ArrangementPlan.Layout.OVERLAY
|
|
146
147
|
val single = plan.layout == ArrangementPlan.Layout.SINGLE
|
|
147
148
|
if (primary is ComponentViewController) {
|
|
148
|
-
val state =
|
|
149
|
-
|
|
149
|
+
val state = ScreenEnvironment.Arrangement(PRIMARY, layout, front = overlay, hidden = false)
|
|
150
|
+
val changes = ScreenEnvironment.changesOf(EventEmitter.of(primary.jsGeneration))
|
|
151
|
+
ScreenEnvironment.setArrangement(primary.id, state, notify, changes)
|
|
150
152
|
}
|
|
151
153
|
if (secondary is ComponentViewController) {
|
|
152
|
-
val state =
|
|
153
|
-
|
|
154
|
+
val state = ScreenEnvironment.Arrangement(SECONDARY, layout, front = false, hidden = single)
|
|
155
|
+
val changes = ScreenEnvironment.changesOf(EventEmitter.of(secondary.jsGeneration))
|
|
156
|
+
ScreenEnvironment.setArrangement(secondary.id, state, notify, changes)
|
|
154
157
|
}
|
|
155
158
|
}
|
|
156
159
|
|
|
@@ -243,9 +246,9 @@ class ArrangementController internal constructor(
|
|
|
243
246
|
// The stack keeps the bar's title, buttons and search by the view of its screen on top: they go with the pair.
|
|
244
247
|
if (!isDestroyed && peekView() != null) performOnParentController { it.onChildDestroyed(this) }
|
|
245
248
|
folds?.removeListener(onFoldsChanged)
|
|
246
|
-
ArrangementStates.remove(primary.id)
|
|
247
|
-
ArrangementStates.remove(secondary.id)
|
|
248
249
|
shownChildren.containerDisappeared()
|
|
250
|
+
// The screens go with the pair: each clears its own environment, its arrangement too, unless its id is
|
|
251
|
+
// another's now.
|
|
249
252
|
super.destroy()
|
|
250
253
|
}
|
|
251
254
|
|
|
@@ -2,24 +2,25 @@ package dev.apexnative.navigation.viewcontrollers.arrangement
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import dev.apexnative.navigation.options.Options
|
|
5
|
+
import dev.apexnative.navigation.viewcontrollers.component.ComponentPresenter
|
|
5
6
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.Presenter
|
|
6
7
|
import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* The presenter of the secondary screen of a pair ([ArrangementController]). The status bar, the navigation bar and
|
|
10
|
-
* the orientation are the primary's, as the top bar is: the secondary applies only its background
|
|
11
|
-
* front it gives them back to the primary.
|
|
11
|
+
* the orientation are the primary's, as the top bar is: the secondary applies only its background (the theme's
|
|
12
|
+
* without `layout.backgroundColor`, as any React screen), and brought to the front it gives them back to the primary.
|
|
12
13
|
*/
|
|
13
14
|
internal class SecondaryScreenPresenter(activity: Activity, defaultOptions: Options) :
|
|
14
15
|
Presenter(activity, defaultOptions) {
|
|
15
16
|
|
|
16
17
|
override fun applyOptions(view: ViewController<*>, options: Options) =
|
|
17
|
-
|
|
18
|
+
applyBackground(view, options.copy().withDefaultOptions(defaultOptions))
|
|
18
19
|
|
|
19
20
|
override fun mergeOptions(viewController: ViewController<*>, options: Options) {
|
|
20
21
|
if (!options.layout.backgroundColor.hasValue()) return
|
|
21
22
|
val merged = viewController.resolveCurrentOptions().copy().mergeWith(options)
|
|
22
|
-
|
|
23
|
+
applyBackground(viewController, merged.withDefaultOptions(defaultOptions))
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
override fun onViewBroughtToFront(viewController: ViewController<*>, options: Options) {
|
|
@@ -27,5 +28,12 @@ internal class SecondaryScreenPresenter(activity: Activity, defaultOptions: Opti
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
override fun onConfigurationChanged(controller: ViewController<*>, options: Options) =
|
|
30
|
-
|
|
31
|
+
applyBackground(controller, options.copy().withDefaultOptions(defaultOptions))
|
|
32
|
+
|
|
33
|
+
// `layout.backgroundColor`, else the theme's background (`ComponentPresenter.themeBackgroundOf`).
|
|
34
|
+
private fun applyBackground(view: ViewController<*>, options: Options) {
|
|
35
|
+
if (options.layout.backgroundColor.hasValue()) return applyBackgroundColor(view, options)
|
|
36
|
+
val color = ComponentPresenter.themeBackgroundOf(activity) ?: return
|
|
37
|
+
view.peekView()?.setBackgroundColor(color)
|
|
38
|
+
}
|
|
31
39
|
}
|