@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
@@ -0,0 +1,199 @@
1
+ package dev.apexnative.navigation.views.element.animators
2
+
3
+ import android.graphics.Canvas
4
+ import android.graphics.Color
5
+ import android.graphics.Paint
6
+ import android.graphics.PorterDuff
7
+ import android.graphics.PorterDuffXfermode
8
+ import android.graphics.RectF
9
+ import android.graphics.drawable.Drawable
10
+ import android.view.View
11
+ import android.widget.ImageView
12
+ import com.facebook.react.uimanager.BackgroundStyleApplicator
13
+ import com.facebook.react.uimanager.style.LogicalEdge
14
+ import com.facebook.react.views.image.ReactImageView
15
+ import com.facebook.react.views.view.ReactViewGroup
16
+ import dev.apexnative.navigation.utils.borderRadius
17
+ import kotlin.math.abs
18
+ import kotlin.math.roundToInt
19
+
20
+ /**
21
+ * A shared image flies as its image, fitted all the way: the copy goes from where `from` is drawn to where `to` is
22
+ * drawn in its screen ([FlightGeometry]), clipped to its corners, and the image of `to` in it goes from the part and
23
+ * place `from` shows its image at (its fit, `resizeMode`) to those of `to`, keeping its proportions where both fits
24
+ * do. The image is `to`'s own drawable, drawn on the layer's canvas and only read: an animated image plays on. The
25
+ * corners are the image's own radius, else that of a parent that clips it (`overflow: hidden`) and that it fills.
26
+ * Where a parent cuts the view, only the part that shows flies, as with a view ([ViewMorph]). A pair whose views draw
27
+ * more than their images (a background, a border) or whose images differ in proportions flies as pictures of the views
28
+ * ([ViewMorph]).
29
+ */
30
+ internal class ImageMorph private constructor(
31
+ private val start: ImageEnd,
32
+ private val end: ImageEnd,
33
+ private val image: Drawable,
34
+ // Where the image's drawable draws the image, in the coordinates it draws in.
35
+ private val drawn: RectF,
36
+ ) : FlightPicture() {
37
+ private val flight = FlightGeometry(start.end, end.end)
38
+ private val cut = FlightMask.of(start.visible, end.visible)?.let(::MaskCut)
39
+ private var at = start.end
40
+ private var progress = 0f
41
+ private val rect = RectF()
42
+ private val imageRect = FloatArray(RECT)
43
+ private val maskPaint = Paint(Paint.ANTI_ALIAS_FLAG)
44
+ // What is drawn over the mask keeps only the mask's pixels: the corners stay smooth.
45
+ private val contentPaint = Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) }
46
+
47
+ override val live: Boolean get() = true
48
+
49
+ override fun update(time: Float, progress: Float) {
50
+ this.progress = progress
51
+ at = flight.at(progress, time)
52
+ for (i in 0 until RECT) imageRect[i] = lerp(start.image[i], end.image[i])
53
+ cut?.update(time, progress)
54
+ }
55
+
56
+ override fun draw(canvas: Canvas) {
57
+ rect.set(-at.width / 2, -at.height / 2, at.width / 2, at.height / 2)
58
+ val imageWidth = imageRect[2] - imageRect[0]
59
+ val imageHeight = imageRect[3] - imageRect[1]
60
+ if (rect.isEmpty || imageWidth <= 0 || imageHeight <= 0 || drawn.isEmpty) return
61
+ val alpha = (lerp(start.alpha, end.alpha) * 255).roundToInt().coerceIn(0, 255)
62
+ if (alpha == 0) return
63
+ val radius = lerp(start.radius, end.radius)
64
+ // The copy turns about its center, mirrored where a view is.
65
+ canvas.translate(at.centerX, at.centerY)
66
+ canvas.rotate(at.rotation)
67
+ canvas.scale(at.mirrorX, at.mirrorY)
68
+ canvas.saveLayer(rect, null)
69
+ canvas.drawRoundRect(rect, radius, radius, maskPaint)
70
+ cut?.draw(canvas, rect)
71
+ contentPaint.alpha = alpha
72
+ canvas.saveLayer(rect, contentPaint)
73
+ canvas.translate(imageRect[0], imageRect[1])
74
+ canvas.scale(imageWidth / drawn.width(), imageHeight / drawn.height())
75
+ canvas.translate(-drawn.left, -drawn.top)
76
+ image.draw(canvas)
77
+ }
78
+
79
+ private fun lerp(from: Float, to: Float) = from + (to - from) * progress
80
+
81
+ // One end of the flight: where the view is drawn, where its image shows in it (left, top, right, bottom from the
82
+ // view's center, at the size it is drawn at), its corner radius there, its opacity and the part of it that shows.
83
+ private class ImageEnd(
84
+ val end: FlightEnd,
85
+ val image: FloatArray,
86
+ val radius: Float,
87
+ val alpha: Float,
88
+ val visible: VisiblePart,
89
+ )
90
+
91
+ companion object {
92
+ private const val RECT = 4
93
+
94
+ // How far from its parent's size an image still fills it, in pixels.
95
+ private const val FILLS = 1
96
+ private const val HIDDEN = "hidden"
97
+
98
+ // How far the proportions of two images may differ for one to stand for the other, in parts of one.
99
+ private const val SAME_ASPECT = 0.02f
100
+
101
+ /**
102
+ * The morph of [from] into [to] in [layer], each cut by its parents up to the view of its screen ([fromRoot],
103
+ * [toRoot]); none when an image isn't there to fit, the images differ in proportions, or a view draws more
104
+ * than its image.
105
+ */
106
+ fun of(from: ImageView, to: ImageView, fromRoot: View?, toRoot: View?, layer: View): ImageMorph? {
107
+ if (!onlyImage(from) || !onlyImage(to)) return null
108
+ val image = to.drawable ?: return null
109
+ val drawn = drawnRect(to) ?: return null
110
+ val fromDrawn = drawnRect(from) ?: return null
111
+ if (!sameAspect(fromDrawn.width(), fromDrawn.height(), drawn.width(), drawn.height())) return null
112
+ val start = endOf(from, layer, fromRoot) ?: return null
113
+ val end = endOf(to, layer, toRoot) ?: return null
114
+ return ImageMorph(start, end, image, drawn)
115
+ }
116
+
117
+ /** Whether an image of [width] × [height] has the proportions of one of [otherWidth] × [otherHeight]. */
118
+ internal fun sameAspect(width: Float, height: Float, otherWidth: Float, otherHeight: Float): Boolean {
119
+ if (width <= 0f || height <= 0f || otherWidth <= 0f || otherHeight <= 0f) return false
120
+ val aspect = width / height
121
+ val other = otherWidth / otherHeight
122
+ return abs(aspect - other) <= SAME_ASPECT * other
123
+ }
124
+
125
+ // The view draws nothing but its image: no background color, no border (React Native draws them as the
126
+ // view's background, around the image).
127
+ private fun onlyImage(view: View): Boolean = try {
128
+ val background = BackgroundStyleApplicator.getBackgroundColor(view)
129
+ (background == null || Color.alpha(background) == 0) && LogicalEdge.entries.none {
130
+ (BackgroundStyleApplicator.getBorderWidth(view, it) ?: 0f) > 0f
131
+ }
132
+ } catch (e: RuntimeException) {
133
+ false
134
+ }
135
+
136
+ private fun endOf(view: ImageView, layer: View, root: View?): ImageEnd? {
137
+ if (view.width <= 0 || view.height <= 0) return null
138
+ val shown = shownRect(view) ?: return null
139
+ val place = LayerPlace.of(view, layer, root)
140
+ val end = place.end(view.width, view.height)
141
+ val image = imageFromCenter(
142
+ floatArrayOf(shown.left, shown.top, shown.right, shown.bottom),
143
+ view.width, view.height, end.width, end.height,
144
+ )
145
+ val radius = cornerRadius(view)
146
+ return ImageEnd(
147
+ end, image, radius * end.scale, view.alpha, place.visible(view.width, view.height, radius),
148
+ )
149
+ }
150
+
151
+ // Where the drawable draws the image when the view draws it: Fresco's image where its fit puts it in the
152
+ // drawable, another image at its drawable's bounds (the view's matrix comes on top).
153
+ private fun drawnRect(view: ImageView): RectF? {
154
+ val rect = if (view is ReactImageView) {
155
+ if (!view.hasHierarchy()) return null
156
+ RectF().also { view.hierarchy.getActualImageBounds(it) }
157
+ } else {
158
+ RectF(view.drawable?.bounds ?: return null)
159
+ }
160
+ return rect.takeUnless { it.isEmpty }
161
+ }
162
+
163
+ // Where the image shows in the view, in its pixels.
164
+ private fun shownRect(view: ImageView): RectF? {
165
+ val rect = drawnRect(view) ?: return null
166
+ if (view !is ReactImageView) view.imageMatrix.mapRect(rect)
167
+ rect.offset(view.paddingLeft.toFloat(), view.paddingTop.toFloat())
168
+ return rect
169
+ }
170
+
171
+ private fun cornerRadius(view: ImageView): Float {
172
+ view.borderRadius.takeIf { it > 0f }?.let { return it }
173
+ val parent = view.parent as? ReactViewGroup ?: return 0f
174
+ val fills = abs(parent.width - view.width) <= FILLS && abs(parent.height - view.height) <= FILLS
175
+ return if (fills && parent.overflow == HIDDEN) parent.borderRadius else 0f
176
+ }
177
+
178
+ /**
179
+ * The rect [shown] (left, top, right, bottom) of a view of [width] × [height], from the view's center, at the
180
+ * size [shownWidth] × [shownHeight] the view is drawn at.
181
+ */
182
+ internal fun imageFromCenter(
183
+ shown: FloatArray,
184
+ width: Int,
185
+ height: Int,
186
+ shownWidth: Float,
187
+ shownHeight: Float,
188
+ ): FloatArray {
189
+ val scaleX = shownWidth / width
190
+ val scaleY = shownHeight / height
191
+ return floatArrayOf(
192
+ (shown[0] - width / 2f) * scaleX,
193
+ (shown[1] - height / 2f) * scaleY,
194
+ (shown[2] - width / 2f) * scaleX,
195
+ (shown[3] - height / 2f) * scaleY,
196
+ )
197
+ }
198
+ }
199
+ }
@@ -1,75 +1,85 @@
1
1
  package dev.apexnative.navigation.views.element.animators
