react-native-smooth-clip-view 0.2.8 → 0.3.0

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 (132) hide show
  1. package/README.md +86 -21
  2. package/android/CMakeLists.txt +7 -0
  3. package/android/build.gradle +14 -2
  4. package/android/proguard-rules.pro +3 -3
  5. package/android/src/main/cpp/SmoothClipAndroid.h +10 -0
  6. package/android/src/main/cpp/SmoothClipBindings.cpp +896 -6
  7. package/android/src/main/cpp/SmoothClipRegistry.cpp +1067 -46
  8. package/android/src/main/cpp/SmoothClipTrace.h +25 -0
  9. package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +265 -0
  10. package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +21 -0
  11. package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +283 -0
  12. package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +385 -24
  13. package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +138 -24
  14. package/cpp/SmoothClipAnimationCurve.h +151 -122
  15. package/cpp/SmoothClipRegistry.h +102 -4
  16. package/cpp/SmoothClipSharedGeometry.h +146 -7
  17. package/cpp/SmoothClipVelocityTracker.h +13 -9
  18. package/ios/SmoothClipGeometry.h +184 -6
  19. package/ios/SmoothClipRegistry.mm +1033 -25
  20. package/ios/SmoothClipRegistrySnapshot.h +13 -0
  21. package/ios/SmoothClipTurboModule.cpp +857 -2
  22. package/ios/SmoothClipTurboModule.h +310 -0
  23. package/ios/SmoothClipView.mm +714 -183
  24. package/ios/SmoothClipViewRegistry.h +1 -0
  25. package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  26. package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  27. package/lib/module/NativeSmoothClipModule.js.map +1 -1
  28. package/lib/module/SmoothClipView.ios.js +22 -6
  29. package/lib/module/SmoothClipView.ios.js.map +1 -1
  30. package/lib/module/SmoothClipView.js +1 -46
  31. package/lib/module/SmoothClipView.js.map +1 -1
  32. package/lib/module/SmoothClipViewNativeComponent.ts +27 -1
  33. package/lib/module/capabilities.js +4 -0
  34. package/lib/module/capabilities.js.map +1 -0
  35. package/lib/module/capabilities.native.js +36 -0
  36. package/lib/module/capabilities.native.js.map +1 -0
  37. package/lib/module/capabilityTypes.js +11 -0
  38. package/lib/module/capabilityTypes.js.map +1 -0
  39. package/lib/module/driverState.js +0 -28
  40. package/lib/module/driverState.js.map +1 -1
  41. package/lib/module/drivers.js +1 -281
  42. package/lib/module/drivers.js.map +1 -1
  43. package/lib/module/drivers.native.js +340 -75
  44. package/lib/module/drivers.native.js.map +1 -1
  45. package/lib/module/geometry.js +147 -17
  46. package/lib/module/geometry.js.map +1 -1
  47. package/lib/module/groupDriverTypes.js +4 -0
  48. package/lib/module/groupDriverTypes.js.map +1 -0
  49. package/lib/module/groupDrivers.android.js +4 -0
  50. package/lib/module/groupDrivers.android.js.map +1 -0
  51. package/lib/module/groupDrivers.ios.js +4 -0
  52. package/lib/module/groupDrivers.ios.js.map +1 -0
  53. package/lib/module/groupDrivers.js +4 -0
  54. package/lib/module/groupDrivers.js.map +1 -0
  55. package/lib/module/groupDrivers.native.js +799 -0
  56. package/lib/module/groupDrivers.native.js.map +1 -0
  57. package/lib/module/index.js +3 -2
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/presentationProtocol.js +15 -0
  60. package/lib/module/presentationProtocol.js.map +1 -0
  61. package/lib/typescript/src/NativeSmoothClipModule.d.ts +25 -0
  62. package/lib/typescript/src/NativeSmoothClipModule.d.ts.map +1 -1
  63. package/lib/typescript/src/SmoothClipView.d.ts +2 -10
  64. package/lib/typescript/src/SmoothClipView.d.ts.map +1 -1
  65. package/lib/typescript/src/SmoothClipView.ios.d.ts +133 -2
  66. package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
  67. package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +8 -0
  68. package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts.map +1 -1
  69. package/lib/typescript/src/capabilities.d.ts +3 -0
  70. package/lib/typescript/src/capabilities.d.ts.map +1 -0
  71. package/lib/typescript/src/capabilities.native.d.ts +3 -0
  72. package/lib/typescript/src/capabilities.native.d.ts.map +1 -0
  73. package/lib/typescript/src/capabilityTypes.d.ts +10 -0
  74. package/lib/typescript/src/capabilityTypes.d.ts.map +1 -0
  75. package/lib/typescript/src/driverState.d.ts +1 -6
  76. package/lib/typescript/src/driverState.d.ts.map +1 -1
  77. package/lib/typescript/src/driverTypes.d.ts +39 -11
  78. package/lib/typescript/src/driverTypes.d.ts.map +1 -1
  79. package/lib/typescript/src/drivers.d.ts +2 -4
  80. package/lib/typescript/src/drivers.d.ts.map +1 -1
  81. package/lib/typescript/src/drivers.native.d.ts.map +1 -1
  82. package/lib/typescript/src/geometry.d.ts +36 -2
  83. package/lib/typescript/src/geometry.d.ts.map +1 -1
  84. package/lib/typescript/src/groupDriverTypes.d.ts +66 -0
  85. package/lib/typescript/src/groupDriverTypes.d.ts.map +1 -0
  86. package/lib/typescript/src/groupDrivers.android.d.ts +2 -0
  87. package/lib/typescript/src/groupDrivers.android.d.ts.map +1 -0
  88. package/lib/typescript/src/groupDrivers.d.ts +2 -0
  89. package/lib/typescript/src/groupDrivers.d.ts.map +1 -0
  90. package/lib/typescript/src/groupDrivers.ios.d.ts +2 -0
  91. package/lib/typescript/src/groupDrivers.ios.d.ts.map +1 -0
  92. package/lib/typescript/src/groupDrivers.native.d.ts +4 -0
  93. package/lib/typescript/src/groupDrivers.native.d.ts.map +1 -0
  94. package/lib/typescript/src/index.d.ts +6 -3
  95. package/lib/typescript/src/index.d.ts.map +1 -1
  96. package/lib/typescript/src/presentationProtocol.d.ts +4 -0
  97. package/lib/typescript/src/presentationProtocol.d.ts.map +1 -0
  98. package/package.json +10 -11
  99. package/src/NativeSmoothClipModule.ts +275 -0
  100. package/src/SmoothClipView.ios.tsx +30 -11
  101. package/src/SmoothClipView.tsx +2 -76
  102. package/src/SmoothClipViewNativeComponent.ts +27 -1
  103. package/src/capabilities.native.ts +69 -0
  104. package/src/capabilities.ts +2 -0
  105. package/src/capabilityTypes.ts +17 -0
  106. package/src/driverState.ts +1 -41
  107. package/src/driverTypes.ts +56 -11
  108. package/src/drivers.native.ts +921 -151
  109. package/src/drivers.ts +2 -423
  110. package/src/geometry.ts +231 -27
  111. package/src/groupDriverTypes.ts +117 -0
  112. package/src/groupDrivers.android.ts +1 -0
  113. package/src/groupDrivers.ios.ts +1 -0
  114. package/src/groupDrivers.native.ts +1189 -0
  115. package/src/groupDrivers.ts +1 -0
  116. package/src/index.ts +30 -2
  117. package/src/presentationProtocol.ts +28 -0
  118. package/lib/module/SmoothClipView.web.js +0 -54
  119. package/lib/module/SmoothClipView.web.js.map +0 -1
  120. package/lib/module/index.web.js +0 -8
  121. package/lib/module/index.web.js.map +0 -1
  122. package/lib/module/webClipPath.js +0 -25
  123. package/lib/module/webClipPath.js.map +0 -1
  124. package/lib/typescript/src/SmoothClipView.web.d.ts +0 -10
  125. package/lib/typescript/src/SmoothClipView.web.d.ts.map +0 -1
  126. package/lib/typescript/src/index.web.d.ts +0 -9
  127. package/lib/typescript/src/index.web.d.ts.map +0 -1
  128. package/lib/typescript/src/webClipPath.d.ts +0 -7
  129. package/lib/typescript/src/webClipPath.d.ts.map +0 -1
  130. package/src/SmoothClipView.web.tsx +0 -66
  131. package/src/index.web.ts +0 -23
  132. package/src/webClipPath.ts +0 -30
