package com.smkituilibrary import android.util.Log import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.WritableNativeMap import com.facebook.react.modules.core.DeviceEventManagerModule import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.Strictness import com.sency.smkitui.SMKitUI import com.sency.smkitui.listener.SMKitUIConfigurationListener import com.sency.smkitui.listener.SMKitUIWorkoutListener import com.sency.smkitui.model.ExerciseData import com.sency.smkitui.model.ScoringParamsData import com.sency.smkitui.model.UserData import com.sency.smkitui.model.WorkoutSummaryData import com.sency.smkitui.model.smkitui.Body360 import com.sency.smkitui.model.smkitui.Custom import com.sency.smkitui.model.smkitui.Fitness import com.sency.smkitui.model.workoutConfig.CounterPreference import com.sency.smkitui.model.workoutConfig.EndExercisePreference import com.sency.smkitui.model.workoutConfig.SMLanguage import com.sency.smkitui.model.workoutConfig.WorkoutConfigModel import com.smkituilibrary.mapper.toSMWorkout import com.smkituilibrary.mapper.toWFPSummary import com.smkituilibrary.mapper.toWorkoutConfig import com.smkituilibrary.model.SMKitWorkout import com.smkituilibrary.model.SMKitWorkoutConfig import com.smkituilibrary.model.SMUserData import com.smkituilibrary.model.WFPExerciseData import com.smkituilibrary.model.WFPSummary import com.smkituilibrary.model.toUserData import com.smkituilibrary.serializers.ExerciseDataSerializer import com.smkituilibrary.serializers.FeedbackSerializer import com.smkituilibrary.serializers.ScoringParamsDataSerializer import com.smkituilibrary.serializers.UserDataSerializer import com.smkituilibrary.serializers.WFPExerciseDataSerializer import com.smkituilibrary.serializers.WFPSummarySerializer import com.smkituilibrary.serializers.WorkoutConfigSerializer import com.smkituilibrary.serializers.WorkoutSummarySerializer import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory class SmkitUiLibraryModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { @Suppress("PrivatePropertyName") private val TAG = "SmkitUiLibraryModule" private var smKitUI: SMKitUI? = null private var resultPromise: Promise? = null private val moshi: Moshi by lazy { moshi() } private val gson: Gson by lazy { gson() } private var compactJson = false override fun getName(): String = "SMKitUIManager" private val listener = object : SMKitUIWorkoutListener { override fun handleWorkoutErrors(error: Error) { val params: WritableMap = WritableNativeMap() params.putString("error", error.message) sendEvent(reactApplicationContext, "workoutError", params) } override fun workoutDidFinish(summary: WorkoutSummaryData) { val params: WritableMap = WritableNativeMap() val summaryJson: String = try { if(compactJson) { gson.toJson(summary.toWFPSummary()) } else { gson.toJson(summary) } } catch (e: Exception) { summary.toString() } params.putString("summary", summaryJson) params.putBoolean("didFinish", true) val result = Arguments.createMap().apply { putString("summary", summaryJson) putBoolean("didFinish", true) } resultPromise?.resolve(result) sendEvent(reactApplicationContext, "workoutDidFinish", params) } override fun didExitWorkout(summary: WorkoutSummaryData) { val params: WritableMap = WritableNativeMap() val summaryJson: String = try { if(compactJson) { gson.toJson(summary.toWFPSummary()) } else { gson.toJson(summary) } } catch (e: Exception) { summary.toString() } params.putString("summary", summaryJson) params.putBoolean("didFinish", false) val result = Arguments.createMap().apply { putString("summary", summaryJson) putBoolean("didFinish", false) } resultPromise?.resolve(result) sendEvent(reactApplicationContext, "didExitWorkout", params) } override fun exerciseDidFinish(data: ExerciseData) { val params: WritableMap = WritableNativeMap() val dataJson: String = try { gson.toJson(data) } catch (e: Exception) { data.toString() } params.putString("data", dataJson) sendEvent(reactApplicationContext, "exerciseDidFinish", params) } private fun sendEvent(reactContext: ReactContext, eventName: String, params: WritableMap) { reactContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) .emit(eventName, params) } } @ReactMethod fun configure(key: String, promise: Promise) { smKitUI = SMKitUI.Configuration(reactApplicationContext) .setUIKey(key) .configure(object : SMKitUIConfigurationListener { override fun onSuccess() { promise.resolve("") } override fun onFailure() { promise.reject("Configure Failed", "null") } }) } @ReactMethod fun setSessionLanguage(lang: String, promise: Promise) { val lang = when(lang) { "he" -> SMLanguage.Hebrew else -> SMLanguage.English } smKitUI?.setSessionLanguage(lang) promise.resolve("") } @ReactMethod fun setEndExercisePreferences(prefs: String, promise: Promise) { val pr = when(prefs) { "Default" -> EndExercisePreference.Default else -> EndExercisePreference.TargetBased } smKitUI?.setEndExercisePreferences(pr) promise.resolve("") } @ReactMethod fun setCounterPreferences(prefs: String, promise: Promise) { val pr = when(prefs) { "Default" -> CounterPreference.Default else -> CounterPreference.PerfectOnly } smKitUI?.setCounterPreferences(pr) promise.resolve("") } @ReactMethod fun startAssessment( type: String, showSummary: Boolean, userData: String?, forceShowUserDataScreen: Boolean, customAssessmentId: String, promise: Promise, ) { this.resultPromise = promise compactJson = false try { val assessmentType = when (type) { "fitness" -> Fitness "body360" -> Body360 else -> Custom(customAssessmentId) } val user = if(userData == null) null else if (forceShowUserDataScreen) null else { serializeUserData(userData) } smKitUI?.startAssessment( listener = listener, assessmentType = assessmentType, userData = user, showSummary = showSummary ) } catch (e: Exception) { promise.reject("Starting Assessment Failed", e) } } @ReactMethod fun startCustomWorkout(jsonArguments: String, promise: Promise) { compactJson = false try { resultPromise = promise serializeWorkout(jsonArguments)?.let { smKitUI?.startCustomizedWorkout(workout = it, listener = listener) } } catch (e: Exception) { promise.reject("Starting Custom Workout Failed", e) } } @ReactMethod fun startCustomAssessment( assessmentJson: String, userDataJson: String?, forceShowUserDataScreen: Boolean, showSummary: Boolean, promise: Promise ) { compactJson = true try { resultPromise = promise serializeWorkout(assessmentJson)?.let { smKitUI?.startCustomizedAssessment(workout = it, showSummary = showSummary, listener = listener) } } catch (e: Exception) { promise.reject("Starting Custom Assessment Failed", e) } } private fun serializeWorkout(jsonArguments: String) = moshi.adapter(SMKitWorkout::class.java).fromJson(jsonArguments)?.toSMWorkout() private fun serializeUserData(jsonArguments: String) = moshi.adapter(SMUserData::class.java).fromJson(jsonArguments)?.toUserData() @ReactMethod fun startWorkoutProgram(jsonArguments: String, promise: Promise) { compactJson = true try { Log.d(TAG, "startWorkoutProgram: $jsonArguments") val adapter = moshi.adapter(SMKitWorkoutConfig::class.java) val smKitWorkoutConfig = adapter.fromJson(jsonArguments) ?: return resultPromise = promise smKitUI?.startWorkoutProgram( workoutConfig = smKitWorkoutConfig.toWorkoutConfig(), listener = listener, ) } catch (e: Exception) { promise.reject("Starting workout from program Failed", e) } } private fun sendResult(summary: WorkoutSummaryData, didFinish: Boolean) { val promise = resultPromise ?: run { resultPromise?.reject("Unable to create summary", "Missing callback", null) return } try { val adapter = moshi.adapter(WorkoutSummaryData::class.java) val result: Map = mapOf( "summary" to adapter.toJson(summary), "didFinish" to didFinish, ) promise.resolve(result) } catch (e: Exception) { promise.reject("Unable to create summary", e) } } private fun gson(): Gson { return GsonBuilder() .registerTypeAdapter(WorkoutSummaryData::class.java, WorkoutSummarySerializer()) .registerTypeAdapter(ExerciseData::class.java, ExerciseDataSerializer()) .registerTypeAdapter(ScoringParamsData::class.java, ScoringParamsDataSerializer()) .registerTypeAdapter(UserData::class.java, UserDataSerializer()) .registerTypeAdapter(WorkoutConfigModel::class.java, WorkoutConfigSerializer()) .registerTypeAdapter(WFPExerciseData::class.java, WFPExerciseDataSerializer()) .registerTypeAdapter(WFPSummary::class.java, WFPSummarySerializer()) .registerTypeAdapter(FeedbackSerializer::class.java, FeedbackSerializer()) .setStrictness(Strictness.LENIENT) .create() } private fun moshi(): Moshi { return Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build() } }