2
2
 
3
- import android.animation.Animator
4
3
  import android.view.View
5
4
  import android.widget.ImageView
6
- import com.facebook.react.views.image.ReactImageView
7
5
  import com.facebook.react.views.text.ReactTextView
8
- import com.facebook.react.views.view.ReactViewGroup
9
6
  import dev.apexnative.navigation.options.SharedElementTransitionOptions
10
7
  import dev.apexnative.navigation.views.animations.AnimationRules
11
8
  import dev.apexnative.navigation.views.animations.Curves
12
- import dev.apexnative.navigation.views.element.TransitionLayer
9
+ import dev.apexnative.navigation.views.element.elementSkipped
13
10
  import dev.apexnative.navigation.views.element.pairName
14
11
 
15
12
  /**
16
- * The animator of a shared element by the kinds of its two views, chosen by explicit classes, never by reflection:
17
- * texts (`TextChangeAnimator`, the module), React views with children (`ViewChangeAnimator`, the module; a failed
18
- * picture flies as a view without children), React images ([ReactImageAnimator]), other images ([ImageMatrixAnimator]),
19
- * any other view ([PlainViewAnimator]). The animator gets the pair's duration (else the fallback, the screen's fade),
20
- * its delay and its curve here, each of its own (a spring in real time); an `AnimatorSet` around it never sets them.
21
- * Main thread only.
13
+ * The flight of a shared pair, as on iOS: a copy drawn in the transition's layer, chosen by the kinds of the two views,
14
+ * by explicit classes, never by reflection — texts ([TextMorph]), images ([ImageMorph]; one without an image to fit
15
+ * flies as a view), any other views ([ViewMorph]). The views themselves are neither moved nor changed. The animator
16
+ * gets the pair's duration (else the fallback, the screen's fade), its delay and its curve here, each of its own (a
17
+ * spring in real time); an `AnimatorSet` around it never sets them. Main thread only.
22
18
  */
