react-native-mytatva-rn-sdk 1.2.46 → 1.2.48

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.
@@ -2,6 +2,8 @@ package com.mytatvarnsdk
2
2
 
3
3
  import android.app.Application
4
4
  import android.bluetooth.BluetoothAdapter
5
+ import android.bluetooth.BluetoothManager
6
+ import android.content.Context
5
7
  import android.content.Intent
6
8
  import android.content.pm.PackageManager
7
9
  import android.os.Build
@@ -52,10 +54,13 @@ import kotlinx.coroutines.Job
52
54
  import kotlinx.coroutines.SupervisorJob
53
55
  import kotlinx.coroutines.delay
54
56
  import kotlinx.coroutines.launch
57
+ import kotlinx.coroutines.withContext
55
58
  import org.json.JSONObject
56
59
  import java.text.SimpleDateFormat
57
60
  import java.util.Date
58
61
  import java.util.Locale
62
+ import java.util.Timer
63
+ import java.util.TimerTask
59
64
  import kotlin.coroutines.resume
60
65
  import kotlin.coroutines.suspendCoroutine
61
66
 
@@ -69,12 +74,15 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
69
74
  var prefsHelper: SharedPreferencesLibraryUtil
70
75
  private val apiScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
71
76
  private var debounceJob: Job? = null
77
+ private var isBatchProcessing = false
72
78
 
73
79
  private var lastDeviceStatus: String? = null
74
80
 
75
81
  private var glucoseObserver: Observer<PocGlucose?>? = null
76
82
  private var isObserving = false
77
83
 
