react-native-smooth-clip-view 0.2.9 → 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 (130) hide show
  1. package/README.md +60 -21
  2. package/android/build.gradle +6 -2
  3. package/android/proguard-rules.pro +3 -3
  4. package/android/src/main/cpp/SmoothClipAndroid.h +10 -0
  5. package/android/src/main/cpp/SmoothClipBindings.cpp +890 -4
  6. package/android/src/main/cpp/SmoothClipRegistry.cpp +1056 -44
  7. package/android/src/main/java/com/smoothclipview/ClipGeometryNormalizer.kt +265 -0
  8. package/android/src/main/java/com/smoothclipview/SmoothClipBindings.kt +21 -0
  9. package/android/src/main/java/com/smoothclipview/SmoothClipModule.kt +283 -0
  10. package/android/src/main/java/com/smoothclipview/SmoothClipView.kt +371 -18
  11. package/android/src/main/java/com/smoothclipview/SmoothClipViewManager.kt +138 -24
  12. package/cpp/SmoothClipAnimationCurve.h +151 -122
  13. package/cpp/SmoothClipRegistry.h +95 -3
  14. package/cpp/SmoothClipSharedGeometry.h +146 -7
  15. package/cpp/SmoothClipVelocityTracker.h +13 -9
  16. package/ios/SmoothClipGeometry.h +184 -6
  17. package/ios/SmoothClipRegistry.mm +1028 -22
  18. package/ios/SmoothClipRegistrySnapshot.h +13 -0
  19. package/ios/SmoothClipTurboModule.cpp +857 -2
  20. package/ios/SmoothClipTurboModule.h +310 -0
  21. package/ios/SmoothClipView.mm +714 -183
  22. package/ios/SmoothClipViewRegistry.h +1 -0
  23. package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  24. package/ios/reactnativesmoothclipview.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  25. package/lib/module/NativeSmoothClipModule.js.map +1 -1
  26. package/lib/module/SmoothClipView.ios.js +22 -6
  27. package/lib/module/SmoothClipView.ios.js.map +1 -1
  28. package/lib/module/SmoothClipView.js +1 -46
  29. package/lib/module/SmoothClipView.js.map +1 -1
  30. package/lib/module/SmoothClipViewNativeComponent.ts +27 -1
  31. package/lib/module/capabilities.js +4 -0
  32. package/lib/module/capabilities.js.map +1 -0
  33. package/lib/module/capabilities.native.js +36 -0
  34. package/lib/module/capabilities.native.js.map +1 -0
  35. package/lib/module/capabilityTypes.js +11 -0
  36. package/lib/module/capabilityTypes.js.map +1 -0
  37. package/lib/module/driverState.js +0 -28
  38. package/lib/module/driverState.js.map +1 -1
  39. package/lib/module/drivers.js +1 -281
  40. package/lib/module/drivers.js.map +1 -1
  41. package/lib/module/drivers.native.js +263 -63
  42. package/lib/module/drivers.native.js.map +1 -1
  43. package/lib/module/geometry.js +147 -17
  44. package/lib/module/geometry.js.map +1 -1
  45. package/lib/module/groupDriverTypes.js +4 -0
  46. package/lib/module/groupDriverTypes.js.map +1 -0
  47. package/lib/module/groupDrivers.android.js +4 -0
  48. package/lib/module/groupDrivers.android.js.map +1 -0
  49. package/lib/module/groupDrivers.ios.js +4 -0
  50. package/lib/module/groupDrivers.ios.js.map +1 -0
  51. package/lib/module/groupDrivers.js +4 -0
  52. package/lib/module/groupDrivers.js.map +1 -0
  53. package/lib/module/groupDrivers.native.js +799 -0
  54. package/lib/module/groupDrivers.native.js.map +1 -0
  55. package/lib/module/index.js +3 -2
  56. package/lib/module/index.js.map +1 -1
  57. package/lib/module/presentationProtocol.js +15 -0
  58. package/lib/module/presentationProtocol.js.map +1 -0
  59. package/lib/typescript/src/NativeSmoothClipModule.d.ts +25 -0
  60. package/lib/typescript/src/NativeSmoothClipModule.d.ts.map +1 -1
  61. package/lib/typescript/src/SmoothClipView.d.ts +2 -10
  62. package/lib/typescript/src/SmoothClipView.d.ts.map +1 -1
  63. package/lib/typescript/src/SmoothClipView.ios.d.ts +133 -2
  64. package/lib/typescript/src/SmoothClipView.ios.d.ts.map +1 -1
  65. package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts +8 -0
  66. package/lib/typescript/src/SmoothClipViewNativeComponent.d.ts.map +1 -1
  67. package/lib/typescript/src/capabilities.d.ts +3 -0
  68. package/lib/typescript/src/capabilities.d.ts.map +1 -0
  69. package/lib/typescript/src/capabilities.native.d.ts +3 -0
  70. package/lib/typescript/src/capabilities.native.d.ts.map +1 -0
  71. package/lib/typescript/src/capabilityTypes.d.ts +10 -0
  72. package/lib/typescript/src/capabilityTypes.d.ts.map +1 -0
  73. package/lib/typescript/src/driverState.d.ts +1 -6
  74. package/lib/typescript/src/driverState.d.ts.map +1 -1
  75. package/lib/typescript/src/driverTypes.d.ts +25 -11
  76. package/lib/typescript/src/driverTypes.d.ts.map +1 -1
  77. package/lib/typescript/src/drivers.d.ts +2 -4
  78. package/lib/typescript/src/drivers.d.ts.map +1 -1
  79. package/lib/typescript/src/drivers.native.d.ts.map +1 -1
  80. package/lib/typescript/src/geometry.d.ts +36 -2
  81. package/lib/typescript/src/geometry.d.ts.map +1 -1
  82. package/lib/typescript/src/groupDriverTypes.d.ts +66 -0
  83. package/lib/typescript/src/groupDriverTypes.d.ts.map +1 -0
  84. package/lib/typescript/src/groupDrivers.android.d.ts +2 -0
  85. package/lib/typescript/src/groupDrivers.android.d.ts.map +1 -0
  86. package/lib/typescript/src/groupDrivers.d.ts +2 -0
  87. package/lib/typescript/src/groupDrivers.d.ts.map +1 -0
  88. package/lib/typescript/src/groupDrivers.ios.d.ts +2 -0
  89. package/lib/typescript/src/groupDrivers.ios.d.ts.map +1 -0
  90. package/lib/typescript/src/groupDrivers.native.d.ts +4 -0
  91. package/lib/typescript/src/groupDrivers.native.d.ts.map +1 -0
  92. package/lib/typescript/src/index.d.ts +6 -3
  93. package/lib/typescript/src/index.d.ts.map +1 -1
  94. package/lib/typescript/src/presentationProtocol.d.ts +4 -0
  95. package/lib/typescript/src/presentationProtocol.d.ts.map +1 -0
  96. package/package.json +10 -11
  97. package/src/NativeSmoothClipModule.ts +275 -0
  98. package/src/SmoothClipView.ios.tsx +30 -11
  99. package/src/SmoothClipView.tsx +2 -76
  100. package/src/SmoothClipViewNativeComponent.ts +27 -1
  101. package/src/capabilities.native.ts +69 -0
  102. package/src/capabilities.ts +2 -0
  103. package/src/capabilityTypes.ts +17 -0
  104. package/src/driverState.ts +1 -41
  105. package/src/driverTypes.ts +42 -11
  106. package/src/drivers.native.ts +824 -124
  107. package/src/drivers.ts +2 -423
  108. package/src/geometry.ts +231 -27
  109. package/src/groupDriverTypes.ts +117 -0
  110. package/src/groupDrivers.android.ts +1 -0
  111. package/src/groupDrivers.ios.ts +1 -0
  112. package/src/groupDrivers.native.ts +1189 -0
  113. package/src/groupDrivers.ts +1 -0
  114. package/src/index.ts +30 -2
  115. package/src/presentationProtocol.ts +28 -0
  116. package/lib/module/SmoothClipView.web.js +0 -54
  117. package/lib/module/SmoothClipView.web.js.map +0 -1
  118. package/lib/module/index.web.js +0 -8
  119. package/lib/module/index.web.js.map +0 -1
  120. package/lib/module/webClipPath.js +0 -25
  121. package/lib/module/webClipPath.js.map +0 -1
  122. package/lib/typescript/src/SmoothClipView.web.d.ts +0 -10
  123. package/lib/typescript/src/SmoothClipView.web.d.ts.map +0 -1
  124. package/lib/typescript/src/index.web.d.ts +0 -9
  125. package/lib/typescript/src/index.web.d.ts.map +0 -1
  126. package/lib/typescript/src/webClipPath.d.ts +0 -7
  127. package/lib/typescript/src/webClipPath.d.ts.map +0 -1
  128. package/src/SmoothClipView.web.tsx +0 -66
  129. package/src/index.web.ts +0 -23
  130. 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,8 +487,233 @@ 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
 