@@ -5,6 +5,7 @@
5
5
  #include <fbjni/fbjni.h>
6
6
  #include <jsi/jsi.h>
7
7
 
8
+ #include <algorithm>
8
9
  #include <cmath>
9
10
  #include <limits>
10
11
  #include <memory>
@@ -32,14 +33,40 @@ bool validDriverId(double value) {
32
33
  value <= kMaxSafeJavaScriptInteger && std::floor(value) == value;
33
34
  }
34
35
 
36
+ bool validCurve(double value) {
37
+ return std::isfinite(value) && std::floor(value) == value &&
38
+ (value == static_cast<double>(ClipCurve::Circular) ||
39
+ value == static_cast<double>(ClipCurve::Continuous));
40
+ }
41
+
42
+ bool validRadiusOverride(double value) {
43
+ return std::isfinite(value) || std::isnan(value);
44
+ }
45
+
35
46
  bool finitePresentation(const Presentation &presentation) {
36
47
  return std::isfinite(presentation.clip.x) &&
37
48
  std::isfinite(presentation.clip.y) &&
38
49
  std::isfinite(presentation.clip.width) &&
39
50
  std::isfinite(presentation.clip.height) &&
40
51
  std::isfinite(presentation.clip.radius) &&
52
+ validRadiusOverride(presentation.clip.topLeftRadius) &&
53
+ validRadiusOverride(presentation.clip.topRightRadius) &&
54
+ validRadiusOverride(presentation.clip.bottomRightRadius) &&
55
+ validRadiusOverride(presentation.clip.bottomLeftRadius) &&
56
+ (presentation.clip.curve == ClipCurve::Circular ||
57
+ presentation.clip.curve == ClipCurve::Continuous) &&
41
58
  std::isfinite(presentation.contentTranslateX) &&
42
- std::isfinite(presentation.contentTranslateY);
59
+ std::isfinite(presentation.contentTranslateY) &&
60
+ std::isfinite(presentation.contentScale) &&
61
+ presentation.contentScale > 0;
62
+ }
63
+
64
+ bool finiteV2Presentation(const Presentation &presentation) {
65
+ return finitePresentation(presentation) &&
66
+ std::isfinite(presentation.clip.topLeftRadius) &&
67
+ std::isfinite(presentation.clip.topRightRadius) &&
68
+ std::isfinite(presentation.clip.bottomRightRadius) &&
69
+ std::isfinite(presentation.clip.bottomLeftRadius);
43
70
  }
44
71
 
45
72
  bool boolArg(const Value *args, size_t count, size_t index) {
@@ -69,6 +96,72 @@ Presentation presentationFromArgs(const Value *args, size_t offset) {
69
96
  args[offset + 6].asNumber()};
70
97
  }
71
98
 
