@sency/react-native-smkit-ui 2.2.1 → 2.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 (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 +238 -5
  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 +237 -36
  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
@@ -12,7 +13,8 @@ import com.facebook.react.bridge.WritableNativeMap
12
13
  import com.facebook.react.modules.core.DeviceEventManagerModule
13
14
  import com.google.gson.Gson
14
15
  import com.google.gson.GsonBuilder
15
- import com.google.gson.Strictness
16
+ import com.sency.smbase.nativeclient.model.FormFeedbackType
17
+ import com.sency.smkit.PoseModelChoice
16
18
  import com.sency.smkitui.SMKitUI
17
19
  import com.sency.smkitui.listener.SMKitUIConfigurationListener
18
20
  import com.sency.smkitui.listener.SMKitUIWorkoutListener
@@ -70,6 +72,24 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
70
72
  private val moshi: Moshi by lazy { moshi() }
71
73
  private val gson: Gson by lazy { gson() }
72
74
  private var compactJson = false
75
+ private var pendingInstructionVideoConfig = InstructionVideoConfig()
76
+ private var pendingPhoneMovementCountPreventionEnabled = false
77
+ private var pendingStartTimerOnFirstActivity = true
78
+ private var pendingWorkoutContinuationTimerSeconds = 10
79
+ private var pendingPlayPhoneCalibrationAudio = false
80
+ private var pendingPlayBodyCalibrationAudio = false
81
+ private var pendingAllowAudioMixing = false
82
+ private var pendingShowExternalAudioControl = false
83
+ private var pendingEnableButtonTutorial = false
84
+ private var pendingButtonTutorialCompletionAudioUri: Uri? = null
85
+ private var pendingUseDefaultGuidanceMode = false
86
+ private var pendingGuidanceDebugLogging = false
87
+ private var pendingVariationMismatchFeedbackEnabled = false
88
+ private var pendingIntelligenceRestEnabled = false
89
+ private var pendingColorTheme = UIColorTheme.GREEN
90
+ private var pendingPoseModelChoice = PoseModelChoice.AdaptiveChoice
91
+ private var pendingConfigString: String? = null
92
+ private var pendingFeedbacksUIToExclude: Set<FormFeedbackType>? = null
73
93
 
74
94
  override fun getName(): String = "SMKitUIManager"
75
95
 
@@ -147,6 +167,26 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
147
167
  }
148
168
  }
149
169
 
170
+ private fun applyPendingRuntimeSettings() {
171
+ smKitUI?.setInstructionVideoConfig(pendingInstructionVideoConfig)
172
+ smKitUI?.setPhoneMovementCountPreventionEnabled(pendingPhoneMovementCountPreventionEnabled)
173
+ smKitUI?.setStartTimerOnFirstActivity(pendingStartTimerOnFirstActivity)
174
+ smKitUI?.setWorkoutContinuationTimerDuration(pendingWorkoutContinuationTimerSeconds)
175
+ smKitUI?.setPlayPhoneCalibrationAudio(pendingPlayPhoneCalibrationAudio)
176
+ smKitUI?.setPlayBodyCalibrationAudio(pendingPlayBodyCalibrationAudio)
177
+ smKitUI?.setAllowAudioMixing(pendingAllowAudioMixing)
178
+ smKitUI?.setShowExternalAudioControl(pendingShowExternalAudioControl)
179
+ smKitUI?.setEnableButtonTutorial(pendingEnableButtonTutorial)
180
+ smKitUI?.setButtonTutorialCompletionAudioUri(pendingButtonTutorialCompletionAudioUri)
181
+ smKitUI?.setUseDefaultGuidanceMode(pendingUseDefaultGuidanceMode)
182
+ smKitUI?.setGuidanceDebugLogging(pendingGuidanceDebugLogging)
183
+ smKitUI?.setVariationMismatchFeedbackEnabled(pendingVariationMismatchFeedbackEnabled)
184
+ smKitUI?.setIntelligenceRestEnabled(pendingIntelligenceRestEnabled)
185
+ smKitUI?.setColorTheme(pendingColorTheme)
186
+ smKitUI?.setConfigString(pendingConfigString)
187
+ pendingFeedbacksUIToExclude?.let { smKitUI?.setFeedbacksUIToExclude(it) }
188
+ }
189
+
150
190
  @ReactMethod
