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

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 (236) hide show
  1. package/ApexNavigation.podspec +2 -1
  2. package/CHANGELOG.md +7 -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 +36 -38
  39. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +124 -181
  40. package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
  41. package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
  42. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +56 -3
  43. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +170 -153
  44. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
  45. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -45
  46. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/{TextChangeAnimator.kt → TextMorph.kt} +79 -129
  47. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
  48. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
  49. package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
  50. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
  51. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
  52. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
  53. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
  54. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
  55. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
  56. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
  57. package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
  58. package/android/src/main/jni/ANNContentHeights.cpp +82 -0
  59. package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
  60. package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
  61. package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
  62. package/android/src/main/res/values/styles.xml +1 -1
  63. package/android/src/main/res/values-night/styles.xml +1 -1
  64. package/cli/doctor/checks/app.js +16 -1
  65. package/cli/project.js +4 -0
  66. package/cli/setup/index.js +5 -0
  67. package/cli/setup/steps/metro.js +46 -0
  68. package/docs-md/architecture/native-screens.md +4 -0
  69. package/docs-md/architecture/theme-and-context.md +131 -12
  70. package/docs-md/architecture/transitions.md +43 -4
  71. package/docs-md/flows/bottom-sheets.md +4 -2
  72. package/docs-md/fundamentals/screens-and-props.md +9 -1
  73. package/docs-md/guides/cli.md +3 -1
  74. package/docs-md/help/troubleshooting.md +1 -1
  75. package/docs-md/index.json +29 -1
  76. package/docs-md/screen-ui/back-and-gestures.md +3 -1
  77. package/docs-md/screen-ui/bar-buttons.md +1 -1
  78. package/docs-md/screen-ui/toolbar-and-fab.md +2 -0
  79. package/docs-md/start/installation.md +16 -1
  80. package/ios/Animations/ANNFrameDriver.h +55 -15
  81. package/ios/Animations/ANNFrameDriver.mm +214 -60
  82. package/ios/Animations/ANNTransitionAnimator.h +26 -5
  83. package/ios/Animations/ANNTransitionAnimator.mm +49 -3
  84. package/ios/Animations/ANNTransitionPlan.h +2 -2
  85. package/ios/Animations/ANNTransitionPlan.mm +10 -0
  86. package/ios/Animations/Elements/ANNElementFinder.h +2 -2
  87. package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
  88. package/ios/Animations/Elements/ANNElementTransition.h +3 -3
  89. package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
  90. package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
  91. package/ios/Animations/Elements/ANNSharedElementFlight.mm +181 -43
  92. package/ios/Animations/Interpolators/ANNViewTracks.h +6 -5
  93. package/ios/Animations/Interpolators/ANNViewTracks.mm +51 -21
  94. package/ios/CLAUDE.md +15 -7
  95. package/ios/CORE.md +1 -1
  96. package/ios/Core/ANNAppDelegate.mm +0 -2
  97. package/ios/Core/ANNDeepLinkQueue.h +6 -5
  98. package/ios/Core/ANNDeepLinkQueue.mm +22 -10
  99. package/ios/Core/ANNSceneDelegate.mm +2 -2
  100. package/ios/Core/ApexNavigation.mm +6 -2
  101. package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
  102. package/ios/Layouts/Screen/ANNComponentViewController.mm +4 -1
  103. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
  104. package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
  105. package/ios/Layouts/Stack/ANNStackController.h +8 -0
  106. package/ios/Layouts/Stack/ANNStackController.mm +153 -11
  107. package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
  108. package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
  109. package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
  110. package/ios/Options/ANNAnimationOptions.h +2 -1
  111. package/ios/Options/ANNAnimationOptions.mm +1 -1
  112. package/ios/Options/ANNOptions.h +7 -0
  113. package/ios/Options/ANNOptions.mm +45 -0
  114. package/ios/Options/ANNOptionsReader.mm +2 -4
  115. package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
  116. package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
  117. package/ios/Options/Params/ColorParser.h +4 -3
  118. package/ios/Options/Params/ColorParser.mm +22 -4
  119. package/ios/React/ANNSurfaceHostingView.h +8 -0
  120. package/ios/React/ANNSurfaceHostingView.mm +28 -1
  121. package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
  122. package/ios/TurboModules/ANNTurboModule.mm +46 -0
  123. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
  124. package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
  125. package/lib/module/ARCHITECTURE.md +9 -3
  126. package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
  127. package/lib/module/api/app/AppRegistry.js +14 -2
  128. package/lib/module/api/app/AppRegistry.js.map +1 -1
  129. package/lib/module/api/app/createNavigation.js +188 -66
  130. package/lib/module/api/app/createNavigation.js.map +1 -1
  131. package/lib/module/api/events/navigationEvents.js +6 -2
  132. package/lib/module/api/events/navigationEvents.js.map +1 -1
  133. package/lib/module/api/linking/linking.js +11 -0
  134. package/lib/module/api/linking/linking.js.map +1 -1
  135. package/lib/module/api/screens/defineScreen.js +45 -3
  136. package/lib/module/api/screens/defineScreen.js.map +1 -1
  137. package/lib/module/api/screens/nativeScreens.js +31 -6
  138. package/lib/module/api/screens/nativeScreens.js.map +1 -1
  139. package/lib/module/api/screens/registerScreens.js +172 -35
  140. package/lib/module/api/screens/registerScreens.js.map +1 -1
  141. package/lib/module/api/theme/theme.js +5 -0
  142. package/lib/module/api/theme/theme.js.map +1 -1
  143. package/lib/module/engine/commands.js +21 -1
  144. package/lib/module/engine/commands.js.map +1 -1
  145. package/lib/module/engine/layout.js +7 -2
  146. package/lib/module/engine/layout.js.map +1 -1
  147. package/lib/module/engine/nativeScreens.js +9 -0
  148. package/lib/module/engine/nativeScreens.js.map +1 -1
  149. package/lib/module/engine/options.js +5 -4
  150. package/lib/module/engine/options.js.map +1 -1
  151. package/lib/module/engine/optionsChange.js +102 -0
  152. package/lib/module/engine/optionsChange.js.map +1 -0
  153. package/lib/module/engine/refresh.js +85 -0
  154. package/lib/module/engine/refresh.js.map +1 -0
  155. package/lib/module/engine/rememberedOptions.js +60 -0
  156. package/lib/module/engine/rememberedOptions.js.map +1 -1
  157. package/lib/module/engine/screens.js +26 -0
  158. package/lib/module/engine/screens.js.map +1 -1
  159. package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
  160. package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
  161. package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
  162. package/lib/typescript/api/app/createNavigation.d.ts +5 -0
  163. package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
  164. package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
  165. package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
  166. package/lib/typescript/api/index.d.ts +1 -1
  167. package/lib/typescript/api/index.d.ts.map +1 -1
  168. package/lib/typescript/api/linking/linking.d.ts +8 -0
  169. package/lib/typescript/api/linking/linking.d.ts.map +1 -1
  170. package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
  171. package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
  172. package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
  173. package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
  174. package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
  175. package/lib/typescript/api/theme/theme.d.ts +2 -0
  176. package/lib/typescript/api/theme/theme.d.ts.map +1 -1
  177. package/lib/typescript/engine/commands.d.ts +5 -0
  178. package/lib/typescript/engine/commands.d.ts.map +1 -1
  179. package/lib/typescript/engine/layout.d.ts.map +1 -1
  180. package/lib/typescript/engine/nativeScreens.d.ts +5 -0
  181. package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
  182. package/lib/typescript/engine/optionsChange.d.ts +28 -0
  183. package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
  184. package/lib/typescript/engine/refresh.d.ts +35 -0
  185. package/lib/typescript/engine/refresh.d.ts.map +1 -0
  186. package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
  187. package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
  188. package/lib/typescript/engine/screens.d.ts +7 -0
  189. package/lib/typescript/engine/screens.d.ts.map +1 -1
  190. package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
  191. package/lib/typescript/interfaces/options/animations.d.ts +40 -15
  192. package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
  193. package/lib/typescript/interfaces/options/bottomTabs.d.ts +2 -2
  194. package/lib/typescript/interfaces/options/buttons.d.ts +2 -2
  195. package/lib/typescript/interfaces/options/modals.d.ts +6 -7
  196. package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
  197. package/lib/typescript/interfaces/options/stack.d.ts +8 -3
  198. package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
  199. package/lib/typescript/interfaces/options/topBar.d.ts +14 -0
  200. package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
  201. package/metro/acceptPlugin.js +57 -0
  202. package/metro/babelTransformer.js +45 -0
  203. package/metro/index.js +36 -0
  204. package/package.json +3 -1
  205. package/src/ARCHITECTURE.md +9 -3
  206. package/src/adapters/NativeANNTurboModule.ts +7 -0
  207. package/src/api/app/AppRegistry.ts +16 -2
  208. package/src/api/app/createNavigation.ts +198 -66
  209. package/src/api/events/navigationEvents.ts +9 -3
  210. package/src/api/index.ts +1 -0
  211. package/src/api/linking/linking.ts +22 -1
  212. package/src/api/screens/defineScreen.ts +49 -5
  213. package/src/api/screens/nativeScreens.ts +40 -4
  214. package/src/api/screens/registerScreens.tsx +170 -42
  215. package/src/api/theme/theme.ts +5 -0
  216. package/src/engine/commands.ts +21 -0
  217. package/src/engine/layout.ts +11 -2
  218. package/src/engine/nativeScreens.ts +10 -1
  219. package/src/engine/options.ts +6 -8
  220. package/src/engine/optionsChange.ts +107 -0
  221. package/src/engine/refresh.ts +97 -0
  222. package/src/engine/rememberedOptions.ts +70 -0
  223. package/src/engine/screens.tsx +24 -0
  224. package/src/interfaces/ComponentEvents.ts +2 -2
  225. package/src/interfaces/options/animations.ts +40 -15
  226. package/src/interfaces/options/bottomTabs.ts +2 -2
  227. package/src/interfaces/options/buttons.ts +2 -2
  228. package/src/interfaces/options/modals.ts +6 -7
  229. package/src/interfaces/options/stack.ts +8 -3
  230. package/src/interfaces/options/topBar.ts +14 -0
  231. package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
  232. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -134
  233. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -78
  234. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
  235. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -57
  236. package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -146