99
+ Presentation presentationV2FromArgs(const Value *args, size_t offset) {
100
+ Geometry geometry{
101
+ args[offset].asNumber(),
102
+ args[offset + 1].asNumber(),
103
+ args[offset + 2].asNumber(),
104
+ args[offset + 3].asNumber(),
105
+ 0.0};
106
+ geometry.topLeftRadius = args[offset + 4].asNumber();
107
+ geometry.topRightRadius = args[offset + 5].asNumber();
108
+ geometry.bottomRightRadius = args[offset + 6].asNumber();
109
+ geometry.bottomLeftRadius = args[offset + 7].asNumber();
110
+ const double curveCode = args[offset + 8].asNumber();
111
+ geometry.curve = curveCode == static_cast<double>(ClipCurve::Continuous)
112
+ ? ClipCurve::Continuous
113
+ : curveCode == static_cast<double>(ClipCurve::Circular)
114
+ ? ClipCurve::Circular
115
+ : static_cast<ClipCurve>(-1);
116
+ if (geometry.topLeftRadius == geometry.topRightRadius &&
117
+ geometry.topLeftRadius == geometry.bottomRightRadius &&
118
+ geometry.topLeftRadius == geometry.bottomLeftRadius) {
119
+ geometry.radius = geometry.topLeftRadius;
120
+ }
121
+ return Presentation{
122
+ geometry,
123
+ args[offset + 9].asNumber(),
124
+ args[offset + 10].asNumber(),
125
+ args[offset + 11].asNumber()};
126
+ }
127
+
128
+ Presentation presentationV2FromArray(
129
+ Runtime &runtime,
130
+ const Array &values,
131
+ size_t offset) {
132
+ Geometry geometry{
133
+ values.getValueAtIndex(runtime, offset).asNumber(),
134
+ values.getValueAtIndex(runtime, offset + 1).asNumber(),
135
+ values.getValueAtIndex(runtime, offset + 2).asNumber(),
136
+ values.getValueAtIndex(runtime, offset + 3).asNumber(),
137
+ 0.0};
138
+ geometry.topLeftRadius =
139
+ values.getValueAtIndex(runtime, offset + 4).asNumber();
140
+ geometry.topRightRadius =
141
+ values.getValueAtIndex(runtime, offset + 5).asNumber();
142
+ geometry.bottomRightRadius =
143
+ values.getValueAtIndex(runtime, offset + 6).asNumber();
144
+ geometry.bottomLeftRadius =
145
+ values.getValueAtIndex(runtime, offset + 7).asNumber();
146
+ const double curveCode =
147
+ values.getValueAtIndex(runtime, offset + 8).asNumber();
148
+ geometry.curve = curveCode == static_cast<double>(ClipCurve::Continuous)
149
+ ? ClipCurve::Continuous
150
+ : curveCode == static_cast<double>(ClipCurve::Circular)
151
+ ? ClipCurve::Circular
152
+ : static_cast<ClipCurve>(-1);
153
+ if (geometry.topLeftRadius == geometry.topRightRadius &&
154
+ geometry.topLeftRadius == geometry.bottomRightRadius &&
155
+ geometry.topLeftRadius == geometry.bottomLeftRadius) {
156
+ geometry.radius = geometry.topLeftRadius;
157
+ }
158
+ return Presentation{
159
+ geometry,
160
+ values.getValueAtIndex(runtime, offset + 9).asNumber(),
161
+ values.getValueAtIndex(runtime, offset + 10).asNumber(),
162
+ values.getValueAtIndex(runtime, offset + 11).asNumber()};
163
+ }
164
+
72
165
  Array presentationArray(Runtime &runtime, const Presentation &presentation) {
73
166
  Array result(runtime, 7);
74
167
  result.setValueAtIndex(runtime, 0, presentation.clip.x);
@@ -81,6 +174,40 @@ Array presentationArray(Runtime &runtime, const Presentation &presentation) {
81
174
  return result;
82
175
  }
83
176
 
177
+ Array presentationArrayV2(Runtime &runtime, const Presentation &presentation) {
178
+ Array result(runtime, 12);
179
+ result.setValueAtIndex(runtime, 0, presentation.clip.x);
180
+ result.setValueAtIndex(runtime, 1, presentation.clip.y);
181
+ result.setValueAtIndex(runtime, 2, presentation.clip.width);
182
+ result.setValueAtIndex(runtime, 3, presentation.clip.height);
183
+ result.setValueAtIndex(
184
+ runtime,
185
+ 4,
186
+ SmoothClipResolvedRadius(
187
+ presentation.clip.topLeftRadius, presentation.clip.radius));
188
+ result.setValueAtIndex(
189
+ runtime,
190
+ 5,
191
+ SmoothClipResolvedRadius(
192
+ presentation.clip.topRightRadius, presentation.clip.radius));
193
+ result.setValueAtIndex(
194
+ runtime,
195
+ 6,
196
+ SmoothClipResolvedRadius(
197
+ presentation.clip.bottomRightRadius, presentation.clip.radius));
198
+ result.setValueAtIndex(
199
+ runtime,
200
+ 7,
201
+ SmoothClipResolvedRadius(
202
+ presentation.clip.bottomLeftRadius, presentation.clip.radius));
203
+ result.setValueAtIndex(
204
+ runtime, 8, static_cast<double>(presentation.clip.curve));
205
+ result.setValueAtIndex(runtime, 9, presentation.contentTranslateX);
206
+ result.setValueAtIndex(runtime, 10, presentation.contentTranslateY);
207
+ result.setValueAtIndex(runtime, 11, presentation.contentScale);
208
+ return result;
209
+ }
210
+
84
211
  Array cancelResultArray(
85
212
  Runtime &runtime,
86
213
  bool handled,
@@ -98,10 +225,206 @@ Array cancelResultArray(
98
225
  return result;
99
226
  }
100
227
 
228
+ Array cancelResultArrayV2(
229
+ Runtime &runtime,
230
+ bool handled,
231
+ const Presentation &presentation) {
232
+ Array result(runtime, 13);
233
+ result.setValueAtIndex(runtime, 0, handled ? 1.0 : 0.0);
234
+ result.setValueAtIndex(runtime, 1, presentation.clip.x);
235
+ result.setValueAtIndex(runtime, 2, presentation.clip.y);
236
+ result.setValueAtIndex(runtime, 3, presentation.clip.width);
237
+ result.setValueAtIndex(runtime, 4, presentation.clip.height);
238
+ result.setValueAtIndex(
239
+ runtime,
240
+ 5,
241
+ SmoothClipResolvedRadius(
242
+ presentation.clip.topLeftRadius, presentation.clip.radius));
243
+ result.setValueAtIndex(
244
+ runtime,
245
+ 6,
246
+ SmoothClipResolvedRadius(
247
+ presentation.clip.topRightRadius, presentation.clip.radius));
248
+ result.setValueAtIndex(
249
+ runtime,
250
+ 7,
251
+ SmoothClipResolvedRadius(
252
+ presentation.clip.bottomRightRadius, presentation.clip.radius));
253
+ result.setValueAtIndex(
254
+ runtime,
255
+ 8,
256
+ SmoothClipResolvedRadius(
257
+ presentation.clip.bottomLeftRadius, presentation.clip.radius));
258
+ result.setValueAtIndex(
259
+ runtime, 9, static_cast<double>(presentation.clip.curve));
260
+ result.setValueAtIndex(runtime, 10, presentation.contentTranslateX);
261
+ result.setValueAtIndex(runtime, 11, presentation.contentTranslateY);
262
+ result.setValueAtIndex(runtime, 12, presentation.contentScale);
263
+ return result;
264
+ }
265
+
266
+ Array snapshotsArrayV2(
267
+ Runtime &runtime,
268
+ const std::vector<DriverSnapshot> &snapshots) {
269
+ Array result(runtime, snapshots.size() * 13);
270
+ size_t offset = 0;
271
+ for (const DriverSnapshot &snapshot : snapshots) {
272
+ result.setValueAtIndex(runtime, offset++, snapshot.ready ? 1.0 : 0.0);
273
+ const Presentation &presentation = snapshot.presentation;
274
+ result.setValueAtIndex(runtime, offset++, presentation.clip.x);
275
+ result.setValueAtIndex(runtime, offset++, presentation.clip.y);
276
+ result.setValueAtIndex(runtime, offset++, presentation.clip.width);
277
+ result.setValueAtIndex(runtime, offset++, presentation.clip.height);
278
+ result.setValueAtIndex(
279
+ runtime,
280
+ offset++,
281
+ SmoothClipResolvedRadius(
282
+ presentation.clip.topLeftRadius, presentation.clip.radius));
283
+ result.setValueAtIndex(
284
+ runtime,
285
+ offset++,
286
+ SmoothClipResolvedRadius(
287
+ presentation.clip.topRightRadius, presentation.clip.radius));
288
+ result.setValueAtIndex(
289
+ runtime,
290
+ offset++,
291
+ SmoothClipResolvedRadius(
292
+ presentation.clip.bottomRightRadius, presentation.clip.radius));
293
+ result.setValueAtIndex(
294
+ runtime,
295
+ offset++,
296
+ SmoothClipResolvedRadius(
297
+ presentation.clip.bottomLeftRadius, presentation.clip.radius));
298
+ result.setValueAtIndex(
299
+ runtime, offset++, static_cast<double>(presentation.clip.curve));
300
+ result.setValueAtIndex(runtime, offset++, presentation.contentTranslateX);
301
+ result.setValueAtIndex(runtime, offset++, presentation.contentTranslateY);
302
+ result.setValueAtIndex(runtime, offset++, presentation.contentScale);
303
+ }
304
+ return result;
305
+ }
306
+
307
+ bool validSuspensionPolicy(double value) {
308
+ return value == static_cast<double>(GroupSuspensionPolicy::Pause) ||
309
+ value == static_cast<double>(GroupSuspensionPolicy::Finish);
310
+ }
311
+
312
+ std::vector<uint64_t> driverIdsFromArray(Runtime &runtime, const Array &values) {
313
+ std::vector<uint64_t> driverIds;
314
+ const size_t length = values.size(runtime);
315
+ driverIds.reserve(length);
316
+ for (size_t index = 0; index < length; index += 1) {
317
+ const double value = values.getValueAtIndex(runtime, index).asNumber();
318
+ if (!validDriverId(value) ||
319
+ std::find(driverIds.begin(), driverIds.end(),
320
+ static_cast<uint64_t>(value)) != driverIds.end()) {
321
+ return {};
322
+ }
323
+ driverIds.push_back(static_cast<uint64_t>(value));
324
+ }
325
+ return driverIds;
326
+ }
327
+
328
+ bool parseGroupMotionEntries(
329
+ Runtime &runtime,
330
+ const Array &values,
331
+ std::vector<GroupMotionEntry> &entries) {
332
+ const size_t length = values.size(runtime);
333
+ if (length < 26 || length % 26 != 0) return false;
334
+ entries.reserve(length / 26);
335
+ for (size_t index = 0; index < length; index += 26) {
336
+ const double driverId = values.getValueAtIndex(runtime, index).asNumber();
337
+ const double hasFromValue =
338
+ values.getValueAtIndex(runtime, index + 1).asNumber();
339
+ const double fromCurve =
340
+ values.getValueAtIndex(runtime, index + 10).asNumber();
341
+ const double targetCurve =
342
+ values.getValueAtIndex(runtime, index + 22).asNumber();
343
+ const Presentation from =
344
+ presentationV2FromArray(runtime, values, index + 2);
345
+ const Presentation target =
346
+ presentationV2FromArray(runtime, values, index + 14);
347
+ if (!validDriverId(driverId) ||
348
+ (hasFromValue != 0.0 && hasFromValue != 1.0) ||
349
+ !validCurve(fromCurve) || !validCurve(targetCurve) ||
350
+ !finiteV2Presentation(from) || !finiteV2Presentation(target)) {
351
+ entries.clear();
352
+ return false;
353
+ }
354
+ entries.push_back(
355
+ {static_cast<uint64_t>(driverId), hasFromValue == 1.0, from, target, {}});
356
+ }
357
+ return true;
358
+ }
359
+
360
+ bool parseGroupKeyframeEntries(
361
+ Runtime &runtime,
362
+ const Array &values,
363
+ std::vector<GroupMotionEntry> &entries) {
364
+ const size_t length = values.size(runtime);
365
+ size_t index = 0;
366
+ while (index < length) {
367
+ if (length - index < 27) return false;
368
+ const double driverId = values.getValueAtIndex(runtime, index).asNumber();
369
+ const double hasFromValue =
370
+ values.getValueAtIndex(runtime, index + 1).asNumber();
371
+ const double fromCurve =
372
+ values.getValueAtIndex(runtime, index + 10).asNumber();
373
+ const double targetCurve =
374
+ values.getValueAtIndex(runtime, index + 22).asNumber();
375
+ const double frameCountValue =
376
+ values.getValueAtIndex(runtime, index + 26).asNumber();
377
+ const Presentation from =
378
+ presentationV2FromArray(runtime, values, index + 2);
379
+ const Presentation target =
380
+ presentationV2FromArray(runtime, values, index + 14);
381
+ if (!validDriverId(driverId) ||
382
+ (hasFromValue != 0.0 && hasFromValue != 1.0) ||
383
+ !validCurve(fromCurve) || !validCurve(targetCurve) ||
384
+ !finiteV2Presentation(from) || !finiteV2Presentation(target) ||
385
+ !std::isfinite(frameCountValue) || frameCountValue < 2 ||
386
+ std::floor(frameCountValue) != frameCountValue) {
387
+ return false;
388
+ }
389
+ const size_t frameCount = static_cast<size_t>(frameCountValue);
390
+ index += 27;
391
+ if (frameCount > (length - index) / 13) return false;
392
+ std::vector<Keyframe> keyframes;
393
+ keyframes.reserve(frameCount);
394
+ double previousOffset = -1;
395
+ for (size_t frameIndex = 0; frameIndex < frameCount; frameIndex += 1) {
396
+ const double offset = values.getValueAtIndex(runtime, index).asNumber();
397
+ const double curve =
398
+ values.getValueAtIndex(runtime, index + 9).asNumber();
399
+ const Presentation presentation =
400
+ presentationV2FromArray(runtime, values, index + 1);
401
+ if (!std::isfinite(offset) || offset < 0 || offset > 1 ||
402
+ offset <= previousOffset || !validCurve(curve) ||
403
+ !finiteV2Presentation(presentation)) {
404
+ return false;
405
+ }
406
+ previousOffset = offset;
407
+ keyframes.push_back({offset, presentation});
408
+ index += 13;
409
+ }
410
+ if (keyframes.front().offset != 0 || keyframes.back().offset != 1) {
411
+ return false;
412
+ }
413
+ entries.push_back({
414
+ static_cast<uint64_t>(driverId),
415
+ hasFromValue == 1.0,
416
+ from,
417
+ target,
418
+ std::move(keyframes)});
419
+ }
420
+ return !entries.empty();
421
+ }
422
+
101
423
  struct BindingsState {
102
424
  Runtime *runtime = nullptr;
103
425
  std::shared_ptr<CallInvoker> callInvoker;
104
426
  std::unordered_map<int32_t, std::shared_ptr<Function>> listeners;
427
+ std::unordered_map<int32_t, std::shared_ptr<Function>> groupListeners;
105
428
  int32_t nextListenerId = 0;
106
429
  };
107
430
 
@@ -137,6 +460,7 @@ void installBindings(
137
460
  // against — the new one; the TurboModule invalidate hook releases them
138
461
  // while their runtime is still alive, and this is the safety net.
139
462
  state->listeners.clear();
463
+ state->groupListeners.clear();
140
464
  state->runtime = &runtime;
141
465
  state->callInvoker = callInvoker;
142
466
 
@@ -163,20 +487,268 @@ void installBindings(
163
487
  });
164
488
  });
