@apexnative/navigation 1.0.0-alpha.1 → 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.
Files changed (573) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +120 -0
  3. package/README.md +147 -51
  4. package/android/CLAUDE.md +78 -25
  5. package/android/CORE.md +49 -25
  6. package/android/RECIPES.md +13 -6
  7. package/android/consumer-rules.pro +6 -0
  8. package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +85 -10
  9. package/android/src/main/java/dev/apexnative/navigation/NavigationApplication.kt +34 -13
  10. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutChecks.kt +5 -1
  11. package/android/src/main/java/dev/apexnative/navigation/layout/LayoutNode.kt +6 -2
  12. package/android/src/main/java/dev/apexnative/navigation/nativescreen/NativeScreenContext.kt +2 -2
  13. package/android/src/main/java/dev/apexnative/navigation/options/AnimationsOptions.kt +2 -2
  14. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  15. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
  16. package/android/src/main/java/dev/apexnative/navigation/options/ButtonMenuOptions.kt +2 -2
  17. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +3 -3
  18. package/android/src/main/java/dev/apexnative/navigation/options/HardwareBackButtonOptions.kt +9 -1
  19. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  20. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  21. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  22. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  23. package/android/src/main/java/dev/apexnative/navigation/options/OptionsCache.kt +8 -4
  24. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  25. package/android/src/main/java/dev/apexnative/navigation/options/SideMenuOptions.kt +6 -1
  26. package/android/src/main/java/dev/apexnative/navigation/options/StackAnimationOptions.kt +40 -2
  27. package/android/src/main/java/dev/apexnative/navigation/options/StatusBarOptions.kt +7 -3
  28. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  29. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  30. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  31. package/android/src/main/java/dev/apexnative/navigation/options/UserChoices.kt +159 -0
  32. package/android/src/main/java/dev/apexnative/navigation/react/CommandAnswer.kt +29 -36
  33. package/android/src/main/java/dev/apexnative/navigation/react/CommandContract.kt +59 -0
  34. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  35. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  36. package/android/src/main/java/dev/apexnative/navigation/react/ErrorTexts.kt +75 -60
  37. package/android/src/main/java/dev/apexnative/navigation/react/Hinge.kt +35 -23
  38. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +12 -7
  39. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +42 -4
  40. package/android/src/main/java/dev/apexnative/navigation/react/PendingCalls.kt +48 -0
  41. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +54 -20
  42. package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +7 -6
  43. package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
  44. package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +19 -50
  45. package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
  46. package/android/src/main/java/dev/apexnative/navigation/react/events/AppearanceDispatcher.kt +50 -81
  47. package/android/src/main/java/dev/apexnative/navigation/react/events/EventContract.kt +226 -0
  48. package/android/src/main/java/dev/apexnative/navigation/react/events/EventEmitter.kt +135 -94
  49. package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationState.kt +154 -0
  50. package/android/src/main/java/dev/apexnative/navigation/react/events/ScreenAppearance.kt +0 -6
  51. package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +12 -10
  52. package/android/src/main/java/dev/apexnative/navigation/slots/SlotContainers.kt +10 -7
  53. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +39 -19
  54. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +16 -4
  55. package/android/src/main/java/dev/apexnative/navigation/slots/SlotViews.kt +40 -0
  56. package/android/src/main/java/dev/apexnative/navigation/utils/ImageKey.kt +66 -0
  57. package/android/src/main/java/dev/apexnative/navigation/utils/ImageLoader.kt +7 -89
  58. package/android/src/main/java/dev/apexnative/navigation/utils/ImageRequests.kt +76 -0
  59. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  60. package/android/src/main/java/dev/apexnative/navigation/utils/OptionImages.kt +262 -0
  61. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  62. package/android/src/main/java/dev/apexnative/navigation/utils/UiUtils.kt +3 -3
  63. package/android/src/main/java/dev/apexnative/navigation/utils/ViewController.kt +3 -1
  64. package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
  65. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +10 -7
  66. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/SecondaryScreenPresenter.kt +13 -5
  67. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/AppliedTabCalls.kt +49 -0
  68. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
  69. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +108 -34
  70. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsPresenter.kt +25 -24
  71. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
  72. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabItems.kt +3 -3
  73. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
  74. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentPresenter.kt +28 -7
  75. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +12 -9
  76. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ContentReadiness.kt +2 -2
  77. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/RenderWait.kt +27 -7
  78. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenFrame.kt +1 -1
  79. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenReadiness.kt +103 -56
  80. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
  81. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
  82. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalAnimations.kt +22 -24
  83. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +109 -21
  84. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalRecord.kt +21 -6
  85. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +476 -73
  86. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +57 -25
  87. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
  88. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +83 -0
  89. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackCheck.kt +50 -0
  90. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/BackCallbackState.kt +49 -14
  91. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +71 -12
  92. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/RootCoordinator.kt +37 -11
  93. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/overlay/OverlayCoordinator.kt +10 -8
  94. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/Capabilities.kt +11 -4
  95. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ContainerBackground.kt +3 -3
  96. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +6 -6
  97. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuController.kt +42 -9
  98. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +19 -8
  99. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuPresenter.kt +16 -7
  100. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuRules.kt +43 -4
  101. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitPanes.kt +23 -0
  102. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/splitview/SplitViewController.kt +77 -17
  103. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +315 -0
  104. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
  105. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +30 -20
  106. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +216 -18
  107. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackDisplay.kt +26 -1
  108. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +149 -41
  109. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackTransitions.kt +26 -8
  110. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +115 -6
  111. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  112. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +222 -48
  113. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/ScreenSearch.kt +3 -5
  114. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +30 -34
  115. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarComponents.kt +5 -51
  116. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  117. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  118. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/MenuChecks.kt +13 -56
  119. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +219 -54
  120. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
  121. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
  122. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ViewController.kt +75 -21
  123. package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
  124. package/android/src/main/java/dev/apexnative/navigation/views/animations/Curves.kt +29 -11
  125. package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
  126. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabs.kt +9 -7
  127. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
  128. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomBottomTabItemView.kt +0 -5
  129. package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
  130. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStep.kt +31 -0
  131. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +143 -135
  132. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +13 -5
  133. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  134. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionTargets.kt +9 -6
  135. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionViews.kt +100 -0
  136. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +112 -4
  137. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +346 -36
  138. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  139. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +56 -41
  140. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextMorph.kt +176 -0
  141. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  142. package/android/src/main/java/dev/apexnative/navigation/views/sidemenu/SideMenuView.kt +12 -3
  143. package/android/src/main/java/dev/apexnative/navigation/views/stack/ScreenEdge.kt +45 -0
  144. package/android/src/main/java/dev/apexnative/navigation/views/stack/StackLayout.kt +140 -1
  145. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/Fab.kt +14 -10
  146. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  147. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  148. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -17
  149. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +73 -34
  150. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  151. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  152. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  153. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +125 -178
  154. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  155. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  156. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  157. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  158. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  159. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  160. package/android/src/main/res/values/styles.xml +1 -1
  161. package/android/src/main/res/values-night/styles.xml +1 -1
  162. package/cli/app.js +1 -1
  163. package/cli/doctor/checks/app.js +77 -14
  164. package/cli/doctor/checks/splash.js +4 -22
  165. package/cli/migrate/native.js +18 -0
  166. package/cli/project.js +10 -2
  167. package/cli/setup/index.js +18 -0
  168. package/cli/setup/source.js +2 -1
  169. package/cli/setup/steps/appDelegate.js +66 -11
  170. package/cli/setup/steps/mainApplication.js +134 -19
  171. package/cli/setup/steps/metro.js +46 -0
  172. package/cli/setup/steps/sceneDelegate.js +108 -0
  173. package/cli/splash/index.js +3 -1
  174. package/docs-md/architecture/native-screens.md +4 -0
  175. package/docs-md/architecture/theme-and-context.md +131 -12
  176. package/docs-md/architecture/transitions.md +58 -12
  177. package/docs-md/containers/modals.md +8 -4
  178. package/docs-md/containers/overlays.md +3 -1
  179. package/docs-md/containers/side-menu.md +19 -2
  180. package/docs-md/containers/tabs.md +5 -1
  181. package/docs-md/flows/bottom-sheets.md +4 -2
  182. package/docs-md/flows/forms-in-a-modal.md +1 -1
  183. package/docs-md/fundamentals/screens-and-props.md +9 -1
  184. package/docs-md/fundamentals/starting-the-app.md +1 -1
  185. package/docs-md/guides/cli.md +4 -3
  186. package/docs-md/guides/deep-links.md +15 -33
  187. package/docs-md/help/glossary.md +4 -4
  188. package/docs-md/help/troubleshooting.md +44 -6
  189. package/docs-md/index.json +57 -1
  190. package/docs-md/migration/from-expo-router.md +1 -1
  191. package/docs-md/migration/from-react-native-navigation.md +9 -3
  192. package/docs-md/migration/from-react-navigation.md +2 -2
  193. package/docs-md/screen-ui/back-and-gestures.md +46 -2
  194. package/docs-md/screen-ui/bar-buttons.md +5 -5
  195. package/docs-md/screen-ui/search.md +1 -1
  196. package/docs-md/screen-ui/system-ui.md +17 -12
  197. package/docs-md/screen-ui/toolbar-and-fab.md +4 -2
  198. package/docs-md/screen-ui/top-bar.md +8 -4
  199. package/docs-md/start/installation.md +21 -3
  200. package/ios/Animations/ANNFrameDriver.h +55 -15
  201. package/ios/Animations/ANNFrameDriver.mm +216 -60
  202. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  203. package/ios/Animations/ANNTransitionAnimator.mm +61 -13
  204. package/ios/Animations/ANNTransitionPlan.h +2 -2
  205. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  206. package/ios/Animations/ANNTransitionWait.mm +6 -7
  207. package/ios/Animations/Elements/ANNElementFinder.h +57 -0
  208. package/ios/Animations/Elements/ANNElementFinder.mm +174 -2
  209. package/ios/Animations/Elements/ANNElementTransition.h +10 -8
  210. package/ios/Animations/Elements/ANNElementTransition.mm +19 -21
  211. package/ios/Animations/Elements/ANNSharedElementFlight.h +16 -11
  212. package/ios/Animations/Elements/ANNSharedElementFlight.mm +308 -69
  213. package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
  214. package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
  215. package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
  216. package/ios/Animations/Interpolators/ANNViewTracks.h +9 -6
  217. package/ios/Animations/Interpolators/ANNViewTracks.mm +100 -30
  218. package/ios/CLAUDE.md +60 -24
  219. package/ios/CORE.md +44 -34
  220. package/ios/Contract/ANNCommandAnswer.h +13 -5
  221. package/ios/Contract/ANNCommandAnswer.mm +40 -32
  222. package/ios/Contract/ANNCommandContract.h +15 -0
  223. package/ios/Contract/ANNCommandContract.mm +78 -0
  224. package/ios/Contract/ANNCommandRunner.h +10 -9
  225. package/ios/Contract/ANNCommandRunner.mm +22 -28
  226. package/ios/Contract/ANNErrorTexts.h +6 -2
  227. package/ios/Contract/ANNErrorTexts.mm +18 -10
  228. package/ios/Contract/ANNLayoutNode.h +3 -3
  229. package/ios/Contract/ANNLayoutReader.h +3 -3
  230. package/ios/Core/ANNAppDelegate.mm +0 -2
  231. package/ios/Core/ANNAppearanceDispatcher.h +5 -4
  232. package/ios/Core/ANNAppearanceDispatcher.mm +12 -1
  233. package/ios/Core/ANNDeepLinkQueue.h +22 -2
  234. package/ios/Core/ANNDeepLinkQueue.mm +121 -14
  235. package/ios/Core/ANNErrorHandler.h +9 -4
  236. package/ios/Core/ANNErrorHandler.mm +20 -2
  237. package/ios/Core/ANNEventContract.h +246 -0
  238. package/ios/Core/ANNEventContract.mm +225 -0
  239. package/ios/Core/ANNEventEmitter.h +6 -2
  240. package/ios/Core/ANNEventEmitter.mm +117 -80
  241. package/ios/Core/ANNHinge.h +6 -0
  242. package/ios/Core/ANNHinge.mm +15 -4
  243. package/ios/Core/ANNJavaScriptCalls.h +33 -0
  244. package/ios/Core/ANNJavaScriptCalls.mm +52 -0
  245. package/ios/Core/ANNLayoutProtocol.h +5 -5
  246. package/ios/Core/ANNNavigationState.h +7 -5
  247. package/ios/Core/ANNNavigationState.mm +8 -7
  248. package/ios/Core/ANNNodeHelper.h +32 -12
  249. package/ios/Core/ANNNodeHelper.mm +100 -3
  250. package/ios/Core/ANNParking.h +5 -4
  251. package/ios/Core/ANNReloadCoordinator.mm +5 -12
  252. package/ios/Core/ANNRenderWait.h +20 -6
  253. package/ios/Core/ANNRenderWait.mm +143 -56
  254. package/ios/Core/ANNSceneDelegate.mm +25 -15
  255. package/ios/Core/ANNSystemUI.h +3 -2
  256. package/ios/Core/ANNSystemUI.mm +7 -0
  257. package/ios/Core/ANNUtils.h +7 -0
  258. package/ios/Core/ANNUtils.mm +10 -0
  259. package/ios/Core/ApexNavigation+Internal.h +1 -1
  260. package/ios/Core/ApexNavigation.mm +7 -6
  261. package/ios/Core/UIViewController+LayoutProtocol.h +9 -2
  262. package/ios/Core/UIViewController+LayoutProtocol.mm +8 -0
  263. package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
  264. package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
  265. package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +10 -8
  266. package/ios/Layouts/Modal/ANNModalCoordinator.h +1 -1
  267. package/ios/Layouts/Modal/ANNModalCoordinator.mm +57 -16
  268. package/ios/Layouts/Modal/ANNModalPresentation.h +8 -5
  269. package/ios/Layouts/Modal/ANNModalPresentation.mm +12 -12
  270. package/ios/Layouts/Modal/ANNSheetPresenter.mm +83 -1
  271. package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +2 -8
  272. package/ios/Layouts/Root/ANNRootCoordinator.mm +58 -19
  273. package/ios/Layouts/Root/ANNSplashViewController.h +5 -3
  274. package/ios/Layouts/Root/ANNSplashViewController.mm +147 -0
  275. package/ios/Layouts/Screen/ANNComponentPresenter.h +11 -3
  276. package/ios/Layouts/Screen/ANNComponentPresenter.mm +34 -6
  277. package/ios/Layouts/Screen/ANNComponentViewController.h +15 -13
  278. package/ios/Layouts/Screen/ANNComponentViewController.mm +130 -75
  279. package/ios/Layouts/Screen/ANNNativeScreenContext.mm +7 -5
  280. package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
  281. package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
  282. package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
  283. package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
  284. package/ios/Layouts/Screen/ANNScreenPlace.h +53 -0
  285. package/ios/Layouts/Screen/ANNScreenPlace.mm +205 -0
  286. package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
  287. package/ios/Layouts/Screen/ANNScreenRegions.mm +9 -44
  288. package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
  289. package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
  290. package/ios/Layouts/Screen/ANNScreenSettling.h +32 -0
  291. package/ios/Layouts/Screen/ANNScreenSettling.mm +137 -0
  292. package/ios/Layouts/SideMenu/ANNSideMenuTypes.h +8 -0
  293. package/ios/Layouts/SideMenu/ANNSideMenuViewController.mm +83 -14
  294. package/ios/Layouts/SplitView/ANNSplitViewCommands.h +3 -2
  295. package/ios/Layouts/SplitView/ANNSplitViewCommands.mm +105 -39
  296. package/ios/Layouts/SplitView/ANNSplitViewController.mm +7 -8
  297. package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
  298. package/ios/Layouts/Stack/ANNPreviewPush.mm +142 -27
  299. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  300. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  301. package/ios/Layouts/Stack/ANNStackCommands.mm +1 -0
  302. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  303. package/ios/Layouts/Stack/ANNStackController.mm +184 -16
  304. package/ios/Layouts/Stack/ANNStackQueue.h +5 -2
  305. package/ios/Layouts/Stack/ANNStackQueue.mm +56 -43
  306. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  307. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  308. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  309. package/ios/Layouts/Stack/UINavigationController+ANNCommands.h +4 -2
  310. package/ios/Layouts/Stack/UINavigationController+ANNCommands.mm +2 -3
  311. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.h +4 -0
  312. package/ios/Layouts/Tabs/ANNBottomTabItemBuilder.mm +11 -0
  313. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
  314. package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
  315. package/ios/Layouts/Tabs/ANNBottomTabsController.h +3 -0
  316. package/ios/Layouts/Tabs/ANNBottomTabsController.mm +237 -57
  317. package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
  318. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +6 -2
  319. package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +232 -43
  320. package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
  321. package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
  322. package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
  323. package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
  324. package/ios/Options/ANNAnimationOptions.h +2 -1
  325. package/ios/Options/ANNAnimationOptions.mm +1 -1
  326. package/ios/Options/ANNBottomTabsOptions.h +2 -0
  327. package/ios/Options/ANNBottomTabsOptions.mm +1 -0
  328. package/ios/Options/ANNOptions.h +25 -0
  329. package/ios/Options/ANNOptions.mm +157 -1
  330. package/ios/Options/ANNOptionsCache.h +7 -6
  331. package/ios/Options/ANNOptionsReader.mm +108 -0
  332. package/ios/Options/ANNSearchBarOptions.h +4 -3
  333. package/ios/Options/ANNSheetOptions.h +4 -1
  334. package/ios/Options/ANNSideMenuOptions.h +2 -0
  335. package/ios/Options/ANNSideMenuOptions.mm +2 -0
  336. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  337. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  338. package/ios/Options/ANNUserChoice.h +57 -0
  339. package/ios/Options/ANNUserChoice.mm +44 -0
  340. package/ios/Options/Params/ANNImageSource.h +61 -0
  341. package/ios/Options/Params/ANNImageSource.mm +101 -0
  342. package/ios/Options/Params/ANNOptionImages.h +37 -0
  343. package/ios/Options/Params/ANNOptionImages.mm +284 -0
  344. package/ios/Options/Params/ANNParam.h +15 -0
  345. package/ios/Options/Params/ANNParam.mm +61 -0
  346. package/ios/Options/Params/Color.h +3 -0
  347. package/ios/Options/Params/Color.mm +21 -0
  348. package/ios/Options/Params/ColorParser.h +4 -3
  349. package/ios/Options/Params/ColorParser.mm +22 -4
  350. package/ios/Options/Params/Image.h +15 -2
  351. package/ios/Options/Params/Image.mm +35 -2
  352. package/ios/Options/Params/ImageParser.h +5 -5
  353. package/ios/Options/Params/ImageParser.mm +28 -46
  354. package/ios/Public/ApexNavigation/ANNSceneDelegate.h +3 -0
  355. package/ios/Public/ApexNavigation/ApexNavigation.h +1 -1
  356. package/ios/RECIPES.md +13 -6
  357. package/ios/React/ANNReactView.mm +6 -25
  358. package/ios/React/ANNSafeAreaViewComponentView.mm +28 -17
  359. package/ios/React/ANNSurfaceHostingView.h +14 -0
  360. package/ios/React/ANNSurfaceHostingView.mm +144 -1
  361. package/ios/React/Slots/ANNSlotComponentView.mm +24 -17
  362. package/ios/React/Slots/ANNSlotContainers.h +14 -6
  363. package/ios/React/Slots/ANNSlotContainers.mm +75 -2
  364. package/ios/React/Slots/ANNSlotHost.mm +1 -3
  365. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  366. package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
  367. package/ios/TopBar/ANNBarAppearance.h +2 -1
  368. package/ios/TopBar/ANNBarAppearance.mm +111 -30
  369. package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
  370. package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
  371. package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
  372. package/ios/TopBar/ANNBarButtons.h +14 -8
  373. package/ios/TopBar/ANNBarButtons.mm +397 -80
  374. package/ios/TopBar/ANNIconButtonImage.h +1 -0
  375. package/ios/TopBar/ANNIconButtonImage.mm +34 -1
  376. package/ios/TopBar/ANNLargeTitlePresenter.h +13 -0
  377. package/ios/TopBar/ANNLargeTitlePresenter.mm +50 -7
  378. package/ios/TopBar/ANNMainList.h +3 -0
  379. package/ios/TopBar/ANNMainList.mm +4 -0
  380. package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
  381. package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
  382. package/ios/TopBar/ANNSearchPresenter.mm +172 -21
  383. package/ios/TopBar/ANNSearchResultsViewController.mm +29 -6
  384. package/ios/TopBar/ANNTopBarPresenter.h +8 -2
  385. package/ios/TopBar/ANNTopBarPresenter.mm +155 -33
  386. package/ios/TurboModules/ANNTurboEventEmitter.mm +4 -35
  387. package/ios/TurboModules/ANNTurboModule.mm +104 -33
  388. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  389. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  390. package/lib/module/ARCHITECTURE.md +40 -27
  391. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  392. package/lib/module/api/app/AppRegistry.js +14 -2
  393. package/lib/module/api/app/AppRegistry.js.map +1 -1
  394. package/lib/module/api/app/createNavigation.js +190 -66
  395. package/lib/module/api/app/createNavigation.js.map +1 -1
  396. package/lib/module/api/commands/commands.js +54 -6
  397. package/lib/module/api/commands/commands.js.map +1 -1
  398. package/lib/module/api/events/navigationEvents.js +6 -2
  399. package/lib/module/api/events/navigationEvents.js.map +1 -1
  400. package/lib/module/api/hooks/useNavigation.js +8 -3
  401. package/lib/module/api/hooks/useNavigation.js.map +1 -1
  402. package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
  403. package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
  404. package/lib/module/api/linking/dispatchedLinks.js +23 -0
  405. package/lib/module/api/linking/dispatchedLinks.js.map +1 -0
  406. package/lib/module/api/linking/linking.js +11 -0
  407. package/lib/module/api/linking/linking.js.map +1 -1
  408. package/lib/module/api/navigation.js.map +1 -1
  409. package/lib/module/api/options/OptionsChain.js +2 -2
  410. package/lib/module/api/screens/defineScreen.js +45 -3
  411. package/lib/module/api/screens/defineScreen.js.map +1 -1
  412. package/lib/module/api/screens/nativeScreens.js +31 -6
  413. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  414. package/lib/module/api/screens/registerScreens.js +193 -35
  415. package/lib/module/api/screens/registerScreens.js.map +1 -1
  416. package/lib/module/api/theme/theme.js +5 -0
  417. package/lib/module/api/theme/theme.js.map +1 -1
  418. package/lib/module/engine/commands.js +51 -9
  419. package/lib/module/engine/commands.js.map +1 -1
  420. package/lib/module/engine/layout.js +20 -11
  421. package/lib/module/engine/layout.js.map +1 -1
  422. package/lib/module/engine/nativeScreens.js +9 -0
  423. package/lib/module/engine/nativeScreens.js.map +1 -1
  424. package/lib/module/engine/options.js +49 -15
  425. package/lib/module/engine/options.js.map +1 -1
  426. package/lib/module/engine/optionsChange.js +102 -0
  427. package/lib/module/engine/optionsChange.js.map +1 -0
  428. package/lib/module/engine/places.js +52 -7
  429. package/lib/module/engine/places.js.map +1 -1
  430. package/lib/module/engine/props.js +30 -1
  431. package/lib/module/engine/props.js.map +1 -1
  432. package/lib/module/engine/refresh.js +85 -0
  433. package/lib/module/engine/refresh.js.map +1 -0
  434. package/lib/module/engine/rememberedOptions.js +61 -1
  435. package/lib/module/engine/rememberedOptions.js.map +1 -1
  436. package/lib/module/engine/screens.js +77 -16
  437. package/lib/module/engine/screens.js.map +1 -1
  438. package/lib/module/interfaces/contract/commands.json +101 -0
  439. package/lib/module/interfaces/contract/errors.json +275 -0
  440. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +12 -2
  441. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  442. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  443. package/lib/typescript/api/app/createNavigation.d.ts +8 -3
  444. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  445. package/lib/typescript/api/commands/commands.d.ts.map +1 -1
  446. package/lib/typescript/api/commands/presentation.d.ts +4 -3
  447. package/lib/typescript/api/commands/presentation.d.ts.map +1 -1
  448. package/lib/typescript/api/events/navigationEvents.d.ts +3 -3
  449. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  450. package/lib/typescript/api/hooks/useNavigation.d.ts +1 -1
  451. package/lib/typescript/api/hooks/useNavigation.d.ts.map +1 -1
  452. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
  453. package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
  454. package/lib/typescript/api/index.d.ts +1 -1
  455. package/lib/typescript/api/index.d.ts.map +1 -1
  456. package/lib/typescript/api/linking/dispatchedLinks.d.ts +7 -0
  457. package/lib/typescript/api/linking/dispatchedLinks.d.ts.map +1 -0
  458. package/lib/typescript/api/linking/linking.d.ts +8 -0
  459. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  460. package/lib/typescript/api/navigation.d.ts +4 -1
  461. package/lib/typescript/api/navigation.d.ts.map +1 -1
  462. package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
  463. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  464. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  465. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  466. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  467. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  468. package/lib/typescript/api/theme/theme.d.ts +2 -0
  469. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  470. package/lib/typescript/engine/commands.d.ts +5 -0
  471. package/lib/typescript/engine/commands.d.ts.map +1 -1
  472. package/lib/typescript/engine/layout.d.ts.map +1 -1
  473. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  474. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  475. package/lib/typescript/engine/options.d.ts +5 -2
  476. package/lib/typescript/engine/options.d.ts.map +1 -1
  477. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  478. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  479. package/lib/typescript/engine/places.d.ts +7 -2
  480. package/lib/typescript/engine/places.d.ts.map +1 -1
  481. package/lib/typescript/engine/props.d.ts +8 -1
  482. package/lib/typescript/engine/props.d.ts.map +1 -1
  483. package/lib/typescript/engine/refresh.d.ts +35 -0
  484. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  485. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  486. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  487. package/lib/typescript/engine/screens.d.ts +27 -6
  488. package/lib/typescript/engine/screens.d.ts.map +1 -1
  489. package/lib/typescript/interfaces/ComponentEvents.d.ts +4 -4
  490. package/lib/typescript/interfaces/ComponentEvents.d.ts.map +1 -1
  491. package/lib/typescript/interfaces/Events.d.ts +28 -10
  492. package/lib/typescript/interfaces/Events.d.ts.map +1 -1
  493. package/lib/typescript/interfaces/Options.d.ts +1 -1
  494. package/lib/typescript/interfaces/Options.d.ts.map +1 -1
  495. package/lib/typescript/interfaces/options/animations.d.ts +80 -22
  496. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  497. package/lib/typescript/interfaces/options/bottomTabs.d.ts +21 -7
  498. package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
  499. package/lib/typescript/interfaces/options/buttons.d.ts +23 -15
  500. package/lib/typescript/interfaces/options/buttons.d.ts.map +1 -1
  501. package/lib/typescript/interfaces/options/modals.d.ts +34 -26
  502. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  503. package/lib/typescript/interfaces/options/sideMenu.d.ts +23 -6
  504. package/lib/typescript/interfaces/options/sideMenu.d.ts.map +1 -1
  505. package/lib/typescript/interfaces/options/stack.d.ts +17 -3
  506. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  507. package/lib/typescript/interfaces/options/system.d.ts +18 -11
  508. package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
  509. package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
  510. package/lib/typescript/interfaces/options/topBar.d.ts +68 -38
  511. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  512. package/metro/acceptPlugin.js +57 -0
  513. package/metro/babelTransformer.js +45 -0
  514. package/metro/index.js +36 -0
  515. package/package.json +3 -1
  516. package/src/ARCHITECTURE.md +40 -27
  517. package/src/adapters/NativeANNTurboModule.ts +12 -2
  518. package/src/api/app/AppRegistry.ts +16 -2
  519. package/src/api/app/createNavigation.ts +203 -69
  520. package/src/api/commands/commands.ts +48 -7
  521. package/src/api/commands/presentation.ts +4 -3
  522. package/src/api/events/navigationEvents.ts +10 -4
  523. package/src/api/hooks/useNavigation.ts +9 -4
  524. package/src/api/hooks/useSafeAreaInsets.ts +4 -3
  525. package/src/api/index.ts +1 -0
  526. package/src/api/linking/dispatchedLinks.ts +18 -0
  527. package/src/api/linking/linking.ts +22 -1
  528. package/src/api/navigation.ts +4 -1
  529. package/src/api/options/OptionsChain.ts +2 -2
  530. package/src/api/screens/defineScreen.ts +49 -5
  531. package/src/api/screens/nativeScreens.ts +40 -4
  532. package/src/api/screens/registerScreens.tsx +185 -42
  533. package/src/api/theme/theme.ts +5 -0
  534. package/src/engine/commands.ts +70 -13
  535. package/src/engine/layout.ts +27 -11
  536. package/src/engine/nativeScreens.ts +10 -1
  537. package/src/engine/options.ts +45 -19
  538. package/src/engine/optionsChange.ts +107 -0
  539. package/src/engine/places.ts +65 -7
  540. package/src/engine/props.ts +30 -1
  541. package/src/engine/refresh.ts +97 -0
  542. package/src/engine/rememberedOptions.ts +74 -1
  543. package/src/engine/screens.tsx +76 -21
  544. package/src/interfaces/ComponentEvents.ts +4 -4
  545. package/src/interfaces/Events.ts +29 -10
  546. package/src/interfaces/Options.ts +1 -0
  547. package/src/interfaces/contract/commands.json +101 -0
  548. package/src/interfaces/contract/errors.json +275 -0
  549. package/src/interfaces/options/animations.ts +81 -22
  550. package/src/interfaces/options/bottomTabs.ts +21 -7
  551. package/src/interfaces/options/buttons.ts +23 -15
  552. package/src/interfaces/options/modals.ts +34 -26
  553. package/src/interfaces/options/sideMenu.ts +24 -6
  554. package/src/interfaces/options/stack.ts +17 -3
  555. package/src/interfaces/options/system.ts +18 -11
  556. package/src/interfaces/options/toolbar.ts +2 -2
  557. package/src/interfaces/options/topBar.ts +68 -38
  558. package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -41
  559. package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -69
  560. package/android/src/main/java/dev/apexnative/navigation/react/events/NavigationStateReporter.kt +0 -62
  561. package/android/src/main/java/dev/apexnative/navigation/react/events/StateHold.kt +0 -62
  562. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
  563. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  564. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -138
  565. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -88
  566. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  567. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -66
  568. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +0 -179
  569. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -139
  570. package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
  571. package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -68
  572. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.h +0 -27
  573. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +0 -55