151
191
  fun configure(key: String, promise: Promise) {
152
192
  // If already configured, reuse the existing instance.
@@ -161,8 +201,24 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
161
201
  }
162
202
  smKitUI = SMKitUI.Configuration(reactApplicationContext)
163
203
  .setUIKey(key)
204
+ .setInstructionVideoConfig(pendingInstructionVideoConfig)
205
+ .setPhoneMovementCountPreventionEnabled(pendingPhoneMovementCountPreventionEnabled)
206
+ .setStartTimerOnFirstActivity(pendingStartTimerOnFirstActivity)
207
+ .setWorkoutContinuationTimerDuration(pendingWorkoutContinuationTimerSeconds)
208
+ .setPlayPhoneCalibrationAudio(pendingPlayPhoneCalibrationAudio)
209
+ .setPlayBodyCalibrationAudio(pendingPlayBodyCalibrationAudio)
210
+ .setAllowAudioMixing(pendingAllowAudioMixing)
211
+ .setShowExternalAudioControl(pendingShowExternalAudioControl)
212
+ .setEnableButtonTutorial(pendingEnableButtonTutorial)
213
+ .setButtonTutorialCompletionAudioUri(pendingButtonTutorialCompletionAudioUri)
214
+ .setUseDefaultGuidanceMode(pendingUseDefaultGuidanceMode)
215
+ .setGuidanceDebugLogging(pendingGuidanceDebugLogging)
216
+ .setVariationMismatchFeedbackEnabled(pendingVariationMismatchFeedbackEnabled)
217
+ .setColorTheme(pendingColorTheme)
218
+ .setPoseModelChoice(pendingPoseModelChoice)
164
219
  .configure(object : SMKitUIConfigurationListener {
165
220
  override fun onSuccess() {
221
+ applyPendingRuntimeSettings()
166
222
  promise.resolve("")
167
223
  }
168
224
 
@@ -234,6 +290,7 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
234
290
  val mediumSizeCycles = (configMap["mediumSizeCycles"] as? Number)?.toInt() ?: 2
235
291
 
236
292
  val config = InstructionVideoConfig(displayMode = displayMode, mediumSizeCycles = mediumSizeCycles)
293
+ pendingInstructionVideoConfig = config
237
294
  smKitUI?.setInstructionVideoConfig(config)
238
295
  promise.resolve("")
239
296
  } catch (e: Exception) {
@@ -245,6 +302,7 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
245
302
  @ReactMethod
246
303
  fun setIntelligenceRestEnabled(enabled: Boolean, promise: Promise) {
247
304
  try {
305
+ pendingIntelligenceRestEnabled = enabled
248
306
  smKitUI?.setIntelligenceRestEnabled(enabled)
249
307
  promise.resolve(null)
250
308
  } catch (e: Exception) {
@@ -320,11 +378,175 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
320
378
  }
321
379
  }
322
380
 
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) }
381
+ @ReactMethod
382
+ fun setAllowAudioMixing(enabled: Boolean, promise: Promise) {
383
+ pendingAllowAudioMixing = enabled
384
+ smKitUI?.setAllowAudioMixing(enabled)
385
+ promise.resolve(null)
386
+ }
387
+
388
+ @ReactMethod
389
+ fun setShowExternalAudioControl(enabled: Boolean, promise: Promise) {
390
+ pendingShowExternalAudioControl = enabled
391
+ smKitUI?.setShowExternalAudioControl(enabled)
392
+ promise.resolve(null)
393
+ }
394
+
395
+ // iOS-only. Resolve immediately for API parity.
326
396
  @ReactMethod fun setAccuratePoseEstimation(enabled: Boolean, promise: Promise) { promise.resolve(null) }
327
397
 