165
489
 
490
+ setGroupCompletionCallback(
491
+ state.get(),
492
+ [state](
493
+ uint64_t controllerId,
494
+ int32_t groupId,
495
+ bool finished,
496
+ std::vector<uint64_t> driverIds) {
497
+ auto invoker = state->callInvoker;
498
+ if (!invoker) return;
499
+ invoker->invokeAsync(
500
+ [state, controllerId, groupId, finished,
501
+ driverIds = std::move(driverIds)]() {
502
+ if (state->runtime == nullptr) return;
503
+ Runtime &rt = *state->runtime;
504
+ std::vector<std::shared_ptr<Function>> snapshot;
505
+ snapshot.reserve(state->groupListeners.size());
506
+ for (const auto &entry : state->groupListeners) {
507
+ snapshot.push_back(entry.second);
508
+ }
509
+ for (const auto &listener : snapshot) {
510
+ Object result(rt);
511
+ result.setProperty(
512
+ rt, "controllerId", static_cast<double>(controllerId));
513
+ result.setProperty(rt, "groupId", groupId);
514
+ result.setProperty(rt, "finished", finished);
515
+ Array ids(rt, driverIds.size());
516
+ for (size_t index = 0; index < driverIds.size(); index += 1) {
517
+ ids.setValueAtIndex(
518
+ rt, index, static_cast<double>(driverIds[index]));
519
+ }
520
+ result.setProperty(rt, "driverIds", std::move(ids));
521
+ listener->call(rt, std::move(result));
522
+ }
523
+ });
524
+ });
525
+
166
526
  Object bindings(runtime);
167
527
 
