@rafaelaugusto/jolt 0.1.0-beta.1 → 0.1.0-beta.11

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 (318) hide show
  1. package/README.md +385 -18
  2. package/dist/animate.d.ts +19 -3
  3. package/dist/animate.d.ts.map +1 -1
  4. package/dist/animate.js +186 -14
  5. package/dist/animate.js.map +1 -1
  6. package/dist/color.d.ts +11 -0
  7. package/dist/color.d.ts.map +1 -0
  8. package/dist/color.js +73 -0
  9. package/dist/color.js.map +1 -0
  10. package/dist/config.d.ts +31 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +73 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/decay.d.ts +11 -0
  15. package/dist/decay.d.ts.map +1 -0
  16. package/dist/decay.js +22 -0
  17. package/dist/decay.js.map +1 -0
  18. package/dist/decoration.d.ts +18 -0
  19. package/dist/decoration.d.ts.map +1 -0
  20. package/dist/decoration.js +155 -0
  21. package/dist/decoration.js.map +1 -0
  22. package/dist/drag-input.d.ts +15 -0
  23. package/dist/drag-input.d.ts.map +1 -0
  24. package/dist/drag-input.js +66 -0
  25. package/dist/drag-input.js.map +1 -0
  26. package/dist/drag-physics.d.ts +20 -0
  27. package/dist/drag-physics.d.ts.map +1 -0
  28. package/dist/drag-physics.js +75 -0
  29. package/dist/drag-physics.js.map +1 -0
  30. package/dist/drag.d.ts +58 -0
  31. package/dist/drag.d.ts.map +1 -0
  32. package/dist/drag.js +324 -0
  33. package/dist/drag.js.map +1 -0
  34. package/dist/driver/android-color.d.ts +20 -0
  35. package/dist/driver/android-color.d.ts.map +1 -0
  36. package/dist/driver/android-color.js +136 -0
  37. package/dist/driver/android-color.js.map +1 -0
  38. package/dist/driver/android-decoration.d.ts +22 -0
  39. package/dist/driver/android-decoration.d.ts.map +1 -0
  40. package/dist/driver/android-decoration.js +139 -0
  41. package/dist/driver/android-decoration.js.map +1 -0
  42. package/dist/driver/android-gradient.d.ts +5 -0
  43. package/dist/driver/android-gradient.d.ts.map +1 -0
  44. package/dist/driver/android-gradient.js +89 -0
  45. package/dist/driver/android-gradient.js.map +1 -0
  46. package/dist/driver/android-playback.d.ts +13 -0
  47. package/dist/driver/android-playback.d.ts.map +1 -0
  48. package/dist/driver/android-playback.js +141 -0
  49. package/dist/driver/android-playback.js.map +1 -0
  50. package/dist/driver/android.d.ts +1 -1
  51. package/dist/driver/android.d.ts.map +1 -1
  52. package/dist/driver/android.js +165 -13
  53. package/dist/driver/android.js.map +1 -1
  54. package/dist/driver/index.d.ts +25 -2
  55. package/dist/driver/index.d.ts.map +1 -1
  56. package/dist/driver/index.js.map +1 -1
  57. package/dist/driver/ios-color.d.ts +16 -0
  58. package/dist/driver/ios-color.d.ts.map +1 -0
  59. package/dist/driver/ios-color.js +392 -0
  60. package/dist/driver/ios-color.js.map +1 -0
  61. package/dist/driver/ios-decoration.d.ts +17 -0
  62. package/dist/driver/ios-decoration.d.ts.map +1 -0
  63. package/dist/driver/ios-decoration.js +271 -0
  64. package/dist/driver/ios-decoration.js.map +1 -0
  65. package/dist/driver/ios-gradient.d.ts +5 -0
  66. package/dist/driver/ios-gradient.d.ts.map +1 -0
  67. package/dist/driver/ios-gradient.js +109 -0
  68. package/dist/driver/ios-gradient.js.map +1 -0
  69. package/dist/driver/ios-layout.d.ts +9 -0
  70. package/dist/driver/ios-layout.d.ts.map +1 -0
  71. package/dist/driver/ios-layout.js +52 -0
  72. package/dist/driver/ios-layout.js.map +1 -0
  73. package/dist/driver/ios-playback.d.ts +10 -0
  74. package/dist/driver/ios-playback.d.ts.map +1 -0
  75. package/dist/driver/ios-playback.js +47 -0
  76. package/dist/driver/ios-playback.js.map +1 -0
  77. package/dist/driver/ios-shadow.d.ts +6 -0
  78. package/dist/driver/ios-shadow.d.ts.map +1 -0
  79. package/dist/driver/ios-shadow.js +52 -0
  80. package/dist/driver/ios-shadow.js.map +1 -0
  81. package/dist/driver/ios.d.ts.map +1 -1
  82. package/dist/driver/ios.js +163 -28
  83. package/dist/driver/ios.js.map +1 -1
  84. package/dist/engine.d.ts +75 -5
  85. package/dist/engine.d.ts.map +1 -1
  86. package/dist/engine.js +293 -23
  87. package/dist/engine.js.map +1 -1
  88. package/dist/gradient.d.ts +90 -0
  89. package/dist/gradient.d.ts.map +1 -0
  90. package/dist/gradient.js +247 -0
  91. package/dist/gradient.js.map +1 -0
  92. package/dist/haptics.d.ts +21 -0
  93. package/dist/haptics.d.ts.map +1 -0
  94. package/dist/haptics.js +120 -0
  95. package/dist/haptics.js.map +1 -0
  96. package/dist/image-effect-native.d.ts +26 -0
  97. package/dist/image-effect-native.d.ts.map +1 -0
  98. package/dist/image-effect-native.js +73 -0
  99. package/dist/image-effect-native.js.map +1 -0
  100. package/dist/image-effect.d.ts +50 -0
  101. package/dist/image-effect.d.ts.map +1 -0
  102. package/dist/image-effect.js +290 -0
  103. package/dist/image-effect.js.map +1 -0
  104. package/dist/image-owner.d.ts +3 -0
  105. package/dist/image-owner.d.ts.map +1 -0
  106. package/dist/image-owner.js +11 -0
  107. package/dist/image-owner.js.map +1 -0
  108. package/dist/index.d.ts +47 -4
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +22 -2
  111. package/dist/index.js.map +1 -1
  112. package/dist/interaction-surface.d.ts +12 -0
  113. package/dist/interaction-surface.d.ts.map +1 -0
  114. package/dist/interaction-surface.js +36 -0
  115. package/dist/interaction-surface.js.map +1 -0
  116. package/dist/items.d.ts +20 -0
  117. package/dist/items.d.ts.map +1 -0
  118. package/dist/items.js +109 -0
  119. package/dist/items.js.map +1 -0
  120. package/dist/keyboard.d.ts +41 -0
  121. package/dist/keyboard.d.ts.map +1 -0
  122. package/dist/keyboard.js +256 -0
  123. package/dist/keyboard.js.map +1 -0
  124. package/dist/label.d.ts +7 -0
  125. package/dist/label.d.ts.map +1 -0
  126. package/dist/label.js +60 -0
  127. package/dist/label.js.map +1 -0
  128. package/dist/layout.d.ts +13 -0
  129. package/dist/layout.d.ts.map +1 -0
  130. package/dist/layout.js +348 -0
  131. package/dist/layout.js.map +1 -0
  132. package/dist/liquid-grid.d.ts +3 -0
  133. package/dist/liquid-grid.d.ts.map +1 -0
  134. package/dist/liquid-grid.js +24 -0
  135. package/dist/liquid-grid.js.map +1 -0
  136. package/dist/liquid.d.ts +18 -0
  137. package/dist/liquid.d.ts.map +1 -0
  138. package/dist/liquid.js +234 -0
  139. package/dist/liquid.js.map +1 -0
  140. package/dist/loader.d.ts +19 -0
  141. package/dist/loader.d.ts.map +1 -0
  142. package/dist/loader.js +173 -0
  143. package/dist/loader.js.map +1 -0
  144. package/dist/motion-scope.d.ts +39 -0
  145. package/dist/motion-scope.d.ts.map +1 -0
  146. package/dist/motion-scope.js +238 -0
  147. package/dist/motion-scope.js.map +1 -0
  148. package/dist/motion-states.d.ts +43 -0
  149. package/dist/motion-states.d.ts.map +1 -0
  150. package/dist/motion-states.js +220 -0
  151. package/dist/motion-states.js.map +1 -0
  152. package/dist/particles.d.ts +39 -0
  153. package/dist/particles.d.ts.map +1 -0
  154. package/dist/particles.js +265 -0
  155. package/dist/particles.js.map +1 -0
  156. package/dist/path.d.ts +12 -0
  157. package/dist/path.d.ts.map +1 -0
  158. package/dist/path.js +90 -0
  159. package/dist/path.js.map +1 -0
  160. package/dist/playback-group.d.ts +6 -0
  161. package/dist/playback-group.d.ts.map +1 -0
  162. package/dist/playback-group.js +141 -0
  163. package/dist/playback-group.js.map +1 -0
  164. package/dist/playback.d.ts +25 -0
  165. package/dist/playback.d.ts.map +1 -0
  166. package/dist/playback.js +35 -0
  167. package/dist/playback.js.map +1 -0
  168. package/dist/press.d.ts.map +1 -1
  169. package/dist/press.js +23 -7
  170. package/dist/press.js.map +1 -1
  171. package/dist/properties.d.ts +6 -3
  172. package/dist/properties.d.ts.map +1 -1
  173. package/dist/properties.js +26 -2
  174. package/dist/properties.js.map +1 -1
  175. package/dist/reveal.d.ts +25 -0
  176. package/dist/reveal.d.ts.map +1 -0
  177. package/dist/reveal.js +294 -0
  178. package/dist/reveal.js.map +1 -0
  179. package/dist/roll-diff.d.ts +22 -0
  180. package/dist/roll-diff.d.ts.map +1 -0
  181. package/dist/roll-diff.js +36 -0
  182. package/dist/roll-diff.js.map +1 -0
  183. package/dist/roll.d.ts +29 -0
  184. package/dist/roll.d.ts.map +1 -0
  185. package/dist/roll.js +442 -0
  186. package/dist/roll.js.map +1 -0
  187. package/dist/scroll-linked.d.ts +10 -0
  188. package/dist/scroll-linked.d.ts.map +1 -0
  189. package/dist/scroll-linked.js +58 -0
  190. package/dist/scroll-linked.js.map +1 -0
  191. package/dist/sequence.d.ts +10 -5
  192. package/dist/sequence.d.ts.map +1 -1
  193. package/dist/sequence.js +208 -14
  194. package/dist/sequence.js.map +1 -1
  195. package/dist/shimmer.d.ts +12 -0
  196. package/dist/shimmer.d.ts.map +1 -0
  197. package/dist/shimmer.js +161 -0
  198. package/dist/shimmer.js.map +1 -0
  199. package/dist/svg-path.d.ts +14 -0
  200. package/dist/svg-path.d.ts.map +1 -0
  201. package/dist/svg-path.js +281 -0
  202. package/dist/svg-path.js.map +1 -0
  203. package/dist/symbol-effect.d.ts +23 -0
  204. package/dist/symbol-effect.d.ts.map +1 -0
  205. package/dist/symbol-effect.js +187 -0
  206. package/dist/symbol-effect.js.map +1 -0
  207. package/dist/text-layout.d.ts +30 -0
  208. package/dist/text-layout.d.ts.map +1 -0
  209. package/dist/text-layout.js +185 -0
  210. package/dist/text-layout.js.map +1 -0
  211. package/dist/text.d.ts +17 -0
  212. package/dist/text.d.ts.map +1 -0
  213. package/dist/text.js +263 -0
  214. package/dist/text.js.map +1 -0
  215. package/dist/track.d.ts +3 -1
  216. package/dist/track.d.ts.map +1 -1
  217. package/dist/track.js +266 -13
  218. package/dist/track.js.map +1 -1
  219. package/dist/transition.d.ts +32 -1
  220. package/dist/transition.d.ts.map +1 -1
  221. package/dist/transition.js +58 -6
  222. package/dist/transition.js.map +1 -1
  223. package/dist/types.d.ts +48 -1
  224. package/dist/types.d.ts.map +1 -1
  225. package/dist/vue/index.d.ts +95 -0
  226. package/dist/vue/index.d.ts.map +1 -0
  227. package/dist/vue/index.js +163 -0
  228. package/dist/vue/index.js.map +1 -0
  229. package/dist/zoom-native.d.ts +30 -0
  230. package/dist/zoom-native.d.ts.map +1 -0
  231. package/dist/zoom-native.js +56 -0
  232. package/dist/zoom-native.js.map +1 -0
  233. package/dist/zoomable.d.ts +48 -0
  234. package/dist/zoomable.d.ts.map +1 -0
  235. package/dist/zoomable.js +249 -0
  236. package/dist/zoomable.js.map +1 -0
  237. package/package.json +29 -5
  238. package/platforms/android/include.gradle +7 -0
  239. package/platforms/android/java/org/rafaelaugusto/jolt/ColorTarget.java +102 -0
  240. package/platforms/android/java/org/rafaelaugusto/jolt/DecorationTarget.java +154 -0
  241. package/platforms/android/java/org/rafaelaugusto/jolt/GradientLayer.java +195 -0
  242. package/platforms/android/java/org/rafaelaugusto/jolt/ImageEffect.java +288 -0
  243. package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardFollower.java +91 -0
  244. package/platforms/android/java/org/rafaelaugusto/jolt/KeyboardObserver.java +97 -0
  245. package/platforms/android/java/org/rafaelaugusto/jolt/NumberRoll.java +190 -0
  246. package/platforms/android/java/org/rafaelaugusto/jolt/PhotoZoom.java +250 -0
  247. package/platforms/android/java/org/rafaelaugusto/jolt/TextReveal.java +180 -0
  248. package/platforms/android/proguard-rules.pro +21 -0
  249. package/platforms/ios/build.xcconfig +1 -0
  250. package/platforms/ios/src/JoltImageEffect.h +25 -0
  251. package/platforms/ios/src/JoltImageEffect.mm +242 -0
  252. package/platforms/ios/src/JoltInteractiveAnimation.h +18 -0
  253. package/platforms/ios/src/JoltInteractiveAnimation.m +123 -0
  254. package/platforms/ios/src/JoltPhotoZoom.h +23 -0
  255. package/platforms/ios/src/JoltPhotoZoom.m +229 -0
  256. package/platforms/ios/src/module.modulemap +6 -0
  257. package/src/android-plugin.d.ts +184 -0
  258. package/src/animate.ts +249 -36
  259. package/src/color.ts +101 -0
  260. package/src/config.ts +84 -0
  261. package/src/decay.ts +45 -0
  262. package/src/decoration.ts +180 -0
  263. package/src/drag-input.ts +74 -0
  264. package/src/drag-physics.ts +110 -0
  265. package/src/drag.ts +395 -0
  266. package/src/driver/android-color.ts +170 -0
  267. package/src/driver/android-decoration.ts +182 -0
  268. package/src/driver/android-gradient.ts +109 -0
  269. package/src/driver/android-playback.ts +157 -0
  270. package/src/driver/android.ts +208 -20
  271. package/src/driver/index.ts +37 -2
  272. package/src/driver/ios-color.ts +441 -0
  273. package/src/driver/ios-decoration.ts +332 -0
  274. package/src/driver/ios-gradient.ts +114 -0
  275. package/src/driver/ios-layout.ts +63 -0
  276. package/src/driver/ios-playback.ts +51 -0
  277. package/src/driver/ios-shadow.ts +50 -0
  278. package/src/driver/ios.ts +193 -33
  279. package/src/engine.ts +321 -27
  280. package/src/gradient.ts +372 -0
  281. package/src/haptics.ts +148 -0
  282. package/src/image-effect-native.ts +129 -0
  283. package/src/image-effect.ts +336 -0
  284. package/src/image-owner.ts +12 -0
  285. package/src/index.ts +75 -4
  286. package/src/interaction-surface.ts +41 -0
  287. package/src/ios-plugin.d.ts +81 -0
  288. package/src/items.ts +140 -0
  289. package/src/keyboard.ts +291 -0
  290. package/src/label.ts +76 -0
  291. package/src/layout.ts +408 -0
  292. package/src/liquid-grid.ts +28 -0
  293. package/src/liquid.ts +287 -0
  294. package/src/loader.ts +202 -0
  295. package/src/motion-scope.ts +279 -0
  296. package/src/motion-states.ts +269 -0
  297. package/src/particles.ts +323 -0
  298. package/src/path.ts +124 -0
  299. package/src/playback-group.ts +143 -0
  300. package/src/playback.ts +62 -0
  301. package/src/press.ts +26 -10
  302. package/src/properties.ts +32 -6
  303. package/src/reveal.ts +374 -0
  304. package/src/roll-diff.ts +57 -0
  305. package/src/roll.ts +515 -0
  306. package/src/scroll-linked.ts +73 -0
  307. package/src/sequence.ts +204 -14
  308. package/src/shimmer.ts +180 -0
  309. package/src/svg-path.ts +355 -0
  310. package/src/symbol-effect.ts +226 -0
  311. package/src/text-layout.ts +228 -0
  312. package/src/text.ts +279 -0
  313. package/src/track.ts +347 -12
  314. package/src/transition.ts +93 -6
  315. package/src/types.ts +49 -1
  316. package/src/vue/index.ts +231 -0
  317. package/src/zoom-native.ts +124 -0
  318. package/src/zoomable.ts +330 -0