84
+ private var lastProcessedSyncTimeInMillis: Long? = null
85
+
78
86
  init {
79
87
  mReactContext = reactContext
80
88
  prefsHelper = SharedPreferencesLibraryUtil(mReactContext)
@@ -107,8 +115,7 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
107
115
  Handler(Looper.getMainLooper()).post {
108
116
  mModel.device.observeForever { device ->
109
117
  if (device != null) {
110
- postEventDataToAPI(device, "", device?.qrMessage ?: "")
111
-
118
+ postEventDataToAPI(device, "", device.qrMessage ?: "")
112
119
  Log.d("observeDeviceStatus: ", device.toString())
113
120
  }
114
121
  }
@@ -119,7 +126,7 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
119
126
  }
120
127
 
121
128
  @ReactMethod
122
- fun observeTransmitterUnbindStatus(token: String, apiResponse: String) {
129
+ fun observeTransmitterUnbindStatus(token: String, apiResponse: String?) {
123
130
  try {
124
131
  if (apiResponse != null && apiResponse.isNotEmpty()) {
125
132
  userToken = token
@@ -189,19 +196,13 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
189
196
  val endDate = sensor.endDate
190
197
  val sensorId = sensor.sensorId
191
198
 
192
- println("Start Date: $startDate")
193
- println("End Date: $endDate")
194
-
195
199
  if (isCurrentDateInRange(startDate, endDate)) {
196
-
197
200
  println("Current date is in range")
198
201
 
199
202
  val pocDevice =
200
203
  RepositoryDevice.getInstance(BApplication.getContext()).latestDeviceIoThread
201
204
 
202
205
  if (pocDevice != null) {
203
- Log.d("pocDevice logsss", pocDevice.toString())
204
-
205
206
  if (pocDevice.isUnBind) {
206
207
  postEventDataToAPI(
207
208
  pocDevice,
@@ -210,8 +211,6 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
210
211
  )
211
212
  }
212
213
  } else {
213
- Log.d("pocDevice logsss", "Data null")
214
-
215
214
  postEventDataToAPI(
216
215
  pocDevice,
217
216
  DeviceStatus.TRANSMITTER_DISCONNECT.id,
@@ -231,65 +230,100 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
231
230
  }
232
231
  }
233
232
 
233
+ private var debounceDeviceTimer: Timer? = null
234
+ private var isDebounceTimerActive: Boolean = false
235
+
236
+
234
237
  private fun postEventDataToAPI(device: PocDevice?, mStatus: String, sensorId: String?) {
235
- apiScope.launch {
238
+ debounceJob?.cancel()
239
+
240
+
241
+ debounceJob = apiScope.launch {
242
+ delay(500)
243
+
236
244
  try {
237
245
  val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
238
246
  val bleStatus = bluetoothAdapter?.isEnabled == true
239
- var status = ""
240
247
 
241
- status = if (mStatus.isEmpty()) {
248
+ val status: String = mStatus.ifEmpty {
242
249
  when {
243
250
  device?.isBoundAndConnect == true -> DeviceStatus.CONNECTED.id
244
251
  !bleStatus -> DeviceStatus.BLUETOOTH_OFF.id
245
252
  device?.isUnBind == true -> DeviceStatus.TRANSMITTER_DISCONNECT.id
246
253
  device?.isBoundButDisConnect == true -> DeviceStatus.DISCONNECTED.id
247
- else -> "" // fallback if no status matches
254
+ else -> ""
248
255
  }
249
- } else {
250
- mStatus
251
256
  }
252
257
 
253
- Log.d("postEventDataToAPI: ", "Device Event Status==> $status")
258
+ Log.d("Device event Status", "Device event Status: $status")
254
259
 
255
- if (status.isNotEmpty()) {
256
- if (status != lastDeviceStatus) {
260
+ if (status == DeviceStatus.DISCONNECTED.id && !isDebounceTimerActive) {
261
+ // Start debounce timer for 1 minutes
262
+ debounceDeviceTimer = Timer()
263
+ isDebounceTimerActive = true
257
264
 
258
- lastDeviceStatus = status
259
- Log.d("postEventDataToAPI: ", "Device Event Status API==> $status")
265
+ debounceDeviceTimer?.schedule(object : TimerTask() {
266
+ override fun run() {
267
+ isDebounceTimerActive = false
260
268
 
261
- val rawData = JSONObject().apply {
262
- put("transmitterName", device?.name ?: "")
263
- put("SensorId", sensorId ?: "")
264
- put("Sensor", sensorId ?: "")
265
- put("timeInMillis", System.currentTimeMillis())
269
+ callEventAPI(device, status, sensorId)
266
270
  }
267
271
 
268
- val obj = JSONObject().apply {
269
- put("sensorId", sensorId ?: "")
270
- put("status", status)
271
- put("rawData", rawData)
272
- }
272
+ }, 1 * 60 * 1000) // 1 minutes in milliseconds
273
273
 
274
- authenticateSDKService.postDeviceData(
275
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
276
- data = obj.toString(),
277
- token = userToken,
278
- loaderListener = object : LoaderListener {
279
- override fun onShowLoader() {}
280
- override fun onHideLoader() {}
281
- }
282
- )
274
+ Log.d("Debounce", "Timer started for disconnection reminder")
275
+ } else {
276
+ // Either device is not disconnected or timer already running
277
+ debounceDeviceTimer?.cancel()
278
+ debounceDeviceTimer = null
279
+ isDebounceTimerActive = false
283
280
 
284
- resetDebounceTimer()
285
- }
281
+ Log.d("Debounce", "Timer cancelled, event triggered immediately")
282
+
283
+ callEventAPI(device, status, sensorId)
286
284
  }
285
+
287
286
  } catch (e: Exception) {
288
287
  e.printStackTrace()
289
288
  }
290
289
  }
291
290
  }
292
291
 
292
+ private fun callEventAPI(device: PocDevice?, status: String, sensorId: String?) {
293
+ Log.d("Device event Status", "Last Device event Status API: $lastDeviceStatus")
294
+
295
+ if (status.isNotEmpty() && status != lastDeviceStatus) {
296
+ lastDeviceStatus = status
297
+
298
+ Log.d("Device event Status", "Device event Status API: $status")
299
+
300
+ val rawData = JSONObject().apply {
301
+ put("transmitterName", device?.name ?: "")
302
+ put("SensorId", sensorId ?: "")
303
+ put("Sensor", sensorId ?: "")
304
+ put("timeInMillis", Date().time)
305
+ }
306
+
307
+ val obj = JSONObject().apply {
308
+ put("sensorId", sensorId ?: "")
309
+ put("status", status)
310
+ put("rawData", rawData)
311
+ }
312
+
313
+ authenticateSDKService.postDeviceData(
314
+ environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
315
+ data = obj.toString(),
316
+ token = userToken,
317
+ loaderListener = object : LoaderListener {
318
+ override fun onShowLoader() {}
319
+ override fun onHideLoader() {}
320
+ }
321
+ )
322
+
323
+ resetDebounceTimer()
324
+ }
325
+ }
326
+
293
327
  private fun resetDebounceTimer() {
294
328
  debounceJob?.cancel() // Cancel any existing timer
295
329
  debounceJob = apiScope.launch {
@@ -360,6 +394,10 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
360
394
  )
361
395
  return
362
396
  }
397
+ if (isBatchProcessing) {
398
+ Log.d("observeAllGlucoseData", "Batch processing in progress, skipping duplicate call")
399
+ return
400
+ }
363
401
 
364
402
  // Remove existing observer if any to prevent memory leaks
365
403
  stopObservingGlucoseData()
@@ -371,22 +409,39 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
371
409
  val currentTime = System.currentTimeMillis()
372
410
  var dataAge = System.currentTimeMillis()
373
411
 
374
- if (prefsHelper.lastSyncData != null) {
375
- dataAge = currentTime - prefsHelper.lastSyncData.timeInMillis
412
+ val lastSyncData = prefsHelper.lastSyncData
413
+ val currentSyncTimeInMillis = lastSyncData?.timeInMillis
414
+
415
+ if (lastSyncData != null) {
416
+ dataAge = currentTime - lastSyncData.timeInMillis
417
+ }
418
+
419
+ // Only proceed if timeInMillis is different from last processed
420
+ if (currentSyncTimeInMillis != null && lastProcessedSyncTimeInMillis != null && currentSyncTimeInMillis == lastProcessedSyncTimeInMillis) {
421
+ Log.d("SyncTimeCheck", "Duplicate sync time, skipping function calls")
422
+ return@Observer
376
423
  }
377
424
 
378
- if ((pocGlucose != null && prefsHelper.lastSyncData == null) || (pocGlucose != null && prefsHelper.lastSyncData != null && dataAge <= 4 * 60 * 1000L)) {
425
+
426
+ if ((pocGlucose != null && lastSyncData == null) || (pocGlucose != null && lastSyncData != null && dataAge <= 4 * 60 * 1000L)) {
379
427
  handleGlucoseData(pocGlucose)
380
428
  Log.d(
381
429
  "observeGlucoseData",
382
430
  "Received glucose data - processing"
383
431
  )
432
+ // Update last processed sync time
433
+ lastProcessedSyncTimeInMillis = currentSyncTimeInMillis
384
434
  } else {
385
- observeAllGlucoseData(userToken)
435
+
436
+ Handler(Looper.getMainLooper()).postDelayed({
437
+ observeAllGlucoseData(userToken)
438
+ }, 100)
386
439
  Log.d(
387
440
  "observeGlucoseData",
388
441
  "Received null glucose data - skipping processing"
389
442
  )
443
+ // Update last processed sync time
444
+ lastProcessedSyncTimeInMillis = currentSyncTimeInMillis
390
445
  }
391
446
  }
392
447
 
@@ -515,6 +570,11 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
515
570
 
516
571
  @ReactMethod
517
572
  fun observeAllGlucoseData(token: String) {
573
+ Log.d("function call", "observeAllGlucoseData")
574
+ if (isBatchProcessing) {
575
+ Log.d("observeAllGlucoseData", "Batch processing in progress, skipping duplicate call")
576
+ return
577
+ }
518
578
  userToken = token
519
579
 
520
580
  // FIX: Ensure proper cleanup and reset state
@@ -525,11 +585,20 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
525
585
  try {
526
586
  val lastSyncData = prefsHelper.lastSyncData
527
587
  Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
588
+ val currentTime = System.currentTimeMillis()
589
+ var dataAge = System.currentTimeMillis()
528
590
 
529
- if (lastSyncData != null) {
591
+ if (prefsHelper.lastSyncData != null) {
592
+ dataAge = currentTime - prefsHelper.lastSyncData.timeInMillis
593
+ }
594
+
595
+ if (lastSyncData != null && dataAge > 3 * 60 * 1000L) {
596
+ isBatchProcessing = true
530
597
  CoroutineScope(Dispatchers.IO).launch {
531
- val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.lastSyncTime)
598
+
599
+ val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.timeInMillis)
532
600
 
601
+
533
602
  Log.d("observeAllGlucoseData: ", glucoseData.toString())
534
603
  Log.d("Last sync time: ", lastSyncData.lastSyncTime.toString())
535
604
  Log.d("current time: ", System.currentTimeMillis().toString())
@@ -541,16 +610,18 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
541
610
  "observeAllGlucoseData",
542
611
  "No historical data found, starting live observation"
543
612
  )
544
- Handler(Looper.getMainLooper()).post {
613
+
614
+ withContext(Dispatchers.Main) {
545
615
  observeGlucoseData(userToken)
546
616
  }
547
617
  }
548
618
  }
549
619
  } else {
620
+ isBatchProcessing = false
550
621
  observeGlucoseData(userToken)
551
622
  }
552
623
  } catch (e: Exception) {
553
- Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
624
+ isBatchProcessing = false
554
625
  observeGlucoseData(userToken)
555
626
  }
556
627
  }, 100) // Small delay to ensure cleanup
@@ -573,7 +644,8 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
573
644
  }
574
645
 
575
646
  // Ensure we're on main thread and not already observing
576
- Handler(Looper.getMainLooper()).post {
647
+ isBatchProcessing = false
648
+ withContext(Dispatchers.Main) {
577
649
  if (!isObserving) {
578
650
  Log.d(
579
651
  "processBatchDataAndStartObserver",
@@ -591,7 +663,8 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
591
663
  } catch (e: Exception) {
592
664
  Log.e("processBatchDataAndStartObserver", "Error in batch processing: ${e.message}")
593
665
  // Start live observation even on error
594
- Handler(Looper.getMainLooper()).post {
666
+ isBatchProcessing = false
667
+ withContext(Dispatchers.Main) {
595
668
  if (!isObserving) {
596
669
  observeGlucoseData(userToken)
597
670
  }
@@ -620,6 +693,9 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
620
693
  // Clear any cached data if needed
621
694
  prefsHelper.clearQRInformation() // if you have such method
622
695
 
696
+ // Reset last processed sync time
697
+ lastProcessedSyncTimeInMillis = null
698
+
623
699
  } catch (e: Exception) {
624
700
  Log.e("resetCgmState", "Error resetting CGM state: ${e.message}")
625
701
  }
@@ -761,6 +837,9 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
761
837
  // Reset last device status
762
838
  lastDeviceStatus = null
763
839
 
840
+ // Reset last processed sync time
841
+ lastProcessedSyncTimeInMillis = null
842
+
764
843
  } catch (e: Exception) {
765
844
  Log.e("stopObservingGlucoseData", "Error stopping observer: ${e.message}")
766
845
  }
@@ -801,6 +880,7 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
801
880
  it.glucoseId
802
881
  )
803
882
  prefsHelper.lastSyncData = syncData
883
+ lastProcessedSyncTimeInMillis = null
804
884
  Log.d(
805
885
  "Sync Metadata",
806
886
  "Sync metadata updated: glucoseId=${it.glucoseId}, time=${it.timeInMillis}"
@@ -892,6 +972,4 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
892
972
  }
893
973
  }
894
974
 
895
-
896
- }
897
-
975
+ }
@@ -272,8 +272,8 @@ class SearchTransmitterActivity : BaseBleActivity() {
272
272
  binding.tvTransName.text = "Sno: ${transmitterDeviceInfo!!.pocDevice.name}"
273
273
  val formatter = SimpleDateFormat("hh:mm a", Locale.getDefault())
274
274
  binding.tvDateTime.text = "Connected on ${formatter.format(Date()).uppercase()}"
275
- binding.clSuccess.visibility = View.VISIBLE
276
275
  binding.clMain.visibility = View.GONE
276
+ binding.clSuccess.visibility = View.VISIBLE
277
277
  binding.commonButton.btnProceed.isEnabled = true
278
278
  binding.commonButton.btnProceed.alpha = 1.0f
279
279
  binding.view2.background = ContextCompat.getDrawable(this, R.drawable.bg_green_progress)
@@ -46,6 +46,9 @@ class VideoActivity : AppCompatActivity() {
46
46
  .build()
47
47
  binding.youtubePlayerView.enableAutomaticInitialization = false
48
48
 
49
+ binding.ivClose.setOnClickListener {
50
+ handleBackPressedManually()
51
+ }
49
52
 
50
53
  binding.youtubePlayerView.addFullscreenListener(object : FullscreenListener {
51
54
  override fun onEnterFullscreen(fullscreenView: View, exitFullscreen: () -> Unit) {
@@ -83,12 +86,15 @@ class VideoActivity : AppCompatActivity() {
83
86
 
84
87
  private val onBackPressedCallback = object : OnBackPressedCallback(true) {
85
88
  override fun handleOnBackPressed() {
86
- if (isFullscreen) {
87
- // if the player is in fullscreen, exit fullscreen
88
- youTubePlayer.toggleFullscreen()
89
- } else {
90
- finish()
91
- }
89
+ handleBackPressedManually()
90
+ }
91
+ }
92
+
93
+ private fun handleBackPressedManually() {
94
+ if (isFullscreen) {
95
+ youTubePlayer.toggleFullscreen()
96
+ } else {
97
+ finish()
92
98
  }
93
99
  }
94
100
  }
@@ -8,6 +8,15 @@
8
8
  android:background="#E6000000"
9
9
  tools:context=".activity.VideoActivity">
10
10
 
11
+ <androidx.appcompat.widget.AppCompatImageView
12
+ android:id="@+id/ivClose"
13
+ android:layout_width="40dp"
14
+ android:layout_height="40dp"
15
+ android:padding="7dp"
16
+ app:layout_constraintStart_toStartOf="parent"
17
+ app:layout_constraintTop_toTopOf="parent"
18
+ app:srcCompat="@drawable/ic_close_gray" />
19
+
11
20
  <com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
12
21
  android:id="@+id/youtube_player_view"
13
22
  android:layout_width="match_parent"
@@ -23,10 +32,12 @@
23
32
  android:id="@+id/full_screen_view_container"
24
33
  android:layout_width="match_parent"
25
34
  android:layout_height="match_parent"
26
- android:visibility="gone"
35
+ android:visibility="visible"
27
36
  app:layout_constraintBottom_toBottomOf="parent"
28
37
  app:layout_constraintEnd_toEndOf="parent"
29
38
  app:layout_constraintStart_toStartOf="parent"
30
- app:layout_constraintTop_toTopOf="parent" />
39
+ app:layout_constraintTop_toTopOf="parent">
40
+
41
+ </FrameLayout>
31
42
 
32
43
  </androidx.constraintlayout.widget.ConstraintLayout>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-mytatva-rn-sdk",
3
- "version": "1.2.46",
3
+ "version": "1.2.48",
4
4
  "description": "a package to inject data into visit health pwa",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",