168
528
  setHostFunction(
169
- runtime, bindings, "setClipPresentation", 10,
529
+ runtime, bindings, "getPresentationProtocolVersion", 0,
530
+ [](Runtime &, const Value &, const Value *, size_t) -> Value {
531
+ return Value(2);
532
+ });
533
+
534
+ setHostFunction(
535
+ runtime, bindings, "supportsAutonomousComplexPathAnimation", 0,
536
+ [](Runtime &, const Value &, const Value *, size_t) -> Value {
537
+ // Static complex clipping is available. Autonomous complex-path
538
+ // animation is deliberately gated off until physical-device sampling
539
+ // proves the release tolerance and frame-time requirements.
540
+ return Value(false);
541
+ });
542
+
543
+ setHostFunction(
544
+ runtime, bindings, "beginGroupInteractionV2", 1,
545
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
546
+ if (count < 1 || !args[0].isObject() ||
547
+ !args[0].getObject(rt).isArray(rt)) {
548
+ return Array(rt, 0);
549
+ }
550
+ Array values = args[0].getObject(rt).getArray(rt);
551
+ const std::vector<uint64_t> driverIds = driverIdsFromArray(rt, values);
552
+ if (driverIds.empty()) return Array(rt, 0);
553
+ return snapshotsArrayV2(rt, beginGroupInteraction(driverIds));
554
+ });
555
+
556
+ setHostFunction(
557
+ runtime, bindings, "snapshotGroupV2", 1,
558
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
559
+ if (count < 1 || !args[0].isObject() ||
560
+ !args[0].getObject(rt).isArray(rt)) {
561
+ return Array(rt, 0);
562
+ }
563
+ Array values = args[0].getObject(rt).getArray(rt);
564
+ const std::vector<uint64_t> driverIds = driverIdsFromArray(rt, values);
565
+ if (driverIds.empty()) return Array(rt, 0);
566
+ return snapshotsArrayV2(rt, snapshotGroup(driverIds));
567
+ });
568
+
569
+ setHostFunction(
570
+ runtime, bindings, "setClipPresentationBatchV2", 1,
571
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
572
+ if (count < 1 || !args[0].isObject() ||
573
+ !args[0].getObject(rt).isArray(rt)) {
574
+ return Value(false);
575
+ }
576
+ Array values = args[0].getObject(rt).getArray(rt);
577
+ const size_t length = values.size(rt);
578
+ if (length < 13 || length % 13 != 0) return Value(false);
579
+ std::vector<BatchEntry> entries;
580
+ entries.reserve(length / 13);
581
+ for (size_t index = 0; index < length; index += 13) {
582
+ const double driverId =
583
+ values.getValueAtIndex(rt, index).asNumber();
584
+ const double curve =
585
+ values.getValueAtIndex(rt, index + 9).asNumber();
586
+ const Presentation presentation =
587
+ presentationV2FromArray(rt, values, index + 1);
588
+ if (!validDriverId(driverId) || !validCurve(curve) ||
589
+ !finiteV2Presentation(presentation)) {
590
+ return Value(false);
591
+ }
592
+ entries.push_back(
593
+ {static_cast<uint64_t>(driverId), presentation});
594
+ }
595
+ return Value(setPresentationBatch(entries));
596
+ });
597
+
598
+ setHostFunction(
599
+ runtime, bindings, "animateTimingGroupV2", 10,
600
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
601
+ if (count < 9 || !args[1].isObject() ||
602
+ !args[1].getObject(rt).isArray(rt)) {
603
+ return Value(0);
604
+ }
605
+ const double controllerId = args[0].asNumber();
606
+ Array values = args[1].getObject(rt).getArray(rt);
607
+ std::vector<GroupMotionEntry> entries;
608
+ const TimingAnimation animation{
609
+ args[2].asNumber(), args[3].asNumber(), args[4].asNumber(),
610
+ args[5].asNumber(), args[6].asNumber(),
611
+ static_cast<int32_t>(args[7].asNumber())};
612
+ const double suspension = args[8].asNumber();
613
+ if (!validDriverId(controllerId) ||
614
+ !parseGroupMotionEntries(rt, values, entries) ||
615
+ !std::isfinite(animation.durationMs) ||
616
+ !std::isfinite(animation.controlPoint1X) ||
617
+ !std::isfinite(animation.controlPoint1Y) ||
618
+ !std::isfinite(animation.controlPoint2X) ||
619
+ !std::isfinite(animation.controlPoint2Y) ||
620
+ !validSuspensionPolicy(suspension)) {
621
+ return Value(0);
622
+ }
623
+ return Value(animateTimingGroup(
624
+ static_cast<uint64_t>(controllerId),
625
+ std::move(entries),
626
+ animation,
627
+ static_cast<GroupSuspensionPolicy>(
628
+ static_cast<int32_t>(suspension)),
629
+ startStampArg(args, count, 9)));
630
+ });
631
+
632
+ setHostFunction(
633
+ runtime, bindings, "animateSpringGroupV2", 10,
634
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
635
+ if (count < 9 || !args[1].isObject() ||
636
+ !args[1].getObject(rt).isArray(rt)) {
637
+ return Value(0);
638
+ }
639
+ const double controllerId = args[0].asNumber();
640
+ Array values = args[1].getObject(rt).getArray(rt);
641
+ std::vector<GroupMotionEntry> entries;
642
+ const SpringAnimation animation{
643
+ args[2].asNumber(), args[3].asNumber(), args[4].asNumber(),
644
+ args[5].asNumber(), boolArg(args, count, 6),
645
+ static_cast<int32_t>(args[7].asNumber())};
646
+ const double suspension = args[8].asNumber();
647
+ if (!validDriverId(controllerId) ||
648
+ !parseGroupMotionEntries(rt, values, entries) ||
649
+ !std::isfinite(animation.mass) ||
650
+ !std::isfinite(animation.stiffness) ||
651
+ !std::isfinite(animation.damping) ||
652
+ !std::isfinite(animation.initialVelocity) || animation.mass <= 0 ||
653
+ animation.stiffness <= 0 || animation.damping < 0 ||
654
+ !validSuspensionPolicy(suspension)) {
655
+ return Value(0);
656
+ }
657
+ return Value(animateSpringGroup(
658
+ static_cast<uint64_t>(controllerId),
659
+ std::move(entries),
660
+ animation,
661
+ static_cast<GroupSuspensionPolicy>(
662
+ static_cast<int32_t>(suspension)),
663
+ startStampArg(args, count, 9)));
664
+ });
665
+
666
+ setHostFunction(
667
+ runtime, bindings, "animateKeyframesGroupV2", 6,
668
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
669
+ if (count < 5 || !args[1].isObject() ||
670
+ !args[1].getObject(rt).isArray(rt)) {
671
+ return Value(0);
672
+ }
673
+ const double controllerId = args[0].asNumber();
674
+ Array values = args[1].getObject(rt).getArray(rt);
675
+ std::vector<GroupMotionEntry> entries;
676
+ const double durationMs = args[2].asNumber();
677
+ const int32_t reduceMotion = static_cast<int32_t>(args[3].asNumber());
678
+ const double suspension = args[4].asNumber();
679
+ if (!validDriverId(controllerId) ||
680
+ !parseGroupKeyframeEntries(rt, values, entries) ||
681
+ !std::isfinite(durationMs) ||
682
+ !validSuspensionPolicy(suspension)) {
683
+ return Value(0);
684
+ }
685
+ return Value(animateKeyframesGroup(
686
+ static_cast<uint64_t>(controllerId),
687
+ std::move(entries),
688
+ durationMs,
689
+ reduceMotion,
690
+ static_cast<GroupSuspensionPolicy>(
691
+ static_cast<int32_t>(suspension)),
692
+ startStampArg(args, count, 5)));
693
+ });
694
+
695
+ setHostFunction(
696
+ runtime, bindings, "cancelAnimationGroupV2", 2,
697
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
698
+ if (count < 2 || !args[0].isNumber() || !args[1].isNumber()) {
699
+ return Array(rt, 0);
700
+ }
701
+ const double groupId = args[0].asNumber();
702
+ const double behavior = args[1].asNumber();
703
+ if (!std::isfinite(groupId) || groupId <= 0 ||
704
+ std::floor(groupId) != groupId ||
705
+ (behavior != static_cast<double>(GroupCancelBehavior::Freeze) &&
706
+ behavior != static_cast<double>(GroupCancelBehavior::Finish))) {
707
+ return Array(rt, 0);
708
+ }
709
+ return snapshotsArrayV2(
710
+ rt,
711
+ cancelAnimationGroup(
712
+ static_cast<int32_t>(groupId),
713
+ static_cast<GroupCancelBehavior>(
714
+ static_cast<int32_t>(behavior))));
715
+ });
716
+
717
+ setHostFunction(
718
+ runtime, bindings, "setClipPresentation", 11,
170
719
  [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
171
720
  if (count < 9) return Value::undefined();
172
721
  const double driverId = args[0].asNumber();
173
722
  const Presentation presentation = presentationFromArgs(args, 1);
174
723
  const bool takeOwnership = boolArg(args, count, 8);
175
724
  const bool overridePendingAnimation = boolArg(args, count, 9);
725
+ // Optional trailing flag; absent means record (the pre-flag
726
+ // behavior), so only an explicit false skips the velocity sample.
727
+ const bool recordVelocity =
728
+ !(count > 10 && args[10].isBool() && !args[10].getBool());
176
729
  if (validDriverId(driverId) && finitePresentation(presentation)) {
177
730
  setPresentation(
178
731
  static_cast<uint64_t>(driverId), presentation, takeOwnership,
179
- overridePendingAnimation);
732
+ overridePendingAnimation, recordVelocity);
733
+ }
734
+ return Value::undefined();
735
+ });
736
+
737
+ setHostFunction(
738
+ runtime, bindings, "setClipPresentationV2", 16,
739
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
740
+ if (count < 14) return Value::undefined();
741
+ const double driverId = args[0].asNumber();
742
+ const Presentation presentation = presentationV2FromArgs(args, 1);
743
+ const bool takeOwnership = boolArg(args, count, 13);
744
+ const bool overridePendingAnimation = boolArg(args, count, 14);
745
+ const bool recordVelocity =
746
+ !(count > 15 && args[15].isBool() && !args[15].getBool());
747
+ if (validDriverId(driverId) && validCurve(args[9].asNumber()) &&
748
+ finiteV2Presentation(presentation)) {
749
+ setPresentation(
750
+ static_cast<uint64_t>(driverId), presentation, takeOwnership,
751
+ overridePendingAnimation, recordVelocity);
180
752
  }
181
753
  return Value::undefined();
182
754
  });