528
+ setHostFunction(
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
+
168
717
  setHostFunction(
169
718
  runtime, bindings, "setClipPresentation", 11,
170
719
  [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
@@ -185,6 +734,25 @@ void installBindings(
185
734
  return Value::undefined();
186
735
  });
187
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);
752
+ }
753
+ return Value::undefined();
754
+ });
755
+
188
756
  setHostFunction(
189
757
  runtime, bindings, "beginInteraction", 1,
190
758
  [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
@@ -195,6 +763,28 @@ void installBindings(
195
763
  rt, beginInteraction(static_cast<uint64_t>(args[0].asNumber())));
196
764
  });
197
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
+
198
788
  setHostFunction(
199
789
  runtime, bindings, "animateTiming", 23,
200
790
  [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
@@ -215,6 +805,31 @@ void installBindings(
215
805
  static_cast<uint64_t>(driverId),
216
806
  {boolArg(args, count, 1), start, startStampArg(args, count, 22)},
217
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,
218
833
  animation));
219
834
  });
220
835
 
@@ -233,15 +848,42 @@ void installBindings(
233
848
  !finitePresentation(target) || !std::isfinite(animation.mass) ||
234
849
  !std::isfinite(animation.stiffness) ||
235
850
  !std::isfinite(animation.damping) ||
236
- !std::isfinite(animation.initialVelocity) ||
237
- animation.mass <= 0 || animation.stiffness <= 0 ||
238
- animation.damping < 0) {
851
+ !std::isfinite(animation.initialVelocity)) {
239
852
  return Value(0);
240
853
  }
