@apexnative/navigation 1.0.0-alpha.6 → 1.0.0-alpha.8

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 (242) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +15 -0
  3. package/android/CLAUDE.md +19 -10
  4. package/android/CORE.md +3 -2
  5. package/android/consumer-rules.pro +6 -0
  6. package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
  7. package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
  8. package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
  9. package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
  10. package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
  11. package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
  12. package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
  13. package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
  14. package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
  15. package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
  16. package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
  17. package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
  18. package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +4 -0
  19. package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
  20. package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
  21. package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
  22. package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
  23. package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
  24. package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
  25. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +329 -55
  26. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +26 -5
  27. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
  28. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
  29. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
  30. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +46 -7
  31. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
  32. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +7 -4
  33. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
  34. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
  35. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
  36. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
  37. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
  38. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +86 -39
  39. package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarVisibility.kt +9 -2
  40. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +124 -181
  41. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
  42. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  43. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +56 -3
  44. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +170 -153
  45. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  46. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -45
  47. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/{TextChangeAnimator.kt → TextMorph.kt} +79 -129
  48. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  49. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  50. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  51. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
  52. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
  53. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  54. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  55. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  56. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
  57. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  58. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  59. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  60. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  61. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  62. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  63. package/android/src/main/res/values/styles.xml +1 -1
  64. package/android/src/main/res/values-night/styles.xml +1 -1
  65. package/cli/doctor/checks/app.js +16 -1
  66. package/cli/project.js +4 -0
  67. package/cli/setup/index.js +5 -0
  68. package/cli/setup/steps/metro.js +46 -0
  69. package/docs-md/architecture/native-screens.md +4 -0
  70. package/docs-md/architecture/theme-and-context.md +131 -12
  71. package/docs-md/architecture/transitions.md +43 -4
  72. package/docs-md/flows/bottom-sheets.md +4 -2
  73. package/docs-md/fundamentals/screens-and-props.md +9 -1
  74. package/docs-md/guides/cli.md +3 -1
  75. package/docs-md/help/troubleshooting.md +1 -1
  76. package/docs-md/index.json +29 -1
  77. package/docs-md/screen-ui/back-and-gestures.md +3 -1
  78. package/docs-md/screen-ui/bar-buttons.md +1 -1
  79. package/docs-md/screen-ui/toolbar-and-fab.md +2 -0
  80. package/docs-md/start/installation.md +16 -1
  81. package/ios/Animations/ANNFrameDriver.h +55 -15
  82. package/ios/Animations/ANNFrameDriver.mm +214 -60
  83. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  84. package/ios/Animations/ANNTransitionAnimator.mm +49 -3
  85. package/ios/Animations/ANNTransitionPlan.h +2 -2
  86. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  87. package/ios/Animations/Elements/ANNElementFinder.h +2 -2
  88. package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
  89. package/ios/Animations/Elements/ANNElementTransition.h +3 -3
  90. package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
  91. package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
  92. package/ios/Animations/Elements/ANNSharedElementFlight.mm +181 -43
  93. package/ios/Animations/Interpolators/ANNViewTracks.h +6 -5
  94. package/ios/Animations/Interpolators/ANNViewTracks.mm +51 -21
  95. package/ios/CLAUDE.md +19 -8
  96. package/ios/CORE.md +1 -1
  97. package/ios/Core/ANNAppDelegate.mm +0 -2
  98. package/ios/Core/ANNDeepLinkQueue.h +6 -5
  99. package/ios/Core/ANNDeepLinkQueue.mm +22 -10
  100. package/ios/Core/ANNSceneDelegate.mm +2 -2
  101. package/ios/Core/ANNUtils.h +7 -0
  102. package/ios/Core/ANNUtils.mm +10 -0
  103. package/ios/Core/ApexNavigation.mm +6 -2
  104. package/ios/Layouts/Overlay/ANNOverlayCoordinator.mm +1 -7
  105. package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
  106. package/ios/Layouts/Screen/ANNComponentViewController.mm +4 -1
  107. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  108. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  109. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  110. package/ios/Layouts/Stack/ANNStackController.mm +153 -11
  111. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  112. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  113. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  114. package/ios/Layouts/Tabs/ANNTabsPreloadWindow.mm +2 -4
  115. package/ios/Options/ANNAnimationOptions.h +2 -1
  116. package/ios/Options/ANNAnimationOptions.mm +1 -1
  117. package/ios/Options/ANNOptions.h +7 -0
  118. package/ios/Options/ANNOptions.mm +45 -0
  119. package/ios/Options/ANNOptionsReader.mm +2 -4
  120. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  121. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  122. package/ios/Options/Params/ColorParser.h +4 -3
  123. package/ios/Options/Params/ColorParser.mm +22 -4
  124. package/ios/React/ANNSurfaceHostingView.h +8 -0
  125. package/ios/React/ANNSurfaceHostingView.mm +28 -1
  126. package/ios/React/Slots/ANNSlotHost.mm +1 -3
  127. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  128. package/ios/TurboModules/ANNTurboModule.mm +46 -0
  129. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  130. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  131. package/lib/module/ARCHITECTURE.md +9 -3
  132. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  133. package/lib/module/api/app/AppRegistry.js +14 -2
  134. package/lib/module/api/app/AppRegistry.js.map +1 -1
  135. package/lib/module/api/app/createNavigation.js +188 -66
  136. package/lib/module/api/app/createNavigation.js.map +1 -1
  137. package/lib/module/api/events/navigationEvents.js +6 -2
  138. package/lib/module/api/events/navigationEvents.js.map +1 -1
  139. package/lib/module/api/linking/linking.js +11 -0
  140. package/lib/module/api/linking/linking.js.map +1 -1
  141. package/lib/module/api/screens/defineScreen.js +45 -3
  142. package/lib/module/api/screens/defineScreen.js.map +1 -1
  143. package/lib/module/api/screens/nativeScreens.js +31 -6
  144. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  145. package/lib/module/api/screens/registerScreens.js +193 -35
  146. package/lib/module/api/screens/registerScreens.js.map +1 -1
  147. package/lib/module/api/theme/theme.js +5 -0
  148. package/lib/module/api/theme/theme.js.map +1 -1
  149. package/lib/module/engine/commands.js +21 -1
  150. package/lib/module/engine/commands.js.map +1 -1
  151. package/lib/module/engine/layout.js +7 -2
  152. package/lib/module/engine/layout.js.map +1 -1
  153. package/lib/module/engine/nativeScreens.js +9 -0
  154. package/lib/module/engine/nativeScreens.js.map +1 -1
  155. package/lib/module/engine/options.js +5 -4
  156. package/lib/module/engine/options.js.map +1 -1
  157. package/lib/module/engine/optionsChange.js +102 -0
  158. package/lib/module/engine/optionsChange.js.map +1 -0
  159. package/lib/module/engine/refresh.js +85 -0
  160. package/lib/module/engine/refresh.js.map +1 -0
  161. package/lib/module/engine/rememberedOptions.js +60 -0
  162. package/lib/module/engine/rememberedOptions.js.map +1 -1
  163. package/lib/module/engine/screens.js +26 -0
  164. package/lib/module/engine/screens.js.map +1 -1
  165. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
  166. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  167. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  168. package/lib/typescript/api/app/createNavigation.d.ts +5 -0
  169. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  170. package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
  171. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  172. package/lib/typescript/api/index.d.ts +1 -1
  173. package/lib/typescript/api/index.d.ts.map +1 -1
  174. package/lib/typescript/api/linking/linking.d.ts +8 -0
  175. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  176. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  177. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  178. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  179. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  180. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  181. package/lib/typescript/api/theme/theme.d.ts +2 -0
  182. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  183. package/lib/typescript/engine/commands.d.ts +5 -0
  184. package/lib/typescript/engine/commands.d.ts.map +1 -1
  185. package/lib/typescript/engine/layout.d.ts.map +1 -1
  186. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  187. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  188. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  189. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  190. package/lib/typescript/engine/refresh.d.ts +35 -0
  191. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  192. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  193. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  194. package/lib/typescript/engine/screens.d.ts +7 -0
  195. package/lib/typescript/engine/screens.d.ts.map +1 -1
  196. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  197. package/lib/typescript/interfaces/options/animations.d.ts +40 -15
  198. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  199. package/lib/typescript/interfaces/options/bottomTabs.d.ts +2 -2
  200. package/lib/typescript/interfaces/options/buttons.d.ts +2 -2
  201. package/lib/typescript/interfaces/options/modals.d.ts +6 -7
  202. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  203. package/lib/typescript/interfaces/options/stack.d.ts +8 -3
  204. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  205. package/lib/typescript/interfaces/options/topBar.d.ts +14 -0
  206. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  207. package/metro/acceptPlugin.js +57 -0
  208. package/metro/babelTransformer.js +45 -0
  209. package/metro/index.js +36 -0
  210. package/package.json +3 -1
  211. package/src/ARCHITECTURE.md +9 -3
  212. package/src/adapters/NativeANNTurboModule.ts +7 -0
  213. package/src/api/app/AppRegistry.ts +16 -2
  214. package/src/api/app/createNavigation.ts +198 -66
  215. package/src/api/events/navigationEvents.ts +9 -3
  216. package/src/api/index.ts +1 -0
  217. package/src/api/linking/linking.ts +22 -1
  218. package/src/api/screens/defineScreen.ts +49 -5
  219. package/src/api/screens/nativeScreens.ts +40 -4
  220. package/src/api/screens/registerScreens.tsx +185 -42
  221. package/src/api/theme/theme.ts +5 -0
  222. package/src/engine/commands.ts +21 -0
  223. package/src/engine/layout.ts +11 -2
  224. package/src/engine/nativeScreens.ts +10 -1
  225. package/src/engine/options.ts +6 -8
  226. package/src/engine/optionsChange.ts +107 -0
  227. package/src/engine/refresh.ts +97 -0
  228. package/src/engine/rememberedOptions.ts +70 -0
  229. package/src/engine/screens.tsx +24 -0
  230. package/src/interfaces/ComponentEvents.ts +2 -2
  231. package/src/interfaces/options/animations.ts +40 -15
  232. package/src/interfaces/options/bottomTabs.ts +2 -2
  233. package/src/interfaces/options/buttons.ts +2 -2
  234. package/src/interfaces/options/modals.ts +6 -7
  235. package/src/interfaces/options/stack.ts +8 -3
  236. package/src/interfaces/options/topBar.ts +14 -0
  237. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  238. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -134
  239. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -78
  240. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  241. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -57
  242. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -146