@@ -191,6 +763,28 @@ void installBindings(
191
763
  rt, beginInteraction(static_cast<uint64_t>(args[0].asNumber())));
192
764
  });
193
765
 
766
+ setHostFunction(
767
+ runtime, bindings, "beginInteractionV2", 1,
768
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
769
+ if (count < 1 || !validDriverId(args[0].asNumber())) {
770
+ return presentationArrayV2(rt, {{0, 0, 0, 0, 0}, 0, 0});
771
+ }
772
+ return presentationArrayV2(
773
+ rt, beginInteraction(static_cast<uint64_t>(args[0].asNumber())));
774
+ });
775
+
776
+ setHostFunction(
777
+ runtime, bindings, "snapshotCurrentV2", 1,
778
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
779
+ if (count < 1 || !validDriverId(args[0].asNumber())) {
780
+ return presentationArrayV2(rt, {{0, 0, 0, 0, 0}, 0, 0});
781
+ }
782
+ return presentationArrayV2(
783
+ rt,
784
+ snapshotCurrentAndroid(
785
+ static_cast<uint64_t>(args[0].asNumber())));
786
+ });
787
+
194
788
  setHostFunction(
195
789
  runtime, bindings, "animateTiming", 23,
196
790
  [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
@@ -211,6 +805,31 @@ void installBindings(
211
805
  static_cast<uint64_t>(driverId),
212
806
  {boolArg(args, count, 1), start, startStampArg(args, count, 22)},
213
807
  target,
808
+ animation,
809
+ AnimationValidationMode::LegacyV1));
810
+ });
811
+
812
+ setHostFunction(
813
+ runtime, bindings, "animateTimingV2", 33,
814
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
815
+ if (count < 32) return Value(0);
816
+ const double driverId = args[0].asNumber();
817
+ const Presentation start = presentationV2FromArgs(args, 2);
818
+ const Presentation target = presentationV2FromArgs(args, 14);
819
+ const TimingAnimation animation{
820
+ args[26].asNumber(), args[27].asNumber(), args[28].asNumber(),
821
+ args[29].asNumber(), args[30].asNumber(),
822
+ static_cast<int32_t>(args[31].asNumber())};
823
+ if (!validDriverId(driverId) || !validCurve(args[10].asNumber()) ||
824
+ !validCurve(args[22].asNumber()) ||
825
+ !finiteV2Presentation(start) || !finiteV2Presentation(target) ||
826
+ !std::isfinite(animation.durationMs)) {
827
+ return Value(0);
828
+ }
829
+ return Value(animateTiming(
830
+ static_cast<uint64_t>(driverId),
831
+ {boolArg(args, count, 1), start, startStampArg(args, count, 32)},
832
+ target,
214
833
  animation));
215
834
  });
216
835
 
@@ -229,15 +848,42 @@ void installBindings(
229
848
  !finitePresentation(target) || !std::isfinite(animation.mass) ||
230
849
  !std::isfinite(animation.stiffness) ||
231
850
  !std::isfinite(animation.damping) ||
232
- !std::isfinite(animation.initialVelocity) ||
233
- animation.mass <= 0 || animation.stiffness <= 0 ||
234
- animation.damping < 0) {
851
+ !std::isfinite(animation.initialVelocity)) {
235
852
  return Value(0);
236
853
  }
237
854
  return Value(animateSpring(
238
855
  static_cast<uint64_t>(driverId),
239
856
  {boolArg(args, count, 1), start, startStampArg(args, count, 22)},
240
857
  target,
858
+ animation,
859
+ AnimationValidationMode::LegacyV1));
860
+ });
861
+
862
+ setHostFunction(
863
+ runtime, bindings, "animateSpringV2", 33,
864
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
865
+ if (count < 32) return Value(0);
866
+ const double driverId = args[0].asNumber();
867
+ const Presentation start = presentationV2FromArgs(args, 2);
868
+ const Presentation target = presentationV2FromArgs(args, 14);
869
+ const SpringAnimation animation{
870
+ args[26].asNumber(), args[27].asNumber(), args[28].asNumber(),
871
+ args[29].asNumber(), boolArg(args, count, 30),
872
+ static_cast<int32_t>(args[31].asNumber())};
873
+ if (!validDriverId(driverId) || !validCurve(args[10].asNumber()) ||
874
+ !validCurve(args[22].asNumber()) ||
875
+ !finiteV2Presentation(start) || !finiteV2Presentation(target) ||
876
+ !std::isfinite(animation.mass) ||
877
+ !std::isfinite(animation.stiffness) ||
878
+ !std::isfinite(animation.damping) ||
879
+ !std::isfinite(animation.initialVelocity) || animation.mass <= 0 ||
880
+ animation.stiffness <= 0 || animation.damping < 0) {
881
+ return Value(0);
882
+ }
883
+ return Value(animateSpring(
884
+ static_cast<uint64_t>(driverId),
885
+ {boolArg(args, count, 1), start, startStampArg(args, count, 32)},
886
+ target,
241
887
  animation));
242
888
  });