241
854
  return Value(animateSpring(
242
855
  static_cast<uint64_t>(driverId),
243
856
  {boolArg(args, count, 1), start, startStampArg(args, count, 22)},
244
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,
245
887
  animation));
246
888
  });
247
889
 
@@ -293,6 +935,157 @@ void installBindings(
293
935
  target,
294
936
  durationMs,
295
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),
296
1089
  reduceMotion));
297
1090
  });
298
1091
 
@@ -317,6 +1110,21 @@ void installBindings(
317
1110
  return cancelResultArray(rt, result.handled, result.presentation);
318
1111
  });
319
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
+
320
1128
  setHostFunction(
321
1129
  runtime, bindings, "destroyDriver", 1,
322
1130
  [](Runtime &rt, const Value &, const Value *args, size_t count) -> Value {
@@ -349,6 +1157,29 @@ void installBindings(
349
1157
  return subscription;
350
1158
  });
351
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
+
352
1183
  runtime.global().setProperty(runtime, "__SmoothClipView", bindings);
353
1184
  }
354
1185
 
@@ -359,7 +1190,9 @@ void invalidateBindings() {
359
1190
  // own runtime.
360
1191
  auto state = bindingsState();
361
1192
  clearCompletionCallback(state.get());
1193
+ clearGroupCompletionCallback(state.get());
362
1194
  state->listeners.clear();
1195
+ state->groupListeners.clear();
363
1196
  state->runtime = nullptr;
364
1197
  state->callInvoker = nullptr;
365
1198
  }
@@ -407,6 +1240,58 @@ void nativeRegisterView(
407
1240
  lifecycleVisible != 0);
408
1241
  }
409
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
+
410
1295
  void nativeSetViewHostGeometry(
411
1296
  jni::alias_ref<jni::JObject>,
412
1297
  jdouble driverId,
@@ -454,6 +1339,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
454
1339
  {
455
1340
  makeNativeMethod("getBindingsInstaller", getBindingsInstaller),
456
1341
  makeNativeMethod("nativeRegisterView", nativeRegisterView),
1342
+ makeNativeMethod("nativeRegisterViewV2", nativeRegisterViewV2),
457
1343
  makeNativeMethod(
458
1344
  "nativeSetViewHostGeometry", nativeSetViewHostGeometry),
459
1345
  makeNativeMethod("nativeUnregisterView", nativeUnregisterView),