@@ -2,150 +2,78 @@ package dev.apexnative.navigation.views.element
2
2
 
3
3
  import android.animation.Animator
4
4
  import android.animation.AnimatorListenerAdapter
5
- import android.graphics.Rect
6
5
  import android.os.Build
7
6
  import android.util.Log
8
7
  import android.view.View
9
8
  import android.view.ViewGroup
10
- import com.facebook.react.views.view.ReactViewGroup
11
9
  import dev.apexnative.navigation.options.ElementTransitionOptions
12
10
  import dev.apexnative.navigation.options.LayoutAnimation
13
11
  import dev.apexnative.navigation.options.SharedElementTransitionOptions
14
12
  import dev.apexnative.navigation.react.events.AppearanceDispatcher
15
13
  import dev.apexnative.navigation.utils.DevelopmentLog
16
14
  import dev.apexnative.navigation.utils.NavigationLog
15
+ import dev.apexnative.navigation.utils.SystemAnimations
17
16
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
18
17
  import dev.apexnative.navigation.views.animations.AnimationLayer
19
18
  import dev.apexnative.navigation.views.animations.SideAnimator
20
19
  import dev.apexnative.navigation.views.element.animators.SharedAnimatorFactory
20
+ import java.util.Collections
21
+ import java.util.WeakHashMap
21
22
 