243
889
 
@@ -289,6 +935,157 @@ void installBindings(
289
935
  target,
290
936
  durationMs,
291
937
  std::move(keyframes),
938
+ reduceMotion,
939
+ AnimationValidationMode::LegacyV1));
940
+ });
941
+
942
+ setHostFunction(
943
+ runtime, bindings, "animateKeyframesV2", 30,
944
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
945
+ if (count < 29 || !args[27].isObject() ||
946
+ !args[27].getObject(rt).isArray(rt)) {
947
+ return Value(0);
948
+ }
949
+ const double driverId = args[0].asNumber();
950
+ const Presentation start = presentationV2FromArgs(args, 2);
951
+ const Presentation target = presentationV2FromArgs(args, 14);
952
+ const double durationMs = args[26].asNumber();
953
+ const int32_t reduceMotion = static_cast<int32_t>(args[28].asNumber());
954
+ Array frames = args[27].getObject(rt).getArray(rt);
955
+ const size_t length = frames.size(rt);
956
+ if (!validDriverId(driverId) || !validCurve(args[10].asNumber()) ||
957
+ !validCurve(args[22].asNumber()) ||
958
+ !finiteV2Presentation(start) || !finiteV2Presentation(target) ||
959
+ !std::isfinite(durationMs) || length < 26 || length % 13 != 0) {
960
+ return Value(0);
961
+ }
962
+ std::vector<Keyframe> keyframes;
963
+ keyframes.reserve(length / 13);
964
+ double previousOffset = -1;
965
+ for (size_t index = 0; index < length; index += 13) {
966
+ const double offset = frames.getValueAtIndex(rt, index).asNumber();
967
+ const double curveCode =
968
+ frames.getValueAtIndex(rt, index + 9).asNumber();
969
+ const Presentation frame =
970
+ presentationV2FromArray(rt, frames, index + 1);
971
+ if (!std::isfinite(offset) || offset < 0 || offset > 1 ||
972
+ offset <= previousOffset || !validCurve(curveCode) ||
973
+ !finiteV2Presentation(frame)) {
974
+ return Value(0);
975
+ }
976
+ previousOffset = offset;
977
+ keyframes.push_back({offset, frame});
978
+ }
979
+ if (keyframes.front().offset != 0 || keyframes.back().offset != 1) {
980
+ return Value(0);
981
+ }
982
+ return Value(animateKeyframes(
983
+ static_cast<uint64_t>(driverId),
984
+ {boolArg(args, count, 1), start, startStampArg(args, count, 29)},
985
+ target,
986
+ durationMs,
987
+ std::move(keyframes),
988
+ reduceMotion));
989
+ });
990
+
991
+ setHostFunction(
992
+ runtime, bindings, "animateTimingFromV2", 32,
993
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
994
+ if (count < 31) return Value(0);
995
+ const double driverId = args[0].asNumber();
996
+ const Presentation start = presentationV2FromArgs(args, 1);
997
+ const Presentation target = presentationV2FromArgs(args, 13);
998
+ const TimingAnimation animation{
999
+ args[25].asNumber(), args[26].asNumber(), args[27].asNumber(),
1000
+ args[28].asNumber(), args[29].asNumber(),
1001
+ static_cast<int32_t>(args[30].asNumber())};
1002
+ if (!validDriverId(driverId) || !validCurve(args[9].asNumber()) ||
1003
+ !validCurve(args[21].asNumber()) ||
1004
+ !finiteV2Presentation(start) || !finiteV2Presentation(target) ||
1005
+ !std::isfinite(animation.durationMs)) {
1006
+ return Value(0);
1007
+ }
1008
+ return Value(animateTiming(
1009
+ static_cast<uint64_t>(driverId),
1010
+ {true, start, startStampArg(args, count, 31)},
1011
+ target,
1012
+ animation));
1013
+ });
1014
+
1015
+ setHostFunction(
1016
+ runtime, bindings, "animateSpringFromV2", 32,
1017
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
1018
+ if (count < 31) return Value(0);
1019
+ const double driverId = args[0].asNumber();
1020
+ const Presentation start = presentationV2FromArgs(args, 1);
1021
+ const Presentation target = presentationV2FromArgs(args, 13);
1022
+ const SpringAnimation animation{
1023
+ args[25].asNumber(), args[26].asNumber(), args[27].asNumber(),
1024
+ args[28].asNumber(), boolArg(args, count, 29),
1025
+ static_cast<int32_t>(args[30].asNumber())};
1026
+ if (!validDriverId(driverId) || !validCurve(args[9].asNumber()) ||
1027
+ !validCurve(args[21].asNumber()) ||
1028
+ !finiteV2Presentation(start) || !finiteV2Presentation(target) ||
1029
+ !std::isfinite(animation.mass) ||
1030
+ !std::isfinite(animation.stiffness) ||
1031
+ !std::isfinite(animation.damping) ||
1032
+ !std::isfinite(animation.initialVelocity) || animation.mass <= 0 ||
1033
+ animation.stiffness <= 0 || animation.damping < 0) {
1034
+ return Value(0);
1035
+ }
1036
+ return Value(animateSpring(
1037
+ static_cast<uint64_t>(driverId),
1038
+ {true, start, startStampArg(args, count, 31)},
1039
+ target,
1040
+ animation));
1041
+ });
1042
+
1043
+ setHostFunction(
1044
+ runtime, bindings, "animateKeyframesFromV2", 29,
1045
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
1046
+ if (count < 28 || !args[26].isObject() ||
1047
+ !args[26].getObject(rt).isArray(rt)) {
1048
+ return Value(0);
1049
+ }
1050
+ const double driverId = args[0].asNumber();
1051
+ const Presentation start = presentationV2FromArgs(args, 1);
1052
+ const Presentation target = presentationV2FromArgs(args, 13);
1053
+ const double durationMs = args[25].asNumber();
1054
+ const int32_t reduceMotion = static_cast<int32_t>(args[27].asNumber());
1055
+ Array frames = args[26].getObject(rt).getArray(rt);
1056
+ const size_t length = frames.size(rt);
1057
+ if (!validDriverId(driverId) || !validCurve(args[9].asNumber()) ||
1058
+ !validCurve(args[21].asNumber()) ||
1059
+ !finiteV2Presentation(start) || !finiteV2Presentation(target) ||
1060
+ !std::isfinite(durationMs) || length < 26 || length % 13 != 0) {
1061
+ return Value(0);
1062
+ }
1063
+ std::vector<Keyframe> keyframes;
1064
+ keyframes.reserve(length / 13);
1065
+ double previousOffset = -1;
1066
+ for (size_t index = 0; index < length; index += 13) {
1067
+ const double offset = frames.getValueAtIndex(rt, index).asNumber();
1068
+ const double frameCurve =
1069
+ frames.getValueAtIndex(rt, index + 9).asNumber();
1070
+ const Presentation frame =
1071
+ presentationV2FromArray(rt, frames, index + 1);
1072
+ if (!std::isfinite(offset) || offset < 0 || offset > 1 ||
1073
+ offset <= previousOffset || !validCurve(frameCurve) ||
1074
+ !finiteV2Presentation(frame)) {
1075
+ return Value(0);
1076
+ }
1077
+ previousOffset = offset;
1078
+ keyframes.push_back({offset, frame});
1079
+ }
1080
+ if (keyframes.front().offset != 0 || keyframes.back().offset != 1) {
1081
+ return Value(0);
1082
+ }
1083
+ return Value(animateKeyframes(
1084
+ static_cast<uint64_t>(driverId),
1085
+ {true, start, startStampArg(args, count, 28)},
1086
+ target,
1087
+ durationMs,
1088
+ std::move(keyframes),
292
1089
  reduceMotion));
293
1090
  });
