react-native-mytatva-rn-sdk 1.2.29 → 1.2.31

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.
@@ -69,6 +69,8 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
69
69
  var prefsHelper: SharedPreferencesLibraryUtil
70
70
  private val apiScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
71
71
  private var debounceJob: Job? = null
72
+
73
+ @Volatile
72
74
  private var lastDeviceStatus: PocDevice? = null
73
75
 
74
76
  private var glucoseObserver: Observer<PocGlucose?>? = null
@@ -98,6 +100,11 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
98
100
  fun observeDeviceStatus(token: String) {
99
101
  try {
100
102
  userToken = token
103
+
104
+ // Reset previous state
105
+ lastDeviceStatus = null
106
+ debounceJob?.cancel()
107
+
101
108
  Handler(Looper.getMainLooper()).post {
102
109
  mModel.device.observeForever { device ->
103
110
  if (device != lastDeviceStatus) {
@@ -117,66 +124,112 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
117
124
  }
118
125
 
119
126
  @ReactMethod
120
- fun observeTransmitterUnbindStatus(token: String) {
127
+ fun observeTransmitterUnbindStatus(token: String, apiResponse: String) {
121
128
  try {
122
129
  userToken = token
123
130
 
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) {
131
+ /*authenticateSDKService.getCGMData(
132
+ environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
133
+ token = userToken,
134
+ responseListener = object : AuthenticateSDKService.ResponseListener {
135
+ override fun onResponseSuccess(response: String) {
129
136
 
130
- val response = Gson().fromJson(response, CgmSensorResponse::class.java)
131
- val sensor = response.data?.firstOrNull()
137
+ val response = Gson().fromJson(response, CgmSensorResponse::class.java)
138
+ val sensor = response.data?.firstOrNull()
132
139
 
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
140
+ if (sensor != null && !sensor.startDate.isNullOrEmpty() && !sensor.endDate.isNullOrEmpty()) {
141
+ val startDate = sensor.startDate
142
+ val endDate = sensor.endDate
143
+ val sensorId = sensor.sensorId
137
144
 
138
- println("Start Date: $startDate")
139
- println("End Date: $endDate")
145
+ println("Start Date: $startDate")
146
+ println("End Date: $endDate")
140
147
 
141
- if (isCurrentDateInRange(startDate, endDate)) {
148
+ if (isCurrentDateInRange(startDate, endDate)) {
142
149
 
143
- println("Current date is in range")
150
+ println("Current date is in range")
144
151
 
145
- val pocDevice =
146
- RepositoryDevice.getInstance(BApplication.getContext()).latestDeviceIoThread
152
+ val pocDevice =
153
+ RepositoryDevice.getInstance(BApplication.getContext()).latestDeviceIoThread
147
154
 
148
- if (pocDevice != null) {
149
- Log.d("pocDevice logsss", pocDevice.toString())
155
+ if (pocDevice != null) {
156
+ Log.d("pocDevice logsss", pocDevice.toString())
150
157
 
151
- if (pocDevice.isUnBind) {
152
- postEventDataToAPI(
153
- pocDevice,
154
- DeviceStatus.TRANSMITTER_DISCONNECT.id,
155
- pocDevice.qrMessage
156
- )
157
- }
158
- } else {
159
- Log.d("pocDevice logsss", "Data null")
158
+ if (pocDevice.isUnBind) {
159
+ postEventDataToAPI(
160
+ pocDevice,
161
+ DeviceStatus.TRANSMITTER_DISCONNECT.id,
162
+ pocDevice.qrMessage
163
+ )
164
+ }
165
+ } else {
166
+ Log.d("pocDevice logsss", "Data null")
160
167
 
161
- postEventDataToAPI(
162
- pocDevice,
163
- DeviceStatus.TRANSMITTER_DISCONNECT.id,
164
- sensorId
165
- )
166
- }
168
+ postEventDataToAPI(
169
+ pocDevice,
170
+ DeviceStatus.TRANSMITTER_DISCONNECT.id,
171
+ sensorId
172
+ )
173
+ }
167
174
 
168
- } else {
169
- println("Current date is out of range")
170
- }
171
- } else {
172
- println("Start or End date not available")
173
- }
175
+ } else {
176
+ println("Current date is out of range")
174
177
  }
178
+ } else {
179
+ println("Start or End date not available")
180
+ }
181
+ }
182
+
183
+ override fun onResponseFail() {
184
+ }
185
+ }
186
+ )*/
175
187
 
176
- override fun onResponseFail() {
188
+ val response = Gson().fromJson(apiResponse, CgmSensorResponse::class.java)
189
+ val sensor = response.data?.firstOrNull()
190
+
191
+ if (sensor != null && !sensor.startDate.isNullOrEmpty() && !sensor.endDate.isNullOrEmpty()) {
192
+ val startDate = sensor.startDate
193
+ val endDate = sensor.endDate
194
+ val sensorId = sensor.sensorId
195
+
196
+ println("Start Date: $startDate")
197
+ println("End Date: $endDate")
198
+
199
+ if (isCurrentDateInRange(startDate, endDate)) {
200
+
201
+ println("Current date is in range")
202
+
203
+ val pocDevice =
204
+ RepositoryDevice.getInstance(BApplication.getContext()).latestDeviceIoThread
205
+
206
+ if (pocDevice != null) {
207
+ Log.d("pocDevice logsss", pocDevice.toString())
208
+
209
+ if (pocDevice.isUnBind) {
210
+ postEventDataToAPI(
211
+ pocDevice,
212
+ DeviceStatus.TRANSMITTER_DISCONNECT.id,
213
+ pocDevice.qrMessage
214
+ )
215
+ }
216
+ } else {
217
+ Log.d("pocDevice logsss", "Data null")
218
+
219
+ postEventDataToAPI(
220
+ pocDevice,
221
+ DeviceStatus.TRANSMITTER_DISCONNECT.id,
222
+ sensorId
223
+ )
177
224
  }
225
+
226
+ } else {
227
+ println("Current date is out of range")
178
228
  }
179
- )
229
+ } else {
230
+ println("Start or End date not available")
231
+ }
232
+
180
233
 
181
234
  } catch (e: Exception) {
182
235
  Log.e("observeTransmitterUnbindStatus", "observeTransmitterUnbindStatus: ${e.message}")
@@ -317,122 +370,71 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
317
370
  }
318
371
  }
319
372
 
320
-
321
- /*@ReactMethod
322
- fun observeGlucoseData(token: String) {
323
- try {
324
- userToken = token
325
- Handler(Looper.getMainLooper()).post {
326
- //Observe latest glucose data
327
- mModel.latestGlucose.observeForever { pocGlucose ->
328
- if (pocGlucose != null) {
329
-
330
- if (pocGlucose.errorCode == enumError.NONE) {
331
- if (pocGlucose.showGlucoseMG > 0) {
332
- val dto: GlucoseLog = mapToDto(pocGlucose)
333
- val logs: ArrayList<GlucoseLog> = ArrayList()
334
- logs.add(dto)
335
-
336
- val request: GlucoseLogRequest =
337
- GlucoseLogRequest(vendor = "GoodFlip", logs = logs)
338
- val gson: Gson = GsonBuilder().create()
339
- val json = gson.toJson(request)
340
-
341
- Log.d("Glucose data 3 min==> ", "Glucose data 3 min==> final Json: $json")
342
-
343
- authenticateSDKService.postCGMData(
344
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
345
- data = json.toString(),
346
- token = userToken,
347
- responseListener = object :
348
- AuthenticateSDKService.ResponseListener {
349
- override fun onResponseSuccess() {
350
- }
351
-
352
- override fun onResponseFail() {
353
- }
354
- }
355
- )
356
- }
357
- } else {
358
- if (pocGlucose.errorCode == enumError.ERROR_FLOODING_WATER) {
359
- lastDeviceStatus =
360
- pocGlucose.deviceId.toInt().let { mModel.getDeviceInfo(it) }
361
- lastDeviceStatus?.let {
362
- postEventDataToAPI(
363
- it,
364
- DeviceStatus.MOISTURE_DETECT.id
365
- )
366
- }
367
- resetDebounceTimer()
368
- } else if (pocGlucose.errorCode == enumError.ERROR_CURRENT_SMALL || pocGlucose.errorCode == enumError.ERROR_NOISE || pocGlucose.errorCode == enumError.ERROR_SENSITIVITY_ATTENUATION) {
369
- lastDeviceStatus =
370
- pocGlucose.deviceId.toInt().let { mModel.getDeviceInfo(it) }
371
- lastDeviceStatus?.let {
372
- postEventDataToAPI(
373
- it,
374
- DeviceStatus.WEAK_SIGNAL.id
375
- )
376
- }
377
- resetDebounceTimer()
378
- } else {
379
- lastDeviceStatus =
380
- pocGlucose.deviceId.toInt().let { mModel.getDeviceInfo(it) }
381
- lastDeviceStatus?.let {
382
- postEventDataToAPI(
383
- it,
384
- DeviceStatus.ERROR_COMMON.id
385
- )
386
- }
387
- resetDebounceTimer()
388
- }
389
- }
390
- }
391
- }
392
- }
393
- } catch (e: Exception) {
394
- Log.e("observeGlucoseData", "observeGlucoseData: ${e.message}")
395
- }
396
- }*/
397
-
398
373
  @ReactMethod
399
374
  fun observeGlucoseData(token: String) {
400
375
  try {
401
376
  userToken = token
402
377
 
378
+ // Ensure we're not already observing
379
+ if (isObserving && glucoseObserver != null) {
380
+ Log.d(
381
+ "observeGlucoseData",
382
+ "Already observing glucose data, skipping duplicate setup"
383
+ )
384
+ return
385
+ }
386
+
403
387
  // Remove existing observer if any to prevent memory leaks
404
388
  stopObservingGlucoseData()
405
389
 
406
- // Create new observer with explicit nullable parameter type
407
- glucoseObserver = Observer<PocGlucose?> { pocGlucose ->
408
- // Critical fix: Explicitly handle nullable parameter
409
- if (pocGlucose != null) {
410
- handleGlucoseData(pocGlucose)
411
- } else {
412
- Log.w("observeGlucoseData", "Received null glucose data - skipping processing")
390
+ // Wait a bit for cleanup to complete
391
+ Handler(Looper.getMainLooper()).postDelayed({
392
+ // Create new observer with explicit nullable parameter type
393
+ glucoseObserver = Observer<PocGlucose?> { pocGlucose ->
394
+ if (pocGlucose != null) {
395
+ // Add timestamp check to prevent processing old data
396
+ val currentTime = System.currentTimeMillis()
397
+ val dataAge = currentTime - pocGlucose.timeInMillis
398
+
399
+ // Only process data that's relatively recent (within last 10 minutes as example)
400
+ if (dataAge <= 10 * 60 * 1000L) { // 10 minutes
401
+ handleGlucoseData(pocGlucose)
402
+ } else {
403
+ Log.d(
404
+ "observeGlucoseData",
405
+ "Skipping old glucose data: age = ${dataAge}ms"
406
+ )
407
+ }
408
+ } else {
409
+ Log.w(
410
+ "observeGlucoseData",
411
+ "Received null glucose data - skipping processing"
412
+ )
413
+ }
413
414
  }
414
- }
415
415
 
416
- // Add observer on main thread
417
- Handler(Looper.getMainLooper()).post {
416
+ // Add observer on main thread
418
417
  glucoseObserver?.let { observer ->
419
418
  try {
420
419
  mModel.latestGlucose.observeForever(observer)
421
420
  isObserving = true
422
- Log.d("observeGlucoseData", "Observer added successfully")
421
+ Log.d("observeGlucoseData", "Live glucose observer started successfully")
423
422
  } catch (e: Exception) {
424
423
  Log.e("observeGlucoseData", "Error adding observer: ${e.message}")
425
424
  glucoseObserver = null
425
+ isObserving = false
426
426
  }
427
427
  }
428
- }
428
+ }, 50) // Small delay to ensure previous cleanup is complete
429
+
429
430
  } catch (e: Exception) {
430
431
  Log.e("observeGlucoseData", "observeGlucoseData: ${e.message}")
431
432
  e.printStackTrace()
433
+ isObserving = false
434
+ glucoseObserver = null
432
435
  }
433
436
  }
434
437
 
435
-
436
438
  private fun handleGlucoseData(pocGlucose: PocGlucose) {
437
439
  try {
438
440
  // Additional safety check
@@ -534,186 +536,252 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
534
536
  }
535
537
  }
536
538
 
537
- /*@ReactMethod
538
- fun observeAllGlucoseData(token: String) {
539
- userToken = token
540
- Log.e("userToken", "userToken: ${userToken}")
541
- try {
542
- val lastSyncData = prefsHelper.lastSyncData
543
- Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
544
-
545
- if (lastSyncData != null) {
546
- CoroutineScope(Dispatchers.IO).launch {
547
- getGlucoseDataAndUploadInBatch(
548
- mModel.getGlucoseBetweenTime(
549
- lastSyncData.lastSyncTime
550
- )
551
- )
552
- }
553
- } else {
554
- observeGlucoseData(userToken)
555
- }
556
-
557
- } catch (e: Exception) {
558
- Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
559
- }
560
- }*/
561
-
562
539
 
563
540
  @ReactMethod
564
541
  fun observeAllGlucoseData(token: String) {
565
542
  userToken = token
566
- Log.e("userToken", "userToken: $token")
567
543
 
568
- // Stop current observation to prevent duplicates
544
+ // FIX: Ensure proper cleanup and reset state
569
545
  stopObservingGlucoseData()
570
546
 
571
- try {
572
- val lastSyncData = prefsHelper.lastSyncData
573
- Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
574
-
575
- if (lastSyncData != null) {
576
- CoroutineScope(Dispatchers.IO).launch {
577
- val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.lastSyncTime)
578
- if (glucoseData != null) {
579
- getGlucoseDataAndUploadInBatch(glucoseData)
580
- } else {
581
- Log.d(
582
- "observeAllGlucoseData",
583
- "No glucose data found, starting live observation"
584
- )
585
- observeGlucoseData(userToken)
547
+ // Add a small delay to ensure cleanup is complete
548
+ Handler(Looper.getMainLooper()).postDelayed({
549
+ try {
550
+ val lastSyncData = prefsHelper.lastSyncData
551
+ Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
552
+
553
+ if (lastSyncData != null) {
554
+ CoroutineScope(Dispatchers.IO).launch {
555
+ val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.lastSyncTime)
556
+ if (glucoseData != null && glucoseData.isNotEmpty()) {
557
+ processBatchDataAndStartObserver(glucoseData)
558
+ } else {
559
+ Log.d(
560
+ "observeAllGlucoseData",
561
+ "No historical data found, starting live observation"
562
+ )
563
+ Handler(Looper.getMainLooper()).post {
564
+ observeGlucoseData(userToken)
565
+ }
566
+ }
586
567
  }
568
+ } else {
569
+ observeGlucoseData(userToken)
587
570
  }
588
- } else {
571
+ } catch (e: Exception) {
572
+ Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
589
573
  observeGlucoseData(userToken)
590
574
  }
575
+ }, 100) // Small delay to ensure cleanup
576
+ }
577
+
578
+ // New method to handle batch processing and ensure proper sequencing
579
+ private suspend fun processBatchDataAndStartObserver(dataList: List<PocGlucose>) {
580
+ try {
581
+ Log.d(
582
+ "processBatchDataAndStartObserver",
583
+ "Starting batch processing with ${dataList.size} records"
584
+ )
585
+
586
+ val success = processBatchDataSynchronously(dataList)
587
+
588
+ if (success) {
589
+ Log.d("processBatchDataAndStartObserver", "Batch processing completed successfully")
590
+ } else {
591
+ Log.w("processBatchDataAndStartObserver", "Batch processing had failures")
592
+ }
593
+
594
+ // Ensure we're on main thread and not already observing
595
+ Handler(Looper.getMainLooper()).post {
596
+ if (!isObserving) {
597
+ Log.d(
598
+ "processBatchDataAndStartObserver",
599
+ "Starting live observer after batch completion"
600
+ )
601
+ observeGlucoseData(userToken)
602
+ } else {
603
+ Log.d(
604
+ "processBatchDataAndStartObserver",
605
+ "Already observing, skipping duplicate observer setup"
606
+ )
607
+ }
608
+ }
609
+
591
610
  } catch (e: Exception) {
592
- Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
611
+ Log.e("processBatchDataAndStartObserver", "Error in batch processing: ${e.message}")
612
+ // Start live observation even on error
613
+ Handler(Looper.getMainLooper()).post {
614
+ if (!isObserving) {
615
+ observeGlucoseData(userToken)
616
+ }
617
+ }
593
618
  }
594
619
  }
595
620
 
596
621
  @ReactMethod
597
- fun stopObservingGlucoseData() {
622
+ fun resetCgmState() {
623
+ Log.d("resetCgmState", "Resetting CGM state for logout")
624
+
598
625
  try {
599
- glucoseObserver?.let { observer ->
600
- Handler(Looper.getMainLooper()).post {
601
- try {
602
- mModel.latestGlucose.removeObserver(observer)
603
- isObserving = false
604
- Log.d("stopObservingGlucoseData", "Observer removed successfully")
605
- } catch (e: Exception) {
606
- Log.e("stopObservingGlucoseData", "Error removing observer: ${e.message}")
607
- }
608
- }
609
- }
610
- glucoseObserver = null
626
+ // Stop all observations
627
+ stopObservingGlucoseData()
628
+
629
+ // Cancel all coroutines
630
+ debounceJob?.cancel()
631
+ debounceJob = null
632
+
633
+ // Reset user token
634
+ userToken = ""
635
+
636
+ // Reset device status
637
+ lastDeviceStatus = null
638
+
639
+ // Clear any cached data if needed
640
+ // prefsHelper.clearCache() // if you have such method
641
+
611
642
  } catch (e: Exception) {
612
- Log.e("stopObservingGlucoseData", "Error stopping observer: ${e.message}")
643
+ Log.e("resetCgmState", "Error resetting CGM state: ${e.message}")
613
644
  }
614
645
  }
615
646
 
616
- fun getGlucoseDataAndUploadInBatch(dataList: List<PocGlucose>) {
647
+ // Updated batch processing method with better sync control
648
+ private suspend fun processBatchDataSynchronously(dataList: List<PocGlucose>): Boolean {
617
649
  if (dataList.isEmpty()) {
618
- Log.d("getGlucoseDataAndUploadInBatch", "No data to upload, starting live observation")
619
- if (!isObserving) {
620
- observeGlucoseData(userToken)
621
- }
622
- return
650
+ Log.d("processBatchDataSynchronously", "No data to upload")
651
+ return true
623
652
  }
624
653
 
625
654
  val batchSize = 40
626
655
  val chunks = dataList.chunked(batchSize)
656
+ var lastSyncedRecord: PocGlucose? = null
657
+ var allBatchesSuccessful = true
627
658
 
628
- Log.d("getGlucoseDataAndUploadInBatch", "Starting batch upload with ${chunks.size} batches")
629
-
630
- CoroutineScope(Dispatchers.IO).launch {
631
- var lastSyncedRecord: PocGlucose? = null
632
- var allBatchesSuccessful = true
633
-
634
- for ((index, batch) in chunks.withIndex()) {
635
- try {
636
- val transformedLogs = batch.filter { it.showGlucoseMG > 0 }.map { pocGlucose ->
637
- CgmLog(
638
- timeInMillis = pocGlucose.timeInMillis,
639
- countdownMinutes = pocGlucose.countdownMinutes,
640
- countdownDays = pocGlucose.countdownDays,
641
- hypoglycemiaEarlyWarnMinutes = pocGlucose.hypoglycemiaEarlyWarnMinutes,
642
- showGlucoseMG = pocGlucose.showGlucoseMG,
643
- glucoseId = pocGlucose.glucoseId,
644
- name = pocGlucose.name,
645
- bytes = pocGlucose.bytes,
646
- showGlucose = pocGlucose.showGlucose,
647
- Ib = pocGlucose.ib,
648
- Iw = pocGlucose.iw,
649
- countdownHours = pocGlucose.countdownHours,
650
- T = pocGlucose.t,
651
- year = pocGlucose.year,
652
- month = pocGlucose.month,
653
- day = pocGlucose.day,
654
- hour = pocGlucose.hour,
655
- minute = pocGlucose.minute,
656
- trendObject = com.mytatvarnsdk.model.TrendObject(
657
- trendId = pocGlucose.trend.trendId,
658
- drawableId = pocGlucose.trend.drawableId,
659
- widgetImg = pocGlucose.trend.widgetImg,
660
- apsChangeRate = pocGlucose.trend.apsChangeRate
661
- ),
662
- glucoseStatusObject = com.mytatvarnsdk.model.GlucoseStatusObject(
663
- statusId = pocGlucose.glucoseStatus.statusId
664
- ),
665
- errorObject = com.mytatvarnsdk.model.ErrorObject(
666
- errorId = pocGlucose.errorCode.errorId,
667
- sound = pocGlucose.errorCode.sound
668
- )
669
- )
670
- }
671
-
672
- if (transformedLogs.isEmpty()) {
673
- Log.d("Batch Upload", "Batch $index skipped - no valid glucose readings")
674
- continue
675
- }
659
+ Log.d("processBatchDataSynchronously", "Starting batch upload with ${chunks.size} batches")
676
660
 
677
- val allResult = AllCGMLogRequest(vendor = "GoodFlip", logs = transformedLogs)
678
- val json = Gson().toJson(allResult)
661
+ for ((index, batch) in chunks.withIndex()) {
662
+ try {
663
+ val transformedLogs = batch.filter { it.showGlucoseMG > 0 }.map { pocGlucose ->
664
+ CgmLog(
665
+ timeInMillis = pocGlucose.timeInMillis,
666
+ countdownMinutes = pocGlucose.countdownMinutes,
667
+ countdownDays = pocGlucose.countdownDays,
668
+ hypoglycemiaEarlyWarnMinutes = pocGlucose.hypoglycemiaEarlyWarnMinutes,
669
+ showGlucoseMG = pocGlucose.showGlucoseMG,
670
+ glucoseId = pocGlucose.glucoseId,
671
+ name = pocGlucose.name,
672
+ bytes = pocGlucose.bytes,
673
+ showGlucose = pocGlucose.showGlucose,
674
+ Ib = pocGlucose.ib,
675
+ Iw = pocGlucose.iw,
676
+ countdownHours = pocGlucose.countdownHours,
677
+ T = pocGlucose.t,
678
+ year = pocGlucose.year,
679
+ month = pocGlucose.month,
680
+ day = pocGlucose.day,
681
+ hour = pocGlucose.hour,
682
+ minute = pocGlucose.minute,
683
+ trendObject = com.mytatvarnsdk.model.TrendObject(
684
+ trendId = pocGlucose.trend.trendId,
685
+ drawableId = pocGlucose.trend.drawableId,
686
+ widgetImg = pocGlucose.trend.widgetImg,
687
+ apsChangeRate = pocGlucose.trend.apsChangeRate
688
+ ),
689
+ glucoseStatusObject = com.mytatvarnsdk.model.GlucoseStatusObject(
690
+ statusId = pocGlucose.glucoseStatus.statusId
691
+ ),
692
+ errorObject = com.mytatvarnsdk.model.ErrorObject(
693
+ errorId = pocGlucose.errorCode.errorId,
694
+ sound = pocGlucose.errorCode.sound
695
+ )
696
+ )
697
+ }
679
698
 
680
- Log.d("Batch Upload JSON", "Batch $index with ${transformedLogs.size} records")
681
- logLongJson("Batch $index JSON=>>> ", json)
699
+ if (transformedLogs.isEmpty()) {
700
+ Log.d(
701
+ "processBatchDataSynchronously",
702
+ "Batch $index skipped - no valid glucose readings"
703
+ )
704
+ continue
705
+ }
682
706
 
683
- val uploadSuccessful = uploadBatchSynchronously(json, index)
707
+ val allResult = AllCGMLogRequest(vendor = "GoodFlip", logs = transformedLogs)
708
+ val json = Gson().toJson(allResult)
684
709
 
685
- if (uploadSuccessful) {
686
- lastSyncedRecord = batch.lastOrNull()
687
- updateSyncMetadata(lastSyncedRecord)
688
- Log.d("Batch Upload", "✅ Batch $index uploaded successfully")
689
- } else {
690
- allBatchesSuccessful = false
691
- Log.e("Batch Upload", "❌ Batch $index failed, stopping further uploads")
692
- break
693
- }
710
+ Log.d(
711
+ "Batch Upload",
712
+ "Processing batch $index with ${transformedLogs.size} records"
713
+ )
714
+ logLongJson("Batch $index JSON=>>> ", json)
694
715
 
695
- delay(500L) // Rate limiting between batches
716
+ val uploadSuccessful = uploadBatchSynchronously(json, index)
696
717
 
697
- } catch (e: Exception) {
698
- Log.e("Batch Upload", "❌ Batch $index exception: ${e.message}")
718
+ if (uploadSuccessful) {
719
+ lastSyncedRecord = batch.lastOrNull()
720
+ // Update sync metadata after each successful batch
721
+ updateSyncMetadata(lastSyncedRecord)
722
+ Log.d("Batch Upload", "✅ Batch $index uploaded and synced successfully")
723
+ } else {
699
724
  allBatchesSuccessful = false
700
- break
725
+ Log.e("Batch Upload", "❌ Batch $index failed")
726
+ // Continue with next batch instead of breaking (optional based on your needs)
727
+ // break
701
728
  }
729
+
730
+ // Rate limiting between batches
731
+ delay(500L)
732
+
733
+ } catch (e: Exception) {
734
+ Log.e("Batch Upload", "❌ Batch $index exception: ${e.message}")
735
+ allBatchesSuccessful = false
736
+ // Continue processing other batches
702
737
  }
738
+ }
703
739
 
704
- // Handle last synced record error status
705
- lastSyncedRecord?.let { record ->
706
- if (record.errorCode != enumError.NONE) {
707
- handleGlucoseError(record)
708
- }
740
+ // Handle error status for the last processed record
741
+ lastSyncedRecord?.let { record ->
742
+ if (record.errorCode != enumError.NONE) {
743
+ handleGlucoseError(record)
709
744
  }
745
+ }
746
+
747
+ Log.d(
748
+ "processBatchDataSynchronously",
749
+ "Batch processing completed. Overall success: $allBatchesSuccessful"
750
+ )
751
+ return allBatchesSuccessful
752
+ }
710
753
 
711
- Log.d("Batch Upload", "Batch upload completed. Success: $allBatchesSuccessful")
712
754
 
713
- // Only start observing new data if we're not already doing so
714
- if (!isObserving) {
715
- observeGlucoseData(userToken)
755
+ @ReactMethod
756
+ fun stopObservingGlucoseData() {
757
+ Log.d("stopObservingGlucoseData", "Stopping glucose data observation")
758
+
759
+ try {
760
+ // Cancel any ongoing coroutines
761
+ debounceJob?.cancel()
762
+ debounceJob = null
763
+
764
+ // Remove observer on main thread with completion callback
765
+ glucoseObserver?.let { observer ->
766
+ Handler(Looper.getMainLooper()).post {
767
+ try {
768
+ mModel.latestGlucose.removeObserver(observer)
769
+ isObserving = false
770
+ Log.d("stopObservingGlucoseData", "Observer removed successfully")
771
+ } catch (e: Exception) {
772
+ Log.e("stopObservingGlucoseData", "Error removing observer: ${e.message}")
773
+ }
774
+ }
716
775
  }
776
+
777
+ // Reset observer reference
778
+ glucoseObserver = null
779
+
780
+ // Reset last device status
781
+ lastDeviceStatus = null
782
+
783
+ } catch (e: Exception) {
784
+ Log.e("stopObservingGlucoseData", "Error stopping observer: ${e.message}")
717
785
  }
718
786
  }
719
787
 
@@ -752,153 +820,16 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
752
820
  it.glucoseId
753
821
  )
754
822
  prefsHelper.lastSyncData = syncData
755
- Log.d("Sync Metadata", "Last sync data stored: ${Gson().toJson(syncData)}")
823
+ Log.d(
824
+ "Sync Metadata",
825
+ "Sync metadata updated: glucoseId=${it.glucoseId}, time=${it.timeInMillis}"
826
+ )
756
827
  } catch (e: Exception) {
757
828
  Log.e("updateSyncMetadata", "Error updating sync metadata: ${e.message}")
758
829
  }
759
830
  }
760
831
  }
761
832
 
762
- /*fun getGlucoseDataAndUploadInBatch(dataList: List<PocGlucose>) {
763
- if (dataList.isNotEmpty()) {
764
- val batchSize = 40
765
-
766
- val chunks = dataList.chunked(batchSize)
767
-
768
- CoroutineScope(Dispatchers.IO).launch {
769
-
770
- var lastSyncedRecord: PocGlucose? = null
771
-
772
- for ((index, batch) in chunks.withIndex()) {
773
- try {
774
- val transformedLogs = batch.filter { it.showGlucoseMG > 0 }.map {
775
- CgmLog(
776
- timeInMillis = it.timeInMillis,
777
- countdownMinutes = it.countdownMinutes,
778
- countdownDays = it.countdownDays,
779
- hypoglycemiaEarlyWarnMinutes = it.hypoglycemiaEarlyWarnMinutes,
780
- showGlucoseMG = it.showGlucoseMG,
781
- glucoseId = it.glucoseId,
782
- name = it.name,
783
- bytes = it.bytes,
784
- showGlucose = it.showGlucose,
785
- Ib = it.ib,
786
- Iw = it.iw,
787
- countdownHours = it.countdownHours,
788
- T = it.t,
789
- year = it.year,
790
- month = it.month,
791
- day = it.day,
792
- hour = it.hour,
793
- minute = it.minute,
794
- trendObject = com.mytatvarnsdk.model.TrendObject(
795
- trendId = it.trend.trendId,
796
- drawableId = it.trend.drawableId,
797
- widgetImg = it.trend.widgetImg,
798
- apsChangeRate = it.trend.apsChangeRate
799
- ),
800
- glucoseStatusObject = com.mytatvarnsdk.model.GlucoseStatusObject(
801
- statusId = it.glucoseStatus.statusId
802
- ),
803
- errorObject = com.mytatvarnsdk.model.ErrorObject(
804
- errorId = it.errorCode.errorId,
805
- sound = it.errorCode.sound
806
- )
807
- )
808
- }
809
-
810
- val allResult = AllCGMLogRequest(
811
- vendor = "GoodFlip",
812
- logs = transformedLogs
813
- )
814
-
815
- val json = Gson().toJson(allResult)
816
- logLongJson("Final JSON=>>> ", json)
817
-
818
- // sendDataToReact(Gson().toJson(batch).toString(), "DATA_FETCH", "CGMData")
819
-
820
- authenticateSDKService.postCGMData(
821
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
822
- data = json.toString(),
823
- token = userToken,
824
- responseListener = object : AuthenticateSDKService.ResponseListener {
825
- override fun onResponseSuccess() {
826
- lastSyncedRecord = batch.lastOrNull()
827
-
828
- lastSyncedRecord?.let {
829
- val syncData =
830
- SyncMeta(
831
- Date().time,
832
- it.timeInMillis,
833
- it.deviceId,
834
- it.glucoseId
835
- )
836
- prefsHelper.lastSyncData = syncData
837
- Log.d(
838
- "lastSyncedRecord stored:--- ",
839
- Gson().toJson(syncData).toString()
840
- )
841
- }
842
- }
843
-
844
- override fun onResponseFail() {
845
- Log.e("Batch Upload", "❌ Batch $index failed")
846
- }
847
- }
848
- )
849
-
850
- delay(500L)
851
- } catch (e: Exception) {
852
- Log.e("Batch Upload", "❌ Batch $index exception: ${e.message}")
853
- break // Optional: stop further processing if exception occurs
854
- }
855
- }
856
-
857
-
858
- if (lastSyncedRecord != null) {
859
- if (lastSyncedRecord?.errorCode != enumError.NONE) {
860
- if (lastSyncedRecord?.errorCode == enumError.ERROR_FLOODING_WATER) {
861
- lastDeviceStatus =
862
- lastSyncedRecord?.deviceId!!.toInt().let { mModel.getDeviceInfo(it) }
863
- lastDeviceStatus?.let {
864
- postEventDataToAPI(
865
- it,
866
- DeviceStatus.MOISTURE_DETECT.id
867
- )
868
- }
869
- resetDebounceTimer()
870
- } else if (lastSyncedRecord?.errorCode == enumError.ERROR_CURRENT_SMALL || lastSyncedRecord?.errorCode == enumError.ERROR_NOISE || lastSyncedRecord?.errorCode == enumError.ERROR_SENSITIVITY_ATTENUATION) {
871
- lastDeviceStatus =
872
- lastSyncedRecord?.deviceId!!.toInt().let { mModel.getDeviceInfo(it) }
873
- lastDeviceStatus?.let {
874
- postEventDataToAPI(
875
- it,
876
- DeviceStatus.WEAK_SIGNAL.id
877
- )
878
- }
879
- resetDebounceTimer()
880
- } else {
881
- lastDeviceStatus =
882
- lastSyncedRecord?.deviceId!!.toInt().let { mModel.getDeviceInfo(it) }
883
- lastDeviceStatus?.let {
884
- postEventDataToAPI(
885
- it,
886
- DeviceStatus.ERROR_COMMON.id
887
- )
888
- }
889
- resetDebounceTimer()
890
- }
891
- }
892
- }
893
-
894
- Log.e("Batch Upload", "All data uploaded")
895
-
896
- observeGlucoseData(userToken)
897
- }
898
- } else {
899
- observeGlucoseData(userToken)
900
- }
901
- }*/
902
833
 
903
834
  fun logLongJson(tag: String, message: String) {
904
835
  val maxLogSize = 4000
@@ -980,5 +911,6 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
980
911
  }
981
912
  }
982
913
 
914
+
983
915
  }
984
916