@@ -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 }
@@ -2,45 +2,35 @@ package dev.apexnative.navigation.views.element
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
- import android.graphics.Point
6
- import android.graphics.Rect
5
+ import android.graphics.Canvas
7
6
  import android.view.MotionEvent
8
7
  import android.view.View
9
8
  import android.view.ViewGroup
9
+ import dev.apexnative.navigation.views.element.animators.FlightPicture
10
10
 
11
11
  /**
12
- * The layer of a transition's elements: full size over the screens' common parent (a stack's view; for a modal, between
13
- * the modals layer and the overlays layer), there only while the transition runs. Each view in it lies where its layout
14
- * params say — `leftMargin`, `topMargin` and its size, in the layer's pixels — and nothing re-places it: Fabric's views
15
- * carry no margins of their own. Nothing in it is clipped by the parents it came from, it takes no touches,
16
- * accessibility doesn't see it. Main thread only.
12
+ * The layer the copies of a transition's shared elements fly in: full size over the screens' common parent (a stack's
13
+ * view; for a modal, between the modals layer and the overlays layer), there only while the transition runs. It holds
14
+ * no views: it draws the pictures of the flights ([FlightPicture]) in the order they are added, each while it is shown,
15
+ * on every frame while one of them is [FlightPicture.live]. A picture whose drawing fails draws no more, and its
16
+ * [FlightPicture.onFailed] runs after the frame. It takes no touches, accessibility doesn't see it. Main thread only.
17
17
  */
