@sency/react-native-smkit-ui 2.2.0 → 2.2.2

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 (30) hide show
  1. package/README.md +218 -65
  2. package/android/build.gradle +3 -1
  3. package/android/src/main/java/com/smkituilibrary/SmkitUiLibraryModule.kt +237 -3
  4. package/android/src/main/java/com/smkituilibrary/mapper/SMMapper.kt +70 -10
  5. package/android/src/main/java/com/smkituilibrary/model/SMKitExercise.kt +40 -0
  6. package/android/src/main/java/com/smkituilibrary/model/SMKitWorkout.kt +8 -0
  7. package/android/src/main/java/com/smkituilibrary/model/SMKitWorkoutConfig.kt +2 -0
  8. package/android/src/main/java/com/smkituilibrary/model/SMUserData.kt +5 -1
  9. package/ios/SMKitUIManager.mm +33 -11
  10. package/ios/SMKitUIManager.swift +243 -26
  11. package/lib/commonjs/SMWorkout.js +239 -177
  12. package/lib/commonjs/SMWorkout.js.map +1 -1
  13. package/lib/commonjs/index.js +187 -5
  14. package/lib/commonjs/index.js.map +1 -1
  15. package/lib/module/SMWorkout.js +232 -181
  16. package/lib/module/SMWorkout.js.map +1 -1
  17. package/lib/module/index.js +165 -5
  18. package/lib/module/index.js.map +1 -1
  19. package/package.json +2 -2
  20. package/react-native-smkit-ui.podspec +2 -2
  21. package/src/SMWorkout.tsx +353 -176
  22. package/src/index.tsx +167 -8
  23. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  24. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
  25. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +0 -1
  26. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +0 -2
  27. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +0 -2
  28. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +0 -2
  29. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +0 -1
  30. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +0 -1
@@ -1,5 +1,6 @@
1
1
  package com.smkituilibrary
2
2
 
3
+ import android.net.Uri
3
4
  import android.util.Log
4
5
  import com.facebook.react.bridge.Arguments
5
6
  import com.facebook.react.bridge.Promise
@@ -13,6 +14,8 @@ import com.facebook.react.modules.core.DeviceEventManagerModule
13
14
  import com.google.gson.Gson
14
15
  import com.google.gson.GsonBuilder
15
16
  import com.google.gson.Strictness
17
+ import com.sency.smbase.nativeclient.model.FormFeedbackType
18
+ import com.sency.smkit.PoseModelChoice
16
19
  import com.sency.smkitui.SMKitUI
17
20
  import com.sency.smkitui.listener.SMKitUIConfigurationListener
18
21
  import com.sency.smkitui.listener.SMKitUIWorkoutListener
@@ -70,6 +73,24 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
70
73
  private val moshi: Moshi by lazy { moshi() }
71
74
  private val gson: Gson by lazy { gson() }
72
75
  private var compactJson = false
76
+ private var pendingInstructionVideoConfig = InstructionVideoConfig()
77
+ private var pendingPhoneMovementCountPreventionEnabled = false
78
+ private var pendingStartTimerOnFirstActivity = true
79
+ private var pendingWorkoutContinuationTimerSeconds = 10
80
+ private var pendingPlayPhoneCalibrationAudio = false
81
+ private var pendingPlayBodyCalibrationAudio = false
82
+ private var pendingAllowAudioMixing = false
83
+ private var pendingShowExternalAudioControl = false
84
+ private var pendingEnableButtonTutorial = false
85
+ private var pendingButtonTutorialCompletionAudioUri: Uri? = null
86
+ private var pendingUseDefaultGuidanceMode = false
87
+ private var pendingGuidanceDebugLogging = false
88
+ private var pendingVariationMismatchFeedbackEnabled = false
89
+ private var pendingIntelligenceRestEnabled = false
90
+ private var pendingColorTheme = UIColorTheme.GREEN
91
+ private var pendingPoseModelChoice = PoseModelChoice.AdaptiveChoice
92
+ private var pendingConfigString: String? = null
93
+ private var pendingFeedbacksUIToExclude: Set<FormFeedbackType>? = null
73
94
 
74
95
  override fun getName(): String = "SMKitUIManager"
75
96
 
@@ -147,6 +168,26 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
147
168
  }
148
169
  }
149
170
 