@@ -0,0 +1,154 @@
1
+ package dev.apexnative.navigation.react.events
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import dev.apexnative.navigation.react.ReloadCoordinator
6
+ import dev.apexnative.navigation.viewcontrollers.bottomtabs.BottomTabsController
7
+ import dev.apexnative.navigation.viewcontrollers.navigator.Navigators
8
+ import dev.apexnative.navigation.viewcontrollers.parent.ParentController
9
+ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
10
+
11
+ /**
12
+ * The one place of the navigation state JavaScript hears (`ANN.NavigationStateChanged`): the screen the user sees and
13
+ * the selected tab, the last state sent, the update posted for a pass of the main loop and its holds. Every change of
14
+ * the screen the user sees comes with a screen appearing: a push, a pop, a modal, a tab, a menu.
15
+ *
16
+ * Two ways ask for it. The dispatcher asks for one update after the events of a pass ([setNeedsUpdate], coalesced and
17
+ * held while a `setRoot` shows the modals and the overlays of its layout). A command's answer sends it at once
18
+ * ([updateNow], never held), once per answer (`CommandAnswer.reportStateNow`): at the end of its transition, else
19
+ * right before its promise. Each goes only when the state differs from the last one sent, through the JavaScript that
20
+ * runs now. Main thread only.
21
+ */
22
+ internal object NavigationState : AppearanceDispatcher.StateUpdates {
23
+ /** What JavaScript hears: the screen the user sees (`null` for none) and the selected tab (`null` out of tabs). */
24
+ data class State(val screenId: String?, val screenName: String?, val tab: Int?)
25
+
26
+ private val handler by lazy { Handler(Looper.getMainLooper()) }
27
+
28
+ // The last state sent to the JavaScript that runs now; none before its first.
29
+ private var sent: State? = null
30
+
31
+ // The one posted update of a pass of the main loop, which checks the holds when it runs.
32
+ private val updates = Updates(
33
+ post = { task -> handler.post(task) },
34
+ generation = { ReloadCoordinator.generation },
35
+ update = ::updateNow,
36
+ )
37
+
38
+ override fun setNeedsUpdate() {
39
+ if (EventEmitter.current().canSend) updates.setNeedsUpdate()
40
+ }
41
+
42
+ override fun hold() = updates.hold()
43
+
44
+ override fun release() = updates.release()
45
+
46
+ /**
47
+ * Sends the state at once when it differs from the last one sent. A command's answer calls it once, at the end of
48
+ * its transition or right before its promise: the event and the resolution reach JavaScript in that order, so the
49
+ * state is new when the `await` of the command goes on. The update posted for the pass then finds nothing new.
50
+ */
51
+ override fun updateNow() {
52
+ val emitter = EventEmitter.current()
53
+ if (!emitter.canSend) return
54
+ // The navigation of the activity the commands run in (`Navigators.current`).
55
+ val navigator = Navigators.current()?.navigator ?: return
56
+ val next = stateOf(navigator.visibleLayout)
57
+ if (next == sent) return
58
+ sent = next
59
+ emitter.emitNavigationStateChanged(next.screenId, next.screenName, next.tab)
60
+ }
61
+
62
+ /** The new JavaScript has no state yet (`ANN.AppLaunched`): the next one goes to it whatever it is. */
63
+ fun reset() {
64
+ sent = null
65
+ }
66
+
67
+ /**
68
+ * The JavaScript went with its layouts: a hold its `setRoot` left, and an update wanted under it, go with it, so
69
+ * the new JavaScript's updates are never held by the old one's.
70
+ */
71
+ fun javaScriptWent() = updates.dropHolds()
72
+
73
+ // The top modal, or the root ([layout]), down to the screen the user sees; overlays don't count. The tab is the
74
+ // selected one of the nearest tabs the screen is in, the last met on the way down, in a modal too: the tabs
75
+ // `selectTab` acts on from it. None for a screen out of tabs.
76
+ internal fun stateOf(layout: ViewController<*>?): State {
77
+ var tab: Int? = null
78
+ var current = layout ?: return State(null, null, null)
79
+ while (current is ParentController<*>) {
80
+ if (current is BottomTabsController) tab = current.selectedIndex
81
+ current = current.currentChild ?: return State(null, null, null)
82
+ }
83
+ return State(current.id, current.currentComponentName, tab)
84
+ }
85
+
86
+ /**
87
+ * The posted update and its holds. [setNeedsUpdate] posts one update ([post]), coalesced: the asks of one pass of
88
+ * the main loop make one, which runs [update] in a later pass. While held, an ask, or a posted update that runs
89
+ * while held (asked before the hold was taken), only marks one as wanted, and the last release posts it. Released
90
+ * right before the answer, that update finds nothing new: the state before the answer is sent at once, never
91
+ * held, and an identical state is never sent twice. Every [hold] has one [release]; a release without a hold does
92
+ * nothing, so the count never goes below zero. An update keeps the JavaScript it was asked for ([generation]): one
93
+ * asked by a JavaScript that went before it runs is dropped; a newer ask of the new JavaScript still goes.
94
+ */
95
+ internal class Updates(
96
+ private val post: (() -> Unit) -> Unit,
97
+ private val generation: () -> Int = { 0 },
98
+ private val update: () -> Unit,
99
+ ) {
100
+ /** The holds not released yet. */
101
+ var count = 0
102
+ private set
103
+
104
+ // An update was asked while held.
105
+ private var wanted = false
106
+ // An update is posted and hasn't run yet.
107
+ private var scheduled = false
108
+ // The JavaScript the last ask was for.
109
+ private var askedIn = 0
110
+
111
+ /** Asks for one update in a later pass of the main loop; while held, once the last hold is released. */
112
+ fun setNeedsUpdate() {
113
+ askedIn = generation()
114
+ if (count > 0) wanted = true else schedule()
115
+ }
116
+
117
+ /** The updates asked from now on, and those posted that run from now on, wait for as many [release]s. */
118
+ fun hold() {
119
+ count++
120
+ }
121
+
122
+ /** One hold ends; with the last, the update asked while held is posted now. */
123
+ fun release() {
124
+ if (count == 0) return
125
+ count--
126
+ if (count > 0 || !wanted) return
127
+ wanted = false
128
+ schedule()
129
+ }
130
+
131
+ /**
132
+ * The holds and the update wanted under them go, unreleased: the JavaScript that took them went. An update
133
+ * already posted stays; it runs only for the JavaScript it was asked for.
134
+ */
135
+ fun dropHolds() {
136
+ count = 0
137
+ wanted = false
138
+ }
139
+
140
+ private fun schedule() {
141
+ if (scheduled) return
142
+ scheduled = true
143
+ post {
144
+ scheduled = false
145
+ // A hold taken since it was posted: the update waits for the release. The JavaScript it was asked for
146
+ // went: nothing goes to the new one, which hears its first state after its own changes.
147
+ when {
148
+ count > 0 -> wanted = true
149
+ askedIn == generation() -> update()
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
@@ -45,12 +45,6 @@ internal class ScreenAppearance(
45
45
  /** Cancels the hold limit running for [held]; none when nothing is held. */
46
46
  internal var cancelLimit: (() -> Unit)? = null
47
47
 
48
- /**
49
- * This appearance started without its WillAppear (`AppearanceDispatcher.withoutWillAppear`: a side menu's drawer
50
- * that jumped open); its DidAppear is its first event. Cleared as it is hidden.
51
- */
52
- internal var skippedWill = false
53
-
54
48
  private var ready = content == null
55
49
  private var waitsForContent = false
56
50
 
@@ -10,7 +10,7 @@ import com.facebook.react.uimanager.StateWrapper
10
10
  import com.facebook.react.uimanager.ThemedReactContext
11
11
  import com.facebook.react.uimanager.UIManagerHelper
12
12
  import com.facebook.react.views.view.ReactViewGroup
13
- import dev.apexnative.navigation.react.ScreenInsets
13
+ import dev.apexnative.navigation.react.ScreenEnvironment
14
14
  import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
15
15
 
16
16
  /**
@@ -20,7 +20,7 @@ import dev.apexnative.navigation.viewcontrollers.component.ScreenReadiness
20
20
  @SuppressLint("ViewConstructor")
21
21
  class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext) {
22
22
  private var stopListening: (() -> Unit)? = null
23
- private var applied: ScreenInsets.Insets? = null
23
+ private var applied: ScreenEnvironment.Insets? = null
24
24
  private var readySent = false
25
25
  private var contentSignalled = false
26
26
 
@@ -48,7 +48,7 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
48
48
  stopListening?.invoke()
49
49
  field = value
50
50
  applied = null
51
- stopListening = value?.let { id -> ScreenInsets.listenToPadding(id) { applyPadding() } }
51
+ stopListening = value?.let { id -> ScreenEnvironment.listenToPadding(id) { applyPadding() } }
52
52
  // The screen has a safe area view: it counts as rendered once this view says so.
53
53
  value?.let(ScreenReadiness::wrapped)
54
54
  applyPadding()
@@ -56,7 +56,7 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
56
56
 
57
57
  private fun applyPadding() {
58
58
  val state = stateWrapper ?: return
59
- val padding = screenId?.let(ScreenInsets::padding) ?: return
59
+ val padding = screenId?.let(ScreenEnvironment::padding) ?: return
60
60
  if (padding == applied) return
61
61
  applied = padding
62
62
  state.updateState(
@@ -69,9 +69,9 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
69
69
  )
70
70
  }
71
71
 
72
- private fun paddingOf(data: ReadableMap): ScreenInsets.Insets? {
72
+ private fun paddingOf(data: ReadableMap): ScreenEnvironment.Insets? {
73
73
  if (!data.hasKey("top")) return null
74
- return ScreenInsets.Insets(data.getDouble("top"), data.getDouble("right"), data.getDouble("bottom"),
74
+ return ScreenEnvironment.Insets(data.getDouble("top"), data.getDouble("right"), data.getDouble("bottom"),
75
75
  data.getDouble("left"))
76
76
  }
77
77
 
@@ -95,8 +95,9 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
95
95
  val reactContext = context as? ReactContext ?: return
96
96
  val dispatcher = UIManagerHelper.getEventDispatcher(reactContext) ?: return
97
97
  readySent = true
98
- dispatcher.dispatchEvent(SafeAreaReadyEvent(UIManagerHelper.getSurfaceId(this), id))
99
- screenId?.let(ScreenReadiness::laidOut)
98
+ val surfaceId = UIManagerHelper.getSurfaceId(this)
99
+ dispatcher.dispatchEvent(SafeAreaReadyEvent(surfaceId, id))
100
+ screenId?.let { ScreenReadiness.laidOut(it, surfaceId) }
100
101
  }
101
102
 
102
103
  // The navigation tells whether this view's screen waits: a component of a bar (title, button, background, tab)
@@ -111,12 +112,13 @@ class SafeAreaView(reactContext: ThemedReactContext) : ReactViewGroup(reactConte
111
112
  }
112
113
 
113
114
  // Once, by the screen's id: the container asks the screen's view for the layout React mounts the content without.
114
- // On attach too: content added before this view was attached is signalled then.
115
+ // On attach too: content added before this view was attached is signalled then. The signal names this view's
116
+ // surface: only the root that shows it takes it as its own.
115
117
  private fun signalContent() {
116
118
  if (contentSignalled) return
117
119
  val id = screenId ?: return
118
120
  contentSignalled = true
119
- ScreenReadiness.contentRendered(id)
121
+ ScreenReadiness.contentRendered(id, UIManagerHelper.getSurfaceId(this))
120
122
  }
121
123
 
122
124
  fun drop() {
@@ -9,14 +9,15 @@ interface SlotContainer {
9
9
  }
10
10
 
11
11
  /**
12
- * The native containers the slots of the app's React tree show their children in, by slot id. A destroyed container
13
- * drops out on its own.
12
+ * The native containers the slots of the app's React tree show their children in, by slot id, and the slot views by
13
+ * the same id: a change of a container goes straight to the views of its id. A destroyed container and a collected
14
+ * view drop out on their own.
14
15
  */
15
16
  object SlotContainers {
16
17
  private class Entry(val view: WeakReference<ViewGroup>, val passThrough: Boolean)
17
18
 
18
19
  private val containers = HashMap<String, Entry>()
19
- private val slots = LinkedHashSet<SlotView>()
20
+ private val slots = SlotViews<SlotView>()
20
21
 
21
22
  /** `passThrough`: touches outside the slot's content go to the screens below, as in an overlay for a toast. */
22
23
  fun put(slotId: String, container: ViewGroup, passThrough: Boolean = false) {
@@ -33,12 +34,14 @@ object SlotContainers {
33
34
  changed(slotId)
34
35
  }
35
36
 
36
- /** The container of a slot appeared or changed its size. */
37
+ /** The container of a slot appeared or changed its size: the slot views under its id, and only they, hear it. */
37
38
  fun changed(slotId: String) {
38
- slots.toList().forEach { it.containerChanged(slotId) }
39
+ slots.views(slotId).forEach { it.containerChanged(slotId) }
39
40
  }
40
41
 
41
- internal fun listen(slot: SlotView) = slots.add(slot)
42
+ /** The slot view goes under its slot id, and out from under the id it had. Main thread only. */
43
+ internal fun register(slotId: String, slot: SlotView) = slots.register(slotId, slot)
42
44
 
43
- internal fun stopListening(slot: SlotView) = slots.remove(slot)
45
+ /** The slot view unmounted or lost its slot id: no container change reaches it. Main thread only. */
46
+ internal fun unregister(slot: SlotView) = slots.unregister(slot)
44
47
  }
@@ -2,9 +2,13 @@ package dev.apexnative.navigation.slots
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.util.Size
5
6
  import android.view.MotionEvent
6
7
  import android.view.ViewGroup
7
8
  import android.widget.FrameLayout
9
+ import com.facebook.react.uimanager.PixelUtil.dpToPx
10
+ import dev.apexnative.navigation.react.ContentHeights
11
+ import dev.apexnative.navigation.react.ContentMounts
8
12
  import dev.apexnative.navigation.react.events.AppearanceDispatcher
9
13
  import dev.apexnative.navigation.react.events.ComponentType
10
14
  import dev.apexnative.navigation.react.events.ScreenAppearance
@@ -16,9 +20,10 @@ import dev.apexnative.navigation.viewcontrollers.viewcontroller.ScrollEventListe
16
20
  * What a slot screen (`slot:<Name>`) shows: the empty container its slot of the app's React tree moves its children
17
21
  * into, under the screen's id ([SlotContainers]), told of each change of its size. It is the screen's content for the
18
22
  * core: ready once the slot's content has mounted into it and, when the screen has a safe area view, the screen
19
- * rendered into that view; its mounted handlers run at each mount. Its appearance events go through its record to the
20
- * core's dispatcher (`AppearanceDispatcher`), which holds them until it is ready or the hold limit. Destroyed while the
21
- * id is still its own, it tells JavaScript to take the slot's content away (`ANN.SlotDestroyed`).
23
+ * rendered into that view; its mounted handlers run at each mount of Fabric and as the content mounts into it. Its
24
+ * content measures without a height limit in the slot's own layout ([contentSize]). Its appearance events go through
25
+ * its record to the core's dispatcher (`AppearanceDispatcher`), which holds them until it is ready or the hold limit.
26
+ * Destroyed while the id is still its own, it tells JavaScript to take the slot's content away (`ANN.SlotDestroyed`).
22
27
  */
23
28
  @SuppressLint("ViewConstructor")
24
29
  class SlotScreenView(
@@ -30,8 +35,13 @@ class SlotScreenView(
30
35
  private val scrollEventListener = ScrollEventListener()
31
36
  private val whenMounted = mutableListOf<Runnable>()
32
37
  private val whenRendered = mutableListOf<Runnable>()
33
- private val mountedHandlers = LinkedHashMap<String, Runnable>()
34
- private var readinessListener: ScreenReadiness.Listener? = null
38
+ private val mounts = ContentMounts { !destroyed && isAttachedToWindow }
39
+ private var listening = false
40
+
41
+ // The safe area view of a slot renders in the app's one React tree: every signal of the id is the screen's.
42
+ private val readiness = object : ScreenReadiness.Listener {
43
+ override fun onContentRendered() = checkReadiness()
44
+ }
35
45
  private var mounted = false
36
46
  private var readyTold = false
37
47
  private var destroyed = false
@@ -79,10 +89,11 @@ class SlotScreenView(
79
89
  destroyed = true
80
90
  whenMounted.clear()
81
91
  whenRendered.clear()
82
- mountedHandlers.clear()
92
+ mounts.clear()
83
93
  stopListening()
84
94
  // A later screen with the same id keeps the slot.
85
95
  if (SlotContainers.get(componentId) !== this) return
96
+ ContentHeights.forgetSlot(componentId)
86
97
  SlotContainers.remove(componentId)
87
98
  if (!isPreparedPush) AppearanceDispatcher.slotDestroyed(componentId, appearance.generation)
88
99
  }
@@ -105,14 +116,9 @@ class SlotScreenView(
105
116
  if (readyTold) return listener.run()
106
117
  whenRendered += listener
107
118
  if (isReady()) return checkReadiness()
108
- if (readinessListener != null) return
109
- val readiness = object : ScreenReadiness.Listener {
110
- override fun onLaidOut() = Unit
111
-
112
- override fun onContentRendered() = checkReadiness()
113
- }
114
- readinessListener = readiness
115
- ScreenReadiness.setListener(componentId, readiness)
119
+ if (listening) return
120
+ listening = true
121
+ ScreenReadiness.addListener(componentId, this, readiness)
116
122
  }
117
123
 
118
124
  override fun whenSlotContentMounted(listener: Runnable) {
@@ -120,7 +126,21 @@ class SlotScreenView(
120
126
  }
121
127
 
122
128
  override fun setOnContentMounted(key: String, handler: Runnable?) {
123
- if (handler == null) mountedHandlers.remove(key) else mountedHandlers[key] = handler
129
+ if (!destroyed) mounts.set(key, handler)
130
+ }
131
+
132
+ // The slot measures its content in its own layout: the first question asks for it and lays the slot out again; the
133
+ // height comes with that layout, before the mount the handlers hear. The width is the container's, at which the
134
+ // slot lays its content out.
135
+ override fun contentSize(maxWidth: Int): Size? {
136
+ if (destroyed) return null
137
+ if (!ContentHeights.isSlotRequested(componentId)) {
138
+ ContentHeights.requestSlot(componentId)
139
+ SlotContainers.changed(componentId)
140
+ return null
141
+ }
142
+ val height = ContentHeights.slotHeight(componentId)?.takeIf { it > 0f } ?: return null
143
+ return Size(if (width > 0) width else maxWidth, Math.round(height.dpToPx()))
124
144
  }
125
145
 
126
146
  // Only the container that holds the slot of the id: a destroyed one, or one a later screen replaced, takes no
@@ -136,7 +156,7 @@ class SlotScreenView(
136
156
  val waiting = whenMounted.toList()
137
157
  whenMounted.clear()
138
158
  waiting.forEach { it.run() }
139
- mountedHandlers.values.toList().forEach { it.run() }
159
+ mounts.run()
140
160
  checkReadiness()
141
161
  }
142
162
 
@@ -151,8 +171,8 @@ class SlotScreenView(
151
171
  }
152
172
 
153
173
  private fun stopListening() {
154
- val readiness = readinessListener ?: return
155
- readinessListener = null
156
- ScreenReadiness.removeListener(componentId, readiness)
174
+ if (!listening) return
175
+ listening = false
176
+ ScreenReadiness.removeListener(componentId, this)
157
177
  }
158
178
  }
@@ -15,6 +15,7 @@ import com.facebook.react.uimanager.ThemedReactContext
15
15
  import com.facebook.react.uimanager.UIManagerHelper
16
16
  import com.facebook.react.uimanager.events.EventDispatcher
17
17
  import com.facebook.react.views.view.ReactViewGroup
18
+ import dev.apexnative.navigation.react.ContentHeights
18
19
 
19
20
  /**
20
21
  * The React children of a slot. They live outside the views of the host's surface, in the container of a screen, so
@@ -79,6 +80,8 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
79
80
  }
80
81
  private var lastWidth = -1f
81
82
  private var lastHeight = -1f
83
+ // The slot was laid out once more, at the same size, for the measure of its content (`ContentHeights`).
84
+ private var measureAsked = false
82
85
 
83
86
  private val slotId: String?
84
87
  get() = getTag(R.id.view_tag_native_id) as? String
@@ -86,7 +89,6 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
86
89
  init {
87
90
  content.visibility = INVISIBLE
88
91
  addView(content)
89
- SlotContainers.listen(this)
90
92
  }
91
93
 
92
94
  override fun setId(id: Int) {
@@ -100,7 +102,9 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
100
102
  }
101
103
 
102
104
  fun attachContent() {
103
- val id = slotId ?: return
105
+ // The view hears the changes of the container under the id it has now, and no other.
106
+ val id = slotId ?: return SlotContainers.unregister(this)
107
+ SlotContainers.register(id, this)
104
108
  val container = SlotContainers.get(id)
105
109
  val target: ViewGroup = container ?: this
106
110
  if (content.parent !== target) {
@@ -115,7 +119,14 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
115
119
  if (container != null && state != null && container.width > 0 && container.height > 0) {
116
120
  val width = container.width.toFloat().pxToDp()
117
121
  val height = container.height.toFloat().pxToDp()
118
- if (width != lastWidth || height != lastHeight) {
122
+ // A container that measures the content gets one layout more for it, with the same size, until the height
123
+ // came.
124
+ val requested = ContentHeights.isSlotRequested(id)
125
+ // A request that went (its screen did) and comes again gets its layout again.
126
+ if (!requested) measureAsked = false
127
+ val measures = !measureAsked && requested && ContentHeights.slotHeight(id) == null
128
+ if (width != lastWidth || height != lastHeight || measures) {
129
+ measureAsked = measureAsked || measures
119
130
  lastWidth = width
120
131
  lastHeight = height
121
132
  state.updateState(
@@ -135,7 +146,8 @@ class SlotView(reactContext: ThemedReactContext) : ReactViewGroup(reactContext)
135
146
  }
136
147
 
137
148
  fun drop() {
138
- SlotContainers.stopListening(this)
149
+ measureAsked = false
150
+ SlotContainers.unregister(this)
139
151
  (content.parent as? ViewGroup)?.removeView(content)
140
152
  }
141
153
  }
@@ -0,0 +1,40 @@
1
+ package dev.apexnative.navigation.slots
2
+
3
+ import java.lang.ref.WeakReference
4
+ import java.util.WeakHashMap
5
+
6
+ /**
7
+ * The slot views by their slot id, held weakly, so a change of a container reaches only the views of its id. A view is
8
+ * under one id at a time: registering it under a new one (its `nativeID` changed, or Fabric reused it) takes it out
9
+ * from under the old one. Two live views under one id both stay, in the order they came, and both hear the change, as
10
+ * every view with that id did when the change went to all of them. Main thread only.
11
+ */
12
+ internal class SlotViews<T : Any> {
13
+ private val byId = HashMap<String, MutableList<WeakReference<T>>>()
14
+ private val idOf = WeakHashMap<T, String>()
15
+
16
+ fun register(slotId: String, view: T) {
17
+ if (idOf[view] == slotId) return
18
+ unregister(view)
19
+ idOf[view] = slotId
20
+ byId.getOrPut(slotId) { ArrayList(1) }.add(WeakReference(view))
21
+ }
22
+
23
+ fun unregister(view: T) {
24
+ val slotId = idOf.remove(view) ?: return
25
+ prune(slotId) { it === view }
26
+ }
27
+
28
+ /** The live views under [slotId], a copy: a view may register or leave while it hears the change. */
29
+ fun views(slotId: String): List<T> {
30
+ prune(slotId) { false }
31
+ return byId[slotId]?.mapNotNull { it.get() }.orEmpty()
32
+ }
33
+
34
+ // Drops the collected views under the id and the ones `drop` picks; an id with no views left goes.
35
+ private fun prune(slotId: String, drop: (T) -> Boolean) {
36
+ val views = byId[slotId] ?: return
37
+ views.removeAll { reference -> reference.get()?.let(drop) ?: true }
38
+ if (views.isEmpty()) byId.remove(slotId)
39
+ }
40
+ }
@@ -0,0 +1,66 @@
1
+ package dev.apexnative.navigation.utils
2
+
3
+ import android.content.Context
4
+ import android.content.res.Configuration
5
+ import android.view.View
6
+ import java.io.File
7
+ import java.net.URI
8
+ import java.net.URISyntaxException
9
+
10
+ /**
11
+ * What an image of the options is cached by: its [uri] and what changes the decoded result for the same uri, the
12
+ * screen's density (a drawable of the app picks its density's file, a decoded file is scaled to it), light or dark
13
+ * (`drawable-night`) and the layout direction (`drawable-ldrtl`, auto-mirrored drawables). For a `file:` uri also the
14
+ * file's [modified] time and [size]: a file the app writes again at the same path is another image. The loader bakes
15
+ * no tint and no size into an image, so neither is part of it.
16
+ */
17
+ internal data class ImageKey(
18
+ val uri: String,
19
+ val densityDpi: Int,
20
+ val night: Boolean,
21
+ val rightToLeft: Boolean,
22
+ /** When the file of a `file:` uri last changed, in milliseconds; 0 for any other uri or a file that isn't there. */
23
+ val modified: Long = 0,
24
+ /** The bytes of the file of a `file:` uri; 0 for any other uri or a file that isn't there. */
25
+ val size: Long = 0,
26
+ ) {
27
+ companion object {
28
+ private const val FILE_SCHEME = "file:"
29
+ private const val FILE_PREFIX = "file://"
30
+
31
+ /** The key of [uri] in the current configuration of [context]. Any thread. */
32
+ fun of(context: Context, uri: String): ImageKey {
33
+ val configuration = context.resources.configuration
34
+ return of(uri, configuration.densityDpi, configuration.uiMode, configuration.layoutDirection)
35
+ }
36
+
37
+ /**
38
+ * The key of [uri] for a configuration's density, `uiMode` (only its night bits count) and layout direction;
39
+ * a `file:` uri reads its file's modification time and size. Any thread.
40
+ */
41
+ fun of(uri: String, densityDpi: Int, uiMode: Int, layoutDirection: Int): ImageKey {
42
+ val file = filePath(uri)?.let(::File)
43
+ return ImageKey(
44
+ uri = uri,
45
+ densityDpi = densityDpi,
46
+ night = uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES,
47
+ rightToLeft = layoutDirection == View.LAYOUT_DIRECTION_RTL,
48
+ // Both 0 when the file isn't there or can't be read.
49
+ modified = file?.lastModified() ?: 0,
50
+ size = file?.length() ?: 0,
51
+ )
52
+ }
53
+
54
+ // The path of a `file:` uri, decoded as the loader's `Uri.getPath` decodes it; none for any other uri. A uri
55
+ // that `URI` can't parse (an unescaped space) is taken as it is after its scheme.
56
+ private fun filePath(uri: String): String? {
57
+ if (!uri.startsWith(FILE_SCHEME)) return null
58
+ val path = try {
59
+ URI(uri).path
60
+ } catch (e: URISyntaxException) {
61
+ null
62
+ }
63
+ return path ?: uri.removePrefix(FILE_PREFIX).removePrefix(FILE_SCHEME).ifEmpty { null }
64
+ }
65
+ }
66
+ }