22
23
  /**
23
- * The elements of one transition: the element transitions and the targets of the shared elements found by their
24
- * `nativeID` (depth first, the first match) in the whole view of the screen each side shows — an element in the screen
25
- * that goes first, then in the one that comes; a pair's `fromId` in the screen that goes, its `toId` in the one that
26
- * comes; each screen is walked once for all of them ([TransitionViews]). What isn't found, what is in another
27
- * transition's layer and an image that isn't loaded is skipped; so is an element whose start fails ([elementStep]):
28
- * it goes back at once and the rest plays. Each view found moves into a [TransitionLayer] at its place on the screen,
29
- * in the order its hierarchy draws it, and an [ElementStub] takes its index in its parent. A pair's `from` stays where
30
- * it is, hidden while its copy flies. [finish] puts every view back at its index with its layout, its pivot and the
31
- * values of its properties read before — unless React removed its stub meanwhile: then it is gone for good —, shows
32
- * the `from`s again and takes the layer away. Main thread only.
24
+ * The elements of one transition: the element transitions and the shared pairs found by their `nativeID` (depth first,
25
+ * the first match) in the whole view of the screen each side shows — an element in the screen that goes first, then in
26
+ * the one that comes; a pair's `fromId` in the screen that goes, its `toId` in the one that comes; each screen is
27
+ * walked once for all of them ([TransitionViews]). What isn't found, what another transition plays now and an image
28
+ * that isn't loaded is skipped; so is an element whose start fails ([elementStep]): it gets its values back at once and
29
+ * the rest plays. No view leaves its parent, as on iOS. An element plays where it is, in its screen: its properties
30
+ * animate about its center and it moves with its screen. A pair flies as a copy drawn in a [TransitionLayer] over the
31
+ * screens, the copies in the order their targets are drawn; its `to` is hidden from the start, its `from` from the
32
+ * start of its flight. [finish] gives every element the values of its properties read before, shows the pairs' views
33
+ * again and takes the layer away. Main thread only.
33
34
  */