171
+ private fun applyPendingRuntimeSettings() {
172
+ smKitUI?.setInstructionVideoConfig(pendingInstructionVideoConfig)
173
+ smKitUI?.setPhoneMovementCountPreventionEnabled(pendingPhoneMovementCountPreventionEnabled)
174
+ smKitUI?.setStartTimerOnFirstActivity(pendingStartTimerOnFirstActivity)
175
+ smKitUI?.setWorkoutContinuationTimerDuration(pendingWorkoutContinuationTimerSeconds)
176
+ smKitUI?.setPlayPhoneCalibrationAudio(pendingPlayPhoneCalibrationAudio)
177
+ smKitUI?.setPlayBodyCalibrationAudio(pendingPlayBodyCalibrationAudio)
178
+ smKitUI?.setAllowAudioMixing(pendingAllowAudioMixing)
179
+ smKitUI?.setShowExternalAudioControl(pendingShowExternalAudioControl)
180
+ smKitUI?.setEnableButtonTutorial(pendingEnableButtonTutorial)
181
+ smKitUI?.setButtonTutorialCompletionAudioUri(pendingButtonTutorialCompletionAudioUri)
182
+ smKitUI?.setUseDefaultGuidanceMode(pendingUseDefaultGuidanceMode)
183
+ smKitUI?.setGuidanceDebugLogging(pendingGuidanceDebugLogging)
184
+ smKitUI?.setVariationMismatchFeedbackEnabled(pendingVariationMismatchFeedbackEnabled)
185
+ smKitUI?.setIntelligenceRestEnabled(pendingIntelligenceRestEnabled)
186
+ smKitUI?.setColorTheme(pendingColorTheme)
187
+ smKitUI?.setConfigString(pendingConfigString)
188
+ pendingFeedbacksUIToExclude?.let { smKitUI?.setFeedbacksUIToExclude(it) }
189
+ }
190
+
150
191
  @ReactMethod
151
192
  fun configure(key: String, promise: Promise) {
152
193
  // If already configured, reuse the existing instance.
@@ -161,8 +202,24 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
161
202
  }
162
203
  smKitUI = SMKitUI.Configuration(reactApplicationContext)
163
204
  .setUIKey(key)
