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