34
- internal class ElementTransition private constructor(private val layer: TransitionLayer) {
35
- // A view in the layer: where it came from and what it was.
36
- private class Moved(
37
- val view: View,
38
- val stub: ElementStub,
39
- val params: ViewGroup.LayoutParams,
40
- val frame: Rect,
41
- val values: FloatArray,
42
- val pivotSet: Boolean,
43
- // Where its parent's origin is in the layer: what `x` and `y` are counted from.
44
- val originX: Int,
45
- val originY: Int,
46
- )
35
+ internal class ElementTransition private constructor(
36
+ private val layer: TransitionLayer?,
37
+ // The views of the screens, as far as the parents that cut a pair's views count.
38
+ private val fromRoot: View?,
39
+ private val toRoot: View?,
40
+ ) {
41
+ // An element playing where it is: what it was.
42
+ private class InPlace(val view: View, val values: FloatArray, val pivotSet: Boolean)
47
43
 
48
- private val moved = ArrayList<Moved>()
49
- private val hidden = ArrayList<Pair<View, Float>>()
44
+ private val inPlace = ArrayList<InPlace>()
45
+ private val taken = ArrayList<View>()
46
+ // The views of the pairs hidden while their copies fly, with the alpha each had.
47
+ private val hidden = LinkedHashMap<View, Float>()
50
48
  private var finished = false
51
49
 
52
50
  /** The animators of the elements and of the shared elements, each with its own time. */
53
51
  val animators = ArrayList<Animator>()
54
52
 
55
- /** Every view back, the `from`s seen, the layer gone; at the end of the transition and at any stop. Once. */
53
+ /**
54
+ * Every element's values back, the pairs' views seen, the layer gone; at the end of the transition and at any stop.
55
+ * Once.
56
+ */
56
57
  fun finish() {
57
58
  if (finished) return
58
59
  finished = true
59
- for ((view, alpha) in hidden) view.alpha = alpha
60
+ for ((view, alpha) in hidden) show(view, alpha)
60
61
  hidden.clear()
61
- // Each view goes back on its own: one that can't (its parent gone wrong) doesn't keep the others in the layer.
62
- for (item in moved.asReversed()) {
62
+ // Each element on its own: one that can't get its values back doesn't keep the others from theirs.
63
+ for (item in inPlace.asReversed()) {
63
64
  try {
64
- giveBack(item)
65
+ restore(item.view, item.values, item.pivotSet)
65
66
  } catch (e: RuntimeException) {
66
- Log.w(NavigationLog.TAG, "[ApexNavigation] An element of a transition couldn't go back", e)
67
+ Log.w(NavigationLog.TAG, "[ApexNavigation] An element of a transition couldn't get its values back", e)
67
68
  }
68
69
  }
69
- moved.clear()
70
- layer.detach()
70
+ inPlace.clear()
71
+ playing -= taken.toSet()
72
+ taken.clear()
73
+ layer?.detach()
71
74
  }
72
75
 
73
- private fun moveIntoLayer(view: View): Moved? {
74
- val parent = view.parent as? ViewGroup ?: return null
75
- // A parent that clips its subviews keeps its own list of children, which a stub swapped in would leave
76
- // behind: its element stays where it is.
77
- if ((parent as? ReactViewGroup)?.removeClippedSubviews == true) return null
78
- val index = parent.indexOfChild(view)
79
- if (index < 0) return null
80
- val onScreen = IntArray(2)
81
- parent.getLocationOnScreen(onScreen)
82
- val origin = layer.originOnScreen
83
- val originX = onScreen[0] - parent.scrollX - origin.x
84
- val originY = onScreen[1] - parent.scrollY - origin.y
85
- val frame = Rect(view.left, view.top, view.right, view.bottom)
86
- val params = view.layoutParams ?: ViewGroup.LayoutParams(view.width, view.height)
87
- val pivotSet = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && view.isPivotSet
88
- val values = floatArrayOf(
89
- view.alpha, view.translationX, view.translationY, view.scaleX, view.scaleY, view.rotation,
90
- view.rotationX, view.rotationY, view.pivotX, view.pivotY,
91
- )
92
- val stub = ElementStub(view.context, view)
93
- try {
94
- parent.removeViewAt(index)
95
- parent.addView(stub, index, params)
96
- stub.layout(frame.left, frame.top, frame.right, frame.bottom)
97
- val left = originX + frame.left
98
- val top = originY + frame.top
99
- layer.put(view, left, top, frame.width(), frame.height())
100
- } catch (e: RuntimeException) {
101
- putBack(view, stub, parent, index, params, frame)
102
- throw e
103
- }
104
- // Scale and rotations turn about the center.
105
- view.pivotX = frame.width() / 2f
106
- view.pivotY = frame.height() / 2f
107
- return Moved(view, stub, params, frame, values, pivotSet, originX, originY).also { moved += it }
108
- }
109
-
110
- // A move that failed half way undone: the view back at its index in its parent, the stub gone.
111
- private fun putBack(
112
- view: View,
113
- stub: ElementStub,
114
- parent: ViewGroup,
115
- index: Int,
116
- params: ViewGroup.LayoutParams,
117
- frame: Rect,
118
- ) {
119
- if (view.parent === layer) layer.removeView(view)
120
- val stubIndex = parent.indexOfChild(stub)
121
- if (stubIndex >= 0) parent.removeViewAt(stubIndex)
122
- if (view.parent == null) {
123
- parent.addView(view, index.coerceAtMost(parent.childCount), params)
124
- view.layout(frame.left, frame.top, frame.right, frame.bottom)
125
- }
126
- }
127
-
128
- private fun giveBack(item: Moved) {
129
- val view = item.view
130
- // A commit of Fabric meanwhile laid it out in its parent's coordinates: the newest such frame holds.
131
- val reactFrame = if (view.parent === layer) layer.frameFromReact(view) else null
132
- if (view.parent === layer) layer.removeView(view)
133
- restore(view, item)
134
- val parent = item.stub.parent as? ViewGroup ?: return
135
- val index = parent.indexOfChild(item.stub)
136
- if (index < 0) return
137
- parent.removeViewAt(index)
138
- parent.addView(view, index, item.params)
139
- val frame = reactFrame ?: item.frame
140
- view.measure(
141
- View.MeasureSpec.makeMeasureSpec(frame.width(), View.MeasureSpec.EXACTLY),
142
- View.MeasureSpec.makeMeasureSpec(frame.height(), View.MeasureSpec.EXACTLY),
143
- )
144
- view.layout(frame.left, frame.top, frame.right, frame.bottom)
145
- }
146
-
147
- private fun restore(view: View, item: Moved) {
148
- val values = item.values
76
+ private fun restore(view: View, values: FloatArray, pivotSet: Boolean) {
149
77
  view.alpha = values[ALPHA]
150
78
  view.translationX = values[TRANSLATION_X]
151
79
  view.translationY = values[TRANSLATION_Y]
@@ -154,7 +82,7 @@ internal class ElementTransition private constructor(private val layer: Transiti
154
82
  view.rotation = values[ROTATION]
155
83
  view.rotationX = values[ROTATION_X]
156
84
  view.rotationY = values[ROTATION_Y]
157
- if (!item.pivotSet && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
85
+ if (!pivotSet && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
158
86
  view.resetPivot()
159
87
  } else {
160
88
  view.pivotX = values[PIVOT_X]
@@ -165,50 +93,71 @@ internal class ElementTransition private constructor(private val layer: Transiti
165
93
  // An element with views in it fades through a hardware layer only where its alpha would draw it into an offscreen
166
94
  // buffer at every frame (AnimationLayer: its rendering overlaps). React's views overlap only with
167
95
  // `needsOffscreenAlphaCompositing`, so most elements fade without one and nothing they draw outside their bounds (a
168
- // box shadow) is clipped. A view without children fades its own drawing at no extra cost.
169
- private fun addElement(options: ElementTransitionOptions, item: Moved) {
170
- val base = SideAnimator.Base(xOffset = item.originX.toFloat(), yOffset = item.originY.toFloat())
171
- val animator = SideAnimator.build(item.view, options, base) ?: return
172
- if (item.view is ViewGroup) AnimationLayer.during(item.view, animator)
96
+ // box shadow) is clipped. A view without children fades its own drawing at no extra cost. Scale and rotations turn
97
+ // about the element's center; `x` and `y` are its place in its own parent.
98
+ private fun addElement(options: ElementTransitionOptions, view: View) {
99
+ inPlace += InPlace(view, valuesOf(view), pivotSetOf(view))
100
+ view.pivotX = view.width / 2f
101
+ view.pivotY = view.height / 2f
102
+ val animator = SideAnimator.build(view, options, SideAnimator.Base()) ?: return
103
+ if (view is ViewGroup) AnimationLayer.during(view, animator)
173
104
  animators += animator
174
105
  }
175
106
 
176
- // The pair's flight: `to` hidden until it starts, `from` hidden from its start; false when it doesn't fly.
107
+ // An element whose start failed gets its values back now.
108
+ private fun giveValuesBack(view: View) {
109
+ val item = inPlace.lastOrNull { it.view === view } ?: return
110
+ inPlace -= item
111
+ try {
112
+ restore(view, item.values, item.pivotSet)
113
+ } catch (e: RuntimeException) {
114
+ Log.w(NavigationLog.TAG, "[ApexNavigation] An element of a transition couldn't get its values back", e)
115
+ }
116
+ }
117
+
118
+ // The pair's flight: `to` hidden from now, `from` from its start, both seen again when it stops or its copy can't
119
+ // be drawn. Nothing changes when it doesn't fly.
177
120
  private fun addPair(
178
121
  options: SharedElementTransitionOptions,
179
122
  from: View,
180
- item: Moved,
181
- toParent: View?,
123
+ to: View,
124
+ name: String,
182
125
  fallbackDuration: Long,
183
- ): Boolean {
184
- val to = item.view
126
+ ) {
127
+ val layer = layer ?: return
185
128
  val flight = SharedAnimatorFactory.create(
186
- from, to, toParent, item.stub, layer, options, fallbackDuration, DevelopmentLog::warn,
187
- ) ?: return false
188
- val alpha = item.values[ALPHA]
129
+ from, to, fromRoot, toRoot, layer, options, fallbackDuration, DevelopmentLog::warn,
130
+ ) ?: return
189
131
  val fromAlpha = from.alpha
190
- to.alpha = 0f
191
- hidden += from to fromAlpha
132
+ val seenAgain = {
133
+ flight.picture.shown = false
134
+ hidden.remove(from)?.let { show(from, it) }
135
+ hidden.remove(to)?.let { show(to, it) }
136
+ layer.invalidate()
137
+ }
138
+ flight.picture.onFailed = { e ->
139
+ elementSkipped(name, e.toString())
140
+ seenAgain()
141
+ }
192
142
  flight.animator.addListener(object : AnimatorListenerAdapter() {
193
143
  private var stopped = false
194
144
 
195
145
  override fun onAnimationStart(animation: Animator) {
196
- if (stopped) return
146
+ if (stopped || finished) return
147
+ hidden[from] = fromAlpha
197
148
  from.alpha = 0f
198
- if (!flight.drawsPictures) to.alpha = alpha
199
149
  }
200
150
 
201
- // A flight whose start or frame failed stops (`MoveAnimator`), maybe inside its own start: `from` shows
202
- // again in its screen, and a `to` that moves itself in its place, as at any stop (a flight of pictures
203
- // shows its `to` at its end).
151
+ // A flight whose start or frame failed stops (`MoveAnimator`), maybe inside its own start.
204
152
  override fun onAnimationCancel(animation: Animator) {
205
153
  stopped = true
206
- from.alpha = fromAlpha
207
- if (!flight.drawsPictures) to.alpha = alpha
154
+ seenAgain()
208
155
  }
209
156
  })
157
+ layer.add(flight.picture)
158
+ hidden[to] = to.alpha
159
+ to.alpha = 0f
210
160
  animators += flight.animator
211
- return true
212
161
  }
213
162
 
214
163
  companion object {
@@ -223,10 +172,26 @@ internal class ElementTransition private constructor(private val layer: Transiti
223
172
  private const val PIVOT_X = 8
224
173
  private const val PIVOT_Y = 9
225
174
 
175
+ // The views the transitions that run now play: another transition leaves them alone.
176
+ private val playing: MutableSet<View> = Collections.newSetFromMap(WeakHashMap())
177
+
178
+ private fun valuesOf(view: View) = floatArrayOf(
179
+ view.alpha, view.translationX, view.translationY, view.scaleX, view.scaleY, view.rotation,
180
+ view.rotationX, view.rotationY, view.pivotX, view.pivotY,
181
+ )
182
+
183
+ private fun pivotSetOf(view: View) = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && view.isPivotSet
184
+
185
+ // A view hidden for a flight shows again with its alpha, unless React gave it another one meanwhile. React
186
+ // writes the same alpha: an opacity it commits during the flight shows the view beside its copy until the end.
187
+ private fun show(view: View, alpha: Float) {
188
+ if (view.alpha == 0f) view.alpha = alpha
189
+ }
190
+
226
191
  /**
227
- * The elements of [animation] from [from] to [to], moved into a layer that joins [host] at [layerIndex] (its
228
- * top without one); none when none of them is found. A shared element without a duration takes
229
- * [fallbackDuration], the screen's fade.
192
+ * The elements of [animation] from [from] to [to]; the copies of the pairs in a layer that joins [host] at
193
+ * [layerIndex] (its top without one). None when none of them is found or the system's animations are off. A
194
+ * shared element without a duration takes [fallbackDuration], the screen's fade.
230
195
  */
231
196
  fun start(
232
197
  animation: LayoutAnimation,
@@ -236,6 +201,7 @@ internal class ElementTransition private constructor(private val layer: Transiti
236
201
  layerIndex: Int?,
237
202
  fallbackDuration: Long,
238
203
  ): ElementTransition? {
204
+ if (SystemAnimations.areOff(host.context)) return null
239
205
  val fromView = from?.let { AppearanceDispatcher.visibleNode(it).peekView() }
240
206
  val toView = AppearanceDispatcher.visibleNode(to).peekView()
241
207
  val transitions = animation.elementTransitions.transitions
@@ -245,27 +211,28 @@ internal class ElementTransition private constructor(private val layer: Transiti
245
211
  val elements = ArrayList<Pair<ElementTransitionOptions, View>>()
246
212
  for (element in transitions) {
247
213
  if (element.id.isEmpty()) continue
248
- val view = found(views.leaving(element.id)) ?: found(views.arriving(element.id)) ?: continue
249
- if (TransitionLayer.holds(view) || !taken.add(view)) continue
214
+ val view = views.leaving(element.id) ?: views.arriving(element.id) ?: continue
215
+ if (view in playing || !taken.add(view)) continue
250
216
  elements += element to view
251
217
  }
252
218
  val pairs = ArrayList<Triple<SharedElementTransitionOptions, View, View>>()
253
219
  for (pair in shared) {
254
220
  val fromId = pair.fromId.get(null) ?: continue
255
221
  val toId = pair.toId.get(null) ?: continue
256
- val source = found(views.leaving(fromId)) ?: continue
257
- val target = found(views.arriving(toId)) ?: continue
258
- if (source === target || TransitionLayer.holds(source) || TransitionLayer.holds(target)) continue
222
+ val source = views.leaving(fromId) ?: continue
223
+ val target = views.arriving(toId) ?: continue
224
+ if (source === target || source in playing || target in playing) continue
259
225
  if (target in taken || source in taken) continue
260
226
  if (!loaded(source, fromId) || !loaded(target, toId)) continue
227
+ taken += source
261
228
  taken += target
262
229
  pairs += Triple(pair, source, target)
263
230
  }
264
231
  if (elements.isEmpty() && pairs.isEmpty()) return null
265
- val layer = TransitionLayer(host.context)
266
- val transition = ElementTransition(layer)
232
+ val layer = if (pairs.isEmpty()) null else TransitionLayer(host.context)
233
+ val transition = ElementTransition(layer, fromView, toView)
267
234
  try {
268
- layer.attachTo(host, layerIndex ?: host.childCount)
235
+ layer?.attachTo(host, layerIndex ?: host.childCount)
269
236
  transition.fill(host, elements, pairs, fallbackDuration)
270
237
  } catch (e: RuntimeException) {
271
238
  transition.finish()
@@ -275,8 +242,8 @@ internal class ElementTransition private constructor(private val layer: Transiti
275
242
  }
276
243
 
277
244
  /**
278
- * [start], and when that fails, a line in the log and none: the screens play without their elements; every view
279
- * moved is back already.
245
+ * [start], and when that fails, a line in the log and none: the screens play without their elements; every
246
+ * element has its values back already.
280
247
  */
281
248
  fun startOrLog(
282
249
  animation: LayoutAnimation,
@@ -292,8 +259,6 @@ internal class ElementTransition private constructor(private val layer: Transiti
292
259
  null
293
260
  }
294
261
 
295
- private fun found(view: View?): View? = view?.takeUnless { it is ElementStub }
296
-
297
262
  // An image not loaded drops out of the transition; its wait logged it already, else it is logged now.
298
263
  private fun loaded(view: View, id: String): Boolean {
299
264
  if (ImageLoads.isLoaded(view)) return true
@@ -302,45 +267,23 @@ internal class ElementTransition private constructor(private val layer: Transiti
302
267
  }
303
268
  }
304
269
 
305
- // The views into the layer in the order they are drawn, then their animators; a pair that doesn't fly goes back at
306
- // once. Each element's steps run on their own ([elementStep]): one that fails goes back, the others play.
270
+ // The elements' animators where they are, then the pairs' flights, their copies in the order their targets are
271
+ // drawn. Each element's steps run on their own ([elementStep]): one that fails is left as it was, the others play.
307
272
  private fun fill(
308
273
  host: ViewGroup,
309
274
  elements: List<Pair<ElementTransitionOptions, View>>,
310
275
  pairs: List<Triple<SharedElementTransitionOptions, View, View>>,
311
276
  fallbackDuration: Long,
312
277
  ) {
313
- val names = HashMap<View, String>()
314
- for ((options, view) in elements) names[view] = elementName(options.id)
315
- for ((options, _, target) in pairs) names[target] = pairName(options.fromId.get(""), options.toId.get(""))
316
- val views = elements.map { it.second } + pairs.map { it.third }
317
- val parents = views.associateWith { it.parent as? View }
318
- val placed = HashMap<View, Moved>()
319
- for (view in DrawOrder.sorted(views, host) { it }) {
320
- var item: Moved? = null
321
- if (elementStep(names.getValue(view)) { item = moveIntoLayer(view) }) item?.let { placed[view] = it }
322
- }
278
+ for ((_, view) in elements) taken += view
279
+ for ((_, source, target) in pairs) taken += listOf(source, target)
280
+ playing += taken
323
281
  for ((options, view) in elements) {
324
- val item = placed[view] ?: continue
325
- if (!elementStep(names.getValue(view)) { addElement(options, item) }) goBack(item)
326
- }
327
- for ((options, source, target) in pairs) {
328
- val item = placed[target] ?: continue
329
- var flies = false
330
- elementStep(names.getValue(target)) {
331
- flies = addPair(options, source, item, parents[target], fallbackDuration)
332
- }
333
- if (!flies) goBack(item)
282
+ if (!elementStep(elementName(options.id)) { addElement(options, view) }) giveValuesBack(view)
334
283
  }
335
- }
336
-
337
- // An element that doesn't play leaves the layer now; one that can't go back is logged, the others go on.
338
- private fun goBack(item: Moved) {
339
- moved -= item
340
- try {
341
- giveBack(item)
342
- } catch (e: RuntimeException) {
343
- Log.w(NavigationLog.TAG, "[ApexNavigation] An element of a transition couldn't go back", e)
284
+ for ((options, source, target) in DrawOrder.sorted(pairs, host) { it.third }) {
285
+ val name = pairName(options.fromId.get(""), options.toId.get(""))
286
+ elementStep(name) { addPair(options, source, target, name, fallbackDuration) }
344
287
  }
345
288
  }
346
289
  }
@@ -7,6 +7,7 @@ import dev.apexnative.navigation.options.LayoutAnimation
7
7
  import dev.apexnative.navigation.react.events.AppearanceDispatcher
8
8
  import dev.apexnative.navigation.utils.DevelopmentLog
9
9
  import dev.apexnative.navigation.utils.NavigationLog
10
+ import dev.apexnative.navigation.utils.SystemAnimations
10
11
  import dev.apexnative.navigation.viewcontrollers.viewcontroller.ViewController
11
12
  import kotlinx.coroutines.CancellationException
12
13
  import kotlinx.coroutines.CoroutineScope
@@ -17,7 +18,8 @@ import kotlinx.coroutines.launch
17
18
  /**
18
19
  * The wait of a forward transition with elements for its targets: the module's frames (`awaitTransitionTargets`, 0.5 s
19
20
  * at most, then one frame more), then the images of the shared elements found ([ImageWait], 1 s at most each). It ends
20
- * early when the screen that goes is destroyed. A failure in it is logged and the transition starts. Main thread only.
21
+ * early when the screen that goes is destroyed, and doesn't wait with the system's animations off. A failure in it is
22
+ * logged and the transition starts. Main thread only.
21
23
  */
22
24
  internal object TargetsWait {
23
25
  // The waits run on the main thread's frames; each is cancelled on its own.
@@ -31,6 +33,11 @@ internal object TargetsWait {
31
33
  waiting: () -> Boolean,
32
34
  done: () -> Unit,
33
35
  ): () -> Unit {
36
+ // With the system's animations off no element moves ([ElementTransition.start]): nothing to wait for.
37
+ if (SystemAnimations.areOff(to.peekView()?.context)) {
38
+ guarded { done() }
39
+ return NOTHING
40
+ }
34
41
  var stopped = false
35
42
  var stopImages: () -> Unit = NOTHING
36
43
  val still = { !stopped && waiting() && from?.isDestroyed != true }