18
18
  @SuppressLint("ViewConstructor")
19
- internal class TransitionLayer(context: Context) : ViewGroup(context) {
20
- // The newest frame Fabric gave a view while it was here (a commit lays it out in its parent's coordinates).
21
- private val reactFrames = HashMap<View, Rect>()
19
+ internal class TransitionLayer(context: Context) : View(context) {
20
+ private val pictures = ArrayList<FlightPicture>()
22
21
 
23
22
  init {
24
- clipChildren = false
25
- clipToPadding = false
26
- importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
23
+ importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
24
+ setWillNotDraw(false)
27
25
  }
28
26
 
29
- /** The layer's top left corner on the screen, in pixels: what the views in it are placed from. */
30
- val originOnScreen: Point
31
- get() {
32
- val location = IntArray(2)
33
- getLocationOnScreen(location)
34
- return Point(location[0], location[1])
35
- }
36
-
37
27
  /**
38
- * Joins [parent] at [index] (its top without one), laid out at once at the parent's size: its views are in their
39
- * places on the frame the transition starts.
28
+ * Joins [parent] at [index] (its top without one), laid out at once at the parent's size: the places of the views
29
+ * are measured from it on the frame the transition starts.
40
30
  */
41
31
  fun attachTo(parent: ViewGroup, index: Int = parent.childCount) {
42
32
  // The parent turns these into its own kind of params.
43
- parent.addView(this, index.coerceIn(0, parent.childCount), LayoutParams(MATCH, MATCH))
33
+ parent.addView(this, index.coerceIn(0, parent.childCount), ViewGroup.LayoutParams(MATCH, MATCH))
44
34
  measure(
45
35
  MeasureSpec.makeMeasureSpec(parent.width, MeasureSpec.EXACTLY),
46
36
  MeasureSpec.makeMeasureSpec(parent.height, MeasureSpec.EXACTLY),
@@ -48,51 +38,10 @@ internal class TransitionLayer(context: Context) : ViewGroup(context) {
48
38
  layout(0, 0, parent.width, parent.height)
49
39
  }
50
40
 
51
- /** [view] lies at [left], [top] (the layer's pixels) at the size [width] × [height], laid out at once. */
52
- fun put(view: View, left: Int, top: Int, width: Int, height: Int) {
53
- val params = MarginLayoutParams(width, height).apply {
54
- leftMargin = left
55
- topMargin = top
56
- }
57
- addView(view, params)
58
- layOut(view, left, top, width, height)
59
- }
60
-
61
- /** [view], in the layer, lies at [left], [top] at the size [width] × [height] from now on. */
62
- fun place(view: View, left: Int, top: Int, width: Int, height: Int) {
63
- noteReactFrame(view)
64
- (view.layoutParams as? MarginLayoutParams)?.let {
65
- it.leftMargin = left
66
- it.topMargin = top
67
- it.width = width
68
- it.height = height
69
- }
70
- layOut(view, left, top, width, height)
71
- }
72
-
73
- private fun layOut(view: View, left: Int, top: Int, width: Int, height: Int) {
74
- view.measure(
75
- MeasureSpec.makeMeasureSpec(width.coerceAtLeast(0), MeasureSpec.EXACTLY),
76
- MeasureSpec.makeMeasureSpec(height.coerceAtLeast(0), MeasureSpec.EXACTLY),
77
- )
78
- view.layout(left, top, left + width, top + height)
79
- }
80
-
81
- /**
82
- * The newest frame React gave [view] while it was in the layer, in its parent's coordinates; none when React
83
- * didn't lay it out meanwhile. A frame other than the one the layer gave it can only be React's.
84
- */
85
- fun frameFromReact(view: View): Rect? {
86
- noteReactFrame(view)
87
- return reactFrames.remove(view)
88
- }
89
-
90
- // The view's frame isn't where the layer put it: React laid it out since.
91
- private fun noteReactFrame(view: View) {
92
- val params = view.layoutParams as? MarginLayoutParams ?: return
93
- val placed = view.left == params.leftMargin && view.top == params.topMargin &&
94
- view.width == params.width && view.height == params.height
95
- if (!placed) reactFrames[view] = Rect(view.left, view.top, view.right, view.bottom)
41
+ /** [picture] is drawn over those added before it. */
42
+ fun add(picture: FlightPicture) {
43
+ pictures += picture
44
+ invalidate()
96
45
  }
97
46
 
98
47
  /** The layer leaves its parent. */
@@ -100,53 +49,29 @@ internal class TransitionLayer(context: Context) : ViewGroup(context) {
100
49
  (parent as? ViewGroup)?.removeView(this)
101
50
  }
102
51
 
103
- override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
104
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec))
105
- for (index in 0 until childCount) {
106
- val child = getChildAt(index)
107
- val params = child.layoutParams
108
- child.measure(
109
- MeasureSpec.makeMeasureSpec(params.width.coerceAtLeast(0), MeasureSpec.EXACTLY),
110
- MeasureSpec.makeMeasureSpec(params.height.coerceAtLeast(0), MeasureSpec.EXACTLY),
111
- )
112
- }
113
- }
114
-
115
- override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
116
- for (index in 0 until childCount) {
117
- val child = getChildAt(index)
118
- val params = child.layoutParams as? MarginLayoutParams ?: continue
119
- noteReactFrame(child)
120
- child.layout(
121
- params.leftMargin,
122
- params.topMargin,
123
- params.leftMargin + child.measuredWidth,
124
- params.topMargin + child.measuredHeight,
125
- )
52
+ override fun onDraw(canvas: Canvas) {
53
+ var live = false
54
+ for (picture in pictures) {
55
+ if (!picture.shown) continue
56
+ live = live || picture.live
57
+ val count = canvas.save()
58
+ try {
59
+ picture.draw(canvas)
60
+ } catch (e: RuntimeException) {
61
+ picture.shown = false
62
+ post { picture.onFailed(e) }
63
+ } finally {
64
+ canvas.restoreToCount(count)
65
+ }
126
66
  }
67
+ // A landed copy gets no more frames from its flight: one that changes by itself asks for them.
68
+ if (live) postInvalidateOnAnimation()
127
69
  }
128
70
 
129
- override fun checkLayoutParams(params: LayoutParams?): Boolean = params is MarginLayoutParams
130
-
131
- override fun generateDefaultLayoutParams(): LayoutParams = MarginLayoutParams(0, 0)
132
-
133
- override fun generateLayoutParams(params: LayoutParams?): LayoutParams = MarginLayoutParams(params)
134
-
71
+ @SuppressLint("ClickableViewAccessibility")
135
72
  override fun dispatchTouchEvent(event: MotionEvent?): Boolean = false
136
73
 
137
- override fun shouldDelayChildPressedState(): Boolean = false
138
-
139
- companion object {
140
- private const val MATCH = LayoutParams.MATCH_PARENT
141
-
142
- /** Whether [view] is in a transition's layer now (an element of another transition). */
143
- fun holds(view: View): Boolean {
144
- var parent = view.parent
145
- while (parent != null) {
146
- if (parent is TransitionLayer) return true
147
- parent = parent.parent
148
- }
149
- return false
150
- }
74
+ private companion object {
75
+ const val MATCH = ViewGroup.LayoutParams.MATCH_PARENT
151
76
  }
152
77
  }
@@ -4,7 +4,12 @@ import android.animation.Animator
4
4
  import android.animation.AnimatorListenerAdapter
5
5
  import android.animation.TimeInterpolator
6
6
  import android.animation.ValueAnimator
7
+ import android.graphics.Canvas
8
+ import android.graphics.Paint
7
9
  import android.graphics.Picture
10
+ import android.graphics.PorterDuff
11
+ import android.graphics.PorterDuffXfermode
12
+ import android.graphics.RectF
8
13
  import android.util.Log
9
14
  import android.view.View
10
15
  import android.view.animation.LinearInterpolator
@@ -13,8 +18,7 @@ import dev.apexnative.navigation.views.element.elementStep
13
18
 
14
19
  /**
15
20
  * The view's own drawing with its children, as drawing commands: sharp at any scale. Its opacity and place belong to
16
- * the parent. Recording can fail (a hardware bitmap in an image on an older Android): the element then moves without
17
- * pictures.
21
+ * the parent. Recording can fail (a hardware bitmap in an image before Android 9): the pair then doesn't fly.
18
22
  */
19
23
  internal fun recordPicture(view: View): Picture? = try {
20
24
  Picture().also {
@@ -26,10 +30,59 @@ internal fun recordPicture(view: View): Picture? = try {
26
30
  }
27
31
  }
28
32
  } catch (e: RuntimeException) {
29
- Log.w(NavigationLog.TAG, "Element transition: no picture of the element, it moves without one", e)
33
+ Log.w(NavigationLog.TAG, "[ApexNavigation] Element transition: no picture of the element", e)
30
34
  null
31
35
  }
32
36
 
37
+ /**
38
+ * What a shared pair's flight draws in its transition's layer, in the layer's coordinates: the copy where the flight
39
+ * has brought it ([update]). The layer draws it while it is [shown]: from the start of the flight, and after a landing
40
+ * until the layer goes.
41
+ */
42
+ internal abstract class FlightPicture {
43
+ /** Whether the layer draws it now. */
44
+ var shown = false
45
+
46
+ /** Whether what it draws changes by itself (an animated image): the layer draws it on every frame while shown. */
47
+ open val live: Boolean get() = false
48
+
49
+ /** What runs after its drawing failed: it is drawn no more, and the views of its pair show again. */
50
+ var onFailed: (Exception) -> Unit = {}
51
+
52
+ /** The copy at [time] (the part of the time), [progress] the curve's value. */
53
+ abstract fun update(time: Float, progress: Float)
54
+
55
+ /** The copy, on the layer's canvas; the layer saves and restores the canvas around it. */
56
+ abstract fun draw(canvas: Canvas)
57
+ }
58
+
59
+ /**
60
+ * The cut of a copy whose views' parents hide part of them ([FlightMask]): it keeps, of the mask the copy draws on its
61
+ * layer, only the part that shows. A layer of that part's rounded rect laid over the mask (`DST_IN`), not a clip path:
62
+ * its edges stay smooth.
63
+ */
64
+ internal class MaskCut(private val mask: FlightMask) {
65
+ private val values = FloatArray(FlightMask.SIZE)
66
+ private val rect = RectF()
67
+ private val shape = Paint(Paint.ANTI_ALIAS_FLAG)
68
+ private val cut = Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN) }
69
+
70
+ /** The cut at [time] (the part of the time), [progress] the curve's value. */
71
+ fun update(time: Float, progress: Float) = mask.at(progress, time, values)
72
+
73
+ /** Cuts the mask drawn so far on the copy's layer of [bounds], in the copy's coordinates (0, 0 its center). */
74
+ fun draw(canvas: Canvas, bounds: RectF) {
75
+ rect.set(values[0], values[1], values[2], values[3])
76
+ canvas.saveLayer(bounds, cut)
77
+ if (!rect.isEmpty) canvas.drawRoundRect(rect, values[RADIUS], values[RADIUS], shape)
78
+ canvas.restore()
79
+ }
80
+
81
+ private companion object {
82
+ const val RADIUS = 4
83
+ }
84
+ }
85
+
33
86
  // Runs in linear time and keeps the curve it is given for the move: an element moves along the transition's curve,
34
87
  // while what has to follow the clock (the pictures of a text changing over) can. Its start and its frames each run in
35
88
  // [elementStep]: one that fails stops the element — it is cancelled, so its ends put back what it changed and the