398
+ @ReactMethod
399
+ fun setColorTheme(theme: String, promise: Promise) {
400
+ val colorTheme = mapColorToTheme(theme)
401
+ pendingColorTheme = colorTheme
402
+ smKitUI?.setColorTheme(colorTheme)
403
+ promise.resolve(null)
404
+ }
405
+
406
+ @ReactMethod
407
+ fun setPlayPhoneCalibrationAudio(enabled: Boolean, promise: Promise) {
408
+ pendingPlayPhoneCalibrationAudio = enabled
409
+ smKitUI?.setPlayPhoneCalibrationAudio(enabled)
410
+ promise.resolve(null)
411
+ }
412
+
413
+ @ReactMethod
414
+ fun setPlayBodyCalibrationAudio(enabled: Boolean, promise: Promise) {
415
+ pendingPlayBodyCalibrationAudio = enabled
416
+ smKitUI?.setPlayBodyCalibrationAudio(enabled)
417
+ promise.resolve(null)
418
+ }
419
+
420
+ @ReactMethod
421
+ fun setStartTimerOnFirstActivity(enabled: Boolean, promise: Promise) {
422
+ pendingStartTimerOnFirstActivity = enabled
423
+ smKitUI?.setStartTimerOnFirstActivity(enabled)
424
+ promise.resolve(null)
425
+ }
426
+
427
+ @ReactMethod
428
+ fun setWorkoutContinuationTimerDuration(seconds: Int, promise: Promise) {
429
+ pendingWorkoutContinuationTimerSeconds = seconds.coerceAtLeast(1)
430
+ smKitUI?.setWorkoutContinuationTimerDuration(pendingWorkoutContinuationTimerSeconds)
431
+ promise.resolve(null)
432
+ }
433
+
434
+ @ReactMethod
435
+ fun setPhoneMovementCountPreventionEnabled(enabled: Boolean, promise: Promise) {
436
+ pendingPhoneMovementCountPreventionEnabled = enabled
437
+ smKitUI?.setPhoneMovementCountPreventionEnabled(enabled)
438
+ promise.resolve(null)
439
+ }
440
+
441
+ @ReactMethod
442
+ fun setVariationMismatchFeedbackEnabled(enabled: Boolean, promise: Promise) {
443
+ pendingVariationMismatchFeedbackEnabled = enabled
444
+ smKitUI?.setVariationMismatchFeedbackEnabled(enabled)
445
+ promise.resolve(null)
446
+ }
447
+
448
+ @ReactMethod
449
+ fun setUseDefaultGuidanceMode(enabled: Boolean, promise: Promise) {
450
+ pendingUseDefaultGuidanceMode = enabled
451
+ smKitUI?.setUseDefaultGuidanceMode(enabled)
452
+ promise.resolve(null)
453
+ }
454
+
455
+ @ReactMethod
456
+ fun setGuidanceDebugLogging(enabled: Boolean, promise: Promise) {
457
+ pendingGuidanceDebugLogging = enabled
458
+ smKitUI?.setGuidanceDebugLogging(enabled)
459
+ promise.resolve(null)
460
+ }
461
+
462
+ @ReactMethod
463
+ fun setEnableButtonTutorial(enabled: Boolean, promise: Promise) {
464
+ pendingEnableButtonTutorial = enabled
465
+ smKitUI?.setEnableButtonTutorial(enabled)
466
+ promise.resolve(null)
467
+ }
468
+
469
+ @ReactMethod
470
+ fun setButtonTutorialCompletionAudioUri(uri: String?, promise: Promise) {
471
+ pendingButtonTutorialCompletionAudioUri = uri
472
+ ?.takeIf { it.isNotBlank() }
473
+ ?.let(Uri::parse)
474
+ smKitUI?.setButtonTutorialCompletionAudioUri(pendingButtonTutorialCompletionAudioUri)
475
+ promise.resolve(null)
476
+ }
477
+
478
+ // iOS-only rowing calibration switch. Resolve immediately for API parity.
479
+ @ReactMethod
480
+ fun setShowRowingPhoneCalibration(enabled: Boolean, promise: Promise) {
481
+ promise.resolve(null)
482
+ }
483
+
484
+ @ReactMethod
485
+ fun setFeedbacksUIToExclude(feedbacksJson: String, promise: Promise) {
486
+ try {
487
+ val feedbacks = feedbackTypesFromJson(feedbacksJson)
488
+ pendingFeedbacksUIToExclude = feedbacks
489
+ smKitUI?.setFeedbacksUIToExclude(feedbacks)
490
+ promise.resolve(null)
491
+ } catch (e: Exception) {
492
+ promise.reject("setFeedbacksUIToExclude Failed", e.message, e)
493
+ }
494
+ }
495
+
496
+ // Android SMKitUI currently exposes UI-only feedback exclusion.
497
+ @ReactMethod
498
+ fun setExcludedFeedbacks(feedbacksJson: String, promise: Promise) {
499
+ try {
500
+ feedbackTypesFromJson(feedbacksJson)
501
+ promise.resolve(null)
502
+ } catch (e: Exception) {
503
+ promise.reject("setExcludedFeedbacks Failed", e.message, e)
504
+ }
505
+ }
506
+
507
+ @ReactMethod
508
+ fun setConfigString(configString: String?, promise: Promise) {
509
+ pendingConfigString = configString
510
+ smKitUI?.setConfigString(configString)
511
+ promise.resolve(null)
512
+ }
513
+
514
+ @ReactMethod
515
+ fun clearAdaptiveRomCache(promise: Promise) {
516
+ smKitUI?.clearAdaptiveRomCache()
517
+ promise.resolve(null)
518
+ }
519
+
520
+ @ReactMethod
521
+ fun quitWorkout(promise: Promise) {
522
+ smKitUI?.quitWorkout()
523
+ promise.resolve(null)
524
+ }
525
+
526
+ // iOS-only active-session controls. Resolve immediately for API parity.
527
+ @ReactMethod fun pauseSDK(promise: Promise) { promise.resolve(null) }
528
+ @ReactMethod fun resumeSDK(promise: Promise) { promise.resolve(null) }
529
+
530
+ @ReactMethod
531
+ fun getSupportedMovements(promise: Promise) {
532
+ promise.resolve(Arguments.createArray())
533
+ }
534
+
535
+ @ReactMethod
536
+ fun getExerciseType(detector: String, promise: Promise) {
537
+ promise.resolve("")
538
+ }
539
+
540
+ @ReactMethod
541
+ fun setPoseModelChoice(choice: String, promise: Promise) {
542
+ try {
543
+ pendingPoseModelChoice = PoseModelChoice.valueOf(choice)
544
+ promise.resolve(null)
545
+ } catch (e: Exception) {
546
+ promise.reject("setPoseModelChoice Failed", "Invalid pose model choice: $choice", e)
547
+ }
548
+ }
549
+
328
550
  private fun skeletonPresetFromString(s: String): SkeletonPreset? = when (s) {
329
551
  "defaultPreset" -> SkeletonPreset.DEFAULT
330
552
  "minimalDots" -> SkeletonPreset.MINIMAL_DOTS
@@ -428,6 +650,17 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
428
650
  }