205
+ .setInstructionVideoConfig(pendingInstructionVideoConfig)
206
+ .setPhoneMovementCountPreventionEnabled(pendingPhoneMovementCountPreventionEnabled)
207
+ .setStartTimerOnFirstActivity(pendingStartTimerOnFirstActivity)
208
+ .setWorkoutContinuationTimerDuration(pendingWorkoutContinuationTimerSeconds)
209
+ .setPlayPhoneCalibrationAudio(pendingPlayPhoneCalibrationAudio)
210
+ .setPlayBodyCalibrationAudio(pendingPlayBodyCalibrationAudio)
211
+ .setAllowAudioMixing(pendingAllowAudioMixing)
212
+ .setShowExternalAudioControl(pendingShowExternalAudioControl)
213
+ .setEnableButtonTutorial(pendingEnableButtonTutorial)
214
+ .setButtonTutorialCompletionAudioUri(pendingButtonTutorialCompletionAudioUri)
215
+ .setUseDefaultGuidanceMode(pendingUseDefaultGuidanceMode)
216
+ .setGuidanceDebugLogging(pendingGuidanceDebugLogging)
217
+ .setVariationMismatchFeedbackEnabled(pendingVariationMismatchFeedbackEnabled)
218
+ .setColorTheme(pendingColorTheme)
219
+ .setPoseModelChoice(pendingPoseModelChoice)
164
220
  .configure(object : SMKitUIConfigurationListener {
165
221
  override fun onSuccess() {
222
+ applyPendingRuntimeSettings()
166
223
  promise.resolve("")
167
224
  }
168
225
 
@@ -234,6 +291,7 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
234
291
  val mediumSizeCycles = (configMap["mediumSizeCycles"] as? Number)?.toInt() ?: 2
235
292
 
236
293
  val config = InstructionVideoConfig(displayMode = displayMode, mediumSizeCycles = mediumSizeCycles)
294
+ pendingInstructionVideoConfig = config
237
295
  smKitUI?.setInstructionVideoConfig(config)
238
296
  promise.resolve("")
239
297
  } catch (e: Exception) {
@@ -245,6 +303,7 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
245
303
  @ReactMethod
246
304
  fun setIntelligenceRestEnabled(enabled: Boolean, promise: Promise) {
247
305
  try {
306
+ pendingIntelligenceRestEnabled = enabled
248
307
  smKitUI?.setIntelligenceRestEnabled(enabled)
249
308
  promise.resolve(null)
250
309
  } catch (e: Exception) {
@@ -320,11 +379,175 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
320
379
  }
321
380
  }
322
381
 
323
- // iOS-only stubs — no-op on Android, resolve immediately for API parity
324
- @ReactMethod fun setAllowAudioMixing(enabled: Boolean, promise: Promise) { promise.resolve(null) }
325
- @ReactMethod fun setShowExternalAudioControl(enabled: Boolean, promise: Promise) { promise.resolve(null) }
382
+ @ReactMethod
383
+ fun setAllowAudioMixing(enabled: Boolean, promise: Promise) {
384
+ pendingAllowAudioMixing = enabled
385
+ smKitUI?.setAllowAudioMixing(enabled)
386
+ promise.resolve(null)
387
+ }
388
+
389
+ @ReactMethod
390
+ fun setShowExternalAudioControl(enabled: Boolean, promise: Promise) {
391
+ pendingShowExternalAudioControl = enabled
392
+ smKitUI?.setShowExternalAudioControl(enabled)
393
+ promise.resolve(null)
394
+ }
395
+
396
+ // iOS-only. Resolve immediately for API parity.
326
397
  @ReactMethod fun setAccuratePoseEstimation(enabled: Boolean, promise: Promise) { promise.resolve(null) }
327
398
 
399
+ @ReactMethod
400
+ fun setColorTheme(theme: String, promise: Promise) {
401
+ val colorTheme = mapColorToTheme(theme)
402
+ pendingColorTheme = colorTheme
403
+ smKitUI?.setColorTheme(colorTheme)
404
+ promise.resolve(null)
405
+ }
406
+
407
+ @ReactMethod
408
+ fun setPlayPhoneCalibrationAudio(enabled: Boolean, promise: Promise) {
409
+ pendingPlayPhoneCalibrationAudio = enabled
410
+ smKitUI?.setPlayPhoneCalibrationAudio(enabled)
411
+ promise.resolve(null)
412
+ }
413
+
414
+ @ReactMethod
415
+ fun setPlayBodyCalibrationAudio(enabled: Boolean, promise: Promise) {
416
+ pendingPlayBodyCalibrationAudio = enabled
417
+ smKitUI?.setPlayBodyCalibrationAudio(enabled)
418
+ promise.resolve(null)
419
+ }
420
+
421
+ @ReactMethod
422
+ fun setStartTimerOnFirstActivity(enabled: Boolean, promise: Promise) {
423
+ pendingStartTimerOnFirstActivity = enabled
424
+ smKitUI?.setStartTimerOnFirstActivity(enabled)
425
+ promise.resolve(null)
426
+ }
427
+
428
+ @ReactMethod
429
+ fun setWorkoutContinuationTimerDuration(seconds: Int, promise: Promise) {
430
+ pendingWorkoutContinuationTimerSeconds = seconds.coerceAtLeast(1)
431
+ smKitUI?.setWorkoutContinuationTimerDuration(pendingWorkoutContinuationTimerSeconds)
432
+ promise.resolve(null)
433
+ }
434
+
435
+ @ReactMethod
436
+ fun setPhoneMovementCountPreventionEnabled(enabled: Boolean, promise: Promise) {
437
+ pendingPhoneMovementCountPreventionEnabled = enabled
438
+ smKitUI?.setPhoneMovementCountPreventionEnabled(enabled)
439
+ promise.resolve(null)
440
+ }
441
+
442
+ @ReactMethod
443
+ fun setVariationMismatchFeedbackEnabled(enabled: Boolean, promise: Promise) {
444
+ pendingVariationMismatchFeedbackEnabled = enabled
445
+ smKitUI?.setVariationMismatchFeedbackEnabled(enabled)
446
+ promise.resolve(null)
447
+ }
448
+
449
+ @ReactMethod
450
+ fun setUseDefaultGuidanceMode(enabled: Boolean, promise: Promise) {
451
+ pendingUseDefaultGuidanceMode = enabled
452
+ smKitUI?.setUseDefaultGuidanceMode(enabled)
453
+ promise.resolve(null)
454
+ }
455
+
456
+ @ReactMethod
457
+ fun setGuidanceDebugLogging(enabled: Boolean, promise: Promise) {
458
+ pendingGuidanceDebugLogging = enabled
459
+ smKitUI?.setGuidanceDebugLogging(enabled)
460
+ promise.resolve(null)
461
+ }
462
+
463
+ @ReactMethod
464
+ fun setEnableButtonTutorial(enabled: Boolean, promise: Promise) {
465
+ pendingEnableButtonTutorial = enabled
466
+ smKitUI?.setEnableButtonTutorial(enabled)
467
+ promise.resolve(null)
468
+ }
469
+
470
+ @ReactMethod
471
+ fun setButtonTutorialCompletionAudioUri(uri: String?, promise: Promise) {
472
+ pendingButtonTutorialCompletionAudioUri = uri
473
+ ?.takeIf { it.isNotBlank() }
474
+ ?.let(Uri::parse)
475
+ smKitUI?.setButtonTutorialCompletionAudioUri(pendingButtonTutorialCompletionAudioUri)
476
+ promise.resolve(null)
477
+ }
478
+
479
+ // iOS-only rowing calibration switch. Resolve immediately for API parity.
480
+ @ReactMethod
481
+ fun setShowRowingPhoneCalibration(enabled: Boolean, promise: Promise) {
482
+ promise.resolve(null)
483
+ }
484
+
485
+ @ReactMethod
486
+ fun setFeedbacksUIToExclude(feedbacksJson: String, promise: Promise) {
487
+ try {
488
+ val feedbacks = feedbackTypesFromJson(feedbacksJson)
489
+ pendingFeedbacksUIToExclude = feedbacks
490
+ smKitUI?.setFeedbacksUIToExclude(feedbacks)
491
+ promise.resolve(null)
492
+ } catch (e: Exception) {
493
+ promise.reject("setFeedbacksUIToExclude Failed", e.message, e)
494
+ }
495
+ }
496
+
497
+ // Android SMKitUI currently exposes UI-only feedback exclusion.
498
+ @ReactMethod
499
+ fun setExcludedFeedbacks(feedbacksJson: String, promise: Promise) {
500
+ try {
501
+ feedbackTypesFromJson(feedbacksJson)
502
+ promise.resolve(null)
503
+ } catch (e: Exception) {
504
+ promise.reject("setExcludedFeedbacks Failed", e.message, e)
505
+ }
506
+ }
507
+
508
+ @ReactMethod
509
+ fun setConfigString(configString: String?, promise: Promise) {
510
+ pendingConfigString = configString
511
+ smKitUI?.setConfigString(configString)
512
+ promise.resolve(null)
513
+ }
514
+
515
+ @ReactMethod
516
+ fun clearAdaptiveRomCache(promise: Promise) {
517
+ smKitUI?.clearAdaptiveRomCache()
518
+ promise.resolve(null)
519
+ }
520
+
521
+ @ReactMethod
522
+ fun quitWorkout(promise: Promise) {
523
+ smKitUI?.quitWorkout()
524
+ promise.resolve(null)
525
+ }
526
+
527
+ // iOS-only active-session controls. Resolve immediately for API parity.
528
+ @ReactMethod fun pauseSDK(promise: Promise) { promise.resolve(null) }
529
+ @ReactMethod fun resumeSDK(promise: Promise) { promise.resolve(null) }
530
+
531
+ @ReactMethod
532
+ fun getSupportedMovements(promise: Promise) {
533
+ promise.resolve(Arguments.createArray())
534
+ }
535
+
536
+ @ReactMethod
537
+ fun getExerciseType(detector: String, promise: Promise) {
538
+ promise.resolve("")
539
+ }
540
+
541
+ @ReactMethod
542
+ fun setPoseModelChoice(choice: String, promise: Promise) {
543
+ try {
544
+ pendingPoseModelChoice = PoseModelChoice.valueOf(choice)
545
+ promise.resolve(null)
546
+ } catch (e: Exception) {
547
+ promise.reject("setPoseModelChoice Failed", "Invalid pose model choice: $choice", e)
548
+ }
549
+ }
550
+
328
551
  private fun skeletonPresetFromString(s: String): SkeletonPreset? = when (s) {
329
552
  "defaultPreset" -> SkeletonPreset.DEFAULT
330
553
  "minimalDots" -> SkeletonPreset.MINIMAL_DOTS
@@ -428,6 +651,17 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
428
651
  }
429
652
  }
430
653
 
654
+ private fun feedbackTypesFromJson(feedbacksJson: String): Set<FormFeedbackType> {
655
+ val arr = org.json.JSONArray(feedbacksJson)
656
+ return (0 until arr.length())
657
+ .map { index ->
658
+ val name = arr.getString(index)
659
+ FormFeedbackType.fromString(name)
660
+ ?: throw IllegalArgumentException("Invalid feedback type: $name")
661
+ }
662
+ .toSet()
663
+ }
664
+
431
665
  private fun processModifications(modificationsJson: String?): Pair<String?, Boolean> {
432
666
  if (modificationsJson.isNullOrEmpty()) {
433
667
  return Pair(null, true) // Default: show phone calibration
@@ -2,8 +2,11 @@ package com.smkituilibrary.mapper
2
2
 
3
3
  import com.sency.smkitui.data.entity.ScoringParams
4
4
  import com.sency.smkitui.model.ExerciseData
5
+ import com.sency.smkitui.model.GuidanceVideoSegment
5
6
  import com.sency.smkitui.model.SMExercise
7
+ import com.sency.smkitui.model.SMStretchSetConfig
6
8
  import com.sency.smkitui.model.SMWorkout
9
+ import com.sency.smkitui.model.SMWorkoutContinuation
7
10
  import com.sency.smkitui.model.WorkoutSummaryData
8
11
  import com.sency.smkitui.model.workoutConfig.BodyZone
9
12
  import com.sency.smkitui.model.workoutConfig.DifficultyLevel
@@ -11,9 +14,12 @@ import com.sency.smkitui.model.workoutConfig.SMLanguage
11
14
  import com.sency.smkitui.model.workoutConfig.WorkoutConfig
12
15
  import com.sency.smkitui.model.workoutConfig.WorkoutDuration
13
16
  import com.smkituilibrary.model.SMKitExercise
17
+ import com.smkituilibrary.model.SMKitGuidanceVideoSegment
14
18
  import com.smkituilibrary.model.SMKitScoringParams
19
+ import com.smkituilibrary.model.SMKitStretchSetConfig
15
20
  import com.smkituilibrary.model.SMKitWorkout
16
21
  import com.smkituilibrary.model.SMKitWorkoutConfig
22
+ import com.smkituilibrary.model.SMKitWorkoutContinuation
17
23
  import com.smkituilibrary.model.WFPExerciseData
18
24
  import com.smkituilibrary.model.WFPSummary
19
25
 
@@ -27,6 +33,7 @@ internal fun SMKitWorkoutConfig.toWorkoutConfig(): WorkoutConfig = WorkoutConfig
27
33
  "he" -> SMLanguage.Hebrew
28
34
  else -> SMLanguage.English
29
35
  } ,
36
+ shortIntro = shortIntro,
30
37
  )
31
38
 
32
39
  internal fun SMKitWorkout.toSMWorkout(): SMWorkout = SMWorkout(
@@ -37,7 +44,8 @@ internal fun SMKitWorkout.toSMWorkout(): SMWorkout = SMWorkout(
37
44
  exercises = exercises.map(SMKitExercise::toSMExercise),
38
45
  workoutClosure = workoutClosure ?: "",
39
46
  getInFrame = getInFrame ?: "",
40
- bodycalFinished = bodycalFinished ?: ""
47
+ bodycalFinished = bodycalFinished ?: "",
48
+ continuation = continuation?.toSMWorkoutContinuation()
41
49
  )
42
50
 
43
51
  internal fun SMKitWorkout.toSMWorkoutWithoutScoringParams(): SMWorkout = SMWorkout(
@@ -48,11 +56,12 @@ internal fun SMKitWorkout.toSMWorkoutWithoutScoringParams(): SMWorkout = SMWorko
48
56
  exercises = exercises.map(SMKitExercise::toSMExerciseWithoutScoringParams),
49
57
  workoutClosure = workoutClosure ?: "",
50
58
  getInFrame = getInFrame ?: "",
51
- bodycalFinished = bodycalFinished ?: ""
59
+ bodycalFinished = bodycalFinished ?: "",
60
+ continuation = continuation?.toSMWorkoutContinuationWithoutScoringParams()
52
61
  )
53
62
 
54
63
  private fun SMKitExercise.toSMExercise(): SMExercise = SMExercise(
55
- prettyName = prettyName ?: "",
64
+ prettyName = prettyName ?: name ?: "",
56
65
  exerciseIntro = exerciseIntro ?: "",
57
66
  totalSeconds = totalSeconds ?: 0,
58
67
  videoInstruction = videoInstruction ?: "",
@@ -63,13 +72,23 @@ private fun SMKitExercise.toSMExercise(): SMExercise = SMExercise(
63
72
  scoringParams = scoringParams?.toParams(),
64
73
  summaryTitle = summaryTitle ?: "",
65
74
  summarySubTitle = summarySubTitle ?: "",
66
- summaryMainMetricTitle = summaryTitleMainMetric ?: "",
67
- summaryMainMetricSubTitle = summarySubTitleMainMetric ?: "",
68
- side = side
75
+ summaryMainMetricTitle = summaryMainMetricTitle ?: summaryTitleMainMetric ?: "",
76
+ summaryMainMetricSubTitle = summaryMainMetricSubTitle ?: summarySubTitleMainMetric ?: "",
77
+ side = side,
78
+ shortIntro = shortIntro,
79
+ playPreExerciseCountdown = playPreExerciseCountdown,
80
+ playSoundOnEachRep = playSoundOnEachRep,
81
+ playRepMilestoneVoice = playRepMilestoneVoice,
82
+ repMilestoneInterval = repMilestoneInterval,
83
+ guidanceMode = guidanceMode,
84
+ guidanceVideoSegments = guidanceVideoSegments?.mapValues { it.value.toGuidanceVideoSegment() },
85
+ adaptiveRomFeedbackEnabled = adaptiveRomFeedbackEnabled,
86
+ adaptiveRomWarmupReps = adaptiveRomWarmupReps.coerceAtLeast(1),
87
+ stretchSetConfig = stretchSetConfig?.toStretchSetConfig()
69
88
  )
70
89
 
71
90
  private fun SMKitExercise.toSMExerciseWithoutScoringParams(): SMExercise = SMExercise(
72
- prettyName = prettyName ?: "",
91
+ prettyName = prettyName ?: name ?: "",
73
92
  exerciseIntro = exerciseIntro ?: "",
74
93
  totalSeconds = totalSeconds ?: 0,
75
94
  videoInstruction = videoInstruction ?: "",
@@ -80,9 +99,50 @@ private fun SMKitExercise.toSMExerciseWithoutScoringParams(): SMExercise = SMExe
80
99
  scoringParams = null, // Exclude scoring params for workouts
81
100
  summaryTitle = summaryTitle ?: "",
82
101
  summarySubTitle = summarySubTitle ?: "",
83
- summaryMainMetricTitle = summaryTitleMainMetric ?: "",
84
- summaryMainMetricSubTitle = summarySubTitleMainMetric ?: "",
85
- side = side
102
+ summaryMainMetricTitle = summaryMainMetricTitle ?: summaryTitleMainMetric ?: "",
103
+ summaryMainMetricSubTitle = summaryMainMetricSubTitle ?: summarySubTitleMainMetric ?: "",
104
+ side = side,
105
+ shortIntro = shortIntro,
106
+ playPreExerciseCountdown = playPreExerciseCountdown,
107
+ playSoundOnEachRep = playSoundOnEachRep,
108
+ playRepMilestoneVoice = playRepMilestoneVoice,
109
+ repMilestoneInterval = repMilestoneInterval,
110
+ guidanceMode = guidanceMode,
111
+ guidanceVideoSegments = guidanceVideoSegments?.mapValues { it.value.toGuidanceVideoSegment() },
112
+ adaptiveRomFeedbackEnabled = adaptiveRomFeedbackEnabled,
113
+ adaptiveRomWarmupReps = adaptiveRomWarmupReps.coerceAtLeast(1),
114
+ stretchSetConfig = stretchSetConfig?.toStretchSetConfig()
115
+ )
116
+
117
+ private fun SMKitWorkoutContinuation.toSMWorkoutContinuation(): SMWorkoutContinuation = SMWorkoutContinuation(
118
+ introSoundKey = introSoundKey,
119
+ interactionUnlockSoundKey = interactionUnlockSoundKey,
120
+ exercises = exercises.map(SMKitExercise::toSMExercise)
121
+ )
122
+
123
+ private fun SMKitWorkoutContinuation.toSMWorkoutContinuationWithoutScoringParams(): SMWorkoutContinuation = SMWorkoutContinuation(
124
+ introSoundKey = introSoundKey,
125
+ interactionUnlockSoundKey = interactionUnlockSoundKey,
126
+ exercises = exercises.map(SMKitExercise::toSMExerciseWithoutScoringParams)
127
+ )
128
+
129
+ private fun SMKitGuidanceVideoSegment.toGuidanceVideoSegment(): GuidanceVideoSegment = GuidanceVideoSegment(
130
+ kind = if (kind.equals("freeze", ignoreCase = true)) {
131
+ GuidanceVideoSegment.Kind.FREEZE
132
+ } else {
133
+ GuidanceVideoSegment.Kind.PLAY
134
+ },
135
+ startSeconds = startSeconds,
136
+ endSeconds = endSeconds
137
+ )
138
+
139
+ private fun SMKitStretchSetConfig.toStretchSetConfig(): SMStretchSetConfig = SMStretchSetConfig(
140
+ enabled = enabled,
141
+ repetitions = repetitions,
142
+ secondsPerStretch = secondsPerStretch,
143
+ restSecondsBetweenStretches = restSecondsBetweenStretches,
144
+ introSoundKey = introSoundKey,
145
+ positionDetectors = positionDetectors
86
146
  )
87
147
 
88
148
  private fun SMKitScoringParams.toParams(): ScoringParams {
@@ -12,15 +12,32 @@ internal data class SMKitExercise(
12
12
  val introSeconds: Int? = null,
13
13
  val totalSeconds: Int? = null,
14
14
  val scoringParams: SMKitScoringParams? = null,
15
+ val name: String? = null,
15
16
  val prettyName: String? = null,
16
17
  val exerciseIntro: String? = null,
17
18
  val exerciseClosure: String? = null,
18
19
  val closureFailedSound: String? = null,
19
20
  val summaryTitle: String? = null,
20
21
  val summarySubTitle: String? = null,
22
+ val summaryMainMetricTitle: String? = null,
23
+ val summaryMainMetricSubTitle: String? = null,
21
24
  val summaryTitleMainMetric: String? = null,
22
25
  val summarySubTitleMainMetric: String? = null,
23
26
  val side: String? = null,
27
+ val shortIntro: Boolean = false,
28
+ val quickMotionParams: SMKitQuickMotionParams? = null,
29
+ val rowingFeedbackLevel: String? = null,
30
+ val phonePosition: String? = null,
31
+ val guidanceMode: Boolean? = null,
32
+ val useWideAngleCamera: Boolean? = null,
33
+ val playPreExerciseCountdown: Boolean = false,
34
+ val playSoundOnEachRep: Boolean = false,
35
+ val playRepMilestoneVoice: Boolean = false,
36
+ val repMilestoneInterval: Int = 10,
37
+ val guidanceVideoSegments: Map<String, SMKitGuidanceVideoSegment>? = null,
38
+ val adaptiveRomFeedbackEnabled: Boolean = false,
39
+ val adaptiveRomWarmupReps: Int = 2,
40
+ val stretchSetConfig: SMKitStretchSetConfig? = null,
24
41
  )
25
42
 
26
43
  @Keep
@@ -32,3 +49,26 @@ internal data class SMKitScoringParams(
32
49
  val targetRom: String? = null,
33
50
  val passCriteria: List<String>? = null,
34
51
  )
52
+
53
+ @Keep
54
+ internal data class SMKitQuickMotionParams(
55
+ val validityWindow: Double? = null,
56
+ val checkInterval: Double? = null,
57
+ )
58
+
59
+ @Keep
60
+ internal data class SMKitGuidanceVideoSegment(
61
+ val kind: String,
62
+ val startSeconds: Double,
63
+ val endSeconds: Double? = null,
64
+ )
65
+
66
+ @Keep
67
+ internal data class SMKitStretchSetConfig(
68
+ val enabled: Boolean = true,
69
+ val repetitions: Int = 1,
70
+ val secondsPerStretch: Int = 0,
71
+ val restSecondsBetweenStretches: Int = 3,
72
+ val introSoundKey: String? = null,
73
+ val positionDetectors: List<String>? = null,
74
+ )
@@ -12,4 +12,12 @@ internal data class SMKitWorkout(
12
12
  val workoutClosure: String? = null,
13
13
  val getInFrame: String? = null,
14
14
  val bodycalFinished: String? = null,
15
+ val continuation: SMKitWorkoutContinuation? = null,
16
+ )
17
+
18
+ @Keep
19
+ internal data class SMKitWorkoutContinuation(
20
+ val introSoundKey: String? = null,
21
+ val interactionUnlockSoundKey: String,
22
+ val exercises: List<SMKitExercise>,
15
23
  )
@@ -10,4 +10,6 @@ internal data class SMKitWorkoutConfig(
10
10
  val workoutDuration: String,
11
11
  val programID: String,
12
12
  val language: String,
13
+ val phonePosition: String? = null,
14
+ val shortIntro: Boolean = false,
13
15
  )
@@ -6,12 +6,16 @@ import com.sency.smkitui.model.UserData
6
6
  data class SMUserData(
7
7
  val gender: String,
8
8
  val age: Int,
9
+ val email: String? = null,
9
10
  )
10
11
 
11
12
  fun SMUserData.toUserData() : UserData {
12
13
  val g = when(gender) {
13
14
  "Female" -> Gender.Female
15
+ "Rather not say" -> Gender.Idle
16
+ "Other" -> Gender.Idle
17
+ "Idle" -> Gender.Idle
14
18
  else -> Gender.Male
15
19
  }
16
- return UserData(age, g)
20
+ return UserData(age, g, email)
17
21
  }
@@ -7,17 +7,39 @@ RCT_EXTERN_METHOD(startCustomWorkout:(NSString*)rawJson modifications:(NSString*
7
7
  RCT_EXTERN_METHOD(startCustomAssessment:(NSString*)rawJson userData:(NSString* _Nullable)userData forceShowUserDataScreen:(BOOL)forceShowUserDataScreen showSummary:(BOOL)showSummary modifications:(NSString* _Nullable)modifications onWorkoutDidFinish:(RCTPromiseResolveBlock)onWorkoutDidFinish onWorkoutFailed:(RCTPromiseRejectBlock)onWorkoutFailed)
8
8
  RCT_EXTERN_METHOD(startWorkoutProgram:(NSString*)dic modifications:(NSString* _Nullable)modifications onWorkoutDidFinish:(RCTPromiseResolveBlock)onWorkoutDidFinish onWorkoutFailed:(RCTPromiseRejectBlock)onWorkoutFailed)
9
9
 
10
- RCT_EXTERN_METHOD(setSessionLanguage:(NSString*)language)
11
- RCT_EXTERN_METHOD(setPhoneCalibrationLanguage:(NSString*)language)
12
- RCT_EXTERN_METHOD(setEndExercisePreferences:(NSString*)preferencesString)
13
- RCT_EXTERN_METHOD(setCounterPreferences:(NSString*)preferencesString)
10
+ RCT_EXTERN_METHOD(setSessionLanguage:(NSString*)language onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
11
+ RCT_EXTERN_METHOD(setPhoneCalibrationLanguage:(NSString*)language onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
12
+ RCT_EXTERN_METHOD(setEndExercisePreferences:(NSString*)preferencesString onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
13
+ RCT_EXTERN_METHOD(setCounterPreferences:(NSString*)preferencesString onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
14
14
 
15
- RCT_EXTERN_METHOD(setIntelligenceRestEnabled:(BOOL)enabled)
16
- RCT_EXTERN_METHOD(setSkeletonSettings:(NSString*)configString)
17
- RCT_EXTERN_METHOD(setPauseTypes:(NSString*)typesJson)
18
- RCT_EXTERN_METHOD(setAllowAudioMixing:(BOOL)enabled)
19
- RCT_EXTERN_METHOD(setShowExternalAudioControl:(BOOL)enabled)
20
- RCT_EXTERN_METHOD(setAccuratePoseEstimation:(BOOL)enabled)
21
- RCT_EXTERN_METHOD(setInstructionVideoConfig:(NSString*)configString)
15
+ RCT_EXTERN_METHOD(setIntelligenceRestEnabled:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
16
+ RCT_EXTERN_METHOD(setSkeletonSettings:(NSString*)configString onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
17
+ RCT_EXTERN_METHOD(setPauseTypes:(NSString*)typesJson onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
18
+ RCT_EXTERN_METHOD(setAllowAudioMixing:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
19
+ RCT_EXTERN_METHOD(setShowExternalAudioControl:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
20
+ RCT_EXTERN_METHOD(setAccuratePoseEstimation:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
21
+ RCT_EXTERN_METHOD(setInstructionVideoConfig:(NSString*)configString onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
22
+ RCT_EXTERN_METHOD(setColorTheme:(NSString*)theme onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
23
+ RCT_EXTERN_METHOD(setPlayPhoneCalibrationAudio:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
24
+ RCT_EXTERN_METHOD(setPlayBodyCalibrationAudio:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
25
+ RCT_EXTERN_METHOD(setStartTimerOnFirstActivity:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
26
+ RCT_EXTERN_METHOD(setWorkoutContinuationTimerDuration:(nonnull NSNumber*)seconds onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
27
+ RCT_EXTERN_METHOD(setPhoneMovementCountPreventionEnabled:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
28
+ RCT_EXTERN_METHOD(setVariationMismatchFeedbackEnabled:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
29
+ RCT_EXTERN_METHOD(setUseDefaultGuidanceMode:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
30
+ RCT_EXTERN_METHOD(setGuidanceDebugLogging:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
31
+ RCT_EXTERN_METHOD(setEnableButtonTutorial:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
32
+ RCT_EXTERN_METHOD(setButtonTutorialCompletionAudioUri:(NSString* _Nullable)uri onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
33
+ RCT_EXTERN_METHOD(setShowRowingPhoneCalibration:(BOOL)enabled onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
34
+ RCT_EXTERN_METHOD(setFeedbacksUIToExclude:(NSString*)feedbacksJson onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
35
+ RCT_EXTERN_METHOD(setExcludedFeedbacks:(NSString*)feedbacksJson onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
36
+ RCT_EXTERN_METHOD(setConfigString:(NSString* _Nullable)configString onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
37
+ RCT_EXTERN_METHOD(clearAdaptiveRomCache:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
38
+ RCT_EXTERN_METHOD(quitWorkout:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
39
+ RCT_EXTERN_METHOD(pauseSDK:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
40
+ RCT_EXTERN_METHOD(resumeSDK:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
41
+ RCT_EXTERN_METHOD(getSupportedMovements:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
42
+ RCT_EXTERN_METHOD(getExerciseType:(NSString*)detector onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
43
+ RCT_EXTERN_METHOD(setPoseModelChoice:(NSString*)choice onSuccess:(RCTPromiseResolveBlock)onSuccess onFailure:(RCTPromiseRejectBlock)onFailure)
22
44
 
23
45
  @end