23
19
  internal object SharedAnimatorFactory {
24
- /**
25
- * A pair's animator, and how it shows the pair: by pictures in the layer's overlay while `to` is hidden (the
26
- * modules), or by moving `to` itself, which shows from the start of the flight.
27
- */
28
- class Flight(val animator: Animator, val drawsPictures: Boolean)
20
+ /** A pair's animator and the copy it moves. */
21
+ class Flight(val animator: MoveAnimator, val picture: FlightPicture)
29
22
 
30
23
  /**
31
- * The flight of the pair [from] → [to]; [to] is in [layer] at its margins, at its real alpha (the pictures are
32
- * taken now). [toParent] is the parent [to] had in its screen, [toStandIn] the view in its place there. None: the
33
- * pair doesn't fly (a text that didn't change, a picture that can't be taken, no place).
24
+ * The flight of the pair [from] → [to] in [layer], its pictures taken now; a view or an image shows only the part
25
+ * its parents up to the view of its screen ([fromRoot], [toRoot]) leave visible, a text shows whole, as on iOS.
26
+ * None: the pair doesn't fly — a text that doesn't change, or, with a line in [log], a view without a size or a
27
+ * picture that can't be taken.
34
28
  */
35
29
  fun create(
36
30
  from: View,
37
31
  to: View,
38
- toParent: View?,
39
- toStandIn: View?,
40
- layer: TransitionLayer,
32
+ fromRoot: View?,
33
+ toRoot: View?,
34
+ layer: View,
41
35
  options: SharedElementTransitionOptions,
42
36
  fallbackDuration: Long,
43
37
  log: (String) -> Unit,
44
38
  ): Flight? {
45
- val flight = when {
46
- from is ReactTextView && to is ReactTextView -> pictures(
47
- TextChangeAnimator(from, to, toStandIn).takeIf { it.shouldAnimate() }?.create(options),
48
- )
49
- hasChildren(from) || hasChildren(to) -> pictures(
50
- ViewChangeAnimator(from, to, toStandIn).takeIf { it.shouldAnimate() }?.create(options),
51
- ) ?: moves(PlainViewAnimator(from, to, toStandIn, layer).create())
52
- from is ReactImageView && to is ReactImageView ->
53
- moves(ReactImageAnimator(from, to, toStandIn, layer).create())
54
- from is ImageView && to is ImageView -> moves(
55
- ImageMatrixAnimator(
56
- from, to, toStandIn, layer, from.parent as? View, toParent, options.toId.get(""), log,
57
- ).create(),
58
- )
59
- else -> moves(PlainViewAnimator(from, to, toStandIn, layer).create())
60
- } ?: return null
61
- val animator = flight.animator
62
- (animator as? MoveAnimator)?.element = pairName(options.fromId.get(""), options.toId.get(""))
39
+ val name = pairName(options.fromId.get(""), options.toId.get(""))
40
+ if (from.width <= 0 || from.height <= 0 || to.width <= 0 || to.height <= 0) {
41
+ elementSkipped(name, "a view of the pair has no size, it isn't laid out", log)
42
+ return null
43
+ }
44
+ val picture = if (from is ReactTextView && to is ReactTextView) {
45
+ val text = TextMorph.of(from, to, layer) ?: return noPicture(name, log)
46
+ if (!text.changes) return null
47
+ text
48
+ } else {
49
+ val image = if (from is ImageView && to is ImageView) {
50
+ ImageMorph.of(from, to, fromRoot, toRoot, layer)
51
+ } else {
52
+ null
53
+ }
54
+ image ?: ViewMorph.of(from, to, fromRoot, toRoot, layer) ?: return noPicture(name, log)
55
+ }
56
+ val animator = MoveAnimator().apply {
57
+ element = name
58
+ onStart {
59
+ picture.shown = true
60
+ picture.update(0f, curve.getInterpolation(0f))
61
+ layer.invalidate()
62
+ }
63
+ onFrame {
64
+ picture.update(it, curve.getInterpolation(it))
65
+ layer.invalidate()
66
+ }
67
+ // A flight that landed keeps its last frame until the transition takes the layer away: the screens may
68
+ // still move. A stopped one draws nothing more.
69
+ onEnd {
70
+ if (!landed) picture.shown = false
71
+ layer.invalidate()
72
+ }
73
+ }
63
74
  val duration = AnimationRules.durationOf(options.duration.get(null), fallbackDuration)
64
75
  animator.duration = duration
65
76
  animator.startDelay = AnimationRules.delayOf(options.startDelay.get(null))
66
77
  animator.interpolator = Curves.of(options.interpolation, duration)
67
- return flight
78
+ return Flight(animator, picture)
68
79
  }
69
80
 
70
- private fun pictures(animator: Animator?): Flight? = animator?.let { Flight(it, drawsPictures = true) }
71
-
72
- private fun moves(animator: Animator?): Flight? = animator?.let { Flight(it, drawsPictures = false) }
73
-
74
- private fun hasChildren(view: View): Boolean = view is ReactViewGroup && view.childCount > 0
81
+ private fun noPicture(name: String, log: (String) -> Unit): Flight? {
82
+ elementSkipped(name, "no picture of a view of the pair can be taken", log)
83
+ return null
84
+ }
75
85
  }