429
651
  }
430
652
 
653
+ private fun feedbackTypesFromJson(feedbacksJson: String): Set<FormFeedbackType> {
654
+ val arr = org.json.JSONArray(feedbacksJson)
655
+ return (0 until arr.length())
656
+ .map { index ->
657
+ val name = arr.getString(index)
658
+ FormFeedbackType.fromString(name)
659
+ ?: throw IllegalArgumentException("Invalid feedback type: $name")
660
+ }
661
+ .toSet()
662
+ }
663
+
431
664
  private fun processModifications(modificationsJson: String?): Pair<String?, Boolean> {
432
665
  if (modificationsJson.isNullOrEmpty()) {
433
666
  return Pair(null, true) // Default: show phone calibration
@@ -728,7 +961,7 @@ class SmkitUiLibraryModule(reactContext: ReactApplicationContext) :
728
961
  .registerTypeAdapter(WFPExerciseData::class.java, WFPExerciseDataSerializer())
729
962
  .registerTypeAdapter(WFPSummary::class.java, WFPSummarySerializer())
730
963
  .registerTypeAdapter(FeedbackSerializer::class.java, FeedbackSerializer())
731
- .setStrictness(Strictness.LENIENT)
964
+ .setLenient()
732
965
  .create()
733
966
  }
734
967
 
@@ -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