294
1091
 
@@ -313,6 +1110,21 @@ void installBindings(
313
1110
  return cancelResultArray(rt, result.handled, result.presentation);
314
1111
  });
315
1112
 
1113
+ setHostFunction(
1114
+ runtime, bindings, "cancelAnimationV2", 3,
1115
+ [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
1116
+ if (count < 3 || !validDriverId(args[0].asNumber())) {
1117
+ return cancelResultArrayV2(
1118
+ rt, false, {{0, 0, 0, 0, 0}, 0, 0});
1119
+ }
1120
+ const CancelResult result = cancelAnimation(
1121
+ static_cast<uint64_t>(args[0].asNumber()),
1122
+ static_cast<int32_t>(args[1].asNumber()),
1123
+ static_cast<int32_t>(args[2].asNumber()) == 1);
1124
+ return cancelResultArrayV2(
1125
+ rt, result.handled, result.presentation);
1126
+ });
1127
+
316
1128
  setHostFunction(
317
1129
  runtime, bindings, "destroyDriver", 1,
318
1130
  [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
@@ -345,6 +1157,29 @@ void installBindings(
345
1157
  return subscription;
346
1158
  });
347
1159
 
1160
+ setHostFunction(
1161
+ runtime, bindings, "onClipGroupAnimationComplete", 1,
1162
+ [state](Runtime &rt, const Value &, const Value *args, size_t count)
1163
+ -> Value {
1164
+ if (count < 1 || !args[0].isObject() ||
1165
+ !args[0].getObject(rt).isFunction(rt)) {
1166
+ return Value::undefined();
1167
+ }
1168
+ auto listener = std::make_shared<Function>(
1169
+ args[0].getObject(rt).getFunction(rt));
1170
+ const int32_t listenerId = ++state->nextListenerId;
1171
+ state->groupListeners.emplace(listenerId, std::move(listener));
1172
+ Object subscription(rt);
1173
+ setHostFunction(
1174
+ rt, subscription, "remove", 0,
1175
+ [state, listenerId](
1176
+ Runtime &, const Value &, const Value *, size_t) -> Value {
1177
+ state->groupListeners.erase(listenerId);
1178
+ return Value::undefined();
1179
+ });
1180
+ return subscription;
1181
+ });
1182
+
348
1183
  runtime.global().setProperty(runtime, "__SmoothClipView", bindings);
349
1184
  }
350
1185
 
@@ -355,7 +1190,9 @@ void invalidateBindings() {
355
1190
  // own runtime.
356
1191
  auto state = bindingsState();
357
1192
  clearCompletionCallback(state.get());
1193
+ clearGroupCompletionCallback(state.get());
358
1194
  state->listeners.clear();
1195
+ state->groupListeners.clear();
359
1196
  state->runtime = nullptr;
360
1197
  state->callInvoker = nullptr;
361
1198
  }
@@ -403,6 +1240,58 @@ void nativeRegisterView(
403
1240
  lifecycleVisible != 0);
404
1241
  }
405
1242
 
1243
+ void nativeRegisterViewV2(
1244
+ jni::alias_ref<jni::JObject>,
1245
+ jdouble driverId,
1246
+ jni::alias_ref<smoothclip::JSmoothClipView> view,
1247
+ jdouble x,
1248
+ jdouble y,
1249
+ jdouble width,
1250
+ jdouble height,
1251
+ jdouble topLeftRadius,
1252
+ jdouble topRightRadius,
1253
+ jdouble bottomRightRadius,
1254
+ jdouble bottomLeftRadius,
1255
+ jint curveCode,
1256
+ jdouble contentTranslateX,
1257
+ jdouble contentTranslateY,
1258
+ jdouble contentScale,
1259
+ jdouble density,
1260
+ jdouble widthPx,
1261
+ jdouble heightPx,
1262
+ jboolean lifecycleVisible) {
1263
+ if (driverId <= 0 || !std::isfinite(driverId) || !std::isfinite(x) ||
1264
+ !std::isfinite(y) || !std::isfinite(width) || !std::isfinite(height) ||
1265
+ !std::isfinite(topLeftRadius) || !std::isfinite(topRightRadius) ||
1266
+ !std::isfinite(bottomRightRadius) || !std::isfinite(bottomLeftRadius) ||
1267
+ !std::isfinite(contentTranslateX) ||
1268
+ !std::isfinite(contentTranslateY) || !std::isfinite(contentScale) ||
1269
+ contentScale <= 0 ||
1270
+ (curveCode != static_cast<jint>(smoothclip::ClipCurve::Circular) &&
1271
+ curveCode != static_cast<jint>(smoothclip::ClipCurve::Continuous))) {
1272
+ return;
1273
+ }
1274
+ smoothclip::Geometry geometry{x, y, width, height, 0.0};
1275
+ geometry.topLeftRadius = topLeftRadius;
1276
+ geometry.topRightRadius = topRightRadius;
1277
+ geometry.bottomRightRadius = bottomRightRadius;
1278
+ geometry.bottomLeftRadius = bottomLeftRadius;
1279
+ geometry.curve = static_cast<smoothclip::ClipCurve>(curveCode);
1280
+ if (topLeftRadius == topRightRadius && topLeftRadius == bottomRightRadius &&
1281
+ topLeftRadius == bottomLeftRadius) {
1282
+ geometry.radius = topLeftRadius;
1283
+ }
1284
+ smoothclip::registerViewAndroid(
1285
+ static_cast<uint64_t>(driverId),
1286
+ view,
1287
+ smoothclip::Presentation{
1288
+ geometry, contentTranslateX, contentTranslateY, contentScale},
1289
+ density,
1290
+ widthPx,
1291
+ heightPx,
1292
+ lifecycleVisible != 0);
1293
+ }
1294
+
406
1295
  void nativeSetViewHostGeometry(
407
1296
  jni::alias_ref<jni::JObject>,
408
1297
  jdouble driverId,
@@ -450,6 +1339,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
450
1339
  {
451
1340
  makeNativeMethod("getBindingsInstaller", getBindingsInstaller),
452
1341
  makeNativeMethod("nativeRegisterView", nativeRegisterView),
1342
+ makeNativeMethod("nativeRegisterViewV2", nativeRegisterViewV2),
453
1343
  makeNativeMethod(
454
1344
  "nativeSetViewHostGeometry", nativeSetViewHostGeometry),
455
1345
  makeNativeMethod("nativeUnregisterView", nativeUnregisterView),