@@ -0,0 +1,182 @@
1
+ import { Utils, type View } from '@nativescript/core'
2
+ import {
3
+ isWritingDecoration,
4
+ radiusKeys,
5
+ widthKeys,
6
+ validateDecorationTarget,
7
+ type DecorationProperty,
8
+ } from '../decoration.js'
9
+ import { androidPlayback, type AndroidPlayback } from './android-playback.js'
10
+ import type { PlaybackCommand } from '../playback.js'
11
+ import type { Track } from '../track.js'
12
+ interface NativeTarget {
13
+ getValue(): number
14
+ setValue(value: number): void
15
+ isSupported(): boolean
16
+ acquire(): void
17
+ release(): void
18
+ }
19
+ interface Handle {
20
+ control(elapsed: number, paused: boolean, command?: PlaybackCommand): void
21
+ cancel(): void
22
+ elapsed(): number
23
+ target: NativeTarget
24
+ }
25
+ const playing = new WeakMap<View, Map<DecorationProperty, Handle>>()
26
+ const ids = { borderRadius: 0, borderWidth: 1, elevation: 2, translationZ: 3 } as const
27
+ export function syncAndroidDecoration(view: View) {
28
+ for (const handle of playing.get(view)?.values() ?? [])
29
+ handle.target.setValue(handle.target.getValue())
30
+ }
31
+ export const presentedAndroidDecoration = (view: View, property: DecorationProperty) => {
32
+ const target = playing.get(view)?.get(property)?.target
33
+ return target ? target.getValue() / Utils.layout.getDisplayDensity() : undefined
34
+ }
35
+ export function playAndroidDecoration(
36
+ view: View,
37
+ track: Track,
38
+ onEnd: (finished?: boolean) => void,
39
+ ): Handle {
40
+ const property = track.property as keyof typeof ids
41
+ const density = Utils.layout.getDisplayDensity()
42
+ const bridge = globalThis as unknown as {
43
+ org: {
44
+ rafaelaugusto: {
45
+ jolt: {
46
+ DecorationTarget: new (
47
+ view: android.view.View,
48
+ property: number,
49
+ value: number,
50
+ borderColor: number,
51
+ ) => NativeTarget
52
+ }
53
+ }
54
+ }
55
+ }
56
+ let target: NativeTarget
57
+ try {
58
+ target = new bridge.org.rafaelaugusto.jolt.DecorationTarget(
59
+ view.nativeViewProtected,
60
+ ids[property],
61
+ track.values[0]! * density,
62
+ view.borderTopColor?.argb ?? 0,
63
+ )
64
+ } catch {
65
+ onEnd(false)
66
+ return {
67
+ cancel() {},
68
+ control() {},
69
+ elapsed: () => 0,
70
+ target: {
71
+ getValue: () => track.to * density,
72
+ setValue() {},
73
+ isSupported: () => false,
74
+ acquire() {},
75
+ release() {},
76
+ },
77
+ }
78
+ }
79
+ const frames = Array.create(android.animation.Keyframe, track.values.length)
80
+ track.values.forEach((value, index) => {
81
+ frames[index] = android.animation.Keyframe.ofFloat(
82
+ track.times[index]!,
83
+ value * density,
84
+ )
85
+ })
86
+ const holders = Array.create(android.animation.PropertyValuesHolder, 1)
87
+ holders[0] = android.animation.PropertyValuesHolder.ofKeyframe('value', frames)
88
+ const animator = android.animation.ObjectAnimator.ofPropertyValuesHolder(
89
+ target as unknown as java.lang.Object,
90
+ holders,
91
+ )
92
+ animator.setDuration(Math.max(track.duration, 1))
93
+ animator.setStartDelay(track.delay)
94
+ animator.setInterpolator(new android.view.animation.LinearInterpolator())
95
+ if (track.repeat > 0) {
96
+ animator.setRepeatCount(
97
+ Number.isFinite(track.repeat)
98
+ ? track.repeat
99
+ : android.animation.ValueAnimator.INFINITE,
100
+ )
101
+ animator.setRepeatMode(
102
+ track.reverse
103
+ ? android.animation.ValueAnimator.REVERSE
104
+ : android.animation.ValueAnimator.RESTART,
105
+ )
106
+ }
107
+ let stopped = false
108
+ const changes = [
109
+ ...radiusKeys,
110
+ ...widthKeys,
111
+ 'clipPath',
112
+ 'cornerShape',
113
+ 'boxShadow',
114
+ 'androidElevation',
115
+ ].map((name) => `${name}Change`)
116
+ const cleanup = () => {
117
+ if (stopped) return
118
+ stopped = true
119
+ view.off('unloaded', cancel)
120
+ for (const event of changes) view.style.off(event, external)
121
+ target.release()
122
+ if (playing.get(view)?.get(property) === handle) playing.get(view)?.delete(property)
123
+ }
124
+ let player: AndroidPlayback | undefined
125
+ const cancel = () => {
126
+ if (!stopped) {
127
+ cleanup()
128
+ player?.cancel()
129
+ onEnd(false)
130
+ }
131
+ }
132
+ const external = (event: { eventName: string }) => {
133
+ if (isWritingDecoration(view)) return
134
+ const own =
135
+ property === 'borderRadius'
136
+ ? radiusKeys
137
+ : property === 'borderWidth'
138
+ ? widthKeys
139
+ : property === 'elevation'
140
+ ? ['androidElevation']
141
+ : []
142
+ void Promise.resolve().then(() => {
143
+ if (stopped) return
144
+ if (own.some((name) => event.eventName === `${name}Change`)) return cancel()
145
+ try {
146
+ validateDecorationTarget(view, property)
147
+ } catch {
148
+ cancel()
149
+ }
150
+ })
151
+ }
152
+ const handle: Handle = {
153
+ target,
154
+ elapsed: () => player?.elapsed() ?? 0,
155
+ control(time, paused, command) {
156
+ player?.control(time, paused, command)
157
+ },
158
+ cancel() {
159
+ cleanup()
160
+ player?.cancel()
161
+ },
162
+ }
163
+ const entries = playing.get(view) ?? new Map<DecorationProperty, Handle>()
164
+ playing.set(view, entries)
165
+ entries.set(property, handle)
166
+ view.on('unloaded', cancel)
167
+ for (const event of changes) view.style.on(event, external)
168
+ if (target.isSupported()) {
169
+ target.acquire()
170
+ target.setValue(track.values[0]! * density)
171
+ player = androidPlayback(animator, track, () => {
172
+ if (!stopped) {
173
+ cleanup()
174
+ onEnd()
175
+ }
176
+ })
177
+ } else {
178
+ cleanup()
179
+ onEnd(false)
180
+ }
181
+ return handle
182
+ }
@@ -0,0 +1,109 @@
1
+ import { Utils, type View } from '@nativescript/core'
2
+ import type { GradientFrame, GradientPainter, GradientType } from '../gradient.js'
3
+
4
+ const floats = (values: readonly number[]) => {
5
+ const array = Array.create('float', values.length)
6
+ values.forEach((value, i) => (array[i] = value))
7
+ return array
8
+ }
9
+ const ints = (values: readonly number[]) => {
10
+ const array = Array.create('int', values.length)
11
+ values.forEach((value, i) => (array[i] = value | 0))
12
+ return array
13
+ }
14
+
15
+ /** A gradient drawable stacked on the view's own background: above its color, below its children. */
16
+ export function androidGradient(
17
+ view: View,
18
+ type: GradientType,
19
+ ): GradientPainter | undefined {
20
+ let layer: org.rafaelaugusto.jolt.GradientLayer
21
+ try {
22
+ layer = new org.rafaelaugusto.jolt.GradientLayer(
23
+ type === 'linear' ? 0 : type === 'radial' ? 1 : 2,
24
+ )
25
+ } catch {
26
+ // A JS-only update cannot install the native plugin.
27
+ return undefined
28
+ }
29
+ let stack: android.graphics.drawable.LayerDrawable | undefined
30
+ let queued = false
31
+
32
+ const wrap = () => {
33
+ const native = view.nativeViewProtected as android.view.View | undefined
34
+ if (!native) return
35
+ const corner = typeof view.borderRadius === 'number' ? view.borderRadius : 0
36
+ layer.setCorner(corner * Utils.layout.getDisplayDensity())
37
+ const background = native.getBackground()
38
+ if (stack && background === stack) return
39
+ const layers = Array.create(android.graphics.drawable.Drawable, 2)
40
+ layers[0] = background ?? new android.graphics.drawable.ColorDrawable(0)
41
+ layers[1] = layer
42
+ stack = new android.graphics.drawable.LayerDrawable(layers)
43
+ native.setBackground(stack)
44
+ }
45
+ // Core replaces the background drawable when its style changes; stack on the new one.
46
+ const rewrap = () => {
47
+ if (queued) return
48
+ queued = true
49
+ void Promise.resolve().then(() => {
50
+ queued = false
51
+ wrap()
52
+ })
53
+ }
54
+ view.on('loaded', wrap)
55
+ view.style.on('backgroundInternalChange', rewrap)
56
+ view.style.on('borderTopLeftRadiusChange', rewrap)
57
+ wrap()
58
+
59
+ return {
60
+ paint(frame: GradientFrame) {
61
+ layer.paint(
62
+ ints(frame.colors),
63
+ floats(frame.stops),
64
+ frame.angle,
65
+ frame.x,
66
+ frame.y,
67
+ frame.radius,
68
+ )
69
+ },
70
+ play(frames, times, timing, onEnd) {
71
+ let active = true
72
+ layer.play(
73
+ floats(times),
74
+ ints(frames.flatMap((frame) => frame.colors)),
75
+ floats(frames.flatMap((frame) => frame.stops)),
76
+ floats(frames.flatMap((frame) => [frame.angle, frame.x, frame.y, frame.radius])),
77
+ Math.round(timing.duration),
78
+ Math.round(timing.delay),
79
+ timing.repeat === Infinity ? -1 : timing.repeat,
80
+ timing.reverse,
81
+ new org.rafaelaugusto.jolt.GradientLayer.Listener({
82
+ onEnd: (finished: boolean) => {
83
+ if (!active) return
84
+ active = false
85
+ onEnd(finished)
86
+ },
87
+ }),
88
+ )
89
+ return {
90
+ cancel() {
91
+ if (!active) return
92
+ active = false
93
+ layer.cancel()
94
+ onEnd(false)
95
+ },
96
+ }
97
+ },
98
+ remove() {
99
+ view.off('loaded', wrap)
100
+ view.style.off('backgroundInternalChange', rewrap)
101
+ view.style.off('borderTopLeftRadiusChange', rewrap)
102
+ layer.cancel()
103
+ const native = view.nativeViewProtected as android.view.View | undefined
104
+ if (native && stack && native.getBackground() === stack)
105
+ native.setBackground(stack.getDrawable(0))
106
+ stack = undefined
107
+ },
108
+ }
109
+ }
@@ -0,0 +1,157 @@
1
+ import type { Track } from '../track.js'
2
+ import type { PlaybackClock, PlaybackCommand } from '../playback.js'
3
+
4
+ export interface AndroidPlayback {
5
+ cancel(): void
6
+ elapsed(): number
7
+ control(elapsed: number, paused: boolean, command?: PlaybackCommand): void
8
+ }
9
+
10
+ /** Reuse native keyframes when seeking, including rewinding into a start delay. */
11
+ export function androidPlayback(
12
+ animator: android.animation.ValueAnimator,
13
+ track: Track,
14
+ onEnd: () => void,
15
+ onStart?: () => void,
16
+ onRelease?: () => void,
17
+ ): AndroidPlayback {
18
+ let suppressed = false
19
+ let stopped = false
20
+ let started = false
21
+ let acquired = false
22
+ let origin = 0
23
+ let delay = track.delay
24
+ let held: number | undefined
25
+ animator.addListener(
26
+ new android.animation.Animator.AnimatorListener({
27
+ onAnimationStart() {
28
+ if (!suppressed && !stopped) {
29
+ started = true
30
+ if (!acquired) {
31
+ acquired = true
32
+ onStart?.()
33
+ }
34
+ }
35
+ },
36
+ onAnimationRepeat() {},
37
+ onAnimationCancel() {
38
+ if (!suppressed && !stopped) onRelease?.()
39
+ },
40
+ onAnimationEnd() {
41
+ if (suppressed || stopped || held !== undefined) return
42
+ stopped = true
43
+ onRelease?.()
44
+ onEnd()
45
+ },
46
+ }),
47
+ )
48
+ const elapsed = () =>
49
+ held ?? (started ? origin + delay + animator.getCurrentPlayTime() : origin)
50
+ const control = (
51
+ position: number,
52
+ paused: boolean,
53
+ command: PlaybackCommand = 'seek',
54
+ ) => {
55
+ if (stopped) return
56
+ if (command === 'pause') {
57
+ held = position
58
+ animator.pause()
59
+ return
60
+ }
61
+ if (command === 'play') {
62
+ held = undefined
63
+ animator.resume()
64
+ return
65
+ }
66
+ suppressed = true
67
+ animator.cancel()
68
+ started = false
69
+ origin = Math.min(position, track.delay)
70
+ delay = Math.max(0, track.delay - position)
71
+ animator.setStartDelay(delay)
72
+ held = paused ? position : undefined
73
+ suppressed = false
74
+ animator.start()
75
+ if (paused) animator.pause()
76
+ if (position >= track.delay)
77
+ animator.setCurrentPlayTime(Math.max(0, position - track.delay))
78
+ }
79
+ animator.start()
80
+ return {
81
+ elapsed,
82
+ control,
83
+ cancel() {
84
+ if (stopped) return
85
+ stopped = true
86
+ onRelease?.()
87
+ animator.cancel()
88
+ },
89
+ }
90
+ }
91
+
92
+ /** Share one native clock among active animations. Pause it when every owner
93
+ * is paused, and dispose it when the last owner settles. No frame callbacks. */
94
+ export function androidClockFactory() {
95
+ let animator: android.animation.ValueAnimator | undefined
96
+ let owners = 0,
97
+ running = 0,
98
+ accumulated = 0
99
+ const now = () => accumulated + (animator?.getCurrentPlayTime() ?? 0)
100
+ const start = () => {
101
+ if (animator) return
102
+ const values = Array.create('float', 2)
103
+ values[0] = 0
104
+ values[1] = 1
105
+ animator = android.animation.ValueAnimator.ofFloat(values)
106
+ animator.setDuration(60_000)
107
+ animator.setRepeatCount(android.animation.ValueAnimator.INFINITE)
108
+ animator.start()
109
+ }
110
+ const stop = () => {
111
+ accumulated = now()
112
+ animator?.cancel()
113
+ animator = undefined
114
+ }
115
+ return (_duration: number): PlaybackClock => {
116
+ start()
117
+ owners++
118
+ running++
119
+ let offset = 0,
120
+ anchor = now(),
121
+ paused = false,
122
+ stopped = false
123
+ const time = () => offset + (paused || stopped ? 0 : now() - anchor)
124
+ const pause = () => {
125
+ if (paused || stopped) return
126
+ offset = time()
127
+ paused = true
128
+ // getCurrentPlayTime keeps counting while a ValueAnimator is paused and
129
+ // corrects itself only on its next frame. Start a fresh marker on resume
130
+ // so command-time reads cannot jump forward and then backward.
131
+ if (--running === 0) stop()
132
+ }
133
+ return {
134
+ time,
135
+ pause,
136
+ play() {
137
+ if (!paused || stopped) return
138
+ if (running++ === 0) start()
139
+ anchor = now()
140
+ paused = false
141
+ },
142
+ seek(ms) {
143
+ offset = ms
144
+ anchor = now()
145
+ },
146
+ cancel() {
147
+ if (stopped) return
148
+ pause()
149
+ stopped = true
150
+ if (--owners === 0) {
151
+ stop()
152
+ accumulated = 0
153
+ }
154
+ },
155
+ }
156
+ }
157
+ }