react-native-mytatva-rn-sdk 1.2.28 → 1.2.30

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.
@@ -61,925 +61,730 @@ import kotlin.coroutines.suspendCoroutine
61
61
 
62
62
  @ReactModule(name = "CgmTrackyLib")
63
63
  class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
64
- ReactContextBaseJavaModule(reactContext) {
65
- private val mModel: MainActivityModel
66
- var authenticateSDKService: AuthenticateSDKService
67
- private val job = Job()
68
- private val scope = CoroutineScope(Dispatchers.IO + job)
69
- var prefsHelper: SharedPreferencesLibraryUtil
70
- private val apiScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
71
- private var debounceJob: Job? = null
72
- private var lastDeviceStatus: PocDevice? = null
73
-
74
- private var glucoseObserver: Observer<PocGlucose?>? = null
75
- private var isObserving = false
76
-
77
- init {
78
- mReactContext = reactContext
79
- prefsHelper = SharedPreferencesLibraryUtil(mReactContext)
80
- val viewModelStore = ViewModelStore()
81
- val factory =
82
- ViewModelProvider.AndroidViewModelFactory.getInstance(reactContext.applicationContext as Application)
83
- mModel = ViewModelProvider(viewModelStore, factory)[MainActivityModel::class.java]
84
- authenticateSDKService = AuthenticateSDKService(scope = scope)
64
+ ReactContextBaseJavaModule(reactContext) {
65
+ private val mModel: MainActivityModel
66
+ var authenticateSDKService: AuthenticateSDKService
67
+ private val job = Job()
68
+ private val scope = CoroutineScope(Dispatchers.IO + job)
69
+ var prefsHelper: SharedPreferencesLibraryUtil
70
+ private val apiScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
71
+ private var debounceJob: Job? = null
72
+ private var lastDeviceStatus: PocDevice? = null
73
+
74
+ private var glucoseObserver: Observer<PocGlucose?>? = null
75
+ private var isObserving = false
76
+
77
+ init {
78
+ mReactContext = reactContext
79
+ prefsHelper = SharedPreferencesLibraryUtil(mReactContext)
80
+ val viewModelStore = ViewModelStore()
81
+ val factory =
82
+ ViewModelProvider.AndroidViewModelFactory.getInstance(reactContext.applicationContext as Application)
83
+ mModel = ViewModelProvider(viewModelStore, factory)[MainActivityModel::class.java]
84
+ authenticateSDKService = AuthenticateSDKService(scope = scope)
85
+ }
86
+
87
+
88
+ companion object {
89
+ var mReactContext: ReactApplicationContext? = null
90
+ var userToken: String = ""
91
+ }
92
+
93
+ override fun getName(): String {
94
+ return "CgmTrackyLib"
95
+ }
96
+
97
+ @ReactMethod
98
+ fun observeDeviceStatus(token: String) {
99
+ try {
100
+ userToken = token
101
+ Handler(Looper.getMainLooper()).post {
102
+ mModel.device.observeForever { device ->
103
+ if (device != lastDeviceStatus) {
104
+ lastDeviceStatus = device
105
+ postEventDataToAPI(device, "", lastDeviceStatus?.qrMessage ?: "")
106
+ resetDebounceTimer()
107
+ }
108
+
109
+ if (device != null) {
110
+ Log.d("observeDeviceStatus: ", device.toString())
111
+ }
112
+ }
113
+ }
114
+ } catch (e: Exception) {
115
+ Log.e("observeDeviceStatus", "observeDeviceStatus: ${e.message}")
85
116
  }
117
+ }
86
118
 
119
+ @ReactMethod
120
+ fun observeTransmitterUnbindStatus(token: String) {
121
+ try {
122
+ userToken = token
87
123
 
88
- companion object {
89
- var mReactContext: ReactApplicationContext? = null
90
- var userToken: String = ""
91
- }
124
+ authenticateSDKService.getCGMData(
125
+ environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
126
+ token = userToken,
127
+ responseListener = object : AuthenticateSDKService.ResponseListener {
128
+ override fun onResponseSuccess(response: String) {
92
129
 
93
- override fun getName(): String {
94
- return "CgmTrackyLib"
95
- }
130
+ val response = Gson().fromJson(response, CgmSensorResponse::class.java)
131
+ val sensor = response.data?.firstOrNull()
96
132
 
97
- @ReactMethod
98
- fun observeDeviceStatus(token: String) {
99
- try {
100
- userToken = token
101
- Handler(Looper.getMainLooper()).post {
102
- mModel.device.observeForever { device ->
103
- if (device != lastDeviceStatus) {
104
- lastDeviceStatus = device
105
- postEventDataToAPI(device, "", lastDeviceStatus?.qrMessage ?: "")
106
- resetDebounceTimer()
107
- }
108
-
109
- if (device != null) {
110
- Log.d("observeDeviceStatus: ", device.toString())
111
- }
112
- }
113
- }
114
- } catch (e: Exception) {
115
- Log.e("observeDeviceStatus", "observeDeviceStatus: ${e.message}")
116
- }
117
- }
133
+ if (sensor != null && !sensor.startDate.isNullOrEmpty() && !sensor.endDate.isNullOrEmpty()) {
134
+ val startDate = sensor.startDate
135
+ val endDate = sensor.endDate
136
+ val sensorId = sensor.sensorId
118
137
 
119
- @ReactMethod
120
- fun observeTransmitterUnbindStatus(token: String) {
121
- try {
122
- userToken = token
123
-
124
- authenticateSDKService.getCGMData(
125
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
126
- token = userToken,
127
- responseListener = object : AuthenticateSDKService.ResponseListener {
128
- override fun onResponseSuccess(response: String) {
129
-
130
- val response = Gson().fromJson(response, CgmSensorResponse::class.java)
131
- val sensor = response.data?.firstOrNull()
132
-
133
- if (sensor != null && !sensor.startDate.isNullOrEmpty() && !sensor.endDate.isNullOrEmpty()) {
134
- val startDate = sensor.startDate
135
- val endDate = sensor.endDate
136
- val sensorId = sensor.sensorId
137
-
138
- println("Start Date: $startDate")
139
- println("End Date: $endDate")
140
-
141
- if (isCurrentDateInRange(startDate, endDate)) {
142
-
143
- println("Current date is in range")
144
-
145
- val pocDevice =
146
- RepositoryDevice.getInstance(BApplication.getContext()).latestDeviceIoThread
147
- Log.d("pocDevice logsss", pocDevice.toString())
148
- if (pocDevice != null) {
149
- if (pocDevice.isUnBind) {
150
- postEventDataToAPI(
151
- pocDevice,
152
- DeviceStatus.TRANSMITTER_DISCONNECT.id,
153
- pocDevice.qrMessage
154
- )
155
- }
156
- } else {
157
- postEventDataToAPI(
158
- pocDevice,
159
- DeviceStatus.TRANSMITTER_DISCONNECT.id,
160
- sensorId
161
- )
162
- }
163
-
164
- } else {
165
- println("Current date is out of range")
166
- }
167
- } else {
168
- println("Start or End date not available")
169
- }
170
- }
171
-
172
- override fun onResponseFail() {
173
- }
174
- }
175
- )
138
+ println("Start Date: $startDate")
139
+ println("End Date: $endDate")
176
140
 
177
- } catch (e: Exception) {
178
- Log.e("observeTransmitterUnbindStatus", "observeTransmitterUnbindStatus: ${e.message}")
179
- }
180
- }
141
+ if (isCurrentDateInRange(startDate, endDate)) {
181
142
 
182
- private fun postEventDataToAPI(device: PocDevice?, mStatus: String, sensorId: String?) {
183
- apiScope.launch {
184
- try {
185
- val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
186
- val bleStatus = bluetoothAdapter?.isEnabled == true
187
- var status = ""
188
-
189
- Log.d("ble Status", bleStatus.toString())
190
- Log.d("device.isBoundAndConnect Status", device?.isBoundAndConnect.toString())
191
- Log.d("device.isUnBind Status", device?.isUnBind.toString())
192
- Log.d("device.isBoundButDisConnect Status", device?.isBoundButDisConnect.toString())
193
-
194
- status = if (mStatus.isEmpty()) {
195
- when {
196
- device?.isBoundAndConnect == true -> DeviceStatus.CONNECTED.id
197
- !bleStatus -> DeviceStatus.BLUETOOTH_OFF.id
198
- device?.isUnBind == true -> DeviceStatus.TRANSMITTER_DISCONNECT.id
199
- device?.isBoundButDisConnect == true -> DeviceStatus.DISCONNECTED.id
200
- else -> "" // fallback if no status matches
201
- }
202
- } else {
203
- mStatus
204
- }
143
+ println("Current date is in range")
144
+
145
+ val pocDevice =
146
+ RepositoryDevice.getInstance(BApplication.getContext()).latestDeviceIoThread
205
147
 
206
- if (status.isNotEmpty()) {
207
- val rawData = JSONObject().apply {
208
- put("transmitterName", device?.name ?: "")
209
- put("SensorId", sensorId ?: "")
210
- put("Sensor", sensorId ?: "")
211
- put("timeInMillis", Date().time)
212
- }
213
-
214
- val obj = JSONObject().apply {
215
- put("sensorId", sensorId ?: "")
216
- put("status", status)
217
- put("rawData", rawData)
218
- }
219
-
220
- authenticateSDKService.postDeviceData(
221
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
222
- data = obj.toString(),
223
- token = userToken,
224
- loaderListener = object : LoaderListener {
225
- override fun onShowLoader() {}
226
- override fun onHideLoader() {}
227
- }
148
+ if (pocDevice != null) {
149
+ Log.d("pocDevice logsss", pocDevice.toString())
150
+
151
+ if (pocDevice.isUnBind) {
152
+ postEventDataToAPI(
153
+ pocDevice,
154
+ DeviceStatus.TRANSMITTER_DISCONNECT.id,
155
+ pocDevice.qrMessage
228
156
  )
157
+ }
158
+ } else {
159
+ Log.d("pocDevice logsss", "Data null")
160
+
161
+ postEventDataToAPI(
162
+ pocDevice,
163
+ DeviceStatus.TRANSMITTER_DISCONNECT.id,
164
+ sensorId
165
+ )
229
166
  }
230
167
 
231
- } catch (e: Exception) {
232
- e.printStackTrace()
168
+ } else {
169
+ println("Current date is out of range")
170
+ }
171
+ } else {
172
+ println("Start or End date not available")
233
173
  }
234
- }
235
- }
174
+ }
236
175
 
237
- private fun resetDebounceTimer() {
238
- debounceJob?.cancel() // Cancel any existing timer
239
- debounceJob = apiScope.launch {
240
- delay(60 * 60 * 1000L) // 60 minutes in ms
241
- lastDeviceStatus = null // Reset status after timeout
242
- println("60 min window expired, status reset.")
176
+ override fun onResponseFail() {
177
+ }
243
178
  }
244
- }
179
+ )
245
180
 
246
- @ReactMethod
247
- fun startCgmTracky(token: String) {
248
- try {
249
- userToken = token
250
- val intent = Intent(currentActivity, StartCGMActivity::class.java)
251
- currentActivity?.startActivity(intent)
252
- } catch (e: Exception) {
253
- Log.e("startCgmTracky", "startCgmTracky: ${e.message}")
254
- }
181
+ } catch (e: Exception) {
182
+ Log.e("observeTransmitterUnbindStatus", "observeTransmitterUnbindStatus: ${e.message}")
255
183
  }
184
+ }
256
185
 
257
- fun isCurrentDateInRange(startDateStr: String, endDateStr: String): Boolean {
258
- val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
186
+ private fun postEventDataToAPI(device: PocDevice?, mStatus: String, sensorId: String?) {
187
+ apiScope.launch {
188
+ try {
189
+ val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
190
+ val bleStatus = bluetoothAdapter?.isEnabled == true
191
+ var status = ""
192
+
193
+ status = if (mStatus.isEmpty()) {
194
+ when {
195
+ device?.isBoundAndConnect == true -> DeviceStatus.CONNECTED.id
196
+ !bleStatus -> DeviceStatus.BLUETOOTH_OFF.id
197
+ device?.isUnBind == true -> DeviceStatus.TRANSMITTER_DISCONNECT.id
198
+ device?.isBoundButDisConnect == true -> DeviceStatus.DISCONNECTED.id
199
+ else -> "" // fallback if no status matches
200
+ }
201
+ } else {
202
+ mStatus
203
+ }
259
204
 
260
- val startDate = format.parse(startDateStr)
261
- val endDate = format.parse(endDateStr)
262
- val currentDate = Date()
205
+ if (status.isNotEmpty()) {
206
+ val rawData = JSONObject().apply {
207
+ put("transmitterName", device?.name ?: "")
208
+ put("SensorId", sensorId ?: "")
209
+ put("Sensor", sensorId ?: "")
210
+ put("timeInMillis", Date().time)
211
+ }
263
212
 
264
- return startDate != null && endDate != null &&
265
- !currentDate.before(startDate) && !currentDate.after(endDate)
266
- }
213
+ val obj = JSONObject().apply {
214
+ put("sensorId", sensorId ?: "")
215
+ put("status", status)
216
+ put("rawData", rawData)
217
+ }
267
218
 
268
- @ReactMethod
269
- fun reconnectCgmTracky(token: String) {
270
- try {
271
- userToken = token
272
- val intent = if (areAllPermissionsGranted()) {
273
- Intent(currentActivity, SearchTransmitterActivity::class.java).putExtra(
274
- "IsForReconnect",
275
- true
276
- )
277
- } else {
278
- Intent(currentActivity, PermissionActivity::class.java).putExtra(
279
- "IsForReconnect",
280
- true
281
- )
219
+ authenticateSDKService.postDeviceData(
220
+ environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
221
+ data = obj.toString(),
222
+ token = userToken,
223
+ loaderListener = object : LoaderListener {
224
+ override fun onShowLoader() {}
225
+ override fun onHideLoader() {}
282
226
  }
283
- currentActivity?.startActivity(intent)
284
- } catch (e: Exception) {
285
- Log.e("reconnectCgmTracky", "reconnectCgmTracky: ${e.message}")
227
+ )
286
228
  }
287
- }
288
229
 
289
- @ReactMethod
290
- fun openHelpSupport() {
291
- try {
292
- val intent = Intent(currentActivity, HelpActivity::class.java)
293
- currentActivity?.startActivity(intent)
294
- } catch (e: Exception) {
295
- Log.e("openHelpSupport", "openHelpSupport: ${e.message}")
296
- }
230
+ } catch (e: Exception) {
231
+ e.printStackTrace()
232
+ }
233
+ }
234
+ }
235
+
236
+ private fun resetDebounceTimer() {
237
+ debounceJob?.cancel() // Cancel any existing timer
238
+ debounceJob = apiScope.launch {
239
+ delay(60 * 60 * 1000L) // 60 minutes in ms
240
+ lastDeviceStatus = null // Reset status after timeout
241
+ println("60 min window expired, status reset.")
297
242
  }
243
+ }
298
244
 
299
- private fun areAllPermissionsGranted(): Boolean {
300
- val requiredPermissions = mutableListOf<String>()
245
+ @ReactMethod
246
+ fun startCgmTracky(token: String) {
247
+ try {
248
+ userToken = token
249
+ val intent = Intent(currentActivity, StartCGMActivity::class.java)
250
+ currentActivity?.startActivity(intent)
251
+ } catch (e: Exception) {
252
+ Log.e("startCgmTracky", "startCgmTracky: ${e.message}")
253
+ }
254
+ }
301
255
 
302
- // Add Bluetooth permissions
303
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
304
- requiredPermissions.addAll(PermissionUtils.BLUETOOTH_S)
305
- }
256
+ fun isCurrentDateInRange(startDateStr: String, endDateStr: String): Boolean {
257
+ val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
306
258
 
307
- // Add location permissions
308
- requiredPermissions.addAll(PermissionUtils.LOCAL_PERMISSION)
259
+ val startDate = format.parse(startDateStr)
260
+ val endDate = format.parse(endDateStr)
261
+ val currentDate = Date()
309
262
 
310
- // Add camera permission
311
- requiredPermissions.addAll(PermissionUtils.CAMERA_PERMISSION)
263
+ return startDate != null && endDate != null &&
264
+ !currentDate.before(startDate) && !currentDate.after(endDate)
265
+ }
312
266
 
313
- return requiredPermissions.all {
314
- ContextCompat.checkSelfPermission(
315
- mReactContext!!,
316
- it
317
- ) == PackageManager.PERMISSION_GRANTED
318
- }
267
+ @ReactMethod
268
+ fun reconnectCgmTracky(token: String) {
269
+ try {
270
+ userToken = token
271
+ val intent = if (areAllPermissionsGranted()) {
272
+ Intent(currentActivity, SearchTransmitterActivity::class.java).putExtra(
273
+ "IsForReconnect",
274
+ true
275
+ )
276
+ } else {
277
+ Intent(currentActivity, PermissionActivity::class.java).putExtra(
278
+ "IsForReconnect",
279
+ true
280
+ )
281
+ }
282
+ currentActivity?.startActivity(intent)
283
+ } catch (e: Exception) {
284
+ Log.e("reconnectCgmTracky", "reconnectCgmTracky: ${e.message}")
319
285
  }
286
+ }
287
+
288
+ @ReactMethod
289
+ fun openHelpSupport() {
290
+ try {
291
+ val intent = Intent(currentActivity, HelpActivity::class.java)
292
+ currentActivity?.startActivity(intent)
293
+ } catch (e: Exception) {
294
+ Log.e("openHelpSupport", "openHelpSupport: ${e.message}")
295
+ }
296
+ }
320
297
 
298
+ private fun areAllPermissionsGranted(): Boolean {
299
+ val requiredPermissions = mutableListOf<String>()
321
300
 
322
- /*@ReactMethod
323
- fun observeGlucoseData(token: String) {
324
- try {
325
- userToken = token
326
- Handler(Looper.getMainLooper()).post {
327
- //Observe latest glucose data
328
- mModel.latestGlucose.observeForever { pocGlucose ->
329
- if (pocGlucose != null) {
330
-
331
- if (pocGlucose.errorCode == enumError.NONE) {
332
- if (pocGlucose.showGlucoseMG > 0) {
333
- val dto: GlucoseLog = mapToDto(pocGlucose)
334
- val logs: ArrayList<GlucoseLog> = ArrayList()
335
- logs.add(dto)
336
-
337
- val request: GlucoseLogRequest =
338
- GlucoseLogRequest(vendor = "GoodFlip", logs = logs)
339
- val gson: Gson = GsonBuilder().create()
340
- val json = gson.toJson(request)
341
-
342
- Log.d("Glucose data 3 min==> ", "Glucose data 3 min==> final Json: $json")
343
-
344
- authenticateSDKService.postCGMData(
345
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
346
- data = json.toString(),
347
- token = userToken,
348
- responseListener = object :
349
- AuthenticateSDKService.ResponseListener {
350
- override fun onResponseSuccess() {
351
- }
352
-
353
- override fun onResponseFail() {
354
- }
355
- }
356
- )
357
- }
358
- } else {
359
- if (pocGlucose.errorCode == enumError.ERROR_FLOODING_WATER) {
360
- lastDeviceStatus =
361
- pocGlucose.deviceId.toInt().let { mModel.getDeviceInfo(it) }
362
- lastDeviceStatus?.let {
363
- postEventDataToAPI(
364
- it,
365
- DeviceStatus.MOISTURE_DETECT.id
366
- )
367
- }
368
- resetDebounceTimer()
369
- } else if (pocGlucose.errorCode == enumError.ERROR_CURRENT_SMALL || pocGlucose.errorCode == enumError.ERROR_NOISE || pocGlucose.errorCode == enumError.ERROR_SENSITIVITY_ATTENUATION) {
370
- lastDeviceStatus =
371
- pocGlucose.deviceId.toInt().let { mModel.getDeviceInfo(it) }
372
- lastDeviceStatus?.let {
373
- postEventDataToAPI(
374
- it,
375
- DeviceStatus.WEAK_SIGNAL.id
376
- )
377
- }
378
- resetDebounceTimer()
379
- } else {
380
- lastDeviceStatus =
381
- pocGlucose.deviceId.toInt().let { mModel.getDeviceInfo(it) }
382
- lastDeviceStatus?.let {
383
- postEventDataToAPI(
384
- it,
385
- DeviceStatus.ERROR_COMMON.id
386
- )
387
- }
388
- resetDebounceTimer()
389
- }
390
- }
391
- }
392
- }
393
- }
394
- } catch (e: Exception) {
395
- Log.e("observeGlucoseData", "observeGlucoseData: ${e.message}")
396
- }
397
- }*/
398
-
399
- @ReactMethod
400
- fun observeGlucoseData(token: String) {
401
- try {
402
- userToken = token
301
+ // Add Bluetooth permissions
302
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
303
+ requiredPermissions.addAll(PermissionUtils.BLUETOOTH_S)
304
+ }
403
305
 
404
- // Remove existing observer if any to prevent memory leaks
405
- stopObservingGlucoseData()
306
+ // Add location permissions
307
+ requiredPermissions.addAll(PermissionUtils.LOCAL_PERMISSION)
406
308
 
407
- // Create new observer with explicit nullable parameter type
408
- glucoseObserver = Observer<PocGlucose?> { pocGlucose ->
409
- // Critical fix: Explicitly handle nullable parameter
410
- if (pocGlucose != null) {
411
- handleGlucoseData(pocGlucose)
412
- } else {
413
- Log.w("observeGlucoseData", "Received null glucose data - skipping processing")
414
- }
415
- }
309
+ // Add camera permission
310
+ requiredPermissions.addAll(PermissionUtils.CAMERA_PERMISSION)
416
311
 
417
- // Add observer on main thread
418
- Handler(Looper.getMainLooper()).post {
419
- glucoseObserver?.let { observer ->
420
- try {
421
- mModel.latestGlucose.observeForever(observer)
422
- isObserving = true
423
- Log.d("observeGlucoseData", "Observer added successfully")
424
- } catch (e: Exception) {
425
- Log.e("observeGlucoseData", "Error adding observer: ${e.message}")
426
- glucoseObserver = null
427
- }
428
- }
429
- }
430
- } catch (e: Exception) {
431
- Log.e("observeGlucoseData", "observeGlucoseData: ${e.message}")
432
- e.printStackTrace()
433
- }
312
+ return requiredPermissions.all {
313
+ ContextCompat.checkSelfPermission(
314
+ mReactContext!!,
315
+ it
316
+ ) == PackageManager.PERMISSION_GRANTED
434
317
  }
318
+ }
435
319
 
320
+ @ReactMethod
321
+ fun observeGlucoseData(token: String) {
322
+ try {
323
+ userToken = token
436
324
 
437
- private fun handleGlucoseData(pocGlucose: PocGlucose) {
438
- try {
439
- // Additional safety check
440
- if (pocGlucose.glucoseId == null) {
441
- Log.w("handleGlucoseData", "Glucose ID is null, skipping processing")
442
- return
443
- }
325
+ // Remove existing observer if any to prevent memory leaks
326
+ stopObservingGlucoseData()
327
+
328
+ // Create new observer with explicit nullable parameter type
329
+ glucoseObserver = Observer<PocGlucose?> { pocGlucose ->
330
+ if (pocGlucose != null) {
331
+ // Add timestamp check to prevent processing old data
332
+ val currentTime = System.currentTimeMillis()
333
+ val dataAge = currentTime - pocGlucose.timeInMillis
334
+
335
+ // Only process data that's relatively recent (within last 10 minutes as example)
336
+ // Adjust this threshold based on your requirements
337
+ if (dataAge <= 10 * 60 * 1000L) { // 10 minutes
338
+ handleGlucoseData(pocGlucose)
339
+ } else {
340
+ Log.d("observeGlucoseData", "Skipping old glucose data: age = ${dataAge}ms")
341
+ }
342
+ } else {
343
+ Log.w("observeGlucoseData", "Received null glucose data - skipping processing")
344
+ }
345
+ }
444
346
 
445
- Log.d("handleGlucoseData", "Processing glucose data: ${pocGlucose.glucoseId}")
446
-
447
- if (pocGlucose.errorCode == enumError.NONE) {
448
- if (pocGlucose.showGlucoseMG > 0) {
449
- val dto: GlucoseLog = mapToDto(pocGlucose)
450
- val logs: ArrayList<GlucoseLog> = ArrayList()
451
- logs.add(dto)
452
-
453
- val request: GlucoseLogRequest =
454
- GlucoseLogRequest(vendor = "GoodFlip", logs = logs)
455
- val gson: Gson = GsonBuilder().create()
456
- val json = gson.toJson(request)
457
-
458
- Log.d("Glucose data 3 min==> ", "Glucose data 3 min==> final Json: $json")
459
-
460
- authenticateSDKService.postCGMData(
461
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
462
- data = json,
463
- token = userToken,
464
- responseListener = object : AuthenticateSDKService.ResponseListener {
465
- override fun onResponseSuccess(response: String) {
466
- updateSyncMetadata(pocGlucose)
467
- Log.d("CGM Data", "Single glucose data uploaded successfully")
468
- }
469
-
470
- override fun onResponseFail() {
471
- Log.e("CGM Data", "Failed to upload single glucose data")
472
- }
473
- }
474
- )
475
- } else {
476
- Log.d(
477
- "handleGlucoseData",
478
- "Glucose value is 0 or negative: ${pocGlucose.showGlucoseMG}"
479
- )
480
- }
481
- } else {
482
- Log.d("handleGlucoseData", "Glucose data has error: ${pocGlucose.errorCode}")
483
- handleGlucoseError(pocGlucose)
484
- }
485
- } catch (e: Exception) {
486
- Log.e("handleGlucoseData", "Error handling glucose data: ${e.message}")
487
- e.printStackTrace()
347
+ // Add observer on main thread
348
+ Handler(Looper.getMainLooper()).post {
349
+ glucoseObserver?.let { observer ->
350
+ try {
351
+ mModel.latestGlucose.observeForever(observer)
352
+ isObserving = true
353
+ Log.d("observeGlucoseData", "Live glucose observer started successfully")
354
+ } catch (e: Exception) {
355
+ Log.e("observeGlucoseData", "Error adding observer: ${e.message}")
356
+ glucoseObserver = null
357
+ }
488
358
  }
359
+ }
360
+ } catch (e: Exception) {
361
+ Log.e("observeGlucoseData", "observeGlucoseData: ${e.message}")
362
+ e.printStackTrace()
489
363
  }
364
+ }
365
+
366
+ private fun handleGlucoseData(pocGlucose: PocGlucose) {
367
+ try {
368
+ // Additional safety check
369
+ if (pocGlucose.glucoseId == null) {
370
+ Log.w("handleGlucoseData", "Glucose ID is null, skipping processing")
371
+ return
372
+ }
490
373
 
491
- // Extract error handling logic
492
- private fun handleGlucoseError(pocGlucose: PocGlucose) {
493
- try {
494
- // Additional safety check for deviceId
495
- if (pocGlucose.deviceId == null) {
496
- Log.e("handleGlucoseError", "Device ID is null, cannot process error")
497
- return
498
- }
499
-
500
- val deviceInfo = mModel.getDeviceInfo(pocGlucose.deviceId.toInt())
501
- deviceInfo?.let {
502
- val statusId = when (pocGlucose.errorCode) {
503
- enumError.ERROR_FLOODING_WATER -> {
504
- Log.d("handleGlucoseError", "Moisture detected")
505
- DeviceStatus.MOISTURE_DETECT.id
506
- }
507
-
508
- enumError.ERROR_CURRENT_SMALL,
509
- enumError.ERROR_NOISE,
510
- enumError.ERROR_SENSITIVITY_ATTENUATION -> {
511
- Log.d("handleGlucoseError", "Weak signal detected")
512
- DeviceStatus.WEAK_SIGNAL.id
513
- }
514
-
515
- else -> {
516
- Log.d(
517
- "handleGlucoseError",
518
- "Common error detected: ${pocGlucose.errorCode}"
519
- )
520
- DeviceStatus.ERROR_COMMON.id
521
- }
522
- }
374
+ Log.d("handleGlucoseData", "Processing glucose data: ${pocGlucose.glucoseId}")
375
+
376
+ if (pocGlucose.errorCode == enumError.NONE) {
377
+ if (pocGlucose.showGlucoseMG > 0) {
378
+ val dto: GlucoseLog = mapToDto(pocGlucose)
379
+ val logs: ArrayList<GlucoseLog> = ArrayList()
380
+ logs.add(dto)
381
+
382
+ val request: GlucoseLogRequest =
383
+ GlucoseLogRequest(vendor = "GoodFlip", logs = logs)
384
+ val gson: Gson = GsonBuilder().create()
385
+ val json = gson.toJson(request)
386
+
387
+ Log.d("Glucose data 3 min==> ", "Glucose data 3 min==> final Json: $json")
388
+
389
+ authenticateSDKService.postCGMData(
390
+ environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
391
+ data = json,
392
+ token = userToken,
393
+ responseListener = object : AuthenticateSDKService.ResponseListener {
394
+ override fun onResponseSuccess(response: String) {
395
+ updateSyncMetadata(pocGlucose)
396
+ Log.d("CGM Data", "Single glucose data uploaded successfully")
397
+ }
523
398
 
524
- postEventDataToAPI(it, statusId, it.qrMessage)
525
- resetDebounceTimer()
526
- } ?: run {
527
- Log.e(
528
- "handleGlucoseError",
529
- "Device info not found for deviceId: ${pocGlucose.deviceId}"
530
- )
399
+ override fun onResponseFail() {
400
+ Log.e("CGM Data", "Failed to upload single glucose data")
401
+ }
531
402
  }
532
- } catch (e: Exception) {
533
- Log.e("handleGlucoseError", "Error handling glucose error: ${e.message}")
534
- e.printStackTrace()
403
+ )
404
+ } else {
405
+ Log.d(
406
+ "handleGlucoseData",
407
+ "Glucose value is 0 or negative: ${pocGlucose.showGlucoseMG}"
408
+ )
535
409
  }
410
+ } else {
411
+ Log.d("handleGlucoseData", "Glucose data has error: ${pocGlucose.errorCode}")
412
+ handleGlucoseError(pocGlucose)
413
+ }
414
+ } catch (e: Exception) {
415
+ Log.e("handleGlucoseData", "Error handling glucose data: ${e.message}")
416
+ e.printStackTrace()
536
417
  }
418
+ }
419
+
420
+ // Extract error handling logic
421
+ private fun handleGlucoseError(pocGlucose: PocGlucose) {
422
+ try {
423
+ // Additional safety check for deviceId
424
+ if (pocGlucose.deviceId == null) {
425
+ Log.e("handleGlucoseError", "Device ID is null, cannot process error")
426
+ return
427
+ }
537
428
 
538
- /*@ReactMethod
539
- fun observeAllGlucoseData(token: String) {
540
- userToken = token
541
- Log.e("userToken", "userToken: ${userToken}")
542
- try {
543
- val lastSyncData = prefsHelper.lastSyncData
544
- Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
545
-
546
- if (lastSyncData != null) {
547
- CoroutineScope(Dispatchers.IO).launch {
548
- getGlucoseDataAndUploadInBatch(
549
- mModel.getGlucoseBetweenTime(
550
- lastSyncData.lastSyncTime
551
- )
429
+ val deviceInfo = mModel.getDeviceInfo(pocGlucose.deviceId.toInt())
430
+ deviceInfo?.let {
431
+ val statusId = when (pocGlucose.errorCode) {
432
+ enumError.ERROR_FLOODING_WATER -> {
433
+ Log.d("handleGlucoseError", "Moisture detected")
434
+ DeviceStatus.MOISTURE_DETECT.id
435
+ }
436
+
437
+ enumError.ERROR_CURRENT_SMALL,
438
+ enumError.ERROR_NOISE,
439
+ enumError.ERROR_SENSITIVITY_ATTENUATION -> {
440
+ Log.d("handleGlucoseError", "Weak signal detected")
441
+ DeviceStatus.WEAK_SIGNAL.id
442
+ }
443
+
444
+ else -> {
445
+ Log.d(
446
+ "handleGlucoseError",
447
+ "Common error detected: ${pocGlucose.errorCode}"
552
448
  )
449
+ DeviceStatus.ERROR_COMMON.id
553
450
  }
554
- } else {
555
- observeGlucoseData(userToken)
556
451
  }
557
452
 
558
- } catch (e: Exception) {
559
- Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
453
+ postEventDataToAPI(it, statusId, it.qrMessage)
454
+ resetDebounceTimer()
455
+ } ?: run {
456
+ Log.e(
457
+ "handleGlucoseError",
458
+ "Device info not found for deviceId: ${pocGlucose.deviceId}"
459
+ )
560
460
  }
561
- }*/
562
-
563
-
564
- @ReactMethod
565
- fun observeAllGlucoseData(token: String) {
566
- userToken = token
567
- Log.e("userToken", "userToken: $token")
568
-
569
- // Stop current observation to prevent duplicates
570
- stopObservingGlucoseData()
571
-
572
- try {
573
- val lastSyncData = prefsHelper.lastSyncData
574
- Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
575
-
576
- if (lastSyncData != null) {
577
- CoroutineScope(Dispatchers.IO).launch {
578
- val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.lastSyncTime)
579
- if (glucoseData != null) {
580
- getGlucoseDataAndUploadInBatch(glucoseData)
581
- } else {
582
- Log.d(
583
- "observeAllGlucoseData",
584
- "No glucose data found, starting live observation"
585
- )
586
- observeGlucoseData(userToken)
587
- }
588
- }
589
- } else {
590
- observeGlucoseData(userToken)
591
- }
592
- } catch (e: Exception) {
593
- Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
594
- }
461
+ } catch (e: Exception) {
462
+ Log.e("handleGlucoseError", "Error handling glucose error: ${e.message}")
463
+ e.printStackTrace()
595
464
  }
465
+ }
596
466
 
597
- @ReactMethod
598
- fun stopObservingGlucoseData() {
599
- try {
600
- glucoseObserver?.let { observer ->
601
- Handler(Looper.getMainLooper()).post {
602
- try {
603
- mModel.latestGlucose.removeObserver(observer)
604
- isObserving = false
605
- Log.d("stopObservingGlucoseData", "Observer removed successfully")
606
- } catch (e: Exception) {
607
- Log.e("stopObservingGlucoseData", "Error removing observer: ${e.message}")
608
- }
609
- }
610
- }
611
- glucoseObserver = null
612
- } catch (e: Exception) {
613
- Log.e("stopObservingGlucoseData", "Error stopping observer: ${e.message}")
614
- }
615
- }
616
467
 
617
- fun getGlucoseDataAndUploadInBatch(dataList: List<PocGlucose>) {
618
- if (dataList.isEmpty()) {
619
- Log.d("getGlucoseDataAndUploadInBatch", "No data to upload, starting live observation")
620
- if (!isObserving) {
621
- observeGlucoseData(userToken)
622
- }
623
- return
624
- }
468
+ @ReactMethod
469
+ fun observeAllGlucoseData(token: String) {
470
+ userToken = token
625
471
 
626
- val batchSize = 40
627
- val chunks = dataList.chunked(batchSize)
472
+ // Stop current observation to prevent duplicates
473
+ stopObservingGlucoseData()
628
474
 
629
- Log.d("getGlucoseDataAndUploadInBatch", "Starting batch upload with ${chunks.size} batches")
475
+ try {
476
+ val lastSyncData = prefsHelper.lastSyncData
477
+ Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
630
478
 
479
+ if (lastSyncData != null) {
631
480
  CoroutineScope(Dispatchers.IO).launch {
632
- var lastSyncedRecord: PocGlucose? = null
633
- var allBatchesSuccessful = true
634
-
635
- for ((index, batch) in chunks.withIndex()) {
636
- try {
637
- val transformedLogs = batch.filter { it.showGlucoseMG > 0 }.map { pocGlucose ->
638
- CgmLog(
639
- timeInMillis = pocGlucose.timeInMillis,
640
- countdownMinutes = pocGlucose.countdownMinutes,
641
- countdownDays = pocGlucose.countdownDays,
642
- hypoglycemiaEarlyWarnMinutes = pocGlucose.hypoglycemiaEarlyWarnMinutes,
643
- showGlucoseMG = pocGlucose.showGlucoseMG,
644
- glucoseId = pocGlucose.glucoseId,
645
- name = pocGlucose.name,
646
- bytes = pocGlucose.bytes,
647
- showGlucose = pocGlucose.showGlucose,
648
- Ib = pocGlucose.ib,
649
- Iw = pocGlucose.iw,
650
- countdownHours = pocGlucose.countdownHours,
651
- T = pocGlucose.t,
652
- year = pocGlucose.year,
653
- month = pocGlucose.month,
654
- day = pocGlucose.day,
655
- hour = pocGlucose.hour,
656
- minute = pocGlucose.minute,
657
- trendObject = com.mytatvarnsdk.model.TrendObject(
658
- trendId = pocGlucose.trend.trendId,
659
- drawableId = pocGlucose.trend.drawableId,
660
- widgetImg = pocGlucose.trend.widgetImg,
661
- apsChangeRate = pocGlucose.trend.apsChangeRate
662
- ),
663
- glucoseStatusObject = com.mytatvarnsdk.model.GlucoseStatusObject(
664
- statusId = pocGlucose.glucoseStatus.statusId
665
- ),
666
- errorObject = com.mytatvarnsdk.model.ErrorObject(
667
- errorId = pocGlucose.errorCode.errorId,
668
- sound = pocGlucose.errorCode.sound
669
- )
670
- )
671
- }
672
-
673
- if (transformedLogs.isEmpty()) {
674
- Log.d("Batch Upload", "Batch $index skipped - no valid glucose readings")
675
- continue
676
- }
677
-
678
- val allResult = AllCGMLogRequest(vendor = "GoodFlip", logs = transformedLogs)
679
- val json = Gson().toJson(allResult)
680
-
681
- Log.d("Batch Upload JSON", "Batch $index with ${transformedLogs.size} records")
682
- logLongJson("Batch $index JSON=>>> ", json)
683
-
684
- val uploadSuccessful = uploadBatchSynchronously(json, index)
685
-
686
- if (uploadSuccessful) {
687
- lastSyncedRecord = batch.lastOrNull()
688
- updateSyncMetadata(lastSyncedRecord)
689
- Log.d("Batch Upload", "✅ Batch $index uploaded successfully")
690
- } else {
691
- allBatchesSuccessful = false
692
- Log.e("Batch Upload", "❌ Batch $index failed, stopping further uploads")
693
- break
694
- }
695
-
696
- delay(500L) // Rate limiting between batches
697
-
698
- } catch (e: Exception) {
699
- Log.e("Batch Upload", "❌ Batch $index exception: ${e.message}")
700
- allBatchesSuccessful = false
701
- break
702
- }
703
- }
704
-
705
- // Handle last synced record error status
706
- lastSyncedRecord?.let { record ->
707
- if (record.errorCode != enumError.NONE) {
708
- handleGlucoseError(record)
709
- }
481
+ val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.lastSyncTime)
482
+ if (glucoseData != null && glucoseData.isNotEmpty()) {
483
+ // Process batch data and wait for completion
484
+ processBatchDataAndStartObserver(glucoseData)
485
+ } else {
486
+ Log.d("observeAllGlucoseData", "No historical data found, starting live observation")
487
+ // Start live observation immediately if no historical data
488
+ Handler(Looper.getMainLooper()).post {
489
+ observeGlucoseData(userToken)
710
490
  }
491
+ }
492
+ }
493
+ } else {
494
+ // No sync data exists, start live observation
495
+ observeGlucoseData(userToken)
496
+ }
497
+ } catch (e: Exception) {
498
+ Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
499
+ // Fallback to live observation on error
500
+ observeGlucoseData(userToken)
501
+ }
502
+ }
503
+
504
+ // New method to handle batch processing and ensure proper sequencing
505
+ private suspend fun processBatchDataAndStartObserver(dataList: List<PocGlucose>) {
506
+ try {
507
+ // Process all batches
508
+ val success = processBatchDataSynchronously(dataList)
509
+
510
+ // Only start live observer after batch processing is completely done
511
+ if (success) {
512
+ Log.d(
513
+ "processBatchDataAndStartObserver",
514
+ "Batch processing completed successfully, starting live observer"
515
+ )
516
+ } else {
517
+ Log.w(
518
+ "processBatchDataAndStartObserver",
519
+ "Batch processing had failures, still starting live observer"
520
+ )
521
+ }
711
522
 
712
- Log.d("Batch Upload", "Batch upload completed. Success: $allBatchesSuccessful")
523
+ // Start live observation on main thread after batch completion
524
+ Handler(Looper.getMainLooper()).post {
525
+ if (!isObserving) {
526
+ observeGlucoseData(userToken)
527
+ }
528
+ }
713
529
 
714
- // Only start observing new data if we're not already doing so
715
- if (!isObserving) {
716
- observeGlucoseData(userToken)
717
- }
530
+ } catch (e: Exception) {
531
+ Log.e("processBatchDataAndStartObserver", "Error in batch processing: ${e.message}")
532
+ // Start live observation even on error
533
+ Handler(Looper.getMainLooper()).post {
534
+ if (!isObserving) {
535
+ observeGlucoseData(userToken)
718
536
  }
537
+ }
719
538
  }
539
+ }
720
540
 
721
- // Helper method for synchronous batch upload
722
- private suspend fun uploadBatchSynchronously(json: String, batchIndex: Int): Boolean {
723
- return suspendCoroutine { continuation ->
724
- try {
725
- authenticateSDKService.postCGMData(
726
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
727
- data = json,
728
- token = userToken,
729
- responseListener = object : AuthenticateSDKService.ResponseListener {
730
- override fun onResponseSuccess(response: String) {
731
- continuation.resume(true)
732
- }
733
-
734
- override fun onResponseFail() {
735
- continuation.resume(false)
736
- }
737
- }
738
- )
739
- } catch (e: Exception) {
740
- Log.e("uploadBatchSynchronously", "Exception in batch $batchIndex: ${e.message}")
741
- continuation.resume(false)
742
- }
743
- }
541
+ // Updated batch processing method with better sync control
542
+ private suspend fun processBatchDataSynchronously(dataList: List<PocGlucose>): Boolean {
543
+ if (dataList.isEmpty()) {
544
+ Log.d("processBatchDataSynchronously", "No data to upload")
545
+ return true
744
546
  }
745
547
 
746
- private fun updateSyncMetadata(lastRecord: PocGlucose?) {
747
- lastRecord?.let {
748
- try {
749
- val syncData = SyncMeta(
750
- Date().time,
751
- it.timeInMillis,
752
- it.deviceId,
753
- it.glucoseId
754
- )
755
- prefsHelper.lastSyncData = syncData
756
- Log.d("Sync Metadata", "Last sync data stored: ${Gson().toJson(syncData)}")
757
- } catch (e: Exception) {
758
- Log.e("updateSyncMetadata", "Error updating sync metadata: ${e.message}")
759
- }
548
+ val batchSize = 40
549
+ val chunks = dataList.chunked(batchSize)
550
+ var lastSyncedRecord: PocGlucose? = null
551
+ var allBatchesSuccessful = true
552
+
553
+ Log.d("processBatchDataSynchronously", "Starting batch upload with ${chunks.size} batches")
554
+
555
+ for ((index, batch) in chunks.withIndex()) {
556
+ try {
557
+ val transformedLogs = batch.filter { it.showGlucoseMG > 0 }.map { pocGlucose ->
558
+ CgmLog(
559
+ timeInMillis = pocGlucose.timeInMillis,
560
+ countdownMinutes = pocGlucose.countdownMinutes,
561
+ countdownDays = pocGlucose.countdownDays,
562
+ hypoglycemiaEarlyWarnMinutes = pocGlucose.hypoglycemiaEarlyWarnMinutes,
563
+ showGlucoseMG = pocGlucose.showGlucoseMG,
564
+ glucoseId = pocGlucose.glucoseId,
565
+ name = pocGlucose.name,
566
+ bytes = pocGlucose.bytes,
567
+ showGlucose = pocGlucose.showGlucose,
568
+ Ib = pocGlucose.ib,
569
+ Iw = pocGlucose.iw,
570
+ countdownHours = pocGlucose.countdownHours,
571
+ T = pocGlucose.t,
572
+ year = pocGlucose.year,
573
+ month = pocGlucose.month,
574
+ day = pocGlucose.day,
575
+ hour = pocGlucose.hour,
576
+ minute = pocGlucose.minute,
577
+ trendObject = com.mytatvarnsdk.model.TrendObject(
578
+ trendId = pocGlucose.trend.trendId,
579
+ drawableId = pocGlucose.trend.drawableId,
580
+ widgetImg = pocGlucose.trend.widgetImg,
581
+ apsChangeRate = pocGlucose.trend.apsChangeRate
582
+ ),
583
+ glucoseStatusObject = com.mytatvarnsdk.model.GlucoseStatusObject(
584
+ statusId = pocGlucose.glucoseStatus.statusId
585
+ ),
586
+ errorObject = com.mytatvarnsdk.model.ErrorObject(
587
+ errorId = pocGlucose.errorCode.errorId,
588
+ sound = pocGlucose.errorCode.sound
589
+ )
590
+ )
760
591
  }
761
- }
762
592
 
763
- /*fun getGlucoseDataAndUploadInBatch(dataList: List<PocGlucose>) {
764
- if (dataList.isNotEmpty()) {
765
- val batchSize = 40
593
+ if (transformedLogs.isEmpty()) {
594
+ Log.d("processBatchDataSynchronously", "Batch $index skipped - no valid glucose readings")
595
+ continue
596
+ }
766
597
 
767
- val chunks = dataList.chunked(batchSize)
598
+ val allResult = AllCGMLogRequest(vendor = "GoodFlip", logs = transformedLogs)
599
+ val json = Gson().toJson(allResult)
768
600
 
769
- CoroutineScope(Dispatchers.IO).launch {
601
+ Log.d("Batch Upload", "Processing batch $index with ${transformedLogs.size} records")
602
+ logLongJson("Batch $index JSON=>>> ", json)
770
603
 
771
- var lastSyncedRecord: PocGlucose? = null
772
-
773
- for ((index, batch) in chunks.withIndex()) {
774
- try {
775
- val transformedLogs = batch.filter { it.showGlucoseMG > 0 }.map {
776
- CgmLog(
777
- timeInMillis = it.timeInMillis,
778
- countdownMinutes = it.countdownMinutes,
779
- countdownDays = it.countdownDays,
780
- hypoglycemiaEarlyWarnMinutes = it.hypoglycemiaEarlyWarnMinutes,
781
- showGlucoseMG = it.showGlucoseMG,
782
- glucoseId = it.glucoseId,
783
- name = it.name,
784
- bytes = it.bytes,
785
- showGlucose = it.showGlucose,
786
- Ib = it.ib,
787
- Iw = it.iw,
788
- countdownHours = it.countdownHours,
789
- T = it.t,
790
- year = it.year,
791
- month = it.month,
792
- day = it.day,
793
- hour = it.hour,
794
- minute = it.minute,
795
- trendObject = com.mytatvarnsdk.model.TrendObject(
796
- trendId = it.trend.trendId,
797
- drawableId = it.trend.drawableId,
798
- widgetImg = it.trend.widgetImg,
799
- apsChangeRate = it.trend.apsChangeRate
800
- ),
801
- glucoseStatusObject = com.mytatvarnsdk.model.GlucoseStatusObject(
802
- statusId = it.glucoseStatus.statusId
803
- ),
804
- errorObject = com.mytatvarnsdk.model.ErrorObject(
805
- errorId = it.errorCode.errorId,
806
- sound = it.errorCode.sound
807
- )
808
- )
809
- }
604
+ val uploadSuccessful = uploadBatchSynchronously(json, index)
810
605
 
811
- val allResult = AllCGMLogRequest(
812
- vendor = "GoodFlip",
813
- logs = transformedLogs
814
- )
815
-
816
- val json = Gson().toJson(allResult)
817
- logLongJson("Final JSON=>>> ", json)
818
-
819
- // sendDataToReact(Gson().toJson(batch).toString(), "DATA_FETCH", "CGMData")
820
-
821
- authenticateSDKService.postCGMData(
822
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
823
- data = json.toString(),
824
- token = userToken,
825
- responseListener = object : AuthenticateSDKService.ResponseListener {
826
- override fun onResponseSuccess() {
827
- lastSyncedRecord = batch.lastOrNull()
828
-
829
- lastSyncedRecord?.let {
830
- val syncData =
831
- SyncMeta(
832
- Date().time,
833
- it.timeInMillis,
834
- it.deviceId,
835
- it.glucoseId
836
- )
837
- prefsHelper.lastSyncData = syncData
838
- Log.d(
839
- "lastSyncedRecord stored:--- ",
840
- Gson().toJson(syncData).toString()
841
- )
842
- }
843
- }
606
+ if (uploadSuccessful) {
607
+ lastSyncedRecord = batch.lastOrNull()
608
+ // Update sync metadata after each successful batch
609
+ updateSyncMetadata(lastSyncedRecord)
610
+ Log.d("Batch Upload", "✅ Batch $index uploaded and synced successfully")
611
+ } else {
612
+ allBatchesSuccessful = false
613
+ Log.e("Batch Upload", "❌ Batch $index failed")
614
+ // Continue with next batch instead of breaking (optional based on your needs)
615
+ // break
616
+ }
844
617
 
845
- override fun onResponseFail() {
846
- Log.e("Batch Upload", "❌ Batch $index failed")
847
- }
848
- }
849
- )
618
+ // Rate limiting between batches
619
+ delay(500L)
850
620
 
851
- delay(500L)
852
- } catch (e: Exception) {
853
- Log.e("Batch Upload", "❌ Batch $index exception: ${e.message}")
854
- break // Optional: stop further processing if exception occurs
855
- }
856
- }
621
+ } catch (e: Exception) {
622
+ Log.e("Batch Upload", "❌ Batch $index exception: ${e.message}")
623
+ allBatchesSuccessful = false
624
+ // Continue processing other batches
625
+ }
626
+ }
857
627
 
628
+ // Handle error status for the last processed record
629
+ lastSyncedRecord?.let { record ->
630
+ if (record.errorCode != enumError.NONE) {
631
+ handleGlucoseError(record)
632
+ }
633
+ }
858
634
 
859
- if (lastSyncedRecord != null) {
860
- if (lastSyncedRecord?.errorCode != enumError.NONE) {
861
- if (lastSyncedRecord?.errorCode == enumError.ERROR_FLOODING_WATER) {
862
- lastDeviceStatus =
863
- lastSyncedRecord?.deviceId!!.toInt().let { mModel.getDeviceInfo(it) }
864
- lastDeviceStatus?.let {
865
- postEventDataToAPI(
866
- it,
867
- DeviceStatus.MOISTURE_DETECT.id
868
- )
869
- }
870
- resetDebounceTimer()
871
- } else if (lastSyncedRecord?.errorCode == enumError.ERROR_CURRENT_SMALL || lastSyncedRecord?.errorCode == enumError.ERROR_NOISE || lastSyncedRecord?.errorCode == enumError.ERROR_SENSITIVITY_ATTENUATION) {
872
- lastDeviceStatus =
873
- lastSyncedRecord?.deviceId!!.toInt().let { mModel.getDeviceInfo(it) }
874
- lastDeviceStatus?.let {
875
- postEventDataToAPI(
876
- it,
877
- DeviceStatus.WEAK_SIGNAL.id
878
- )
879
- }
880
- resetDebounceTimer()
881
- } else {
882
- lastDeviceStatus =
883
- lastSyncedRecord?.deviceId!!.toInt().let { mModel.getDeviceInfo(it) }
884
- lastDeviceStatus?.let {
885
- postEventDataToAPI(
886
- it,
887
- DeviceStatus.ERROR_COMMON.id
888
- )
889
- }
890
- resetDebounceTimer()
891
- }
892
- }
893
- }
635
+ Log.d(
636
+ "processBatchDataSynchronously",
637
+ "Batch processing completed. Overall success: $allBatchesSuccessful"
638
+ )
639
+ return allBatchesSuccessful
640
+ }
894
641
 
895
- Log.e("Batch Upload", "All data uploaded")
896
642
 
897
- observeGlucoseData(userToken)
643
+ @ReactMethod
644
+ fun stopObservingGlucoseData() {
645
+ try {
646
+ glucoseObserver?.let { observer ->
647
+ Handler(Looper.getMainLooper()).post {
648
+ try {
649
+ mModel.latestGlucose.removeObserver(observer)
650
+ isObserving = false
651
+ Log.d("stopObservingGlucoseData", "Observer removed successfully")
652
+ } catch (e: Exception) {
653
+ Log.e("stopObservingGlucoseData", "Error removing observer: ${e.message}")
654
+ }
898
655
  }
899
- } else {
900
- observeGlucoseData(userToken)
901
656
  }
902
- }*/
903
-
904
- fun logLongJson(tag: String, message: String) {
905
- val maxLogSize = 4000
906
- for (i in 0..message.length / maxLogSize) {
907
- val start = i * maxLogSize
908
- val end = (i + 1) * maxLogSize
909
- if (start < message.length) {
910
- Log.d(tag, message.substring(start, minOf(end, message.length)))
911
- }
912
- }
657
+ glucoseObserver = null
658
+ } catch (e: Exception) {
659
+ Log.e("stopObservingGlucoseData", "Error stopping observer: ${e.message}")
913
660
  }
661
+ }
914
662
 
915
- fun mapToDto(glucose: PocGlucose): GlucoseLog {
916
- val dto: GlucoseLog = GlucoseLog()
917
- dto.timeInMillis = glucose.getTimeInMillis()
918
- dto.countdownMinutes = glucose.getCountdownMinutes()
919
- dto.countdownHours = glucose.getCountdownHours()
920
- dto.countdownDays = glucose.getCountdownDays()
921
- dto.hypoglycemiaEarlyWarnMinutes = glucose.getHypoglycemiaEarlyWarnMinutes()
922
- dto.showGlucoseMG = glucose.getShowGlucoseMG()
923
- dto.glucoseId = glucose.getGlucoseId()
924
- dto.name = glucose.getName()
925
- dto.showGlucose = glucose.getShowGlucose()
926
- dto.Ib = glucose.getIb()
927
- dto.Iw = glucose.getIw()
928
- dto.T = glucose.getT()
929
- dto.year = glucose.getYear()
930
- dto.month = glucose.getMonth()
931
- dto.day = glucose.getDay()
932
- dto.hour = glucose.getHour()
933
- dto.minute = glucose.getMinute()
934
-
935
- // Convert byte[] to List<Integer>
936
- dto.bytes = ArrayList()
937
- for (b in glucose.getBytes()) {
938
- dto.bytes?.add(b.toInt() and 0xFF) // Prevent negative values
939
- }
663
+ // Helper method for synchronous batch upload
664
+ private suspend fun uploadBatchSynchronously(json: String, batchIndex: Int): Boolean {
665
+ return suspendCoroutine { continuation ->
666
+ try {
667
+ authenticateSDKService.postCGMData(
668
+ environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
669
+ data = json,
670
+ token = userToken,
671
+ responseListener = object : AuthenticateSDKService.ResponseListener {
672
+ override fun onResponseSuccess(response: String) {
673
+ continuation.resume(true)
674
+ }
675
+
676
+ override fun onResponseFail() {
677
+ continuation.resume(false)
678
+ }
679
+ }
680
+ )
681
+ } catch (e: Exception) {
682
+ Log.e("uploadBatchSynchronously", "Exception in batch $batchIndex: ${e.message}")
683
+ continuation.resume(false)
684
+ }
685
+ }
686
+ }
940
687
 
941
- // Trend
942
- val trendObj: TrendObject = TrendObject()
943
- trendObj.trendId = glucose.getTrend().getTrendId()
944
- trendObj.drawableId = glucose.getTrend().getDrawableId()
945
- trendObj.widgetImg = glucose.getTrend().getWidgetImg()
946
- trendObj.apsChangeRate = glucose.getTrend().getApsChangeRate()
947
- dto.trendObject = trendObj
948
-
949
- // Status
950
- val statusObj: GlucoseStatusObject = GlucoseStatusObject()
951
- statusObj.statusId = glucose.getGlucoseStatus().getStatusId()
952
- dto.glucoseStatusObject = statusObj
953
-
954
- // Error
955
- val errorObj: ErrorObject = ErrorObject()
956
- errorObj.errorId = glucose.getErrorCode().getErrorId()
957
- errorObj.sound = glucose.getErrorCode().getSound().toString()
958
- dto.errorObject = errorObj
959
-
960
- return dto
688
+ private fun updateSyncMetadata(lastRecord: PocGlucose?) {
689
+ lastRecord?.let {
690
+ try {
691
+ val syncData = SyncMeta(
692
+ Date().time,
693
+ it.timeInMillis,
694
+ it.deviceId,
695
+ it.glucoseId
696
+ )
697
+ prefsHelper.lastSyncData = syncData
698
+ Log.d(
699
+ "Sync Metadata",
700
+ "Sync metadata updated: glucoseId=${it.glucoseId}, time=${it.timeInMillis}"
701
+ )
702
+ } catch (e: Exception) {
703
+ Log.e("updateSyncMetadata", "Error updating sync metadata: ${e.message}")
704
+ }
961
705
  }
706
+ }
962
707
 
963
708
 
964
- @ReactMethod
965
- fun sendDataToReact(data: String, status: String, eventName: String) {
966
- Log.d("sendDataToReact: data ", data)
709
+ fun logLongJson(tag: String, message: String) {
710
+ val maxLogSize = 4000
711
+ for (i in 0..message.length / maxLogSize) {
712
+ val start = i * maxLogSize
713
+ val end = (i + 1) * maxLogSize
714
+ if (start < message.length) {
715
+ Log.d(tag, message.substring(start, minOf(end, message.length)))
716
+ }
717
+ }
718
+ }
719
+
720
+ fun mapToDto(glucose: PocGlucose): GlucoseLog {
721
+ val dto: GlucoseLog = GlucoseLog()
722
+ dto.timeInMillis = glucose.getTimeInMillis()
723
+ dto.countdownMinutes = glucose.getCountdownMinutes()
724
+ dto.countdownHours = glucose.getCountdownHours()
725
+ dto.countdownDays = glucose.getCountdownDays()
726
+ dto.hypoglycemiaEarlyWarnMinutes = glucose.getHypoglycemiaEarlyWarnMinutes()
727
+ dto.showGlucoseMG = glucose.getShowGlucoseMG()
728
+ dto.glucoseId = glucose.getGlucoseId()
729
+ dto.name = glucose.getName()
730
+ dto.showGlucose = glucose.getShowGlucose()
731
+ dto.Ib = glucose.getIb()
732
+ dto.Iw = glucose.getIw()
733
+ dto.T = glucose.getT()
734
+ dto.year = glucose.getYear()
735
+ dto.month = glucose.getMonth()
736
+ dto.day = glucose.getDay()
737
+ dto.hour = glucose.getHour()
738
+ dto.minute = glucose.getMinute()
739
+
740
+ // Convert byte[] to List<Integer>
741
+ dto.bytes = ArrayList()
742
+ for (b in glucose.getBytes()) {
743
+ dto.bytes?.add(b.toInt() and 0xFF) // Prevent negative values
744
+ }
967
745
 
968
- try {
969
- val map: WritableMap = Arguments.createMap().apply {
970
- putString("data", data)
971
- putString("status", status)
972
- }
746
+ // Trend
747
+ val trendObj: TrendObject = TrendObject()
748
+ trendObj.trendId = glucose.getTrend().getTrendId()
749
+ trendObj.drawableId = glucose.getTrend().getDrawableId()
750
+ trendObj.widgetImg = glucose.getTrend().getWidgetImg()
751
+ trendObj.apsChangeRate = glucose.getTrend().getApsChangeRate()
752
+ dto.trendObject = trendObj
753
+
754
+ // Status
755
+ val statusObj: GlucoseStatusObject = GlucoseStatusObject()
756
+ statusObj.statusId = glucose.getGlucoseStatus().getStatusId()
757
+ dto.glucoseStatusObject = statusObj
758
+
759
+ // Error
760
+ val errorObj: ErrorObject = ErrorObject()
761
+ errorObj.errorId = glucose.getErrorCode().getErrorId()
762
+ errorObj.sound = glucose.getErrorCode().getSound().toString()
763
+ dto.errorObject = errorObj
764
+
765
+ return dto
766
+ }
767
+
768
+
769
+ @ReactMethod
770
+ fun sendDataToReact(data: String, status: String, eventName: String) {
771
+ Log.d("sendDataToReact: data ", data)
772
+
773
+ try {
774
+ val map: WritableMap = Arguments.createMap().apply {
775
+ putString("data", data)
776
+ putString("status", status)
777
+ }
973
778
 
974
- Log.d("sendDataToReact: ", map.toString())
779
+ Log.d("sendDataToReact: ", map.toString())
975
780
 
976
- mReactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
977
- ?.emit(eventName, map)
978
- } catch (e: Exception) {
979
- Log.e("Error sendDataToReact: ", e.message.toString())
980
- e.printStackTrace()
981
- }
781
+ mReactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
782
+ ?.emit(eventName, map)
783
+ } catch (e: Exception) {
784
+ Log.e("Error sendDataToReact: ", e.message.toString())
785
+ e.printStackTrace()
982
786
  }
787
+ }
983
788
 
984
789
  }
985
790