@@ -1,91 +1,105 @@
1
1
  package dev.apexnative.navigation.views.element.animators
2
2
 
3
- import android.animation.Animator
4
3
  import android.graphics.Canvas
5
- import android.graphics.ColorFilter
6
4
  import android.graphics.Picture
7
- import android.graphics.PixelFormat
8
5
  import android.graphics.PointF
9
- import android.graphics.drawable.Drawable
10
6
  import android.text.Layout
11
7
  import android.view.View
12
- import android.view.ViewGroup
13
8
  import android.widget.TextView
14
9
  import com.facebook.react.views.text.ReactTextView
15
- import dev.apexnative.navigation.options.SharedElementTransitionOptions
16
10
  import dev.apexnative.navigation.utils.TextViewUtils
17
11
  import dev.apexnative.navigation.views.animations.ValueInterpolation
18
12
  import kotlin.math.abs
19
13
  import kotlin.math.min
20
14
  import kotlin.math.roundToInt
21
15
 
22
- // The text moves as two pictures of itself, of the text that goes away and of the text that comes, the same text or
23
- // another. Both keep their proportions, scaled by the font size from a point of the first line, and one turns into the
24
- // other in a quarter of the move: at its start when the text grows, at its end when it shrinks, so the larger picture
25
- // shows most of the way (as on iOS). Each text is where it is drawn, at the scale and angle its parents and its own
26
- // properties give it; the angle goes along the shorter arc (FlightGeometry). The pictures are drawing commands, not
27
- // pixels: they stay sharp at any scale and keep the colors, emoji and background of the text. They are drawn in the
28
- // overlay of the transition layer while `to` is hidden.
29
- class TextChangeAnimator internal constructor(
30
- from: View,
31
- to: View,
32
- // Where `to` was in its screen: what the text flies to.
33
- private val toStandIn: View?,
34
- ) : PropertyAnimatorCreator<ReactTextView>(from, to, ReactTextView::class.java) {
35
- private val pictures by lazy { pictures() }
16
+ /**
17
+ * The text moves as two pictures of itself, of the text that goes away and of the text that comes, the same text or
18
+ * another. Both keep their proportions, scaled by the font size from a point of the first line, and one turns into the
19
+ * other in a quarter of the move: at its start when the text grows, at its end when it shrinks, so the larger picture
20
+ * shows most of the way (as on iOS). Each text is where it is drawn, at the scale and angle its parents and its own
21
+ * properties give it; the angle goes along the shorter arc ([FlightGeometry]). The pictures are drawing commands, not
22
+ * pixels: they stay sharp at any scale and keep the colors, emoji and background of the text.
23
+ */
24
+ internal class TextMorph private constructor(private val start: TextPicture, private val end: TextPicture) :
25
+ FlightPicture() {
26
+ /** Whether the text changes on the way: another text, another size, or drawn elsewhere. */
27
+ val changes: Boolean
28
+ get() = start.text != end.text || start.fontSize != end.fontSize || start.drawnElsewhere(end)
36
29
 
37
- override fun shouldAnimateProperty(fromChild: ReactTextView, toChild: ReactTextView): Boolean {
38
- val (start, end) = pictures ?: return false
39
- return fromChild.text.toString() != toChild.text.toString() ||
40
- TextViewUtils.getTextSize(fromChild) != TextViewUtils.getTextSize(toChild) || start.drawnElsewhere(end)
30
+ private val grows = end.shownSize >= start.shownSize
31
+ private var progress = 0f
32
+ private var time = 0f
33
+ private var changeOver = 0f
34
+ private var mirrorX = 1f
35
+ private var mirrorY = 1f
36
+
37
+ override fun update(time: Float, progress: Float) {
38
+ this.progress = progress
39
+ this.time = time
40
+ val changeOverStart = if (grows) 0f else 1f - CHANGE_OVER
41
+ changeOver = ((time - changeOverStart) / CHANGE_OVER).coerceIn(0f, 1f)
41
42
  }
42
43
 
43
- override fun create(options: SharedElementTransitionOptions): Animator {
44
- val (start, end) = checkNotNull(pictures)
45
- val layer = to.parent as ViewGroup
46
- val drawable = TextMorphDrawable(start, end)
47
- // Everything changes between the start and the end of the animation: a cancelled one ends too.
48
- return MoveAnimator().apply {
49
- onStart {
50
- drawable.setBounds(0, 0, layer.width, layer.height)
51
- layer.overlay.add(drawable)
52
- to.alpha = 0f
53
- }
54
- onFrame {
55
- drawable.update(it, curve.getInterpolation(it))
56
- layer.invalidate()
57
- }
58
- // A flight that landed where `to` shows only in its screen keeps its last picture: the transition takes the
59
- // layer away with it and shows `to` in its place.
60
- val holds = FlightEnd.holdsAtEnd(to, toStandIn, layer)
61
- onEnd {
62
- if (!holds || !landed) {
63
- layer.overlay.remove(drawable)
64
- layer.invalidate()
65
- to.alpha = end.alpha
66
- }
67
- }
44
+ override fun draw(canvas: Canvas) {
45
+ val x = start.position.x + (end.position.x - start.position.x) * progress
46
+ val y = start.position.y + (end.position.y - start.position.y) * progress
47
+ val fontSize = start.shownSize + (end.shownSize - start.shownSize) * progress
48
+ val rotation = ValueInterpolation.angle(start.rotation, end.rotation, progress, time)
49
+ mirrorX = ValueInterpolation.number(start.mirrorX, end.mirrorX, progress, time)
50
+ mirrorY = ValueInterpolation.number(start.mirrorY, end.mirrorY, progress, time)
51
+ draw(canvas, start, x, y, fontSize, rotation, 1f - changeOver)
52
+ draw(canvas, end, x, y, fontSize, rotation, changeOver)
53
+ }
54
+
55
+ private fun draw(
56
+ canvas: Canvas,
57
+ text: TextPicture,
58
+ x: Float,
59
+ y: Float,
60
+ fontSize: Float,
61
+ rotation: Float,
62
+ shown: Float,
63
+ ) {
64
+ val alpha = (shown * text.alpha * 255).roundToInt().coerceIn(0, 255)
65
+ if (alpha == 0) return
66
+ val scale = fontSize / text.fontSize
67
+ val count = canvas.save()
68
+ canvas.translate(x, y)
69
+ canvas.rotate(rotation)
70
+ canvas.scale(scale * mirrorX, scale * mirrorY)
71
+ canvas.translate(-text.anchor.x, -text.anchor.y)
72
+ // The picture fades as a whole: its letters and background don't show through each other.
73
+ if (alpha < 255) {
74
+ canvas.saveLayerAlpha(0f, 0f, text.picture.width.toFloat(), text.picture.height.toFloat(), alpha)
68
75
  }
76
+ canvas.drawPicture(text.picture)
77
+ canvas.restoreToCount(count)
69
78
  }
70
79
 
71
- // `to` is in the transition layer already: the pictures are drawn in its coordinates. `from` is where it is drawn
72
- // on the screen, `to` where it is drawn once back in its screen. Texts that line up the same way go from that point
73
- // of their first lines (the same text lies over itself); others from the start of the line, as on iOS: a centered
74
- // text no longer sticks out of a text at the start.
75
- private fun pictures(): Pair<TextPicture, TextPicture>? {
76
- val layer = to.parent as? ViewGroup ?: return null
77
- val fromLayout = (from as TextView).layout ?: return null
78
- val toLayout = (to as TextView).layout ?: return null
79
- val edge = lineEdge(fromLayout).takeIf { it == lineEdge(toLayout) } ?: LineEdge.START
80
- val start = TextPicture.of(from, LayerPlace.of(from, layer), edge) ?: return null
81
- val end = TextPicture.of(to, LayerPlace.of(to, layer, toStandIn), edge) ?: return null
82
- return start to end
80
+ companion object {
81
+ private const val CHANGE_OVER = 0.25f
82
+
83
+ /**
84
+ * The morph of [from] into [to] in [layer]; none when a text isn't laid out or its picture can't be taken.
85
+ * Texts that line up the same way go from that point of their first lines (the same text lies over itself);
86
+ * others from the start of the line, as on iOS: a centered text no longer sticks out of a text at the start.
87
+ */
88
+ fun of(from: ReactTextView, to: ReactTextView, layer: View): TextMorph? {
89
+ val fromLayout = from.layout ?: return null
90
+ val toLayout = to.layout ?: return null
91
+ val edge = lineEdge(fromLayout).takeIf { it == lineEdge(toLayout) } ?: LineEdge.START
92
+ val start = TextPicture.of(from, LayerPlace.of(from, layer), edge) ?: return null
93
+ val end = TextPicture.of(to, LayerPlace.of(to, layer), edge) ?: return null
94
+ return TextMorph(start, end)
95
+ }
83
96
  }
84
97
  }
85
98
 
86
- // A text's picture, the point of its first line it is scaled from (in the view) and where that point is drawn in the
87
- // transition layer, its font size, the scale and angle it is drawn at, and its opacity.
99
+ // A text's picture and its text, the point of its first line it is scaled from (in the view) and where that point is
100
+ // drawn in the transition layer, its font size, the scale and angle it is drawn at, and its opacity.
88
101
  private class TextPicture(
102
+ val text: String,
89
103
  val picture: Picture,
90
104
  val anchor: PointF,
91
105
  val position: PointF,
@@ -124,7 +138,8 @@ private class TextPicture(
124
138
  val position = place.point(anchor.x, anchor.y)
125
139
  val scale = min(abs(place.scaleX), abs(place.scaleY))
126
140
  return TextPicture(
127
- picture, anchor, PointF(position[0], position[1]), fontSize, scale, place.rotation,
141
+ view.text.toString(), picture, anchor, PointF(position[0], position[1]), fontSize, scale,
142
+ place.rotation,
128
143
  mirrorX = if (place.scaleX < 0) -1f else 1f,
129
144
  mirrorY = if (place.scaleY < 0) -1f else 1f,
130
145
  alpha = view.alpha,
@@ -159,68 +174,3 @@ private fun lineEdge(layout: Layout): LineEdge {
159
174
  else -> LineEdge.START
160
175
  }
161
176
  }
162
-
163
- // Both pictures where the move has brought the text: the font size it shows at, its angle and the point of the first
164
- // line go from the start to the end along the transition's curve, the pictures change over by the clock.
165
- private class TextMorphDrawable(private val start: TextPicture, private val end: TextPicture) : Drawable() {
166
- private val grows = end.shownSize >= start.shownSize
167
- private var progress = 0f
168
- private var time = 0f
169
- private var changeOver = 0f
170
- private var mirrorX = 1f
171
- private var mirrorY = 1f
172
-
173
- fun update(time: Float, progress: Float) {
174
- this.progress = progress
175
- this.time = time
176
- val changeOverStart = if (grows) 0f else 1f - CHANGE_OVER
177
- changeOver = ((time - changeOverStart) / CHANGE_OVER).coerceIn(0f, 1f)
178
- }
179
-
180
- override fun draw(canvas: Canvas) {
181
- val x = start.position.x + (end.position.x - start.position.x) * progress
182
- val y = start.position.y + (end.position.y - start.position.y) * progress
183
- val fontSize = start.shownSize + (end.shownSize - start.shownSize) * progress
184
- val rotation = ValueInterpolation.angle(start.rotation, end.rotation, progress, time)
185
- mirrorX = ValueInterpolation.number(start.mirrorX, end.mirrorX, progress, time)
186
- mirrorY = ValueInterpolation.number(start.mirrorY, end.mirrorY, progress, time)
187
- draw(canvas, start, x, y, fontSize, rotation, 1f - changeOver)
188
- draw(canvas, end, x, y, fontSize, rotation, changeOver)
189
- }
190
-
191
- private fun draw(
192
- canvas: Canvas,
193
- text: TextPicture,
194
- x: Float,
195
- y: Float,
196
- fontSize: Float,
197
- rotation: Float,
198
- shown: Float,
199
- ) {
200
- val alpha = (shown * text.alpha * 255).roundToInt().coerceIn(0, 255)
201
- if (alpha == 0) return
202
- val scale = fontSize / text.fontSize
203
- val count = canvas.save()
204
- canvas.translate(x, y)
205
- canvas.rotate(rotation)
206
- canvas.scale(scale * mirrorX, scale * mirrorY)
207
- canvas.translate(-text.anchor.x, -text.anchor.y)
208
- // The picture fades as a whole: its letters and background don't show through each other.
209
- if (alpha < 255) {
210
- canvas.saveLayerAlpha(0f, 0f, text.picture.width.toFloat(), text.picture.height.toFloat(), alpha)
211
- }
212
- canvas.drawPicture(text.picture)
213
- canvas.restoreToCount(count)
214
- }
215
-
216
- override fun setAlpha(alpha: Int) = Unit
217
-
218
- override fun setColorFilter(colorFilter: ColorFilter?) = Unit
219
-
220
- @Deprecated("Deprecated in Java")
221
- override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
222
-
223
- private companion object {
224
- const val CHANGE_OVER = 0.25f
225
- }
226
- }