Compare commits

...

310 Commits

Author SHA1 Message Date
Mariya
c379d68679 errors fixed in Repository file 2024-02-15 20:49:29 +05:30
Pritimay Sarkar
f5068c70c2 build file change for pipeline 2024-02-13 17:59:26 +05:30
Pritimay Sarkar
8019ed2c30 add refresh and qr code scan - jig 2024-02-13 17:57:38 +05:30
Pritimay Sarkar
6afee91d27 add test jig screen 2024-02-13 14:30:21 +05:30
Pritimay Sarkar
830eb525fd add kit capacity in constants 2024-02-13 12:44:02 +05:30
Pritimay Sarkar
06241008f1 rounded corners logo 2024-02-13 09:00:29 +05:30
Pritimay Sarkar
0721be0114 save nats token in backend 2024-02-13 08:59:28 +05:30
Pritimay Sarkar
23cf38445f handle method and dynamic config for NATS 2024-02-13 08:59:03 +05:30
Pritimay Sarkar
6577842ba9 add dynamic topic based on deviceId 2024-02-13 00:07:05 +05:30
Pritimay Sarkar
c52d9835cd refactor and fix pipeline 2024-02-12 22:16:17 +05:30
Pritimay Sarkar
9f04125f68 fix conflict 2024-02-12 18:07:19 +05:30
Pritimay Sarkar
2b2c16df22 Merge remote-tracking branch 'origin/device-provision-response-store' into dev 2024-02-12 18:04:58 +05:30
Pritimay Sarkar
727040fabf Merge remote-tracking branch 'origin/dev-bulk-upload' into dev 2024-02-12 18:01:38 +05:30
Mariya
80eb658b07 changes when we tested 2024-02-12 17:56:37 +05:30
Mariya
8c9903c759 changes added for bulkupload the devices 2024-02-12 16:57:56 +05:30
Mariya
9314b155cd Device provision fetching code is added 2024-02-12 13:56:05 +05:30
Mariya
7f47e1dff8 Device provision username and password stored in firebase store separately 2024-02-12 12:01:10 +05:30
Pritimay Sarkar
be21430d0a Merge remote-tracking branch 'origin/nats_certificate_download' into dev 2024-02-11 14:08:00 +05:30
Mariya
d5b6910408 Device provision response storing 2024-02-10 17:37:07 +05:30
Pritimay Sarkar
3609d728c1 release 2.1.110 2024-02-10 12:18:52 +05:30
Pritimay Sarkar
7302ad669f release 2.1.108 2024-02-10 11:10:06 +05:30
Pritimay Sarkar
ef98b09eaf release 2.1.107 2024-02-10 10:46:54 +05:30
Mariya
89dc7f7234 Added code related to device provision fetching fromfirestore 2024-02-09 16:39:02 +05:30
Pritimay Sarkar
f631a273d7 TLS impleamentation on NATS server 2024-02-09 13:50:33 +05:30
Pritimay Sarkar
de74da3135 add package name dynamically in provider 2024-02-09 13:49:16 +05:30
Mariya
dd1144d884 Molbio Flags are updated 2024-02-08 20:48:26 +05:30
Mariya
37c8dde5bd Added code in manifest file for launcher 2024-02-08 15:44:27 +05:30
Mariya
5f460f6aa3 Added code for firefox and Files redirection 2024-02-08 15:30:51 +05:30
Pritimay Sarkar
ad625992a7 fix pipeline 2024-02-08 14:41:27 +05:30
Mariya
c5cb8a0983 Added code for fetching response of device provision api from firebase 2024-02-08 14:24:34 +05:30
Pritimay Sarkar
db518007b3 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-08 12:33:49 +05:30
Pritimay Sarkar
4d09bad516 log addititonal data in diagnostics and auto dac 2024-02-08 12:33:36 +05:30
Mariya
6ddb491bc8 Added code to send response of device provision api into firebase 2024-02-08 12:18:31 +05:30
Mariya
aad33e3907 Merge remote-tracking branch 'origin/dev' into dev 2024-02-08 11:59:06 +05:30
Mariya
52279c4419 code removed from manifest for molbio 2024-02-08 11:58:45 +05:30
Mariya
c5c0467e42 code added to store device provision response 2024-02-08 11:48:35 +05:30
Pritimay Sarkar
190a72e407 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-07 17:19:33 +05:30
Mariya
466d0e2afc code added related to nats 2024-02-07 16:33:57 +05:30
Pritimay Sarkar
4aa6fb020b display device ratio and slope ratio 2024-02-07 16:25:34 +05:30
Mariya
044a545979 code added in manifest file 2024-02-07 13:01:09 +05:30
Mariya
45fd4861f4 nats code 2024-02-07 12:49:31 +05:30
Mariya
1f9f8060cb manifest file changes and drawable file added for release apk 2024-02-06 16:48:19 +05:30
Mariya
1a3bb67dce nats 2024-02-06 16:23:33 +05:30
Pritimay Sarkar
4fef75b937 add new thresholds with additonal method and unit tests 2024-02-06 13:44:00 +05:30
Mariya
3dd247b15d Nats Certificate Download API is completed 2024-02-06 13:25:06 +05:30
Mariya
42b33c9397 Added code for scan , Issue fixed 2024-02-05 17:23:33 +05:30
Pritimay Sarkar
c5980a9cfe add unit tests 2024-02-04 15:21:34 +05:30
Pritimay Sarkar
3e0523e163 refactor kit scan 2024-02-04 14:59:07 +05:30
Pritimay Sarkar
173d4a2767 save dac values and refactor 2024-02-04 14:45:37 +05:30
Pritimay Sarkar
c166bab804 add unit test 2024-02-04 13:55:25 +05:30
Pritimay Sarkar
205c7f1f53 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-02-02 16:55:06 +05:30
Pritimay Sarkar
468cacfbe5 release 2.1.99 2024-02-02 16:53:02 +05:30
Pritimay Sarkar
a4bdfa1c71 Merge branch 'QR-SCAN' into dev 2024-02-02 16:24:02 +05:30
Pritimay Sarkar
985c484b8f release 2.1.99 2024-02-02 16:23:28 +05:30
Pritimay Sarkar
9f2b53c7f7 fix aar error 2024-02-02 16:13:13 +05:30
Pritimay Sarkar
0ac384a7b5 add new screen for workflow change 2024-02-02 13:16:26 +05:30
Mariya
feac2dc75b Changes related to device ratio and lowerlimit added 2024-02-02 12:33:37 +05:30
Mariya
4958af6fd9 add scanner sdk 2024-01-31 18:45:51 +05:30
Pritimay Sarkar
69e129cddb release 2.1.97 2024-01-26 17:19:25 +05:30
Pritimay Sarkar
2460c58bea fix backward compatibility of device id and refactor 2024-01-26 13:22:00 +05:30
Pritimay Sarkar
63331c2253 refactor and add tests 2024-01-26 11:20:13 +05:30
Pritimay Sarkar
3c4a1fa56b change reports folder 2024-01-26 08:41:00 +05:30
Pritimay Sarkar
bddebfaf2e run publish after unit tests 2024-01-26 08:22:46 +05:30
Pritimay Sarkar
1871f8c77a add tests reports job 2024-01-26 07:59:22 +05:30
Pritimay Sarkar
06d62506a9 handle SN device config format 2024-01-26 07:58:50 +05:30
Pritimay Sarkar
0b13e02ece add device id unit tests 2024-01-26 07:48:15 +05:30
Pritimay Sarkar
d1733a817f refactoring hemocube fragment and add tests 2024-01-25 12:19:40 +05:30
Pritimay Sarkar
14bf1bab1f disable download reciver to avoid crash 2024-01-25 12:17:26 +05:30
Pritimay Sarkar
97ec931814 fix sample name and device id empty 2024-01-25 12:15:41 +05:30
Mariya
126aeaed5d Merge remote-tracking branch 'origin/dev' into dev 2024-01-23 18:12:57 +05:30
Mariya
bce12a4fca changes added when testing in device provision- and removed unwanted codes 2024-01-23 18:12:05 +05:30
Pritimay Sarkar
2c21a29d22 lower limit of buffer adc 2024-01-23 18:00:13 +05:30
Pritimay Sarkar
012efd9904 add default device and handle incompatiblity 2024-01-23 17:01:04 +05:30
Pritimay Sarkar
f83823a15f refactoring hemocube fragment and add tests 2024-01-23 13:42:27 +05:30
Mariya
a0fc7f7be4 Added baud rate 2024-01-22 14:51:24 +05:30
Pritimay Sarkar
b139289765 refactoring 2024-01-20 10:56:04 +05:30
Pritimay Sarkar
93ac4dd5d0 Merge remote-tracking branch 'origin/dev-tmp' into dev 2024-01-19 18:31:06 +05:30
Kaif
bc8fe7e398 Make some changes token changes and added feature for multi result upload 2024-01-19 18:22:13 +05:30
Pritimay Sarkar
d8c812ec57 Merge branch 'hb3-hb4' into dev 2024-01-19 18:18:57 +05:30
Pritimay Sarkar
7cf1d838ad add unit tests 2024-01-19 15:12:26 +05:30
Pritimay Sarkar
240840d74c add ui test 2024-01-18 13:29:16 +05:30
Pritimay Sarkar
71ca08edaa 75 percentage cover on calibration 2024-01-18 12:08:03 +05:30
Pritimay Sarkar
faa83db5f3 refactoring for unit tests 2024-01-18 01:53:41 +05:30
Pritimay Sarkar
b12c95c6d8 fix strings and layout change in panel 2024-01-17 13:38:41 +05:30
Pritimay Sarkar
fced5ca3f5 resolve merge conflicts 2024-01-17 13:15:17 +05:30
Pritimay Sarkar
819b5cb137 resolve merge conflicts 2024-01-17 12:03:59 +05:30
Pritimay Sarkar
b8634310db add unit tests 2024-01-17 02:44:33 +05:30
Pritimay Sarkar
9a1cef246f fix pipeline google-services.json not found 2024-01-16 19:11:57 +00:00
Pritimay Sarkar
2c3abe737e unit test errors 2024-01-17 00:20:04 +05:30
Pritimay Sarkar
eb3f1a236d remove prefix based pattern search in deviceid 2024-01-16 23:59:35 +05:30
Pritimay Sarkar
0c004211c9 use default device if the device id is not present 2024-01-16 23:10:49 +05:30
Pritimay Sarkar
fc1544e90d remove debuging code and fix conflicts 2024-01-16 15:10:57 +05:30
Pritimay Sarkar
39ef1665fa Merge remote-tracking branch 'origin/Local-storing-data' into dev 2024-01-16 12:03:24 +05:30
Pritimay Sarkar
38b980f9bc Merge remote-tracking branch 'origin/Button_for_device_Configuration_and_Display_Login_UserID' into dev 2024-01-16 11:01:42 +05:30
Pritimay Sarkar
91f677e2cc Merge remote-tracking branch 'origin/preprod-temp-App-Updation-API-Version' into dev 2024-01-16 10:53:54 +05:30
Pritimay Sarkar
149fe86098 save quick capture config and load next time 2024-01-15 17:22:41 +05:30
Pritimay Sarkar
2b830a4d97 add quick capture and solution dropdowns 2024-01-15 16:24:58 +05:30
Pritimay Sarkar
ba130196cb release 2.1.91 2024-01-14 14:07:37 +05:30
Pritimay Sarkar
9d00e04680 show device ratio class with new thresholds 2024-01-14 14:02:33 +05:30
Pritimay Sarkar
3e4ee64ef3 save calibration data in shared prefs 2024-01-14 13:03:12 +05:30
Pritimay Sarkar
f0f3fcea0a refactor prefname 2024-01-14 11:14:10 +05:30
Pritimay Sarkar
5a309e1b24 release 2.1.90 with upload flags 2024-01-14 08:50:44 +05:30
Pritimay Sarkar
69a0ef68c5 release 2.1.88 2024-01-12 13:22:54 +05:30
Pritimay Sarkar
86151616c7 add ftdi details and device ids 2024-01-12 13:22:30 +05:30
Pritimay Sarkar
406699effd release 2.1.68 for PQ of dev12 13 2024-01-12 10:50:40 +05:30
Pritimay Sarkar
cb1d2997fd add pq mode 2024-01-12 01:16:52 +05:30
Pritimay Sarkar
cc0e3de7de refactoring 2024-01-11 23:44:14 +05:30
Pritimay Sarkar
47a85dcba8 refactor 2024-01-11 23:21:40 +05:30
Pritimay Sarkar
c539eedba2 refactor 2024-01-11 23:21:10 +05:30
Pritimay Sarkar
29ee6a0752 refactor 2024-01-11 23:14:20 +05:30
Pritimay Sarkar
86854a1e27 Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-01-11 17:42:05 +05:30
Pritimay Sarkar
e8973d19f1 handle multiple device id format and refactor 2024-01-11 17:40:47 +05:30
Kaif
25b8522d9e Merge remote-tracking branch 'origin/dev' into dev 2024-01-11 16:03:55 +05:30
Kaif
f0fb18274f Device provision flow implementation 2024-01-11 16:03:36 +05:30
Pritimay Sarkar
764ce5f920 add V0 device ids in constants 2024-01-11 12:30:56 +05:30
Pritimay Sarkar
6937a4c9bd add new device ids in constants 2024-01-11 12:30:23 +05:30
Pritimay Sarkar
1e70f3c9a6 change coefficents 2024-01-10 19:38:31 +05:30
Pritimay Sarkar
c6198386ef Merge branch 'dev' of https://gitlab.com/sminnovations/hpos into dev 2024-01-10 17:50:25 +05:30
Pritimay Sarkar
8248ac009a add new vendor id for pcb test 2024-01-10 17:50:11 +05:30
Kaif
49bddb548c Reduced log file size 2024-01-10 17:16:41 +05:30
Kaif
794ae8684c Merge remote-tracking branch 'origin/dev' into dev 2024-01-09 18:49:09 +05:30
Kaif
d12881f137 Made some internal changes, pritimoi please check and remove unwanted code 2024-01-09 18:48:47 +05:30
Mariya
47888172fd Added Branch for Api call version 2024-01-09 13:45:00 +05:30
Pritimay Sarkar
532a73913b Update .gitlab-ci.yml file 2024-01-08 17:55:00 +00:00
Pritimay Sarkar
21e4318f90 Merge branch 'show-environment' into 'dev'
Show environment

See merge request sminnovations/hpos!25
2024-01-08 15:15:08 +00:00
Pritimay Sarkar
80911ada0f show environment name 2024-01-08 20:43:56 +05:30
Pritimay Sarkar
67559a292d Merge branch 'preprod-temp' into dev 2024-01-08 12:23:55 +05:30
Pritimay Sarkar
81b1e5899a Merge branch 'preprod-temp' of https://gitlab.com/sminnovations/hpos into preprod-temp 2024-01-08 12:23:38 +05:30
Pritimay Sarkar
8f8fa4e302 fix merge errors 2024-01-06 17:48:22 +05:30
Pritimay Sarkar
15e462d91a fix conflicts 2024-01-06 17:24:57 +05:30
Pritimay Sarkar
fe44b9a465 Merge branch 'preprod-temp' into dev 2024-01-06 16:51:08 +05:30
Pritimay Sarkar
3f47213d95 resolve merge conflict 2024-01-06 16:49:41 +05:30
Pritimay Sarkar
2acf3a08d5 quality screen 2024-01-06 16:44:32 +05:30
Kaif
2c2b4aa9c7 Result API, NATS and work manager 2024-01-06 16:32:00 +05:30
Mariya
cdd372fadc Added device Information and Login Id - Added Labels for it 2024-01-06 16:28:37 +05:30
Mariya
7ba0124f1a Issue fixed related to Toast 2024-01-05 18:24:36 +05:30
Mariya
4dffe4196d Create a button on the panel screen to check device connectivity and hardware configuration - DONE
display logged in user id in app - DONE
2024-01-05 15:05:26 +05:30
Kaif
2642106b32 Removed incubation time, error message and displaying only one HB value 2024-01-05 15:03:23 +05:30
Mariya
3e985bd4d6 Create a button on the panel screen to check device connectivity and hardware configuration - DONE
display logged in user id in app - DONE
2024-01-05 14:40:48 +05:30
Pritimay Sarkar
cddfc4518f add key not found error in config 2024-01-05 01:16:55 +05:30
Pritimay Sarkar
36ed59bda4 Merge branch 'repeat-readings' into 'dev'
support auto multiple readings per sample

See merge request sminnovations/hpos!22
2024-01-04 19:24:15 +00:00
Pritimay Sarkar
2368ef5a4f support multiple readings automatically 2024-01-05 00:51:50 +05:30
Mariya Varghese
f9b8df970d Merge branch 'preprod-temp' into 'preprod-temp-App-Updation'
# Conflicts:
#   app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt
2024-01-04 07:44:40 +00:00
Mariya
46d2c2f57b App update Issue fixed, its updated successfully within one app itself 2024-01-04 13:00:47 +05:30
Mariya
18ed6c4e11 App update using api added 2024-01-04 12:10:20 +05:30
Pritimay Sarkar
02cdc49c9c coefficient screen added 2024-01-04 12:01:59 +05:30
Pritimay Sarkar
6d4adbc222 add auto dac 2024-01-04 01:11:30 +05:30
Pritimay Sarkar
9e88bf9109 change profile screen text to panel 2024-01-03 23:09:28 +05:30
Pritimay Sarkar
8fb504098f resolve conflict 2024-01-03 18:13:43 +05:30
Pritimay Sarkar
8892eaf189 fix db schemaerror 2024-01-03 17:56:01 +05:30
Mariya
ae42c96256 Toast Message is Added, if the device is connected 2024-01-03 15:31:58 +05:30
Mariya
b0d348cf6d Implement - In app update after getting the new update link from check update response 2024-01-03 14:49:41 +05:30
Mariya
214a834740 Merge branch 'preprod-temp' into preprod-temp-App-Updation 2024-01-03 12:55:38 +05:30
Kaif
d362a7fa86 Merge remote-tracking branch 'origin/preprod-temp' into preprod-temp 2024-01-03 12:53:45 +05:30
Mariya
dfbcb4af6a error resolved 2024-01-03 12:52:28 +05:30
Kaif
e8b380a4c4 Added pending API calls, like upload log, result upload and device update 2024-01-03 12:52:27 +05:30
Pritimay Sarkar
039e17a3b8 resolve compile errors since merge 2024-01-03 12:38:13 +05:30
Pritimay Sarkar
f54af3c877 Merge branch 'led-gains-data' into preprod 2024-01-03 12:08:29 +05:30
Pritimay Sarkar
0149ec7111 release 2.1.68 2024-01-03 11:54:21 +05:30
Mariya
ba45346fba Implement - In app update after getting the new update link from check update response 2024-01-02 17:09:22 +05:30
Pritimay Sarkar
c02bd1ba2e release 2.1.65 for pq 2024-01-02 11:38:38 +05:30
Pritimay Sarkar
f6b1b83f24 add gains commands and save gains data 2024-01-02 10:56:38 +05:30
Mariya
4b205185d3 error resolved related to previous changing color of text if the result is normal 2023-12-29 11:10:38 +05:30
Kaif
00a3124fbf API call for DeviceProvision and Login is completed and tested 2023-12-26 14:16:19 +05:30
Mariya
0faf4adbd7 ADDED CODE FOR Changing Text color from red to green if the result is normal 2023-12-26 12:59:44 +05:30
Kaif
f50a1d8dcd Added all for values and rebase with preprod 2023-12-24 23:16:51 +05:30
Pritimay Sarkar
5db43bf16c display hb3 and hb4 2023-12-24 23:16:51 +05:30
Pritimay Sarkar
21d53cc2b8 fix msg fragmentation in app and add log dsiplay for admin 2023-12-24 23:16:51 +05:30
Pritimay Sarkar
82360b22c8 handle-msg-fragmentation 2023-12-24 23:16:50 +05:30
Pritimay Sarkar
add61d9c65 add config error msg and v2 usb config 2023-12-24 23:15:56 +05:30
Mariya
92a01dbd9f Digital Card button removed 2023-12-24 23:15:21 +05:30
Kaif
bf73ea70b8 Changes for new Arduieno Device 2023-12-24 23:15:21 +05:30
Mariya
dfe39fb460 gradle version changed 2023-12-24 23:14:56 +05:30
Pritimay Sarkar
aa8a991fbb release 2.1.52 for qa 2023-12-24 23:14:11 +05:30
Pritimay Sarkar
3aeb7ef543 coeffs for dev4 2023-12-24 23:13:06 +05:30
Pritimay Sarkar
7a4b68690d add new slope class thresholds 2023-12-23 18:19:13 +05:30
Pritimay Sarkar
323a76c72a fix merge conflict 2023-12-21 16:26:59 +05:30
Pritimay Sarkar
c305635862 Merge branch 'hb3-hb4' into preprod-temp 2023-12-21 16:25:44 +05:30
Pritimay Sarkar
9183c97aef add slope based class 2023-12-21 16:14:29 +05:30
Pritimay Sarkar
72f1231d93 Merge branch 'Feature_New_firmware_changes_for_kit_check_with_4_LED_device' into 'preprod'
Kit check online and offline

See merge request sminnovations/hpos!18
2023-12-20 17:06:41 +00:00
Pritimay Sarkar
dcf340a3a0 Merge branch 'preprod' into 'Feature_New_firmware_changes_for_kit_check_with_4_LED_device'
# Conflicts:
#   app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt
#   app/src/main/java/com/example/hpostesting/presentation/adapter/UserListAdapter.kt
#   app/src/main/java/com/example/hpostesting/presentation/buffercheck/HemoCubeBufferCheckFragment.kt
#   app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt
#   app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt
2023-12-20 17:06:21 +00:00
Mariya
c573390082 ADDED CODE FOR OVF 2023-12-20 17:15:41 +05:30
Pritimay Sarkar
48b2fefd02 Merge branch 'fix-msg-fragmentation' into 'preprod'
Fix msg fragmentation

See merge request sminnovations/hpos!17
2023-12-20 11:13:55 +00:00
Mariya
59aefacec1 string translation for "SCAN QR CODE OF THE KIT" of kannada is Corrected 2023-12-20 11:52:06 +05:30
Mariya
8e67540833 Fixed issued related to csv file downloading 2023-12-19 17:12:11 +05:30
Mariya
762cbcbe44 changes related to csv added 2023-12-19 15:11:09 +05:30
Pritimay Sarkar
d1b5ca32bf fix msg fragmentation in app and add log dsiplay for admin 2023-12-18 12:48:07 +05:30
Mariya
d25a1bb08a color of download button changed, and try catch block added for download function of csv 2023-12-15 16:43:27 +05:30
Mariya
9093caa9df gradle issues fixed 2023-12-15 16:23:28 +05:30
Pritimay Sarkar
3e690345c2 handle-msg-fragmentation 2023-12-15 14:37:41 +05:30
Mariya
180ecf4b73 fixed issues related to save local data in csv file format 2023-12-15 13:07:22 +05:30
Pritimay Sarkar
4f7aa74314 Merge branch 'v2-hardware-config' into preprod 2023-12-15 10:32:56 +05:30
Pritimay Sarkar
590abb5c73 Merge branch 'preprod' 2023-12-15 10:29:48 +05:30
Pritimay Sarkar
7fe8202968 add config error msg and v2 usb config 2023-12-15 10:22:50 +05:30
Mariya
a00bd6e99e added changes related to save local data in csv file format 2023-12-14 21:11:36 +05:30
Mariya
c7a82fa6c6 added dialog box 2023-12-14 21:05:59 +05:30
Kaif
11d4c99e02 Changes for new Arduieno Device 2023-12-14 16:56:39 +05:30
Pritimay Sarkar
2e700601fe release 2.1.52 for qa 2023-12-14 14:53:53 +05:30
Kaif
75875b9e4f CSV Download 2023-12-14 13:59:48 +05:30
Mariya
41798aa37b Digital Card button removed 2023-12-14 13:07:15 +05:30
Mariya
e7600dafec gradle version changed 2023-12-14 12:43:10 +05:30
Mariya
d6b3df25f8 gradle version changed 2023-12-13 20:53:34 +05:30
Mariya
f048335f77 gradle version changed 2023-12-13 19:12:55 +05:30
Pritimay Sarkar
44d5b4f0ba coeffs for hb est 2023-12-13 19:03:43 +05:30
Mariya
0d7e8b0cd0 Merge remote-tracking branch 'origin/preprod' into preprod 2023-12-13 18:57:03 +05:30
Mariya
3c8570c46b gradle version changed 2023-12-13 18:56:40 +05:30
Pritimay Sarkar
96f9d4d6da coeffs for dev4 2023-12-13 18:55:41 +05:30
Pritimay Sarkar
0b17ea9ab9 display hb3 and hb4 2023-12-13 18:49:19 +05:30
Mariya
e7505077ce Merge remote-tracking branch 'origin/preprod' into preprod
# Conflicts:
#	app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt
2023-12-12 12:21:52 +05:30
Mariya
53bf35ac38 OFFLINE FEATURE ADDED- with csv file data downloading format added 2023-12-12 12:18:42 +05:30
Mariya
976f6f07e8 OFFLINE FEATURE ADDED- DB VERSION CHANGE 2023-12-09 00:03:32 +05:30
Mariya
865844af9b OFFLINE FEATURE ADDED- DB VERSION CHANGE 2023-12-08 23:53:36 +05:30
Mariya
1d9cf3096a OFFLINE FEATURE ADDED 2023-12-08 23:51:53 +05:30
Mariya
32461f03bb OFFLINE FEATURE ADDED 2023-12-08 23:50:28 +05:30
Mariya
ab1c88863a offline adapter added 2023-12-08 23:47:23 +05:30
Mariya
9b2fec81f5 offline adapter added 2023-12-08 23:46:11 +05:30
Mariya
1486848fcc offline adapter added 2023-12-08 23:44:49 +05:30
Mariya
c5993e3e87 offline adapter added 2023-12-08 23:42:23 +05:30
Mariya
58d3281b18 offline adapter added 2023-12-08 23:33:22 +05:30
Mariya
08fa6b84eb offline adapter added 2023-12-08 23:29:22 +05:30
Mariya
1a7e618fa5 offline adapter added 2023-12-08 23:25:23 +05:30
Mariya
f39bce5786 TestRight related issues fixed 2023-12-08 23:11:21 +05:30
Kaif
41e30cf178 Incremented the version number for giving testing release 2023-12-08 13:59:07 +05:30
Pritimay Sarkar
a34c095fde Merge remote-tracking branch 'origin/Feature_Develop_the_feature_to_shift_between_multiple_languages_in_testing_app' into preprod 2023-12-08 13:55:07 +05:30
Mariya
1d34ce2a52 Added Offline Incubation feature
Added testStatus field
2023-12-07 16:42:32 +05:30
Mariya
ec3d0b8628 Added Offline Incubation feature 2023-12-07 16:31:33 +05:30
Mariya
7464bfef6d deleted unwanted code 2023-12-07 16:00:34 +05:30
Mariya
85986c87b1 offline incubation code is added 2023-12-07 15:36:28 +05:30
Mariya
dadddfd558 Issues fixed 2023-12-06 17:25:45 +05:30
Mariya
0903647500 Issues fixed 2023-12-06 16:08:52 +05:30
Mariya
326c66fea7 Issues fixed 2023-12-06 11:32:38 +05:30
Mariya
045eaee578 Issues fixed 2023-12-05 20:43:03 +05:30
Mariya
302960922e Issues fixed 2023-12-05 20:41:47 +05:30
Mariya
ea4fb6e4d2 Issues fixed 2023-12-05 20:16:20 +05:30
Mariya
80fa523ec9 String Issues fixed 2023-12-05 17:57:32 +05:30
Mariya
7a2e5ead51 String Issues fixed 2023-12-05 17:53:46 +05:30
Mariya
f109c52288 app Build added 2023-12-05 17:50:46 +05:30
Mariya
a3a5c0d6fb pending strings added 2023-12-04 15:49:39 +05:30
Kaif
fb5e6f3b49 Fixed string error 2023-12-01 16:22:13 +05:30
Kaif
8a22b44496 Added app version 2023-12-01 16:10:15 +05:30
Kaif
10d822c5d6 Kannada and Hindi language support added 2023-12-01 15:55:49 +05:30
Mariya
4a248f7c49 deleted unwanted codes 2023-12-01 12:23:44 +05:30
Mariya
825a573060 Error resolved 2023-12-01 12:19:02 +05:30
Mariya
2123b2a7b4 Merge remote-tracking branch 'origin/preprod' into preprod 2023-12-01 10:54:05 +05:30
Pritimay Sarkar
704223e403 Merge branch 'LanguageTranslation' into 'preprod'
Merge multi-lingual changes to preprod

See merge request sminnovations/hpos!15
2023-11-30 08:52:52 +00:00
Pritimay Sarkar
6097a05701 Merge branch 'preprod' into 'LanguageTranslation'
# Conflicts:
#   app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt
#   app/src/main/java/com/example/hpostesting/presentation/KitScanActivity.kt
#   app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt
#   app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt
2023-11-30 08:52:01 +00:00
Mariya
290bf77950 Merge remote-tracking branch 'origin/preprod' into preprod 2023-11-30 13:38:13 +05:30
Mariya
ad595a6299 pending translation strings of hemocube fragment added 2023-11-30 13:21:02 +05:30
Kaif
7233305ca3 Kit check online and offline 2023-11-29 22:37:20 +05:30
Mariya
22bf66fc94 kannada and hindi version of strings added 2023-11-29 15:51:38 +05:30
Mariya
1dd66bcd6e kannada and hindi version of strings added 2023-11-28 15:39:57 +05:30
Pritimay Sarkar
a02ba3e9ea release 2.1.39 with updated coeffs for dev2 2023-11-28 12:05:06 +05:30
Pritimay Sarkar
457c652161 add low Hb check and log errors 2023-11-27 10:23:44 +05:30
Mariya
5bebd88dfd Merge remote-tracking branch 'origin/preprod' into preprod 2023-11-24 11:36:20 +05:30
Mariya
657b9aea65 removed Unwanted code 2023-11-22 17:10:40 +05:30
Mariya
90590ee535 removed Unwanted code 2023-11-22 17:10:06 +05:30
Mariya
3d39c43c22 Digital Counselling Card , All is finished 2023-11-22 16:31:46 +05:30
Pritimay Sarkar
6fa5e760c1 Merge branch 'Bug_Entering_a_new_kit_number_through_the_home_screen_triggers_an_invalid_message' into preprod 2023-11-22 10:37:54 +05:30
Pritimay Sarkar
8940352e05 prior testing related changes for clinical validation 2023-11-22 10:36:59 +05:30
Mariya
47fabf036f Merge branch 'DigitalCard' into DigitalPreprodCombine
# Conflicts:
#	app/src/main/java/com/example/hpostesting/data/dao/MyDataBase.kt
#	app/src/main/java/com/example/hpostesting/data/model/patient/UserData.kt
2023-11-21 14:19:36 +05:30
Mariya
d7a41e8f31 updated preprod changes added 2023-11-21 14:17:48 +05:30
Mariya
f26f3098bb updated preprod changes added 2023-11-21 14:15:55 +05:30
Mariya
4d454ea031 Merge remote-tracking branch 'origin/preprod' into preprod 2023-11-21 14:08:37 +05:30
Mariya
e92a80caec digital card fragment coding is finished, testing is pending 2023-11-21 13:40:42 +05:30
Kaif
28bd9cbd43 Kit number invalid error fixed 2023-11-20 14:14:19 +05:30
Pritimay Sarkar
fc3e4d506a Merge branch 'unit-tests' into 'preprod'
add setup for two unit tests

See merge request sminnovations/hpos!14
2023-11-18 01:14:42 +00:00
Pritimay Sarkar
60fbd70f2d correct strings msg 2023-11-17 18:20:33 +05:30
Pritimay Sarkar
7f2cafeef4 add two test 2023-11-17 14:48:53 +05:30
Mariya
b37d3f1fce digital card fragment coding is finished, testing is pending 2023-11-17 12:31:27 +05:30
Pritimay Sarkar
1d22fa54c9 add constants 2023-11-17 11:09:57 +05:30
Pritimay Sarkar
3b3bc1adf4 Merge branch 'variable-buffer-thresholds' into 'preprod'
add variable thresholds parameters across devices

See merge request sminnovations/hpos!13
2023-11-16 10:03:07 +00:00
Pritimay Sarkar
a1206dbe0d Merge branch 'preprod' into 'variable-buffer-thresholds'
# Conflicts:
#   app/src/main/java/com/example/hpostesting/data/constant/Constants.kt
2023-11-16 10:00:22 +00:00
Pritimay Sarkar
6e9850c492 add variable thresholds paramters across devices 2023-11-16 15:26:53 +05:30
Pritimay Sarkar
5982ce822f Merge branch 'battery-instrumentation' into 'preprod'
Battery instrumentation

See merge request sminnovations/hpos!12
2023-11-16 06:50:50 +00:00
Pritimay Sarkar
5c32a17871 add battery instrumentation and fix classification msg 2023-11-16 12:19:29 +05:30
Pritimay Sarkar
16528bd165 fix device constant error 2023-11-16 12:18:01 +05:30
Pritimay Sarkar
f86caf0448 move if conditions outside apply 2023-11-16 07:04:51 +05:30
Pritimay Sarkar
a7a57c7ff4 Merge branch 'deoxy-check' into 'preprod'
Deoxy check

See merge request sminnovations/hpos!11
2023-11-15 12:17:18 +00:00
Pritimay Sarkar
3c70142c15 check deoxygenation and classify with abs 2023-11-15 14:18:01 +05:30
Pritimay Sarkar
467dde9a24 add fields in converter 2023-11-15 13:11:54 +05:30
Pritimay Sarkar
ae50d6a603 filter local data based on test status 2023-11-15 13:11:07 +05:30
Mariya
c0825a1c58 digital card fragment coding is finished, testing is pending 2023-11-15 11:41:29 +05:30
Pritimay Sarkar
78763d8624 Merge branch 'Develop_Offline_Incubation_and_Blood_Group_Feature' of https://gitlab.com/sminnovations/hpos into deoxy-check 2023-11-15 08:37:55 +05:30
Pritimay Sarkar
fcac603ce0 change user messages 2023-11-15 08:26:55 +05:30
Pritimay Sarkar
0a2f1ba2fd refactor 2023-11-15 06:16:30 +05:30
mohamedkaif356
dcc7a921a0 Offline Incubation time 2023-11-14 22:58:34 +05:30
Mariya
b6f8999078 Merge remote-tracking branch 'origin/preprod' into preprod
# Conflicts:
#	app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt
2023-11-14 11:55:49 +05:30
Mariya
84e2a4bed9 digital card fragment User Design is finished 2023-11-14 11:55:15 +05:30
Pritimay Sarkar
74ad03bf8b add buffer validation withn bounds 2023-11-14 00:22:40 +05:30
Mariya
f00a639028 digital card fragment 2023-11-13 10:53:44 +05:30
Pritimay Sarkar
d3083c8dbc add environment name in gradle 2023-11-13 08:06:24 +05:30
Pritimay Sarkar
29846ddf7d show result processing message 2023-11-09 08:14:38 +05:30
Pritimay Sarkar
9deac8e278 fix device ratio calc 2023-11-09 08:08:04 +05:30
Mariya
264c4ebe74 Merge remote-tracking branch 'origin/preprod' into preprod 2023-11-08 12:25:07 +05:30
Mariya
2a976caac5 committed changes 2023-11-08 12:24:29 +05:30
Pritimay Sarkar
fb61b89850 change display msg 2023-11-08 11:34:31 +05:30
Pritimay Sarkar
e42183099c add new device logins 2023-11-08 11:33:59 +05:30
Mariya
bf9288d0a1 committed 2023-11-08 10:45:12 +05:30
Mariya
d57bc713d1 Merge remote-tracking branch 'origin/preprod' into preprod
# Conflicts:
#	app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeFragment.kt
2023-11-07 17:25:42 +05:30
Mariya
f37cfb57e1 1.baud rate is added,
2. existing buffer popup is added,
3. submit button make visible
4.handlevalidresult function changes, added code for fetch ledbuffer1 to ledbuffer4 values and sample1 to sample4 values
2023-11-07 17:25:09 +05:30
Pritimay Sarkar
9d86d874af fix testing steps 2023-11-07 07:38:10 +05:30
Pritimay Sarkar
f0d0740cd7 firmware changes for 4 leds 2023-11-07 05:18:27 +05:30
Mariya
27949352dd 1.baud rate is added,
2. existing buffer popup is added,
3. submit button make visible
4.handlevalidresult function changes, added code for fetch ledbuffer1 to ledbuffer4 values and sample1 to sample4 values
2023-11-06 18:18:07 +05:30
Mariya
36eb659e3a baud rate is completed 2023-11-06 17:46:04 +05:30
Mariya
2d0f11cfb2 new arduino code Result work for molbio is pending 2023-11-06 17:35:57 +05:30
Mariya
3e8933de1b new arduino code Result calculation is pending 2023-11-06 13:37:57 +05:30
Mariya
a068ae492c unitest is pending 2023-11-06 12:36:50 +05:30
Pritimay Sarkar
edf1d70608 add 4 led in data class 2023-11-03 16:35:28 +05:30
Pritimay Sarkar
a6d57fb52a Merge branch 'prod' into preprod 2023-11-03 11:40:40 +05:30
Pritimay Sarkar
e613bbd12b change if check for exsting buffer 2023-09-26 16:41:49 +05:30
186 changed files with 15793 additions and 1113 deletions

101
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,101 @@
# This file is a template, and might need editing before it works on your project.
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Android.gitlab-ci.yml
# Read more about this script on this blog post https://about.gitlab.com/2018/10/24/setting-up-gitlab-ci-for-android-projects/, by Jason Lenny
# If you are interested in using Android with FastLane for publishing take a look at the Android-Fastlane template.
image: eclipse-temurin:17-jdk-jammy
variables:
# ANDROID_COMPILE_SDK is the version of Android you're compiling with.
# It should match compileSdkVersion.
ANDROID_COMPILE_SDK: "34"
# ANDROID_BUILD_TOOLS is the version of the Android build tools you are using.
# It should match buildToolsVersion.
ANDROID_BUILD_TOOLS: "33.0.2"
# It's what version of the command line tools we're going to download from the official site.
# Official Site-> https://developer.android.com/studio/index.html
# There, look down below at the cli tools only, sdk tools package is of format:
# commandlinetools-os_type-ANDROID_SDK_TOOLS_latest.zip
# when the script was last modified for latest compileSdkVersion, it was which is written down below
ANDROID_SDK_TOOLS: "9477386"
# Packages installation before running script
before_script:
- apt-get --quiet update --yes
- apt-get --quiet install --yes wget unzip
# Setup path as android_home for moving/exporting the downloaded sdk into it
- export ANDROID_HOME="${PWD}/android-sdk-root"
# Create a new directory at specified location
- install -d $ANDROID_HOME
# Here we are installing androidSDK tools from official source,
# (the key thing here is the url from where you are downloading these sdk tool for command line, so please do note this url pattern there and here as well)
# after that unzipping those tools and
# then running a series of SDK manager commands to install necessary android SDK packages that'll allow the app to build
- wget --no-verbose --output-document=$ANDROID_HOME/cmdline-tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS}_latest.zip
- unzip -q -d "$ANDROID_HOME/cmdline-tools" "$ANDROID_HOME/cmdline-tools.zip"
- mv -T "$ANDROID_HOME/cmdline-tools/cmdline-tools" "$ANDROID_HOME/cmdline-tools/tools"
- export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/cmdline-tools/tools/bin
# Nothing fancy here, just checking sdkManager version
- sdkmanager --version
# use yes to accept all licenses
- yes | sdkmanager --licenses > /dev/null || true
- sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}"
- sdkmanager "platform-tools"
- sdkmanager "build-tools;${ANDROID_BUILD_TOOLS}"
# Not necessary, but just for surity
- chmod +x ./gradlew
# Basic android and gradle stuff
# Check linting
lintDebug:
interruptible: true
stage: build
script:
- ./gradlew -Pci --console=plain :app:lintDebug -PbuildDir=lint
artifacts:
paths:
- app/lint/reports/lint-results-debug.html
expose_as: "lint-report"
when: always
# Make Project
assembleDebug:
interruptible: true
stage: build
script:
- ./gradlew assembleDebug
artifacts:
paths:
- app/build/outputs/
# Run all tests, if any fails, interrupt the pipeline(fail it)
debugTests:
needs: [lintDebug, assembleDebug]
interruptible: true
stage: test
script:
- ./gradlew -Pci --console=plain :app:testDebug
artifacts:
paths:
- app/build/test-results/
publishTestResults:
stage: test
script:
- echo "Publishing JUnit test results"
needs: [debugTests]
artifacts:
when: always
reports:
junit: app/build/test-results/testDebugUnitTest/*.xml

2
.idea/gradle.xml generated
View File

@@ -5,7 +5,6 @@
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
@@ -14,6 +13,7 @@
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>

View File

@@ -14,16 +14,18 @@ android {
compileSdk 34
namespace 'in.sminnovations.hpostesting'
// dev -> development, quality -> qc, uat -> User Acceptance Testing, preprod -> preproduction, prod -> production, iocl -> iocl-iisc production
defaultConfig {
applicationId "in.sminnovations.hpostesting"
applicationId "in.sminnovations.hpostesting.dev"
minSdk 21
targetSdk 34
versionCode 24
versionName "2.1.24"
versionCode 114
versionName "2.1.114"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
@@ -48,15 +50,26 @@ android {
abortOnError false
checkReleaseBuilds false
}
packagingOptions {
exclude 'mockito-extensions/org.mockito.plugins.MockMaker'
}
// testOptions {
// unitTests {
// includeAndroidResources = true
// }
// }
}
dependencies {
implementation "com.google.dagger:hilt-android:2.46"
kapt "com.google.dagger:hilt-android-compiler:2.46"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
implementation 'androidx.fragment:fragment-ktx:1.6.2'
//firebase
implementation platform('com.google.firebase:firebase-bom:32.1.0')
@@ -68,54 +81,90 @@ dependencies {
implementation 'com.google.firebase:firebase-storage-ktx'
implementation 'com.firebaseui:firebase-ui-firestore:8.0.2'
implementation 'com.google.android.gms:play-services-auth:20.7.0'
implementation 'com.google.android.gms:play-services-location:21.0.1'
implementation 'com.google.android.gms:play-services-location:21.1.0'
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
implementation 'com.google.android.things:androidthings:1.0'
implementation 'com.google.firebase:firebase-appdistribution:16.0.0-beta10'
implementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta10")
implementation 'com.google.firebase:firebase-appdistribution:16.0.0-beta11'
implementation("com.google.firebase:firebase-appdistribution-api-ktx:16.0.0-beta11")
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'com.google.android.play:core:1.10.3'
implementation 'io.nats:jnats:2.11.2'
// Testing
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
testImplementation "androidx.work:work-testing:2.9.0"
androidTestImplementation 'androidx.test:core-ktx:1.5.0'
testImplementation 'androidx.fragment:fragment-testing:1.6.2'
testImplementation "org.robolectric:robolectric:4.7"
// mockk
testImplementation 'io.mockk:mockk:1.10.6'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
implementation 'com.opencsv:opencsv:4.6'
// Mockito dependencies
testImplementation 'org.mockito:mockito-core:3.12.4'
androidTestImplementation 'org.mockito:mockito-android:3.12.4'
androidTestImplementation 'org.mockito:mockito-inline:3.12.4'
androidTestImplementation 'org.mockito:mockito-android:3.12.4'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
testImplementation "androidx.arch.core:core-testing:2.2.0"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0"
implementation 'com.opencsv:opencsv:5.9'
implementation 'com.github.mik3y:usb-serial-for-android:3.5.1'
implementation "androidx.fragment:fragment-ktx:1.6.1"
implementation "androidx.fragment:fragment-ktx:1.6.2"
// CSV read, write
implementation 'com.opencsv:opencsv:4.6'
implementation 'com.opencsv:opencsv:5.9'
// Barcode scanner
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
// Google ML Kit usign play services
// Google ML Kit using play services
implementation 'com.google.android.gms:play-services-code-scanner:16.1.0'
//Room
implementation "androidx.room:room-ktx:2.6.0-rc01"
implementation "androidx.room:room-runtime:2.6.0-rc01"
kapt ("androidx.room:room-compiler:2.6.0-rc01")
implementation "androidx.room:room-ktx:2.6.1"
implementation "androidx.room:room-runtime:2.6.1"
kapt ("androidx.room:room-compiler:2.6.1")
//image
implementation 'com.github.bumptech.glide:glide:4.13.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.13.2'
// Navigation Component
implementation "androidx.navigation:navigation-fragment-ktx:2.7.3"
implementation "androidx.navigation:navigation-ui-ktx:2.7.3"
implementation "androidx.navigation:navigation-fragment-ktx:2.7.6"
implementation "androidx.navigation:navigation-ui-ktx:2.7.6"
//Dagger - Hilt
implementation "com.google.dagger:hilt-android:2.46"
kapt "com.google.dagger:hilt-android-compiler:2.46"
kapt "androidx.hilt:hilt-compiler:1.0.0"
kapt "androidx.hilt:hilt-compiler:1.1.0"
// Retrofit + GSON
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation("com.squareup.okhttp3:okhttp:4.9.3")
implementation "androidx.preference:preference-ktx:1.2.1"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1'
implementation("androidx.work:work-runtime-ktx:2.9.0")
// implementation("io.nats:jnats:2.11.2")
implementation 'com.google.android.play:core:1.10.3'
implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation 'io.nats:jnats:2.11.4'
}

208
app/google-services.json Normal file
View File

@@ -0,0 +1,208 @@
{
"project_info": {
"project_number": "650071678820",
"project_id": "hpos-af3cc",
"storage_bucket": "hpos-af3cc.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:650071678820:android:f1435a1c07f710036c6471",
"android_client_info": {
"package_name": "com.example.hposconsentform"
}
},
"oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:650071678820:android:7865bef608cdee6f6c6471",
"android_client_info": {
"package_name": "com.smi.counselling"
}
},
"oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:650071678820:android:2925e9ce3417d2386c6471",
"android_client_info": {
"package_name": "in.sminnovations.hemocube"
}
},
"oauth_client": [
{
"client_id": "650071678820-srhm9spm9hjn4frcd3r6o02gdhdbtd15.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "in.sminnovations.hemocube",
"certificate_hash": "7714b9268a81d0cf0fb178b0af8dbb630f8fc70a"
}
},
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:650071678820:android:7569c1cad4fc99916c6471",
"android_client_info": {
"package_name": "in.sminnovations.hposregistration"
}
},
"oauth_client": [
{
"client_id": "650071678820-70kp5jvjda4r5diqch2kn4lc40p4f42g.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "in.sminnovations.hposregistration",
"certificate_hash": "7714b9268a81d0cf0fb178b0af8dbb630f8fc70a"
}
},
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:650071678820:android:f96be19e5d43102b6c6471",
"android_client_info": {
"package_name": "in.sminnovations.hpostesting"
}
},
"oauth_client": [
{
"client_id": "650071678820-l87dnr0bdj95get0khgnvfv2an1k6ogq.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "in.sminnovations.hpostesting",
"certificate_hash": "7714b9268a81d0cf0fb178b0af8dbb630f8fc70a"
}
},
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:650071678820:android:a53292637abb7c0d6c6471",
"android_client_info": {
"package_name": "in.sminnovations.hpostesting.dev"
}
},
"oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAVdNGCev_AFX0qmuZJF6kxzRzXUTeAW5I"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "650071678820-to41afi9f0gi5r3k6v7pe1e5p96nupcs.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

Binary file not shown.

Binary file not shown.

View File

@@ -4,15 +4,15 @@
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.example.hpos",
"applicationId": "in.sminnovations.hpostesting.quality",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1,
"versionName": "1.0",
"versionCode": 101,
"versionName": "2.1.101",
"outputFile": "app-release.apk"
}
],

View File

@@ -0,0 +1,42 @@
package com.example.hpostesting
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
//import com.example.hpostesting.R
import com.example.hpostesting.presentation.assurance.AssuranceControlsActivity
import `in`.sminnovations.hpostesting.databinding.ActivityAssuranceControlsBinding
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
@RunWith(AndroidJUnit4::class)
class AssuranceControlsActivityTest {
//
// @Test
// fun testViewBinding() {
// val activityScenario = ActivityScenario.launch(AssuranceControlsActivity::class.java)
// activityScenario.onActivity { activity ->
// assertNotNull(activity.binding)
// assertEquals(ActivityAssuranceControlsBinding::class.java, activity.binding.javaClass)
// }
// }
//
// @Test
// fun testSharedPreferencesInitialization() {
// val activityScenario = ActivityScenario.launch(AssuranceControlsActivity::class.java)
// activityScenario.onActivity { activity ->
// assertNotNull(activity.sharedPreference)
// assertEquals("HEMOCUBE", activity.sharedPreference.getString("HEMOCUBE", null))
// }
// }
// @Test
// fun testFragmentTransaction() {
// val activityScenario = ActivityScenario.launch(AssuranceControlsActivity::class.java)
// onView(withId(R.id.fgAssuranceControls)).check(matches(isDisplayed())) // Using Espresso for UI verification
// }
}

View File

@@ -17,6 +17,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.refactoredapp", appContext.packageName)
// assertEquals("com.example.refactoredapp", appContext.packageName)
}
}

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/primary"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/primary" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>

View File

@@ -5,6 +5,11 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission
android:name="android.permission.BATTERY_STATS"
tools:ignore="ProtectedPermissions" />
<uses-feature android:name="android.hardware.camera" />
@@ -30,8 +35,37 @@
android:theme="@style/Theme.HPOSTesting"
tools:targetApi="31">
<activity
android:name="com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity"
android:name="com.example.hpostesting.presentation.deviceinfo.DeviceActivity"
android:exported="false" />
<activity
android:name="com.example.hpostesting.presentation.hemocube.DigitalCardActivity"
android:exported="false" />
<activity
android:name="com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity"
android:exported="false"
android:theme="@style/Theme.HPOS.NoActionBar" />
<activity
android:name="com.example.hpostesting.presentation.autodac.AutoDacActivity"
android:exported="false"
android:theme="@style/Theme.HPOS.NoActionBar" />
<activity
android:name="com.example.hpostesting.presentation.jig.JigActivity"
android:exported="false"
android:theme="@style/Theme.HPOS.NoActionBar" />
<activity
android:name="com.example.hpostesting.presentation.deviceprovision.DeviceProvisionActivity"
android:exported="false"
android:theme="@style/Theme.HPOS.NoActionBar" />
<activity
android:name="com.example.hpostesting.presentation.calibration.CalibrationActivity"
android:exported="false"
android:theme="@style/Theme.HPOS.NoActionBar"/>
<activity
android:name="com.example.hpostesting.presentation.assurance.AssuranceControlsActivity"
android:exported="false"
android:theme="@style/Theme.HPOS.NoActionBar"/>
<activity
android:name="com.example.hpostesting.presentation.hemocube.HemocubeActivity"
android:exported="false"
@@ -39,6 +73,15 @@
android:parentActivityName="com.example.hpostesting.presentation.MainActivity"
android:theme="@style/Theme.HPOS.NoActionBar"
android:windowSoftInputMode="adjustPan" />
<activity
android:name="com.example.hpostesting.presentation.trueheme.TrueHemeActivity"
android:exported="false"
android:noHistory="true"
android:parentActivityName="com.example.hpostesting.presentation.MainActivity"
android:theme="@style/Theme.HPOS.NoActionBar"
android:windowSoftInputMode="adjustPan" />
<activity
android:name="com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity"
android:exported="false"
@@ -55,7 +98,19 @@
android:name="com.example.hpostesting.presentation.dashboard.DashboardActivity"
android:exported="false"
android:label="@string/title_activity_dashboard"
android:theme="@style/Theme.HPOS.NoActionBar" />
android:theme="@style/Theme.HPOS.NoActionBar"
android:screenOrientation="portrait"
tools:ignore="AppLinkUrlError,MissingClass">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
<data android:scheme="file" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity>
<service
android:name="com.example.hpostesting.presentation.testRight.UsbService"
@@ -68,9 +123,8 @@
android:noHistory="true"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
@@ -107,6 +161,17 @@
android:screenOrientation="portrait"
android:stateNotNeeded="true"
tools:replace="android:screenOrientation" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

View File

@@ -0,0 +1 @@
BASE_URL= https://datacollection.micropcr.com/api/

View File

@@ -0,0 +1,50 @@
package com.example.hpostesting.data
import android.content.Context
import android.os.Environment
import java.io.File
import java.io.FileWriter
import java.io.IOException
class CsvWriter(private val context: Context) {
fun writeCsv(fileName: String, data: List<Array<String>>): Boolean {
try {
val filePath = File(getExternalStorageDirectory(), fileName)
val writer = FileWriter(filePath)
// Write header row
val header = arrayOf(
"ID",
"Blood Group",
"Birth Year",
"Classification Result",
"Test Time",
"User Image URL",
"Name"
)
writer.write(header.joinToString(",") + "\n")
// Write data rows
for (line in data) {
writer.write(line.joinToString(",") + "\n")
}
writer.close()
return true
} catch (e: IOException) {
e.printStackTrace()
return false
}
}
private fun getExternalStorageDirectory(): File {
val folder = File(Environment.getExternalStorageDirectory(), "HposFolder")
if (!folder.exists()) {
folder.mkdirs()
}
return folder
}
}

View File

@@ -1,46 +0,0 @@
package com.example.hpostesting.data
import android.util.Log
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.repository.DatabaseRepository
import com.example.hpostesting.util.MyUtils
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class FileUploader {
private val TAG = "FileUploader"
val repository = DatabaseRepository()
suspend fun start() {
val listOfFiles = repository.getAllFromPendingQueue()
for (each in listOfFiles){
// Checking internet connectivity & mobile as same or not
if (MyUtils.isInternetConnected() && each.mobileId == DataHolder.mobileUniqueId) {
Log.d(TAG, "uploading file ${each.pendingId}")
GlobalScope.launch {
val response = repository.uploadFileToStorage(each.patientId, each.filePath)
when (response) {
is Response.Success -> {
Log.d(TAG, "File Uploaded ${each.pendingId}")
repository.removeFromPendingQueue(each.pendingId)
}
is Response.Error -> {
Log.d(TAG, response.exception.toString())
}
}
}.join()
} else {
return
}
}
return
}
}

View File

@@ -0,0 +1,7 @@
package com.example.hpostesting.data
sealed class Result<out T : Any> {
data class Success<out T : Any>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
class Loading<T : Any> : Result<T>()
}

View File

@@ -0,0 +1,21 @@
package com.example.hpostesting.data.api
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse
import com.example.hpostesting.data.model.login.LoginRequest
import com.example.hpostesting.data.model.login.LoginResponse
import retrofit2.http.Body
import retrofit2.http.POST
interface MolbioAuthApi {
@POST("deviceManagement/device/provision")
suspend fun deviceProvision(
@Body deviceProvisionRequest: DeviceProvisionRequest
): DeviceProvisionResponse
@POST("deviceService/device/login")
suspend fun login(
@Body loginRequest: LoginRequest
): LoginResponse
}

View File

@@ -0,0 +1,46 @@
package com.example.hpostesting.data.api
import com.example.hpostesting.data.model.log.UploadLogsResponse
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse
import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.CheckUpdateResponse
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import okhttp3.MultipartBody
import okhttp3.Response
import okhttp3.ResponseBody
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Part
interface MolbioResultApi {
@PUT("deviceService/results/HPOS")
suspend fun uploadResults(
@Body molbioV2ResultRequest: MolbioV2ResultRequest
): MolbioV2ResultResponse
@POST("deviceService/device/checkUpdate")
suspend fun checkUpdate(
@Body checkUpdateRequest: CheckUpdateRequest
): CheckUpdateResponse
@POST("deviceService/device/getUpdate")
suspend fun deviceUpdate(
@Body deviceUpdateRequest: DeviceUpdateRequest
): ResponseBody
@GET("deviceService/device/getClientCertificate")
suspend fun downloadClientCertificate(
): ResponseBody
@Multipart
@POST("deviceService/device/uploadLogs")
suspend fun uploadLogs(
@Part logFile: MultipartBody.Part
): UploadLogsResponse
}

View File

@@ -0,0 +1,13 @@
package com.example.hpostesting.data.api
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.presentation.UsbServiceListener
interface PropertyProvider {
fun getProperty(key: String): String
}
interface DeviceCommunicationHandler {
fun sendAndListenToDevice(command: HemoCubeCommands, listener: UsbServiceListener)
}

View File

@@ -1,9 +1,16 @@
package com.example.hpostesting.data.constant
enum class HemoCubeCommands(val command: String) {
startBuffer("B\r"),
runDiagnostics("D\r"),
startSample("S\r"),
getSample("P\r"),
getDeviceId("I\r"),
START_BUFFER_COMMAND("B\r"),
AUTO_DAC_COMMAND("C\r"),
DIAGNOSTICS_COMMAND("D\r"),
FIRMWARE_INFO_COMMAND("F\r"),
START_SAMPLE("S\r"),
PRINT_COMMAND("P\r"),
READ_DAC_COMMAND("R\r"),
DEVICE_CONFIGURATION_COMMAND("I\r"),
FIRST_GAIN_COMMAND("T\r"),
SECOND_GAIN_COMMAND("U\r"),
THIRD_GAIN_COMMAND("V\r"),
FORTH_GAIN_COMMAND("W\r"),
}

View File

@@ -0,0 +1,30 @@
package com.example.hpostesting.data.constant
import android.content.Context
import android.content.SharedPreferences
import java.util.Locale
object LanguageManager {
private const val LANGUAGE_PREF_KEY = "language_pref"
fun setLocale(context: Context, languageCode: String) {
val locale = Locale(languageCode)
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.setLocale(locale)
context.createConfigurationContext(configuration)
}
fun persistLanguagePreference(context: Context, languageCode: String) {
val prefs: SharedPreferences = context.getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
prefs.edit().putString(LANGUAGE_PREF_KEY, languageCode).apply()
}
fun getSavedLanguage(context: Context): String {
val prefs: SharedPreferences = context.getSharedPreferences("AppPrefs", Context.MODE_PRIVATE)
return prefs.getString(LANGUAGE_PREF_KEY, "en") ?: "en"
}
}

View File

@@ -0,0 +1,40 @@
package com.example.hpostesting.data.constant
enum class TestStatus(val code: Double) {
TEST_STARTED(1.0),
CONFIG_STARTED(2.0),
CONFIG_COMPLETED(3.0),
FIRST_EMPTY_AIR_READING_STARTED(4.1),
FIRST_EMPTY_AIR_READING_COMPLETED(4.2),
FIRST_EMPTY_AIR_READING_PRINT_STARTED(4.3),
FIRST_EMPTY_AIR_READING_PRINT_COMPLETED(4.4),
BUFFER_STARTED(4.0),
BUFFER_COMPLETED(5.0),
BUFFER_PRINT_STARTED(6.0),
BUFFER_PRINT_COMPLETED(7.0),
SAMPLE_STARTED(8.0),
SAMPLE_COMPLETED(9.0),
SAMPLE_PRINT_STARTED(10.0),
SAMPLE_PRINT_COMPLETED(11.0),
SECOND_EMPTY_AIR_READING_STARTED(11.1),
SECOND_EMPTY_AIR_READING_COMPLETED(11.2),
SECOND_EMPTY_AIR_PRINT_STARTED(11.3),
SECOND_EMPTY_AIR_PRINT_COMPLETED(11.4),
FIRST_GAIN_STARTED(12.0),
FIRST_GAIN_COMPLETED(13.0),
FIRST_GAIN_PRINT_STARTED(14.0),
FIRST_GAIN_PRINT_COMPLETED(15.0),
SECOND_GAIN_STARTED(16.0),
SECOND_GAIN_COMPLETED(17.0),
SECOND_GAIN_PRINT_STARTED(18.0),
SECOND_GAIN_PRINT_COMPLETED(19.0),
THIRD_GAIN_STARTED(20.0),
THIRD_GAIN_COMPLETED(21.0),
THIRD_GAIN_PRINT_STARTED(22.0),
THIRD_GAIN_PRINT_COMPLETED(23.0),
FORTH_GAIN_STARTED(24.0),
FORTH_GAIN_COMPLETED(25.0),
FORTH_GAIN_PRINT_STARTED(26.0),
FORTH_GAIN_PRINT_COMPLETED(27.0),
TEST_COMPLETED(30.0)
}

View File

@@ -0,0 +1,26 @@
package com.example.hpostesting.data.dao
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.hpostesting.data.model.patient.BufferCheckData
@Dao
interface HemoCubeBufferDao {
@Query("SELECT * from hemo_cube_buffer_test_table")
fun getAll(): LiveData<List<BufferCheckData>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(bufferCheckData: BufferCheckData)
@Query("SELECT * FROM hemo_cube_buffer_test_table WHERE _id = :id")
suspend fun getUserByID(id: String): BufferCheckData
@Query("DELETE FROM hemo_cube_buffer_test_table WHERE _id = :id")
suspend fun deleteById(id: String)
@Query("UPDATE hemo_cube_buffer_test_table SET localFlag = :newValue WHERE _id = :id")
suspend fun updateFieldById(id: String, newValue: Boolean)
}

View File

@@ -23,4 +23,10 @@ interface HemoCubeDao {
@Query("UPDATE hemo_cube_test_table SET localFlag = :newValue WHERE _id = :id")
suspend fun updateFieldById(id: String, newValue: Boolean)
@Query("UPDATE hemo_cube_test_table SET molbioFlag = :newValue WHERE _id = :id")
suspend fun updateMolbioFlag(id: String, newValue: Boolean)
@Query("UPDATE hemo_cube_test_table SET isCSVCreated = :newValue WHERE _id = :id")
suspend fun updateCSVFieldById(id: String, newValue: Boolean)
}

View File

@@ -1,19 +1,17 @@
package com.example.hpostesting.data.dao
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.google.android.datatransport.runtime.dagger.Provides
import javax.inject.Singleton
import com.example.hpostesting.data.model.patient.UserData
@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class], version = 3, exportSchema = false)
@Database(entities = [UserData::class, HemoCubeTestData::class, DeviceData::class, BufferCheckData::class], version = 26, exportSchema = false)
@TypeConverters(Converters::class)
abstract class MyDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun hemoCubeDao(): HemoCubeDao
abstract fun hemoCubeBufferDao(): HemoCubeBufferDao
}

View File

@@ -1,23 +1,19 @@
package com.example.hpostesting.data.datasource
import android.os.Environment
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.opencsv.CSVWriter
import java.io.File
import java.io.FileWriter
import java.io.IOException
class LocalFileDataSource {
interface LocalFileDataSource {
fun saveCsvToDisk(filepath: String, contents: ArrayList<Array<String>>){
val writer = CSVWriter(FileWriter(filepath))
writer.writeAll(contents) // data is adding to csv
writer.close()
}
fun saveCsvToDisk(filepath: String, contents: ArrayList<Array<String>>)
fun saveTextToDisk(filepath: String, contents: String){
val writer = FileWriter(File(filepath))
writer.append(contents)
writer.flush()
writer.close()
}
fun saveTextToDisk(filepath: String, contents: String)
fun exportDataToCSV(
fileName: String, dataList: List<HemoCubeTestData>
): Boolean
}

View File

@@ -0,0 +1,161 @@
package com.example.hpostesting.data.datasource
import android.os.Environment
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.opencsv.CSVWriter
import java.io.File
import java.io.FileWriter
import java.io.IOException
import javax.inject.Inject
class LocalFileDataSourceImpl @Inject constructor(): LocalFileDataSource {
override fun saveCsvToDisk(filepath: String, contents: ArrayList<Array<String>>) {
val writer = CSVWriter(FileWriter(filepath))
writer.writeAll(contents) // data is adding to csv
writer.close()
}
override fun saveTextToDisk(filepath: String, contents: String) {
val writer = FileWriter(File(filepath))
writer.append(contents)
writer.flush()
writer.close()
}
override fun exportDataToCSV(
fileName: String, dataList: List<HemoCubeTestData>
): Boolean {
try {
val formattedFileName = fileName.replace(Regex("[^a-zA-Z0-9.-]"), "_") // Replace special characters with underscores
val filePath = File(getExternalStorageDirectory(), formattedFileName)
val writer = FileWriter(filePath)
val csvWriter = CSVWriter(writer)
// Write CSV header
val header = arrayOf(
"_id",
"name",
"incubationTime",
"bloodGroup",
"birthYear", // Include other fields from the data class
"state",
"abhaId",
"userImageURL",
"location",
"reportUploadTime",
"testType",
"testTime",
"testStatus",
"gender",
"localFlag", // Add more fields as necessary
"deviceId",
"appVersion",
"deviceSerialNumber",
"deviceType",
"kitSerial",
"resultData",
"led1Buffer",
"led2Buffer",
"led3Buffer",
"led4Buffer",
"led1Sample",
"led2Sample",
"led3Sample",
"led4Sample",
"led1Average",
"led2Average",
"led3Average",
"led4Average",
"abs1",
"abs2",
"abs3",
"abs4",
"deviceRatio",
"calculatedRatio",
"predictedDenovixRatio",
"coefficients",
"classificationResult",
"prdClassification",
"errorMessages",
"batteryLevel",
"batteryCapacity",
"batteryMaxCapacity",
"batteryTemperature",
"batteryVoltage"
)
csvWriter.writeNext(header)
// Write data rows
for (data in dataList) {
val row = arrayOf(
data._id,
data.name,
data.incubationTime,
data.bloodGroup,
data.birthYear, // Include other fields similarly
data.state,
data.abhaId,
data.userImageURL,
data.location?.toString(),
data.reportUploadTime ?: "",
data.testType ?: "",
data.testTime ?: "",
data.testStatus?.toString() ?: "",
data.gender,
data.localFlag.toString(),
data.deviceId ?: "",
data.appVersion ?: "",
data.deviceSerialNumber,
data.deviceType,
data.kitSerial,
data.resultData,
data.led1Buffer?.toString() ?: "",
data.led2Buffer?.toString() ?: "",
data.led3Buffer?.toString() ?: "",
data.led4Buffer?.toString() ?: "",
data.led1Sample?.toString() ?: "",
data.led2Sample?.toString() ?: "",
data.led3Sample?.toString() ?: "",
data.led4Sample?.toString() ?: "",
data.led1Average?.toString() ?: "",
data.led2Average?.toString() ?: "",
data.led3Average?.toString() ?: "",
data.led4Average?.toString() ?: "",
data.abs1?.toString() ?: "",
data.abs2?.toString() ?: "",
data.abs3?.toString() ?: "",
data.abs4?.toString() ?: "",
data.deviceRatio?.toString() ?: "",
data.calculatedRatio?.toString() ?: "",
data.predictedDenovixRatio?.toString() ?: "",
data.coefficients ?: "",
data.classificationResult,
data.prdClassification,
data.errorMessages,
data.batteryLevel,
data.batteryCapacity,
data.batteryMaxCapacity,
data.batteryTemperature,
data.batteryVoltage
)
csvWriter.writeNext(row)
}
writer.close()
return true
} catch (e: IOException) {
e.printStackTrace()
return false
}
}
private fun getExternalStorageDirectory(): File {
val folder = File(Environment.getExternalStorageDirectory(), "HposFolder")
if (!folder.exists()) {
folder.mkdirs()
}
return folder
}
}

View File

@@ -0,0 +1,44 @@
package com.example.hpostesting.data.model
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
data class TestState (
var testDetails: HemoCubeTestData? = null,
var isOnline: Boolean = false,
var currentDeviceData: DeviceData? = null,
var resultData: String = "",
var currentResultData: String = "",
var isUsingExistingBuffer: Boolean = false,
var isTestOngoing: Boolean = false,
var led1BufferForDevice: Double = 0.0,
var led2BufferForDevice: Double = 0.0,
var led3BufferForDevice: Double = 0.0,
var led4BufferForDevice: Double = 0.0,
var led1SampleForDevice: Double = 0.0,
var led2SampleForDevice: Double = 0.0,
var led3SampleForDevice: Double = 0.0,
var led4SampleForDevice: Double = 0.0,
var fittedAbs1: Double = 0.0,
var fittedAbs2: Double = 0.0,
var fittedAbs3: Double = 0.0,
var fittedAbs4: Double = 0.0,
// var led1Air1: Double? = null,
// var led2Air1: Double? = null,
// var led3Air1: Double? = null,
// var led4Air1: Double? = null,
// var led1Air2: Double? = null,
// var led2Air2: Double? = null,
// var led3Air2: Double? = null,
// var led4Air2: Double? = null,
var calculatedPredictedDenovixRatio: Double = 0.0,
var validationError: Boolean = false,
var deviceHardwareId: String = "",
var allErrorMessages: String = "",
var testStatusCode: Double = 0.0,
var repeatReadingCount: Int = 0,
var readingsPerSample: Int = Constants.READINGS_PER_SAMPLE,
var uploadedToCloud: Boolean = false,
var uploadedToMolbio: Boolean = false
)

View File

@@ -0,0 +1,13 @@
package com.example.hpostesting.data.model.calibration
data class CalibrationData (
var led1Slope: Double = 1.0,
var led1Intercept: Double = 0.0,
var led2Slope: Double = 1.0,
var led2Intercept: Double = 0.0,
var led3Slope: Double = 1.0,
var led3Intercept: Double = 0.0,
var led4Slope: Double = 1.0,
var led4Intercept: Double = 0.0,
var calibratedAt: String = ""
)

View File

@@ -0,0 +1,6 @@
package com.example.hpostesting.data.model.deviceprovision
data class Credentials(
val password: String? = "",
val username: String? = ""
)

View File

@@ -0,0 +1,17 @@
package com.example.hpostesting.data.model.deviceprovision
data class Device(
val assigned: Boolean? = false,
val createdAt: String? = "",
val createdBy: Int? = 0,
val deviceType: DeviceType? = DeviceType(),
val deviceTypeId: Int? = 0,
val deviceUser: DeviceUser? = DeviceUser(),
val enabled: Boolean? = false,
val id: Int? = 0,
val name: String? = "",
val serialNumber: String? = "",
val uid: String? = "",
val updatedAt: String? = "",
val updatedBy: Int? = 0
)

View File

@@ -0,0 +1,6 @@
package com.example.hpostesting.data.model.deviceprovision
data class DeviceProvisionData(
val credentials: Credentials? = Credentials(),
val device: Device? = Device()
)

View File

@@ -0,0 +1,9 @@
package com.example.hpostesting.data.model.deviceprovision
data class DeviceProvisionRequest(
val deviceTypeName: String? = "HPOS",
val email: String? = "",
val password: String? = "",
val serialNumber: String? = "",
val uid: String? = ""
)

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.data.model.deviceprovision
import com.google.gson.annotations.SerializedName
data class DeviceProvisionResponse(
@SerializedName("Data")
val data: DeviceProvisionData? = DeviceProvisionData(),
@SerializedName("Message")
val message: String? = "",
@SerializedName("Result")
val result: String? = ""
)

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.data.model.deviceprovision
data class DeviceType(
val createdAt: String? = "",
val createdBy: Int? = 0,
val description: String? = "",
val enabled: Boolean? = false,
val id: Int? = 0,
val name: String? = "",
val updatedAt: String? = "",
val updatedBy: Int? = 0
)

View File

@@ -0,0 +1,15 @@
package com.example.hpostesting.data.model.deviceprovision
data class DeviceUser(
val createdAt: String? = "",
val createdBy: Int? = 0,
val deviceId: Int? = 0,
val enabled: Boolean? = false,
val id: Int? = 0,
val lastLogin: Any? = Any(),
val natsToken: String? = "",
val natsTokenExpiry: String? = "",
val updatedAt: String? = "",
val updatedBy: Int? = 0,
val username: String? = ""
)

View File

@@ -0,0 +1,8 @@
package com.example.hpostesting.data.model.deviceprovision
data class ProvisionData(
val username: String?,
val password: String?,
val natsToken: String?
)

View File

@@ -5,5 +5,8 @@ data class DiagnosticsData (
var appVersion: String? = "",
var deviceType: String = "HEMOCUBE",
var deviceData: String = "",
var devicePassword: String = "",
var deviceNatsToken: String = "",
var accessToken: String = "",
var runTime: String = ""
)

View File

@@ -0,0 +1,9 @@
package com.example.hpostesting.data.model.jig
data class JigData (
var deviceId: String = "",
var appVersion: String? = "",
var deviceType: String = "JIG",
var scanData: String = "",
var createdAt: String = ""
)

View File

@@ -0,0 +1,5 @@
package com.example.hpostesting.data.model.log
data class UploadLogsData(
val filename: String? = ""
)

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.data.model.log
import com.google.gson.annotations.SerializedName
data class UploadLogsResponse(
@SerializedName("Data")
val data: UploadLogsData? = UploadLogsData(),
@SerializedName("Message")
val message: String? = "",
@SerializedName("Result")
val result: String? = ""
)

View File

@@ -0,0 +1,16 @@
package com.example.hpostesting.data.model.login
data class Device(
val assigned: Boolean? = false,
val createdAt: String? = "",
val createdBy: Int? = 0,
val deviceType: DeviceType? = DeviceType(),
val deviceTypeId: Int? = 0,
val enabled: Boolean? = false,
val id: Int? = 0,
val name: String? = "",
val serialNumber: String? = "",
val uid: String? = "",
val updatedAt: String? = "",
val updatedBy: Int? = 0
)

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.data.model.login
data class DeviceType(
val createdAt: String? = "",
val createdBy: Int? = 0,
val description: String? = "",
val enabled: Boolean? = false,
val id: Int? = 0,
val name: String? = "",
val updatedAt: String? = "",
val updatedBy: Int? = 0
)

View File

@@ -0,0 +1,16 @@
package com.example.hpostesting.data.model.login
data class DeviceUser(
val createdAt: String? = "",
val createdBy: Int? = 0,
val device: Device? = Device(),
val deviceId: Int? = 0,
val enabled: Boolean? = false,
val id: Int? = 0,
val lastLogin: String? = "",
val natsToken: String? = "",
val natsTokenExpiry: String? = "",
val updatedAt: String? = "",
val updatedBy: Int? = 0,
val username: String? = ""
)

View File

@@ -0,0 +1,6 @@
package com.example.hpostesting.data.model.login
data class LoginData(
val accessToken: String? = "",
val deviceUser: DeviceUser? = DeviceUser()
)

View File

@@ -0,0 +1,13 @@
package com.example.hpostesting.data.model.login
data class LoginRequest(
val lab: String? = "",
val latitude: String? = "",
val location: String? = "",
val longitude: String? = "",
val mode: String? = "",
val password: String? = "",
val serialNumber: String? = "",
val username: String? = "",
val version: String? = ""
)

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.data.model.login
import com.google.gson.annotations.SerializedName
data class LoginResponse(
@SerializedName("Data")
val data: LoginData? = LoginData(),
@SerializedName("Message")
val message: String? = "",
@SerializedName("Result")
val result: String? = ""
)

View File

@@ -0,0 +1,38 @@
package com.example.hpostesting.data.model.molbioresult
import com.example.hpostesting.data.model.patient.HemoCubeTestData
data class MolbioV2Result(
val age: Int? = 31,
val analysisDate: String? = "2024-02-08 16:33:56",
val analysisId: String? = "",
val analysisStatus: String? = "",
val analysisType: String? = "HPOS",
val analysisTypeMethod: String? = "",
val bloodGroup: String? = "",
val coefficients: List<Int>? = listOf(22, 22),
val collectionLocation: List<Any>? = listOf(),
val collectionTime: String? = "2024-02-08 16:33:56",
val collector: String? = "",
val curveFitting: String? = "Linear",
val deviceName: String? = "HPOS",
val expiryTime: String? = "2024-02-08 16:33:56",
val gender: String? = "",
val interpretation: String? = "",
val `operator`: String? = "",
val patientId: Int? = 4545,
val pregnancy: Boolean? = false,
val rawData: HemoCubeTestData? = HemoCubeTestData(),
val recommendation: String? = "NA",
val sampleId: String? = "",
val sampleType: String? = "",
val sickleCellHistory: Boolean? = false,
val testId: String? = "",
val testResult: String? = "",
val testStatus: String? = "",
val testTime: String? = "2024-02-08 16:33:56",
val testType: String? = "",
val thresholds: String? = "",
val underMedication: Boolean? = false,
val volume: Int? = 2
)

View File

@@ -0,0 +1,42 @@
package com.example.hpostesting.data.model.molbioresult
import com.example.hpostesting.data.model.patient.HemoCubeTestData
data class MolbioV2ResultData(
val age: Int? = 0,
val analysisDate: String? = "",
val analysisStatus: String? = "",
val analysisType: String? = "",
val analysisTypeMethod: String? = "",
val bloodGroup: String? = "",
val coefficients: List<Int>? = listOf(),
val collectionLocation: List<Any>? = listOf(),
val collectionTime: String? = "",
val collector: String? = "",
val createdAt: String? = "",
val createdBy: Int? = 0,
val curveFitting: String? = "",
val deviceId: Int? = 0,
val expiryTime: String? = "",
val gender: String? = "",
val id: Int? = 0,
val interpretation: String? = "",
val `operator`: String? = "",
val patientId: Int? = 0,
val pregnancy: Boolean? = false,
val rawData: HemoCubeTestData? = HemoCubeTestData(),
val recommendation: String? = "",
val sampleId: String? = "",
val sampleType: String? = "",
val sickleCellHistory: Boolean? = false,
val testId: String? = "",
val testResult: String? = "",
val testStatus: String? = "",
val testTime: String? = "",
val testType: String? = "",
val thresholds: String? = "",
val underMedication: Boolean? = false,
val updatedAt: String? = "",
val updatedBy: Int? = 0,
val volume: Int? = 0
)

View File

@@ -0,0 +1,5 @@
package com.example.hpostesting.data.model.molbioresult
data class MolbioV2ResultRequest(
val results: MutableList<MolbioV2Result>? = mutableListOf()
)

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.data.model.molbioresult
import com.google.gson.annotations.SerializedName
data class MolbioV2ResultResponse(
@SerializedName("Data")
val data: List<MolbioV2ResultData>? = listOf(),
@SerializedName("Message")
val message: String? = "",
@SerializedName("Result")
val result: String? = ""
)

View File

@@ -0,0 +1,55 @@
package com.example.hpostesting.data.model.molbioresult
import com.example.hpostesting.data.model.patient.UserData
data class RawData(
var _id: String = "",
var name: String = "",
var incubationTime: String = "",
var bloodGroup: String = "",
var birthYear: String = "",
var state: String = "",
var abhaId: String = "",
var userImageURL: String = "",
var location: UserData.Location? = null,
var reportUploadTime: String? = "",
var testType: String? = "HEMOCUBE",
var testTime: String? = "",
var testStatus: Boolean? = false,
var gender: String = "",
var localFlag: Boolean = false,
var deviceId: String? = "",
var appVersion:String? = "",
var deviceSerialNumber: String = "",
var deviceType: String = "HEMOCUBE",
var kitSerial: String = "",
var resultData: String = "",
var led1Buffer: Double? = null,
var led2Buffer: Double? = null,
var led3Buffer: Double? = null,
var led4Buffer: Double? = null,
var led1Sample: Double? = null,
var led2Sample: Double? = null,
var led3Sample: Double? = null,
var led4Sample: Double? = null,
var led1Average: Double? = null,
var led2Average: Double? = null,
var led3Average: Double? = null,
var led4Average: Double? = null,
var abs1: Double? = null,
var abs2: Double? = null,
var abs3: Double? = null,
var abs4: Double? = null,
var deviceRatio: Double? = null,
var calculatedRatio: Double? = null,
var predictedDenovixRatio: Double? = null,
var coefficients: String? = "",
var classificationResult: String = "",
var prdClassification: String = "",
var errorMessages: String = "",
var batteryLevel: String = "",
var batteryCapacity: String = "",
var batteryMaxCapacity: String = "",
var batteryTemperature: String = "",
var batteryVoltage: String = "",
)

View File

@@ -1,21 +1,47 @@
package com.example.hpostesting.data.model.patient
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "hemo_cube_buffer_test_table")
data class BufferCheckData(
@PrimaryKey
var _id: String = "",
var kitno: String = "",
var deviceId: String? = "",
var appVersion:String? = "",
var deviceSerialNumber: String = "",
var deviceType: String = "HEMOCUBE",
var localFlag: Boolean = false,
var resultData: String = "",
var led1Buffer: Double? = null,
var led2Buffer: Double? = null,
var led3Buffer: Double? = null,
var led4Buffer: Double? = null,
var led1Sample: Double? = null,
var led2Sample: Double? = null,
var led3Sample: Double? = null,
var led4Sample: Double? = null,
var led1Average: Double? = null,
var led2Average: Double? = null,
var led3Average: Double? = null,
var led4Average: Double? = null,
var abs1: Double? = null,
var abs2: Double? = null,
var abs3: Double? = null,
var abs4: Double? = null,
var deviceRatio: Double? = null,
var calculatedRatio: Double? = null,
var predictedDenovixRatio: Double? = null,
var coefficients: String? = "",
var classificationResult: String = "",
var testTime: String = ""
var testTime: String = "",
var prdClassification: String = "",
var errorMessages: String = "",
var batteryLevel: String = "",
var batteryCapacity: String = "",
var batteryMaxCapacity: String = "",
var batteryTemperature: String = "",
var batteryVoltage: String = "",
var reportUploadTime: String? = "",
)

View File

@@ -15,5 +15,15 @@ data class DeviceData(
@get:PropertyName("coefficients") @set:PropertyName("coefficients")
var coefficients: List<Double> = emptyList(),
@get:PropertyName("calibratedAt") @set:PropertyName("calibratedAt")
var calibratedAt: String = ""
var calibratedAt: String = "" ,
@get:PropertyName("deviceProvisionResponse") @set:PropertyName("deviceProvisionResponse")
var deviceProvisionResponse: String = "" ,
@get:PropertyName("username") @set:PropertyName("username")
var username: String = "",
@get:PropertyName("password") @set:PropertyName("password")
var password: String = "",
@get:PropertyName("natsToken") @set:PropertyName("natsToken")
var natsToken: String = "",
@get:PropertyName("natsTokenExpiry") @set:PropertyName("natsTokenExpiry")
var natsTokenExpiry: String = ""
)

View File

@@ -8,13 +8,18 @@ data class HemoCubeTestData(
@PrimaryKey
var _id: String = "",
var name: String = "",
var incubationTime: String = "",
var bloodGroup: String = "",
var birthYear: String = "",
var state: String = "",
var abhaId: String = "",
var userImageURL: String = "",
var location: UserData.Location? = null,
var reportUploadTime: String? = "",
var testType: String? = "HEMOCUBE",
var testTime: String? = "",
var testStatus: Boolean? = false,
var gender: String = "",
var localFlag: Boolean = false,
var deviceId: String? = "",
var appVersion:String? = "",
@@ -24,12 +29,65 @@ data class HemoCubeTestData(
var resultData: String = "",
var led1Buffer: Double? = null,
var led2Buffer: Double? = null,
var led3Buffer: Double? = null,
var led4Buffer: Double? = null,
var led1Sample: Double? = null,
var led2Sample: Double? = null,
var led3Sample: Double? = null,
var led4Sample: Double? = null,
var led1Average: Double? = null,
var led2Average: Double? = null,
var led3Average: Double? = null,
var led4Average: Double? = null,
var abs1: Double? = null,
var abs2: Double? = null,
var abs3: Double? = null,
var abs4: Double? = null,
var hb3: Double? = null,
var hb4: Double? = null,
var led1Gain1: Double? = null,
var led2Gain1: Double? = null,
var led3Gain1: Double? = null,
var led4Gain1: Double? = null,
var led1Gain2: Double? = null,
var led2Gain2: Double? = null,
var led3Gain2: Double? = null,
var led4Gain2: Double? = null,
var led1Gain3: Double? = null,
var led2Gain3: Double? = null,
var led3Gain3: Double? = null,
var led4Gain3: Double? = null,
var led1Gain4: Double? = null,
var led2Gain4: Double? = null,
var led3Gain4: Double? = null,
var led4Gain4: Double? = null,
var led1Air1: Double? = null,
var led2Air1: Double? = null,
var led3Air1: Double? = null,
var led4Air1: Double? = null,
var led1Air2: Double? = null,
var led2Air2: Double? = null,
var led3Air2: Double? = null,
var led4Air2: Double? = null,
var deviceRatio: Double? = null,
var calculatedRatio: Double? = null,
var predictedDenovixRatio: Double? = null,
var slopeRatio: Double? = null,
var coefficients: String? = "",
var classificationResult: String = ""
var classificationResult: String = "",
var prdClassification: String = "",
var deviceRatioClass: String = "",
var slopeRatioClass: String = "",
var errorMessages: String = "",
var batteryLevel: String = "",
var batteryCapacity: String = "",
var batteryMaxCapacity: String = "",
var batteryTemperature: String = "",
var batteryVoltage: String = "",
var molbioFlag: Boolean = false,
var quickCapture: Boolean = false,
var solution: String? = "",
var concentration: String? = "",
var volume: String? = "",
var isCSVCreated: Boolean = false
)

View File

@@ -10,7 +10,10 @@ data class UserData(
var _id: String = "",
var name: String = "",
var incubationTime: String = "",
var gender: String = "",
var birthYear: String = "",
var abhaId: String = "",
var bloodGroup: String = "",
var userImageURL: String = "",
var location: Location? = null,
var createdBy: String? = "",
@@ -19,12 +22,14 @@ data class UserData(
var testStatus: Boolean? = false,
var mobileId: String = "",
var deviceId: String = "",
var state: String = "",
var deviceSerialNumber: String = "",
var kitSerial: String = "",
var csvPath: String = "",
var reportPath: String = "",
var result: TestRightResultType? = null,
var resultRatio: Double? = null
var resultRatio: Double? = null,
var prdClassification: String = ""
) {
enum class Gender {
MALE,
@@ -41,8 +46,14 @@ data class UserData(
fun UserData.toHemoCubeTestData() = HemoCubeTestData(
_id = _id,
name = name,
bloodGroup = bloodGroup,
birthYear = birthYear,
gender = gender,
state = state,
abhaId = abhaId,
userImageURL = userImageURL,
testStatus = testStatus,
location = location
location = location,
prdClassification = prdClassification,
testTime = testTime
)

View File

@@ -1,55 +0,0 @@
package com.example.hpostesting.data.model.test
import com.example.hpostesting.data.model.Location
import java.util.Date
data class TestDetails(
val testID: String,
var testStatus: Status,
val patientID: String,
val patientName: String,
val patientAge: Int?,
var resultRatio: Double?,
var result: TestRightResultType?,
val reportPath: String?,
val csvPath: String?,
val logPath: String?,
// Meta Data
var deviceId: String?,
val mobileId: String,
var kitSerial: String?,
val location: Location?,
val appVersion: String,
val registeredTime: Date?,
var uploadTime: Date?
) {
constructor() : this(
"",
Status.PENDING,
"",
"",
null,
null,
null,
null,
null,
null,
null,
"",
null,
null,
"",
null,
null
)
}
enum class Status {
COMPLETED,
PENDING,
INPROGRESS
}

View File

@@ -1,11 +0,0 @@
package com.example.hpostesting.data.model.test
data class TestInfo(
val value: String,
val number1: String,
val number2: String,
val result: String,
val resultConfirmatory: String,
val directoryPath: String,
val fullPath: String
)

View File

@@ -0,0 +1,29 @@
package com.example.hpostesting.di
import android.content.Context
import com.example.hpostesting.data.repository.DatabaseRepository
import com.example.hpostesting.data.dao.UserDao
import com.example.hpostesting.domain.SaveRawData
import com.example.hpostesting.domain.SaveRawDataTest
import com.example.hpostesting.presentation.testRight.TestRightViewModel
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.qualifiers.ApplicationContext
@Module
@InstallIn(ViewModelComponent::class)
object ViewModelModule {
@Provides
fun provideTestRightViewModel(
saveRawData: SaveRawData,
saveRawDataTest: SaveRawDataTest,
databaseRepository: DatabaseRepository,
userDao: UserDao,
context: Context
): TestRightViewModel {
return TestRightViewModel(saveRawData, saveRawDataTest, databaseRepository, userDao, context)
}
}

View File

@@ -0,0 +1,5 @@
package com.example.hpostesting.data.model.updates
data class CheckUpdateData(
val version: String? = ""
)

View File

@@ -0,0 +1,5 @@
package com.example.hpostesting.data.model.updates
data class CheckUpdateRequest(
val currentVersion: String? = ""
)

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.data.model.updates
import com.google.gson.annotations.SerializedName
data class CheckUpdateResponse(
@SerializedName("Data")
val data: CheckUpdateData? = CheckUpdateData(),
@SerializedName("Message")
val message: String? = "",
@SerializedName("Result")
val result: String? = ""
)

View File

@@ -0,0 +1,5 @@
package com.example.hpostesting.data.model.updates
data class DeviceUpdateRequest(
val serial_no: String? = ""
)

View File

@@ -1,78 +1,156 @@
package com.example.hpostesting.data.repository
import android.net.Uri
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.api.MolbioAuthApi
import com.example.hpostesting.data.api.MolbioResultApi
import com.example.hpostesting.data.model.PendingUploads
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
import com.example.hpostesting.data.model.jig.JigData
import com.example.hpostesting.data.model.log.UploadLogsResponse
import com.example.hpostesting.data.model.login.LoginRequest
import com.example.hpostesting.data.model.login.LoginResponse
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.CheckUpdateResponse
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.ktx.storage
import kotlinx.coroutines.tasks.await
import okhttp3.MultipartBody
import okhttp3.ResponseBody
import java.io.File
import java.net.ConnectException
import java.net.SocketTimeoutException
import javax.inject.Inject
import javax.inject.Named
class DatabaseRepository : Repository {
class NetworkException(message: String, cause: Throwable) : Exception(message, cause)
class DatabaseRepository @Inject constructor(
@Named("Auth")private val molbioAuthApi: MolbioAuthApi,
private val molbioResultApi: MolbioResultApi
) : Repository {
private val db: FirebaseFirestore = Firebase.firestore
private val storage = Firebase.storage
suspend fun addTestToDatabase(data: HemoCubeTestData?): Response<String> {
private suspend fun <T : Any> safeApiCall(apiCall: suspend () -> T): Result<T> {
return try {
val userdata = db.collection("patientData").whereEqualTo("_id", data!!._id).get().await()
val response = apiCall.invoke()
Result.Success(response)
} catch (e: SocketTimeoutException) {
Result.Error(NetworkException("Network timeout", e))
} catch (e: ConnectException) {
Result.Error(NetworkException("Network connection failed", e))
} catch (e: Exception) {
Result.Error(e)
}
}
override suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result<DeviceProvisionResponse> {
return safeApiCall { molbioAuthApi.deviceProvision(deviceProvisionRequest) }
}
override suspend fun login(loginRequest: LoginRequest): Result<LoginResponse> {
return safeApiCall { molbioAuthApi.login(loginRequest) }
}
override suspend fun uploadResults(molbioV2ResultRequest: MolbioV2ResultRequest): Result<MolbioV2ResultResponse> {
return safeApiCall { molbioResultApi.uploadResults(molbioV2ResultRequest) }
}
override suspend fun checkUpdate(checkUpdateRequest: CheckUpdateRequest): Result<CheckUpdateResponse> {
return safeApiCall { molbioResultApi.checkUpdate(checkUpdateRequest) }
}
override suspend fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest): Result<ResponseBody> {
return safeApiCall { molbioResultApi.deviceUpdate(deviceUpdateRequest) }
}
override suspend fun downloadClientCertificate(): Result<ResponseBody> {
return safeApiCall { molbioResultApi.downloadClientCertificate() }
}
override suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse> {
return safeApiCall { molbioResultApi.uploadLogs(logFile) }
}
override suspend fun addTestToDatabase(data: HemoCubeTestData?): Response<String> {
return try {
val userdata =
db.collection("patientData").whereEqualTo("_id", data!!._id).get().await()
if (userdata.documents.isNotEmpty()) {
userdata.documents.forEach {
db.collection("patientData").document(it.id).update("testStatus", true)
}
}
db.collection("testData").add(data!!).await()
Response.Success(data!!._id)
db.collection("testData").add(data).await()
Response.Success(data._id)
} catch (e: Exception) {
Response.Error(e)
}
}
suspend fun addTestToDatabase(data: UserData?): Response<String> {
override suspend fun addTestToDatabase(data: UserData?): Response<String> {
return try {
val userdata = db.collection("patientData").whereEqualTo("_id", data!!._id).get().await()
val userdata =
db.collection("patientData").whereEqualTo("_id", data!!._id).get().await()
if (userdata.documents.isNotEmpty()) {
userdata.documents.forEach {
db.collection("patientData").document(it.id).update("testStatus", true)
}
}
db.collection("testData").add(data!!).await()
Response.Success(data!!._id)
db.collection("testData").add(data).await()
Response.Success(data._id)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response<String> {
override suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response<String> {
return try {
db.collection("buffers").add(data!!).await()
Response.Success(data!!.kitno)
Response.Success(data.kitno)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
suspend fun addDiagnostics(data: DiagnosticsData?): Response<String> {
override suspend fun addDiagnostics(data: DiagnosticsData?): Response<String> {
return try {
db.collection("diagnostics").add(data!!).await()
Response.Success(data!!.deviceId)
Response.Success(data.deviceId)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
suspend fun uploadFileToStorage(patientID: String, filePath: String): Response<Boolean> {
override suspend fun addTestJigData(data: JigData?): Response<String> {
return try {
db.collection("jigs").add(data!!).await()
Response.Success(data.deviceId)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
override suspend fun uploadFileToStorage(
patientID: String, filePath: String
): Response<Boolean> {
try {
val file = Uri.fromFile(File(filePath))
@@ -131,11 +209,36 @@ class DatabaseRepository : Repository {
return db.collection("devices").get().await().toObjects(DeviceData::class.java)
}
suspend fun getDeviceDataById(deviceId: String): DeviceData? {
override suspend fun getDeviceDataById(deviceId: String): DeviceData? {
val querySnapshot = db.collection("devices").get().await()
val allDeviceDataList = querySnapshot.toObjects(DeviceData::class.java)
// Find the DeviceData object with the specified deviceId
return allDeviceDataList.find { it.deviceId == deviceId }
}
override suspend fun getDeviceResponse(data: DeviceData?): Response<String> {
return try {
db.collection("devices").add(data!!).await()
Response.Success(data.deviceProvisionResponse)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
override suspend fun uploadDeviceId(data: DeviceData): Response<String>? {
return try {
db.collection("devices").add(data!!).await()
Response.Success(data.deviceId)
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
Response.Error(e)
}
}
override fun <UserData> addTestToDatabase(testDetails: UserData): Any {
TODO("Not yet implemented")
}
}

View File

@@ -1,6 +1,12 @@
package com.example.hpostesting.data.repository
import android.os.Environment
import com.example.hpostesting.data.datasource.LocalFileDataSource
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.opencsv.CSVWriter
import java.io.File
import java.io.FileWriter
import java.io.IOException
class LocalFileRepository(private val localFileDataSource: LocalFileDataSource) {
fun saveCsvToDisk(filepath: String, contents: ArrayList<Array<String>>) =

View File

@@ -1,5 +1,55 @@
package com.example.hpostesting.data.repository
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
import com.example.hpostesting.data.model.jig.JigData
import com.example.hpostesting.data.model.log.UploadLogsResponse
import com.example.hpostesting.data.model.login.LoginRequest
import com.example.hpostesting.data.model.login.LoginResponse
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultResponse
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.CheckUpdateResponse
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import okhttp3.MultipartBody
import okhttp3.ResponseBody
interface Repository {
suspend fun addTestToDatabase(data: HemoCubeTestData?): Response<String>
suspend fun addTestToDatabase(data: UserData?): Response<String>
suspend fun addTestToDatabaseforBufferCheck(data: BufferCheckData?): Response<String>
suspend fun addDiagnostics(data: DiagnosticsData?): Response<String>
suspend fun addTestJigData(data: JigData?): Response<String>
suspend fun uploadFileToStorage(patientID: String, filePath: String): Response<Boolean>
suspend fun getDeviceDataById(deviceId: String): DeviceData?
suspend fun getDeviceResponse(data: DeviceData?): Response<String>
suspend fun uploadDeviceId(data: DeviceData): Response<String>?
abstract fun <UserData> addTestToDatabase(testDetails: UserData): Any
// suspend fun addToDatabase(data: PatientDetails)
suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result<DeviceProvisionResponse>
suspend fun login(loginRequest: LoginRequest): Result<LoginResponse>
suspend fun uploadResults(molbioV2ResultRequest: MolbioV2ResultRequest): Result<MolbioV2ResultResponse>
suspend fun checkUpdate(checkUpdateRequest: CheckUpdateRequest): Result<CheckUpdateResponse>
suspend fun deviceUpdate(deviceUpdateRequest: DeviceUpdateRequest): Result<ResponseBody>
suspend fun downloadClientCertificate(): Result<ResponseBody>
suspend fun uploadLogs(logFile: MultipartBody.Part): Result<UploadLogsResponse>
}

View File

@@ -0,0 +1,23 @@
package com.example.hpostesting.domain
import android.content.SharedPreferences
import com.example.hpostesting.data.constant.Constants
import okhttp3.Interceptor
import okhttp3.Response
class AuthInterceptor(private val sharedPreferences: SharedPreferences) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
val token = sharedPreferences.getString(Constants.ACCESS_TOKEN, null)
if (token != null) {
request = request.newBuilder()
.addHeader("Authorization", "Bearer $token")
.build()
}
return chain.proceed(request)
}
}

View File

@@ -0,0 +1,28 @@
package com.example.hpostesting.domain
import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
class CheckUpdateWorker(
context: Context,
workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams) {
override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
try {
// for (i in 1..900){
// delay(1000)
// Log.d("Work for every second", "doWork: Running")
// }
Result.success()
} catch (e: Exception) {
Result.failure()
}
}
}

View File

@@ -0,0 +1,12 @@
package com.example.hpostesting.domain
import android.content.Context
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
interface LogFileManager {
fun createLogFile(): File?
}

View File

@@ -0,0 +1,38 @@
package com.example.hpostesting.domain
import android.content.Context
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import javax.inject.Inject
class LogFileManagerImpl @Inject constructor(private val context: Context) : LogFileManager {
override fun createLogFile(): File? {
val unixTime = System.currentTimeMillis() / 1000L
val logFileName = "hpos_$unixTime.log"
return try {
val process = Runtime.getRuntime().exec("logcat -d -v threadtime -t 1000")
val logBuilder = StringBuilder()
val input = process.inputStream
val bufferedReader = input.bufferedReader()
bufferedReader.forEachLine { line ->
logBuilder.append(line).append("\n")
}
val logContent = logBuilder.toString()
val file = File(context.filesDir, logFileName)
val fileOutputStream = FileOutputStream(file)
fileOutputStream.write(logContent.toByteArray())
fileOutputStream.close()
file
} catch (e: IOException) {
// Log.e("LogFileManager", "Error creating log file: ${e.message}")
null
}
}
}

View File

@@ -2,20 +2,37 @@ package com.example.hpostesting.domain.di
import android.app.Application
import android.content.Context
import android.content.SharedPreferences
import android.content.res.AssetManager
import androidx.room.Room
import com.example.hpostesting.data.api.MolbioAuthApi
import com.example.hpostesting.data.api.MolbioResultApi
import com.example.hpostesting.data.api.PropertyProvider
import com.example.hpostesting.data.dao.HemoCubeBufferDao
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.dao.MyDatabase
import com.example.hpostesting.data.dao.UserDao
import com.example.hpostesting.data.datasource.LocalFileDataSource
import com.example.hpostesting.data.datasource.LocalFileDataSourceImpl
import com.example.hpostesting.data.repository.DatabaseRepository
import com.example.hpostesting.data.repository.LocalFileRepository
import com.example.hpostesting.data.repository.Repository
import com.example.hpostesting.domain.AuthInterceptor
import com.example.hpostesting.domain.LogFileManager
import com.example.hpostesting.domain.LogFileManagerImpl
import com.example.hpostesting.domain.SaveRawData
import com.example.hpostesting.domain.SaveRawDataTest
import com.example.hpostesting.util.PropertyProviderImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Named
import javax.inject.Singleton
@Module
@@ -27,9 +44,7 @@ object AppModule {
fun provideMyDatabase(@ApplicationContext context: Context): MyDatabase {
return Room.databaseBuilder(
context, MyDatabase::class.java, "my_database"
)
.fallbackToDestructiveMigration()
.build()
).fallbackToDestructiveMigration().build()
}
@Provides
@@ -44,6 +59,13 @@ object AppModule {
return myDatabase.hemoCubeDao()
}
@Provides
@Singleton
fun provideMyHemoCubeBuffer(myDatabase: MyDatabase): HemoCubeBufferDao {
return myDatabase.hemoCubeBufferDao()
}
@Provides
@Singleton
fun provideContext(application: Application): Context {
@@ -53,20 +75,108 @@ object AppModule {
@Provides
@Singleton
fun provideSaveRawData(): SaveRawData {
val repository = LocalFileRepository(LocalFileDataSource())
val repository = LocalFileRepository(LocalFileDataSourceImpl())
return SaveRawData(repository)
}
@Provides
@Singleton
fun provideSaveRawDataTest(): SaveRawDataTest {
val repository = LocalFileRepository(LocalFileDataSource())
val repository = LocalFileRepository(LocalFileDataSourceImpl())
return SaveRawDataTest(repository)
}
@Provides
@Singleton
fun provideDatabaseRepository(): DatabaseRepository {
return DatabaseRepository()
fun provideDatabaseRepository(
@Named("Auth") molbioAuthApi: MolbioAuthApi,
molbioResultApi: MolbioResultApi
): DatabaseRepository {
return DatabaseRepository(molbioAuthApi = molbioAuthApi, molbioResultApi = molbioResultApi)
}
@Provides
@Singleton
fun provideRepository(
@Named("Auth") molbioAuthApi: MolbioAuthApi,
molbioResultApi: MolbioResultApi
): Repository {
return DatabaseRepository(molbioAuthApi, molbioResultApi)
}
@Provides
@Singleton
fun provideAssetManager(@ApplicationContext context: Context): AssetManager {
return context.assets
}
@Provides
@Singleton
fun providePropertyProvider(assetManager: AssetManager): PropertyProvider {
return PropertyProviderImpl(assetManager)
}
@Provides
@Singleton
fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences {
return context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
}
@Provides
@Singleton
fun provideAuthInterceptor(sharedPreferences: SharedPreferences): AuthInterceptor {
return AuthInterceptor(sharedPreferences)
}
@Provides
@Singleton
@Named("Auth")
fun provideAuthOkHttpClient(authInterceptor: AuthInterceptor): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(authInterceptor)
.connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).build()
}
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient =
OkHttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS).build()
@Provides
@Singleton
fun provideRetrofit(
propertyProvider: PropertyProvider,@Named("Auth") client: OkHttpClient
): Retrofit {
return Retrofit.Builder().baseUrl(propertyProvider.getProperty("BASE_URL")).client(client)
.addConverterFactory(GsonConverterFactory.create()).build()
}
@Provides
@Singleton
@Named("Auth")
fun provideAuthRetrofit(propertyProvider: PropertyProvider, client: OkHttpClient): Retrofit {
return Retrofit.Builder().baseUrl(propertyProvider.getProperty("BASE_URL")).client(client)
.addConverterFactory(GsonConverterFactory.create()).build()
}
@Provides
@Singleton
@Named("Auth")
fun provideAuthApi(@Named("Auth") retrofit: Retrofit): MolbioAuthApi =
retrofit.create(MolbioAuthApi::class.java)
@Provides
@Singleton
fun provideResultApi(retrofit: Retrofit): MolbioResultApi =
retrofit.create(MolbioResultApi::class.java)
@Provides
@Singleton
fun provideLogFileManager(context: Context): LogFileManager = LogFileManagerImpl(context)
@Provides
@Singleton
fun provideLocalFileDataSource(): LocalFileDataSource {
return LocalFileDataSourceImpl()
}
}

View File

@@ -4,14 +4,16 @@ import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.text.Editable
import android.util.Log
import android.view.View
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity
import com.example.hpostesting.presentation.hemocube.HemocubeActivity
import com.example.hpostesting.presentation.testRight.TestRightActivity
import com.google.android.material.snackbar.Snackbar
@@ -19,21 +21,33 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanIntentResult
import com.journeyapps.barcodescanner.ScanOptions
import com.zebra.barcode.sdk.sms.ConfigurationUpdateEvent
import com.zebra.scannercontrol.DCSSDKDefs
import com.zebra.scannercontrol.DCSSDKDefs.DCSSDK_COMMAND_OPCODE
import com.zebra.scannercontrol.DCSSDKDefs.DCSSDK_RESULT
import com.zebra.scannercontrol.DCSScannerInfo
import com.zebra.scannercontrol.FirmwareUpdateEvent
import com.zebra.scannercontrol.IDcsSdkApiDelegate
import com.zebra.scannercontrol.SDKHandler
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityKitScanBinding
class KitScanActivity : AppCompatActivity() {
class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
private val TAG = "KitScanActivity"
private lateinit var binding: ActivityKitScanBinding
private lateinit var sharedPreference: SharedPreferences
var sdkHandler: SDKHandler? = null
var editBarcode: EditText? = null
var mScannerInfoList = ArrayList<DCSScannerInfo>()
private val barcodeLauncher = registerForActivityResult(
ScanContract()
) { result: ScanIntentResult ->
if (result.contents.isNullOrEmpty()) {
Toast.makeText(this, "Cancelled: Unable to scan, Try Again!!", Toast.LENGTH_LONG).show()
Toast.makeText(this, R.string.cancelled_unable_scan, Toast.LENGTH_LONG).show()
} else {
Log.d(TAG, result.contents)
processScannedData(result.contents)
@@ -44,11 +58,46 @@ class KitScanActivity : AppCompatActivity() {
binding.nameEditText.setText(contents)
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun dcssdkEventScannerAppeared(dcsScannerInfo: DCSScannerInfo?) {}
override fun dcssdkEventScannerDisappeared(i: Int) {}
override fun dcssdkEventCommunicationSessionEstablished(dcsScannerInfo: DCSScannerInfo?) {}
override fun dcssdkEventCommunicationSessionTerminated(i: Int) {}
// override fun dcssdkEventBarcode(p0: ByteArray?, p1: Int, p2: Int) {
// TODO("Not yet implemented")
// }
override fun dcssdkEventImage(bytes: ByteArray?, i: Int) {}
override fun dcssdkEventVideo(bytes: ByteArray?, i: Int) {}
override fun dcssdkEventBinaryData(bytes: ByteArray?, i: Int) {}
override fun dcssdkEventFirmwareUpdate(firmwareUpdateEvent: FirmwareUpdateEvent?) {}
override fun dcssdkEventAuxScannerAppeared(
dcsScannerInfo: DCSScannerInfo?,
dcsScannerInfo1: DCSScannerInfo?
) {
}
override fun dcssdkEventConfigurationUpdate(configurationUpdateEvent: ConfigurationUpdateEvent?) {}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityKitScanBinding.inflate(layoutInflater)
sharedPreference = this.getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
sharedPreference = this.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
setContentView(binding.root)
if (DataHolder.sampleReadCounter <= Constants.MAXIMUM_TEST_ALLOWED_BEFORE_REFERENCE && DataHolder.isReferenceTaken) {
@@ -103,7 +152,8 @@ class KitScanActivity : AppCompatActivity() {
setSupportActionBar(binding.toolbar)
binding.btnScanNow.setOnClickListener {
startScanningNow()
// startScanningNow()
pullTrigger()
}
@@ -120,16 +170,25 @@ class KitScanActivity : AppCompatActivity() {
}
Toast.makeText(
applicationContext,
"Kit number is updated, Please Select user before starting test",
R.string.kit_updated,
Toast.LENGTH_SHORT
).show()
val i = Intent(applicationContext, DashboardActivity::class.java)
startActivity(i)
finish()
} else {
DataHolder.kitSerial = binding.nameEditText.text.toString()
DataHolder.selectedTest?.kitSerial = binding.nameEditText.text.toString()
with(sharedPreference.edit()) {
putString(Constants.KIT_NUMBER, binding.nameEditText.text.toString())
putInt(Constants.KIT_COUNT, 1)
apply()
}
moveToNext()
}
} else {
Toast.makeText(this, "Invalid KIT Number", Toast.LENGTH_LONG).show()
Toast.makeText(this, R.string.invalid_kit_number, Toast.LENGTH_LONG).show()
return@setOnClickListener
}
if (serialNumber.isNotEmpty() && checkDataNotNull() && isSerialValid(
@@ -145,16 +204,73 @@ class KitScanActivity : AppCompatActivity() {
}
moveToNext()
} else {
Toast.makeText(this, "Invalid KIT Number", Toast.LENGTH_LONG).show()
Toast.makeText(this, R.string.invalid_kit_number, Toast.LENGTH_LONG).show()
}
}
//Setting up the SDK handler
sdkHandler = SDKHandler(this)
//Registers a particular object which conforms to IDcsSdkApiDelegate interface as a receiver of SDK notifications.
sdkHandler!!.dcssdkSetDelegate(this)
//this command is telling the sdk that we're going to be connecting to the scanner via USB
sdkHandler!!.dcssdkSetOperationalMode(DCSSDKDefs.DCSSDK_MODE.DCSSDK_OPMODE_SNAPI)
//deciding what kind of notifications we want to receive. Explained more in the function
//first we use bitmapping to set these values into the notifications_mask.
var notifications_mask = 0
// We would like to subscribe to all barcode events
notifications_mask =
notifications_mask or DCSSDKDefs.DCSSDK_EVENT.DCSSDK_EVENT_BARCODE.value
sdkHandler!!.dcssdkSubsribeForEvents(notifications_mask)
mScannerInfoList.addAll(sdkHandler!!.dcssdkGetAvailableScannersList())
Log.e("scannersize",sdkHandler!!.dcssdkGetAvailableScannersList().size.toString())
if (mScannerInfoList.isNotEmpty()) {
sdkHandler!!.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
} else {
Toast.makeText(this,"Error", Toast.LENGTH_LONG).show()
}
}
private fun pullTrigger() {
// Check if the list is not empty before accessing its elements
if (mScannerInfoList.isNotEmpty()) {
// Only proceed if the scanner is not active
if (!mScannerInfoList[0].isActive) {
sdkHandler?.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
}
val inXML = "<inArgs><scannerID> 1 </scannerID></inArgs>"
val outXML = StringBuilder()
val result: DCSSDK_RESULT =
sdkHandler!!.dcssdkExecuteCommandOpCodeInXMLForScanner(
DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_PULL_TRIGGER, inXML, outXML, mScannerInfoList[0].scannerID // Ensure you're using the correct scanner ID
)
if (result == DCSSDK_RESULT.DCSSDK_RESULT_SUCCESS) {
Log.d("Scanning", "Success")
} else if (result == DCSSDK_RESULT.DCSSDK_RESULT_FAILURE) {
Log.d("Scanning", "Failed")
}
} else {
// Handle the case where the list is empty, perhaps notify the user or log an error
Log.e("ScannerError", "No scanners are connected or available.")
}
}
//this function is called if barcode is detected.
override fun dcssdkEventBarcode(barcodeData: ByteArray?, barcodeType: Int, fromScannerID: Int) {
val result = String(barcodeData!!)
Log.d("BARCODE", result)
runOnUiThread {
val editableResult: Editable = Editable.Factory.getInstance().newEditable(result)
binding.nameEditText.text = editableResult
}
}
private fun checkHemoCubeKitData(): Boolean {
return sharedPreference.getString(Constants.KIT_NUMBER, "")
?.isNotBlank() == true && sharedPreference.getInt(
Constants.KIT_COUNT, 0
) > 0 && sharedPreference.getInt(Constants.KIT_COUNT, 0) < 35
) > 0 && sharedPreference.getInt(Constants.KIT_COUNT, 0) < Constants.KIT_CAPACITY
}
private fun isSerialValid(s: String): Boolean {
@@ -169,7 +285,7 @@ class KitScanActivity : AppCompatActivity() {
DataHolder.deviceType.observe(this) { deviceType ->
when (deviceType) {
Constants.DEVICE_TYPE_HOMOCUBE -> {
Constants.DEVICE_TYPE_HEMOCUBE -> {
val i = Intent(applicationContext, HemocubeActivity::class.java)
startActivity(i)
}
@@ -178,6 +294,11 @@ class KitScanActivity : AppCompatActivity() {
val i = Intent(applicationContext, TestRightActivity::class.java)
startActivity(i)
}
Constants.DEVICE_TYPE_TRUEHEME -> {
val i = Intent(applicationContext, HemocubeActivity::class.java)
startActivity(i)
}
}
}
}
@@ -201,7 +322,7 @@ class KitScanActivity : AppCompatActivity() {
private fun checkDataNotNull(): Boolean {
return if (DataHolder.selectedTest?._id == null) {
Toast.makeText(
applicationContext, "Please Select patient before starting test", Toast.LENGTH_SHORT
applicationContext, R.string.select_patient, Toast.LENGTH_SHORT
).show()
val i = Intent(applicationContext, DashboardActivity::class.java)

View File

@@ -1,7 +1,10 @@
package com.example.hpostesting.presentation
import android.Manifest
import android.content.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.hardware.usb.UsbManager
import android.location.Location
@@ -9,8 +12,6 @@ import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
@@ -18,10 +19,15 @@ import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.test.TestType
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.google.android.gms.location.*
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -46,6 +52,12 @@ class MainActivity : AppCompatActivity() {
}
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
@@ -76,7 +88,7 @@ class MainActivity : AppCompatActivity() {
DataHolder.deviceType.observe(this) { deviceType ->
with(binding) {
if (deviceType == Constants.DEVICE_TYPE_HOMOCUBE) {
if (deviceType == Constants.DEVICE_TYPE_HEMOCUBE) {
cvItem1.visibility = View.VISIBLE
cvItem3.visibility = View.VISIBLE
cvItem2.visibility = View.GONE
@@ -99,14 +111,51 @@ class MainActivity : AppCompatActivity() {
val deviceType = when {
availableDrivers.isNotEmpty() -> {
val device = availableDrivers[0].device
Toast.makeText(this, "Connected, productId: ${device.productId}, vendorId: ${device.vendorId}", Toast.LENGTH_SHORT).show()
when {
device.productId == Constants.HOMO_CUBE_ID && device.vendorId == Constants.VENDOR_ID -> {
binding.cvItem1.visibility = View.VISIBLE
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HOMOCUBE)
Constants.DEVICE_TYPE_HOMOCUBE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == Constants.HEMO_CUBE_V2_PRODUCT_ID && device.vendorId == Constants.HEMO_CUBE_V2_VENDOR_ID -> {
binding.cvItem1.visibility = View.VISIBLE
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == 24577 && device.vendorId == 1027 -> {
binding.cvItem1.visibility = View.VISIBLE
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_TRUEHEME)
Constants.DEVICE_TYPE_TRUEHEME
}
device.productId == 8963 && device.vendorId == 1659 -> {
binding.cvItem1.visibility = View.VISIBLE
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == 4614 && device.vendorId == 7111 -> {
binding.cvItem1.visibility = View.VISIBLE
binding.cvItem3.visibility = View.VISIBLE
binding.cvItem2.visibility = View.GONE
binding.cvItem4.visibility = View.GONE
DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_HEMOCUBE)
Constants.DEVICE_TYPE_HEMOCUBE
}
device.productId == Constants.DEVICE_PRODUCT_ID && device.vendorId == Constants.DEVICE_VENDOR_ID -> {
@@ -138,7 +187,7 @@ class MainActivity : AppCompatActivity() {
else -> {
Log.d(TAG, "Device NOT Connected")
Toast.makeText(this, "Device Not Connected", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.device_not, Toast.LENGTH_SHORT).show()
DataHolder.usbConnected.value = false
null
}
@@ -226,7 +275,7 @@ class MainActivity : AppCompatActivity() {
) != PackageManager.PERMISSION_GRANTED
) {
// Handle location permission request if needed
Toast.makeText(this, "Please enable location services", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.enable_location, Toast.LENGTH_SHORT).show()
requestCoarseLocationPermission()
requestFineLocationPermission()
return
@@ -245,7 +294,6 @@ class MainActivity : AppCompatActivity() {
// exception.printStackTrace()
// }
fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
location?.let {
if (DataHolder.selectedTest != null) {
@@ -269,9 +317,6 @@ class MainActivity : AppCompatActivity() {
val rootView = findViewById<View>(android.R.id.content)
Snackbar.make(rootView, errorMessage, Snackbar.LENGTH_LONG).show()
}
}
private fun requestLocationUpdates() {
@@ -313,7 +358,7 @@ class MainActivity : AppCompatActivity() {
requestFineLocationPermission()
} else {
// Coarse location permission denied
Toast.makeText(this, "Coarse location permission denied", Toast.LENGTH_SHORT)
Toast.makeText(this, R.string.coarse_location_denied, Toast.LENGTH_SHORT)
.show()
}
}
@@ -324,7 +369,7 @@ class MainActivity : AppCompatActivity() {
getLocation()
} else {
// Fine location permission denied
Toast.makeText(this, "Fine location permission denied", Toast.LENGTH_SHORT)
Toast.makeText(this, R.string.location_denied, Toast.LENGTH_SHORT)
.show()
}
}

View File

@@ -0,0 +1,213 @@
package com.example.hpostesting.presentation
import android.content.Context
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import io.nats.client.AuthHandler
import io.nats.client.Connection
import io.nats.client.Message
import io.nats.client.NKey
import io.nats.client.Nats
import io.nats.client.Options
import io.nats.client.support.SSLUtils
import java.io.FileInputStream
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.security.GeneralSecurityException
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.CertificateFactory
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory
class NatsManager(datacollector: DashboardActivity) {
val TAG = "Nats Service"
var nc: Connection? = null
val datacollector = datacollector
var connect = false
var sharedPreferences = datacollector.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
private fun createSSLContext(): SSLContext {
val keyStorePassword = "prime24".toCharArray() // Change as necessary
val clientCertPath = "/storage/sdcard0/Download/client.p12"
// Load client certificate and key
val keyStore = KeyStore.getInstance("PKCS12")
FileInputStream(clientCertPath).use { keyStoreInputStream ->
keyStore.load(keyStoreInputStream, keyStorePassword)
}
val caCertPath =
"/storage/sdcard0/Android/data/in.sminnovations.hpostesting.quality/files/NATS/clientCertificate/client-cert.pem"
val caCert = FileInputStream(caCertPath).use { inputStream ->
val certificateFactory = CertificateFactory.getInstance("X.509")
certificateFactory.generateCertificate(inputStream)
}
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null) // Initialize the keystore
setCertificateEntry("caCert", caCert) // Add the CA certificate
}
// Initialize key manager factory
val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm())
kmf.init(keyStore, keyStorePassword)
// Initialize trust manager factory
val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
tmf.init(trustStore)
// Initialize SSLContext
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(kmf.keyManagers, tmf.trustManagers, SecureRandom())
return sslContext
}
@RequiresApi(Build.VERSION_CODES.O)
fun connect() {
Log.d(TAG, "TRY TO CONNECT")
Thread {
try {
val seedString = sharedPreferences.getString(Constants.NATS_TOKEN, "")
val deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "")
Log.e("seedString", seedString.toString())
Log.d("nats deviceId", deviceId.toString())
val seedBytes = seedString?.toCharArray()
val theNKey = NKey.fromSeed(seedBytes) // really should load from somewhere
val options = Options.Builder()
.server("nats://nanodgx.in:4222")
.sslContext(SSLUtils.createOpenTLSContext())
.authHandler(object : AuthHandler {
override fun getID(): CharArray? {
return try {
theNKey?.publicKey
} catch (ex: GeneralSecurityException) {
null
} catch (ex: IOException) {
null
} catch (ex: NullPointerException) {
null
}
}
override fun sign(nonce: ByteArray): ByteArray? {
return try {
theNKey?.sign(nonce)
} catch (ex: GeneralSecurityException) {
null
} catch (ex: IOException) {
null
} catch (ex: NullPointerException) {
null
}
}
override fun getJWT(): CharArray? {
return null
}
})
.build()
nc = Nats.connect(options)
Log.d(TAG, "Connected to Nats server ${options.servers.first()}")
connect = true
datacollector.setConnect(true)
if (nc?.status == Connection.Status.CONNECTED) {
Log.d("NATSCONNECTION", "NATS is successfully connected.")
val d = nc?.createDispatcher { msg: Message? ->
println("Nats dispatcher $msg")
}
nc?.subscribe("device.hpos.${deviceId}.ping")
nc?.publish(
"server.hpos.${deviceId}.ping",
"ALIVE".toByteArray(StandardCharsets.UTF_8)
)
nc?.publish(
"server.hpos.${deviceId}.health",
"ALIVE".toByteArray(StandardCharsets.UTF_8)
)
d?.subscribe("device.hpos.${deviceId}.ping") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response)
println("Message received (up to 100 times): $response")
Log.d(TAG, "subscribed msg ${msg} on topic ping")
}
d?.subscribe("device.hpos.${deviceId}.update") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response)
println("Message received (up to 100 times): $response")
}
d?.subscribe("device.hpos.${deviceId}.uploadlogs") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response + "UPLOAD")
println("Message received (up to 100 times): $response")
}
d?.subscribe("device.hpos.${deviceId}.disable") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response)
println("Message received (up to 100 times): $response")
}
d?.subscribe("device.hpos.${deviceId}.updatecustomer") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response)
println("Message received (up to 100 times): $response")
}
d?.subscribe("device.hpos.${deviceId}.checkupdate") { msg ->
val response = String(msg.data, StandardCharsets.UTF_8)
datacollector.setResponse(response)
println("Message received (up to 100 times): $response")
}
} else {
Log.d("NATSCONNECTION", "NATS is not connected. Current status: ${nc?.status}")
}
} catch (exp: Exception) {
println(exp.printStackTrace())
connect = false
datacollector.setConnect(false)
}
}.start()
}
fun pub(topic: String, msg: String) {
nc?.publish(topic, msg.toByteArray(StandardCharsets.UTF_8))
Log.d(TAG, "Published msg ${msg} on topic ${topic}")
}
fun sub(topic: String) {
val d = nc?.createDispatcher { msg: Message? ->
val response = String(msg?.data ?: ByteArray(0), StandardCharsets.UTF_8)
datacollector.onMessageReceived(topic, response)
Log.d(TAG, "Subscribed msg $msg on topic $topic")
}
d?.subscribe(topic)
}
fun close() {
nc?.close()
Log.d(TAG, "Nats connection close")
}
}

View File

@@ -1,6 +1,8 @@
package com.example.hpostesting.presentation
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
@@ -13,11 +15,9 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.util.MyUtils
import com.google.firebase.firestore.FirebaseFirestoreSettings
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivitySplashBinding
class SplashActivity : AppCompatActivity() {
@@ -34,6 +34,12 @@ class SplashActivity : AppCompatActivity() {
private var allPermissionsGranted = false
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySplashBinding.inflate(layoutInflater)
@@ -77,7 +83,7 @@ class SplashActivity : AppCompatActivity() {
} else {
Toast.makeText(
this,
"You must grant permission to access all files to use the app",
R.string.permission_grant,
Toast.LENGTH_SHORT
).show()
finish()
@@ -139,7 +145,7 @@ class SplashActivity : AppCompatActivity() {
if (allGranted) {
moveToLandingPage()
} else {
Toast.makeText(this, "All permissions are required to use the app.", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.permission_required, Toast.LENGTH_SHORT).show()
finish()
}
}

View File

@@ -0,0 +1,162 @@
package com.example.hpostesting.presentation.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.navigation.findNavController
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.UserData
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.OfflineUserListViewBinding
import org.apache.commons.collections4.MapUtils.getString
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
class OfflineUserListAdapter(private val view: View, private val batLevel: Int) :
RecyclerView.Adapter<OfflineUserListAdapter.OfflineUserListViewHolder>() {
inner class OfflineUserListViewHolder(val binding: OfflineUserListViewBinding) :
RecyclerView.ViewHolder(binding.root) {
}
private val differCallback = object : DiffUtil.ItemCallback<HemoCubeTestData>() {
override fun areItemsTheSame(
oldItem: HemoCubeTestData, newItem: HemoCubeTestData
): Boolean {
return oldItem._id == newItem._id
}
override fun areContentsTheSame(
oldItem: HemoCubeTestData, newItem: HemoCubeTestData
): Boolean {
return oldItem == newItem
}
}
val differ = AsyncListDiffer(this, differCallback)
private val context: Context? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OfflineUserListViewHolder {
return OfflineUserListViewHolder(
OfflineUserListViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
}
override fun getItemCount(): Int {
return differ.currentList.size
}
@SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: OfflineUserListViewHolder, position: Int) {
val userList = differ.currentList[position]
holder.binding.apply {
userID.text = "User ID: ${userList._id}"
bloodGroup.text = "Blood group: ${userList.bloodGroup}"
time.text = "Time: ${isBetween15And30Minutes(userList.incubationTime)} \n Started at: ${
SimpleDateFormat("HH:mm:ss").format(
SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).parse(userList.incubationTime)!!
)
}"
if (userList.testStatus!!) {
teststatus.text = view.context.getString(R.string.test_concluded)
} else {
teststatus.text = view.context.getString(R.string.test_pending)
}
userCard.setOnClickListener {
if (false) {
Toast.makeText(
view.context,
view.context.getString(R.string.low_battery_warning),
Toast.LENGTH_SHORT
).show()
return@setOnClickListener
}
if (userList.testStatus != null) {
if (userList.testStatus!!) {
Toast.makeText(
view.context,
view.context.getString(R.string.test_already_conducted),
Toast.LENGTH_SHORT
).show()
} else {
if (userList.incubationTime != "") {
if (isBetween15And30Minutes(userList.incubationTime) < Constants.INCUBATION_TIME_MIN) {
Toast.makeText(
view.context,
view.context.getString(R.string.incubation_not_completed),
Toast.LENGTH_SHORT
).show()
} else
if (isBetween15And30Minutes(userList.incubationTime) > Constants.INCUBATION_TIME_MAX) {
Toast.makeText(
view.context,
view.context.getString(R.string.incubation_crossed_30_minutes),
Toast.LENGTH_SHORT
).show()
} else {
DataHolder.selectedTest = UserData(
_id = userList._id,
bloodGroup = userList.bloodGroup,
incubationTime = userList.incubationTime
)
view.findNavController()
.navigate(R.id.action_nav_home_to_mainActivity)
}
} else {
Toast.makeText(
view.context,
view.context.getString(R.string.incubation_not_started),
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
}
private fun isBetween15And30Minutes(createdAt: String): Long {
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
val createdAtDate: Date = formatter.parse(createdAt)!!
val currentTime = Calendar.getInstance().time
val diffMillis = currentTime.time - createdAtDate.time
return diffMillis / (60 * 1000)
}
private fun showWarningToast(resId: Int) {
val contextToUse = view.context ?: return
val message = try {
contextToUse.getString(resId)
} catch (e: Resources.NotFoundException) {
"Resource not found for ID: $resId"
}
Toast.makeText(
contextToUse,
message,
Toast.LENGTH_SHORT
).show()
}
}

View File

@@ -70,10 +70,10 @@ class UserListAdapter(
Glide.with(view).load(model.userImageURL).into(userImage)
if (model.testStatus!!) {
teststatus.text = "Test concluded"
teststatus.text = view.context.getString(R.string.test_concluded)
} else {
teststatus.text = "Test is Pending"
teststatus.text = view.context.getString(R.string.test_pending)
}
holder.binding.btnBlood.setOnClickListener {
@@ -94,19 +94,19 @@ class UserListAdapter(
val builder = AlertDialog.Builder(context)
.setView(dialogView)
.setTitle("Add Blood Group")
.setTitle(R.string.blood_group)
val etBloodGroup = dialogView.findViewById<AutoCompleteTextView>(R.id.et_blood_group)
builder.setPositiveButton("OK") { dialog, which ->
builder.setPositiveButton(R.string.ok) { dialog, which ->
val bloodGroup = etBloodGroup.text.toString()
if (bloodGroup.equals("Select Blood Group") || bloodGroup.isNullOrBlank()) {
etBloodGroup.error = "Please enter your blood group"
Toast.makeText(
context,
"Please enter your blood group",
Toast.LENGTH_SHORT
).show()
if (bloodGroup.equals("Select Blood Group") ||bloodGroup.equals("ರಕ್ತ ಗುಂಪು ಆಯ್ಕೆಮಾಡಿ") || bloodGroup.isNullOrBlank()) {
if (view != null) {
etBloodGroup.error =view.context.getString(R.string.blood_group_error)
}
if (view != null) {
Toast.makeText(context, view.context.getString(R.string.blood_group_toast), Toast.LENGTH_SHORT).show()
}
} else {
// Check if hemoCubeViewModel and testDetails are not null
val db: FirebaseFirestore = FirebaseFirestore.getInstance()
@@ -125,27 +125,33 @@ class UserListAdapter(
).format(Calendar.getInstance().time).toString()
)
.addOnSuccessListener {
Toast.makeText(
context,
"Blood group updated successfully",
Toast.LENGTH_SHORT
).show()
if (view != null) {
Toast.makeText(
view.context,
context?.getString(R.string.blood_group_updated_successfully),
Toast.LENGTH_SHORT
).show()
}
btnBlood.visibility = View.GONE
}
.addOnFailureListener { e ->
Toast.makeText(
context,
"Failed to update blood group: ${e.message}",
Toast.LENGTH_SHORT
).show()
if (view != null) {
Toast.makeText(
view.context,
context?.getString(R.string.failed_to_update_blood_group),
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: IndexOutOfBoundsException) {
// Handle the case where no documents are found for the specified ID
Toast.makeText(
context,
"No user data found for the specified ID",
Toast.LENGTH_SHORT
).show()
if (view != null) {
Toast.makeText(
view.context,
context?.getString(R.string.no_user_data_found),
Toast.LENGTH_SHORT
).show()
}
}
}
@@ -160,7 +166,7 @@ class UserListAdapter(
// Handle the selected blood group here
}
builder.setNegativeButton("Cancel") { dialog, which ->
builder.setNegativeButton(R.string.cancel) { dialog, which ->
dialog.dismiss()
}
@@ -190,10 +196,10 @@ class UserListAdapter(
// }
}
userCard.setOnClickListener {
if (batLevel < 40) {
if (false) {
Toast.makeText(
view.context,
"Battery level is low than 40%, please charge the device to continue testing",
context?.getString(R.string.low_battery_warning),
Toast.LENGTH_SHORT
).show()
return@setOnClickListener
@@ -202,21 +208,21 @@ class UserListAdapter(
if (model.testStatus!!) {
Toast.makeText(
view.context,
"Test has been already conducted for this user",
context?.getString(R.string.test_already_conducted),
Toast.LENGTH_SHORT
).show()
} else {
if (model.incubationTime != "") {
if (isBetween15And30Minutes(model.incubationTime) < 15) {
if (isBetween15And30Minutes(model.incubationTime) < -15000) {
Toast.makeText(
view.context,
"Incubation has not completed 15 minutes",
context?.getString(R.string.incubation_not_completed),
Toast.LENGTH_SHORT
).show()
} else if (isBetween15And30Minutes(model.incubationTime) > 30) {
} else if (isBetween15And30Minutes(model.incubationTime) > 300000) {
Toast.makeText(
view.context,
"Incubation crossed 30 minutes, need to repeat the incubation",
context?.getString(R.string.incubation_crossed_30_minutes),
Toast.LENGTH_SHORT
).show()
} else {
@@ -227,7 +233,7 @@ class UserListAdapter(
} else {
Toast.makeText(
view.context,
"Incubation not started for this user",
context?.getString(R.string.incubation_not_started),
Toast.LENGTH_SHORT
).show()
}
@@ -237,8 +243,6 @@ class UserListAdapter(
}
}
private fun isBetween15And30Minutes(createdAt: String): Long {
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
val createdAtDate: Date = formatter.parse(createdAt)!!

View File

@@ -0,0 +1,28 @@
package com.example.hpostesting.presentation.assurance
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.databinding.ActivityAssuranceControlsBinding
@AndroidEntryPoint
class AssuranceControlsActivity: AppCompatActivity() {
lateinit var binding: ActivityAssuranceControlsBinding
lateinit var sharedPreference: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAssuranceControlsBinding.inflate(layoutInflater)
sharedPreference = this.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
setContentView(binding.root)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(binding.fgAssuranceControls.id, AssuranceControlsFragment())
.commit()
}
}
}

View File

@@ -0,0 +1,175 @@
package com.example.hpostesting.presentation.assurance
import android.R
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.presentation.hemocube.HemocubeActivity
import `in`.sminnovations.hpostesting.databinding.FragmentAssuranceControlsBinding
import java.time.Instant
class AssuranceControlsFragment: Fragment() {
lateinit var binding: FragmentAssuranceControlsBinding
private lateinit var sharedPreferences: SharedPreferences
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
binding = FragmentAssuranceControlsBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
DataHolder.hemoCubeTestData!!.solution = ""
DataHolder.hemoCubeTestData!!.volume = ""
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
}
private fun initViews() {
// binding.btnSubmit.visibility = View.GONE
val solutionSpinner: Spinner = binding.spinnerSolutions
val solutionOptions = arrayOf("Select solution", "Tartrazine", "Acid Red")
val solutionAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, solutionOptions)
solutionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
solutionSpinner.adapter = solutionAdapter
solutionSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selectedValue: String = solutionOptions[position]
if (!selectedValue.equals("Select solution")) {
DataHolder.hemoCubeTestData!!.solution = selectedValue
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
val concentrationSpinner: Spinner = binding.spinnerConcentration
val concentrationOptions = arrayOf("Select concentration", "65umol", "45umol", "22.5umol", "12.25umol", "6.125umol", "75umol", "50umol", "25umol", "12.5umol", "6.25umol")
val concentrationAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, concentrationOptions)
concentrationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
concentrationSpinner.adapter = concentrationAdapter
concentrationSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selectedValue: String = concentrationOptions[position]
if (!selectedValue.equals("Select concentration")) {
DataHolder.hemoCubeTestData!!.concentration = selectedValue
}
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
val volumeSpinner: Spinner = binding.spinnerVolume
val volumeOptions = arrayOf("Select volume", "1uml", "2uml", "4uml", "4.5uml", "5uml")
val volumeAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, volumeOptions)
volumeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
volumeSpinner.adapter = volumeAdapter
volumeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
val selectedValue: String = volumeOptions[position]
}
override fun onNothingSelected(parent: AdapterView<*>?) {
}
}
// load saved values
val savedSolution = sharedPreferences.getString(Constants.QUICK_CAPTURE_SOLUTION, "").toString()
val solutionPosition = getPositionOfValue(savedSolution, solutionOptions.toList())
solutionSpinner.setSelection(solutionPosition)
val savedConcentration = sharedPreferences.getString(Constants.QUICK_CAPTURE_CONCENTRATION, "").toString()
val concentrationPosition = getPositionOfValue(savedConcentration, concentrationOptions.toList())
concentrationSpinner.setSelection(concentrationPosition)
val savedVolume = sharedPreferences.getString(Constants.QUICK_CAPTURE_VOLUME, "").toString()
val volumePosition = getPositionOfValue(savedVolume, volumeOptions.toList())
volumeSpinner.setSelection(volumePosition)
binding.btnSubmit.setOnClickListener {
val selectedSolution = binding.spinnerSolutions.selectedItem.toString()
val selectedConcentration = binding.spinnerConcentration.selectedItem.toString()
// Check if any of the selections are default values
if (selectedSolution == "Select solution") {
Toast.makeText(requireContext(), "Please select a solution", Toast.LENGTH_LONG).show()
return@setOnClickListener // Prevent further execution
}
if (selectedConcentration == "Select concentration") {
Toast.makeText(requireContext(), "Please select a concentration", Toast.LENGTH_LONG).show()
return@setOnClickListener // Prevent further execution
}
DataHolder.hemoCubeTestData!!.quickCapture = true
val currentUnixTime = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Instant.now().epochSecond
} else {
""
}
DataHolder.hemoCubeTestData!!._id = currentUnixTime.toString() + "SMI"
DataHolder.hemoCubeTestData!!.name = "${DataHolder.hemoCubeTestData!!.solution} ${DataHolder.hemoCubeTestData!!.concentration} ${DataHolder.hemoCubeTestData!!.volume}"
DataHolder.selectedTest = UserData()
DataHolder.selectedTest?._id = DataHolder.hemoCubeTestData!!._id
DataHolder.selectedTest?.name = DataHolder.hemoCubeTestData!!.name
with(sharedPreferences.edit()) {
putString(Constants.QUICK_CAPTURE_SOLUTION, DataHolder.hemoCubeTestData!!.solution)
putString(Constants.QUICK_CAPTURE_CONCENTRATION, DataHolder.hemoCubeTestData!!.concentration)
putString(Constants.QUICK_CAPTURE_VOLUME, DataHolder.hemoCubeTestData!!.volume)
apply()
}
val i = Intent(requireContext(), HemocubeActivity::class.java)
startActivity(i)
}
}
fun <T> getPositionOfValue(value: T, data: List<T>?): Int {
data?.let {
for (i in it.indices) {
if (it[i] == value) {
return i
}
}
}
return 0
}
}

View File

@@ -0,0 +1,6 @@
package com.example.hpostesting.presentation.assurance
import androidx.lifecycle.ViewModel
class AssuranceControlsViewModel : ViewModel() {
}

View File

@@ -0,0 +1,183 @@
package com.example.hpostesting.presentation.autodac
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityAutoDacBinding
@AndroidEntryPoint
class AutoDacActivity: AppCompatActivity() {
private lateinit var binding: ActivityAutoDacBinding
val viewModel: AutoDacViewModel by viewModels()
private var myMenu: Menu? = null
private lateinit var mDriver: UsbSerialDriver
private var mConnection: UsbDeviceConnection? = null
lateinit var mService: UsbService
private val TAG = "HemoCube"
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
synchronized(this) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device?.apply {
connectUsb(true)
DataHolder.usbConnected.postValue(true)
}
} else {
onErrorReported("permission denied for device")
DataHolder.usbConnected.postValue(true)
}
}
}
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as UsbService.UsbServiceBinder
mService = binder.getService()
viewModel.isServiceConnected = true
mConnection.let { mService.connect(mDriver, mConnection!!) }
moveToNext()
}
override fun onServiceDisconnected(arg0: ComponentName) {
viewModel.isServiceConnected = false
}
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAutoDacBinding.inflate(layoutInflater)
setContentView(binding.root)
// setSupportActionBar(binding.myToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setupListener()
connectUsb(false)
}
private fun setupListener() {
DataHolder.usbConnected.observe(this) {
Log.d("USB OBSERVE", "HemoCube called -> $it")
if (it) {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24)
} else {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
}
}
}
open fun connectUsb(permissionGranted: Boolean) {
Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
if (availableDrivers.isEmpty()) {
onErrorReported("No Device is Connected")
} else {
mDriver = availableDrivers[0]
mConnection = manager.openDevice(mDriver.device)
if (mConnection == null) {
requestUserPermission(manager, mDriver.device)
} else {
setupService()
}
}
}
fun onErrorReported(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
if (!isFinishing) onBackPressed()
}
private fun moveToNext() {
if (supportFragmentManager.isDestroyed) return
supportFragmentManager.beginTransaction().replace(binding.fgAutoDac.id, AutoDacFragment())
.commit()
}
@SuppressLint("MutableImplicitPendingIntent")
private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
val mPendingIntent: PendingIntent
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
mPendingIntent = PendingIntent.getBroadcast(
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
mPendingIntent = PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}
fun setupService() {
val intent = Intent(this, UsbService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.my_menu, menu)
return true
}
override fun onDestroy() {
super.onDestroy()
if (viewModel.isServiceConnected) {
mService.disconnect()
unbindService(connection)
viewModel.isServiceConnected = false
}
}
}

View File

@@ -0,0 +1,191 @@
package com.example.hpostesting.presentation.autodac
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.presentation.UsbServiceListener
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.databinding.FragmentAutoDacBinding
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
class AutoDacFragment: Fragment() {
private lateinit var binding: FragmentAutoDacBinding
private val autoDacViewModel: AutoDacViewModel by activityViewModels()
private lateinit var sharedPreferences: SharedPreferences
private var currentDeviceData: DeviceData? = null
private var resultData: String = ""
private val messages = MutableLiveData<String>()
private var startListening = MutableLiveData(false)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
binding = FragmentAutoDacBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
observeViewModel()
}
private fun initViews() {
binding.btnSubmit.visibility = View.GONE
listenToHemoCube()
getDeviceId()
binding.btnSubmit.setOnClickListener {
binding.btnSubmit.visibility = View.GONE
runAutoDacCommand()
}
}
private fun observeViewModel() {
autoDacViewModel.deviceData.observe(viewLifecycleOwner) {
currentDeviceData = it
}
messages.observe(viewLifecycleOwner) {
binding.tvSubtitle4.text = it
}
autoDacViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
showToast("Auto Dac data uploaded successfully")
}
if (result == "Local") {
showToast("Auto Dac uploading failed, note it down manually")
}
binding.progressBar.visibility = View.GONE
}
}
private fun getDeviceId() {
autoDacViewModel.progressBar.postValue(true)
(activity as AutoDacActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
autoDacViewModel.messages.postValue(stringData)
binding.tvSubtitle4.text = stringData
}
}
override fun onUsbError(e: Exception?) {
autoDacViewModel.progressBar.postValue(false)
}
})
}
private fun runAutoDacCommand() {
autoDacViewModel.progressBar.postValue(true)
(activity as AutoDacActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.AUTO_DAC_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
}
override fun onUsbError(e: Exception?) {
autoDacViewModel.progressBar.postValue(false)
}
})
}
private fun listenToHemoCube() {
val fullReadOutput = StringBuilder()
startListening.postValue(true)
try {
(activity as AutoDacActivity).mService.listenToHemoCube(object :
UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
autoDacViewModel.messages.postValue(stringData)
fullReadOutput.append(stringData)
resultData += stringData
binding.tvSubtitle4.text = resultData
if (stringData.contains("SN")) {
val slData = stringData.split(" ")
if (slData.size > 1) {
val hardwareId = slData[1].trim()
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()
}
}
activity?.runOnUiThread {
binding.btnSubmit.visibility = View.VISIBLE
}
}
}
if (resultData.contains("#CC")) {
autoDacViewModel.addAutoDacDataToDb(
DiagnosticsData(
deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
devicePassword = sharedPreferences.getString(Constants.DEVICE_PASSWORD_API, "").toString(),
deviceNatsToken = sharedPreferences.getString(Constants.NATS_TOKEN, "").toString(),
accessToken = sharedPreferences.getString(Constants.ACCESS_TOKEN, "").toString(),
deviceData = resultData,
runTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
)
)
activity?.runOnUiThread {
binding.ivCheck.visibility = View.VISIBLE
}
}
}
override fun onUsbError(e: Exception?) {
autoDacViewModel.progressBar.postValue(false)
}
})
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
}
}
fun parseData(inputData: List<String>): List<Pair<String, String>> {
val pattern = Regex("([A-Z]+)\\s(\\d+)")
val parsedData = mutableListOf<Pair<String, String>>()
for (item in inputData) {
val matchResult = pattern.find(item)
if (matchResult != null) {
val (letters, numbers) = matchResult.destructured
parsedData.add(Pair(letters, numbers))
}
}
return parsedData
}
private fun showToast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
}

View File

@@ -0,0 +1,52 @@
package com.example.hpostesting.presentation.autodac
import android.content.Context
import android.content.SharedPreferences
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.hpostesting.data.NetworkStatusLiveData
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.repository.Repository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class AutoDacViewModel @Inject constructor(
private val hemoCubeDao: HemoCubeDao,
private val repository: Repository,
context: Context
) : ViewModel() {
var isServiceConnected = false
val progressBar = MutableLiveData(false)
val messages = MutableLiveData<String>()
private val sharedPreference: SharedPreferences =
context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
val deviceData = MutableLiveData<DeviceData?>()
val fireBaseUpload = MutableLiveData<String>()
fun addAutoDacDataToDb(data: DiagnosticsData) {
viewModelScope.launch {
try {
when (val response = repository.addDiagnostics(data)) {
is Response.Success -> {
fireBaseUpload.postValue("Success")
}
is Response.Error -> {
fireBaseUpload.postValue("Error")
}
}
} catch (e: Exception) {
fireBaseUpload.postValue("Error")
}
}
}
}

View File

@@ -7,31 +7,26 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.model.patient.BufferCheckData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.toHemoCubeTestData
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.presentation.utils.MyDialogListener
import com.example.hpostesting.presentation.utils.UIUtils
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding
import kotlin.math.log10
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import java.util.UUID
import kotlin.math.log10
class HemoCubeBufferCheckFragment : Fragment() {
@@ -42,15 +37,30 @@ class HemoCubeBufferCheckFragment : Fragment() {
private var resultData: String = ""
private var kitno: String = ""
private var isOnline = false
private val messages = MutableLiveData<String>()
private var isTestOngoing = false
private var startListening = MutableLiveData(false)
private var led1BufferForDevice = 0.0
private var led2BufferForDevice = 0.0
private var led3BufferForDevice = 0.0
private var led4BufferForDevice = 0.0
private var led1SampleForDevice = 0.0
private var led2SampleForDevice = 0.0
private var led3SampleForDevice = 0.0
private var led4SampleForDevice = 0.0
private var fittedAbs1 = 0.0
private var fittedAbs2 = 0.0
private var fittedAbs3 = 0.0
private var fittedAbs4 = 0.0
private var _predictedDenovixRatio = 0.0
private var validationError = false
private var allErrorMessages = ""
private var deviceHardwareId = ""
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
binding = FragmentHemoCubeReferenceBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
return binding.root
}
@@ -65,15 +75,16 @@ class HemoCubeBufferCheckFragment : Fragment() {
binding.nameEditText.setText("SMI/SC/")
binding.tvTitle.visibility = View.GONE
binding.tvName.visibility = View.GONE
binding.btnPlacebuffer.visibility = View.GONE
binding.btnGo.setOnClickListener {
val serialNumber = binding.nameEditText.text.toString().trim()
if (serialNumber.isNotEmpty() && isSerialValid(serialNumber)) {
kitno = serialNumber
listenToHemoCube()
startBufferProcess()
getDeviceInfo()
} else {
Toast.makeText(context, "Invalid KIT Number", Toast.LENGTH_LONG).show()
Toast.makeText(context, R.string.invalid_kit_number, Toast.LENGTH_LONG).show()
return@setOnClickListener
}
activity?.runOnUiThread {
@@ -85,17 +96,17 @@ class HemoCubeBufferCheckFragment : Fragment() {
}
}
activity?.runOnUiThread {
binding.btnSubmit.isEnabled = true
binding.btnSubmit.isClickable = true
}
binding.btnSubmit.setOnClickListener {
binding.btnSamplestart.setOnClickListener {
startSampleProcess()
it.visibility = View.GONE
}
binding.btnSubmit.isEnabled = false
binding.btnSubmit.isClickable = false
binding.btnPlacebuffer.setOnClickListener {
checkAndStartProcess()
it.visibility = View.GONE
}
}
private fun observeViewModel() {
@@ -108,43 +119,44 @@ class HemoCubeBufferCheckFragment : Fragment() {
hemoCubeViewModel.getDeviceData(sharedPreferences.getString(Constants.USER_ID, ""))
} else {
Toast.makeText(
requireContext(), "No internet connection avaiable", Toast.LENGTH_SHORT
requireContext(), R.string.internt_not, Toast.LENGTH_SHORT
).show()
}
isOnline = isNetworkAvailable
}
hemoCubeViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
showToast("Kit result uploaded successfully")
showToast(R.string.kit_uploaded)
}
if (result == "Error") {
showToast("Error uploading data, Kit result stored locally")
startActivity(Intent(requireActivity(), DashboardActivity::class.java))
}
if (result == "Local") {
showToast("Kit result uploading failed, note it down manually")
showToast(R.string.kit_upload_failed)
}
binding.progressBar.visibility = View.GONE
}
messages.observe(viewLifecycleOwner) {
hemoCubeViewModel.messages.observe(viewLifecycleOwner) {
binding.tvSubtitle4.text = it
}
}
private fun checkAndStartProcess() {
startBufferProcess()
}
private fun listenToHemoCube() {
if (DataHolder.hemoCubeTestData == null) {
DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData()
}
val fullReadOutput = StringBuilder()
startListening.postValue(true)
try {
(activity as HemocubeBufferCheckActivity).mService.listenToHemoCube(object :
UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
fullReadOutput.append(stringData)
handleUsbData(stringData, fullReadOutput)
handleUsbData(stringData)
}
}
@@ -152,102 +164,217 @@ class HemoCubeBufferCheckFragment : Fragment() {
}
})
} catch (e: Exception) {
showToast("test is going on")
showToast(R.string.test_ongoing)
Firebase.crashlytics.recordException(e)
}
}
private fun handleUsbData(stringData: String, fullReadOutput: StringBuilder) {
private fun handleUsbData(stringData: String) {
if (stringData.contains("#")) {
messages.postValue(stringData)
isTestOngoing = true
}
resultData += fullReadOutput.toString()
resultData += stringData
when {
stringData.contains("#Buffer Completed") -> showStartSampleDialog()
stringData.contains("#Sample Completed") -> {
activity?.runOnUiThread {
binding.btnSubmit.isEnabled = true
binding.btnSubmit.isClickable = true
stringData.contains("SN") -> {
val slData = stringData.split(" ")
if (slData.size > 1) {
val hardwareId = slData[1].trim()
deviceHardwareId = hardwareId
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()
}
}
activity?.runOnUiThread {
binding.tvSubtitle4.visibility = View.VISIBLE
binding.btnPlacebuffer.visibility = View.VISIBLE
}
hemoCubeViewModel.messages.postValue("Start")
}
stringData.contains("#BS") -> {
hemoCubeViewModel.messages.postValue("Buffer Started")
}
stringData.contains("#BC") -> {
activity?.runOnUiThread {
binding.tvSubtitle4.text = "Buffer Completed"
binding.btnSamplestart.visibility = View.VISIBLE
}
}
stringData.contains("#SS") -> {
activity?.runOnUiThread {
binding.tvSubtitle4.text = "Sample Started"
binding.btnSamplestart.visibility = View.GONE
}
}
stringData.contains("#SC") -> {
hemoCubeViewModel.messages.postValue("Sample Completed \nGathering data")
fetchResult()
}
stringData.contains("RESULT") || resultData.contains("REND") -> {
var validString: String
val results: List<String>
if (stringData.contains("RESULT")) {
validString = isValidResult(stringData)
if (validString.isEmpty()) {
results = resultData.split("\n")
validString = parseResult(results)
}
} else {
results = resultData.split("\n")
validString = parseResult(results)
}
if (validString.isNotEmpty()) {
handleValidResult(validString)
}
}
}
}
private fun showStartSampleDialog() {
activity?.runOnUiThread {
UIUtils.createAlertDialog(requireContext(),
"Start Sample",
"Do you want to start sample reading?",
getString(R.string.no),
"Yes",
object : MyDialogListener {
override fun onClickNegativeButton() {}
override fun onClickPositiveButton() {
listenToHemoCube()
startSampleProcess()
}
})
}
}
private fun handleValidResult(validString: String) {
try {
val result = validString.split(" ")
if (result.size == 8) {
val deviceId = result[2]
val led1BufferForDevice = result[3].toDoubleOrNull()
val led2BufferForDevice = result[4].toDoubleOrNull()
val led1Sample = result[5].toDoubleOrNull()
val led2Sample = result[6].toDoubleOrNull()
val led1Average = log10(led1BufferForDevice?.div(led1Sample!!) ?: 0.0)
val led2Average = log10(led2BufferForDevice?.div(led2Sample!!) ?: 0.0)
val deviceRatio = led1Average / led2Average
val calculateRatio = calculateRatio(deviceRatio)
val classificationResult = findResult(calculateRatio)
messages.postValue(classificationResult)
val bufferData = BufferCheckData(
deviceId= deviceId,
kitno = kitno,
led1Buffer = led1BufferForDevice,
led2Buffer = led2BufferForDevice,
led1Average = led1Average,
led2Average = led2Average,
led1Sample = led1Sample,
led2Sample = led2Sample,
deviceRatio = deviceRatio,
calculatedRatio = calculateRatio,
coefficients = currentDeviceData?.coefficients?.get(0).toString() + ", " + currentDeviceData?.coefficients?.get(1).toString(),
classificationResult = classificationResult,
deviceSerialNumber = sharedPreferences.getString(Constants.USER_ID, "").toString(),
testTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time)
resultData.contains("REND") -> {
hemoCubeViewModel.messages.postValue(
"Data collected \n" + " Processing data"
)
hemoCubeViewModel.uploadHemoCubeResultToDatabaseforbuffercheckN(bufferData)
val resultLines = resultData.split("\\s+(?=LB|LS)".toRegex())
var bufferIntensity = resultLines[1].split(' ')[1].trim()
led1BufferForDevice = bufferIntensity.toDoubleOrNull()!!
bufferIntensity = resultLines[2].split(' ')[1].trim()
led2BufferForDevice = bufferIntensity.toDoubleOrNull()!!
bufferIntensity = resultLines[3].split(' ')[1].trim()
led3BufferForDevice = bufferIntensity.toDoubleOrNull()!!
bufferIntensity = resultLines[4].split(' ')[1].trim()
led4BufferForDevice = bufferIntensity.toDoubleOrNull()!!
led1SampleForDevice = resultLines[5].split(' ')[1].trim().toDoubleOrNull()!!
led2SampleForDevice = resultLines[6].split(' ')[1].trim().toDoubleOrNull()!!
led3SampleForDevice = resultLines[7].split(' ')[1].trim().toDoubleOrNull()!!
led4SampleForDevice =
resultLines[8].split(' ')[1].split('\r')[0].trim().toDoubleOrNull()!!
processResult()
}
}
}
private fun processResult() {
try {
hemoCubeViewModel.messages.postValue("processing result")
val deviceLog = resultData
val pInfo = requireActivity().packageManager.getPackageInfo(
requireActivity().packageName, 0
)
val version = pInfo.versionName
val led1Average = log10(led1BufferForDevice.div(led1SampleForDevice))
val led2Average = log10(led2BufferForDevice.div(led2SampleForDevice))
val led3Average = log10(led3BufferForDevice.div(led3SampleForDevice))
val led4Average = log10(led4BufferForDevice.div(led4SampleForDevice))
val deviceRatio = led3Average / led1Average
if (led1BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)
?.get(0)!! || led2BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
1
)
?.get(0)!! || led3BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
2
)
?.get(0)!! || led4BufferForDevice < Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
3
)?.get(0)!!
) {
validationError = true
allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
activity?.runOnUiThread {
binding.errorMessage.text =
"Error: Invalid Test. Improper buffer reading (low)" + "\n"
binding.errorMessage.visibility = View.VISIBLE
}
}
if (led1BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(0)
?.get(1)!! || led2BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
1
)
?.get(1)!! || led3BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
2
)
?.get(1)!! || led4BufferForDevice > Constants.BUFFER_INTENSITY_THRESHOLDS[deviceHardwareId]?.get(
3
)?.get(1)!!
) {
validationError = true
allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text =
"Error: Invalid Test. Improper buffer reading (high)"
binding.errorMessage.visibility = View.VISIBLE
}
}
var gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(0)
var constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(0)?.get(1)
fittedAbs1 = gradient?.times(led1Average)?.plus(constant!!)!!
gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(0)
constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(1)?.get(1)
fittedAbs2 = gradient?.times(led2Average)?.plus(constant!!)!!
gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(0)
constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(2)?.get(1)
fittedAbs3 = gradient?.times(led3Average)?.plus(constant!!)!!
gradient = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(3)?.get(0)
constant = Constants.DEVICE_CONFIGURATION[deviceHardwareId]?.get(3)?.get(1)
fittedAbs4 = gradient?.times(led4Average)?.plus(constant!!)!!
_predictedDenovixRatio = fittedAbs3.div(fittedAbs1)
if (fittedAbs1 <= fittedAbs2) {
validationError = true
allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
activity?.runOnUiThread {
binding.errorMessage.text = "Error: Invalid Test. Problem with de-oxygenation"
binding.errorMessage.visibility = View.VISIBLE
}
}
if (fittedAbs1 < 0 || fittedAbs2 < 0 || fittedAbs3 < 0 || fittedAbs4 < 0) {
validationError = true
activity?.runOnUiThread {
binding.errorMessage.text = "Error: Negative Abs. Redo Kit check Reading"
binding.errorMessage.visibility = View.VISIBLE
}
}
val prdClassification = absorbanceBasedClassification(_predictedDenovixRatio)
hemoCubeViewModel.messages.postValue(prdClassification)
val bufferData = BufferCheckData(
_id = UUID.randomUUID().toString(),
deviceId = deviceHardwareId,
kitno = kitno,
appVersion = version,
led1Buffer = led1BufferForDevice,
led2Buffer = led2BufferForDevice,
led3Buffer = led3BufferForDevice,
led4Buffer = led4BufferForDevice,
led1Sample = led1SampleForDevice,
led2Sample = led2SampleForDevice,
led3Sample = led3SampleForDevice,
led4Sample = led4SampleForDevice,
led1Average = led1Average,
led2Average = led2Average,
led3Average = led3Average,
led4Average = led4Average,
abs1 = fittedAbs1,
abs2 = fittedAbs2,
abs3 = fittedAbs3,
abs4 = fittedAbs4,
deviceRatio = deviceRatio,
resultData = deviceLog,
predictedDenovixRatio = _predictedDenovixRatio,
prdClassification = prdClassification,
errorMessages = allErrorMessages,
coefficients = currentDeviceData?.coefficients?.get(0)
.toString() + ", " + currentDeviceData?.coefficients?.get(1).toString(),
deviceSerialNumber = sharedPreferences.getString(Constants.USER_ID, "").toString(),
testTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time),
batteryLevel = hemoCubeViewModel.getBatteryLevel().toString(),
batteryCapacity = hemoCubeViewModel.getBatteryCapacity(requireContext()).toString(),
batteryMaxCapacity = hemoCubeViewModel.getBatteryMaxCapacity(requireContext())
.toString(),
batteryTemperature = hemoCubeViewModel.getBatteryTemperature().toString(),
batteryVoltage = hemoCubeViewModel.getBatteryVoltage(requireContext()).toString()
)
hemoCubeViewModel.uploadHemoCubeResultToDatabaseForBufferCheck(isOnline, bufferData)
} catch (e: Exception) {
Toast.makeText(
requireContext(), "Error while processing device data", Toast.LENGTH_SHORT
@@ -258,32 +385,61 @@ class HemoCubeBufferCheckFragment : Fragment() {
private fun findResult(calculatedRatio: Double?): String {
try {
hemoCubeViewModel.messages.postValue("result classification")
if (calculatedRatio != null) {
if (calculatedRatio < 0.085) return "Kit Failed"
if (calculatedRatio in 0.085..0.155) return "Kit Passed"
if (calculatedRatio in 0.155..0.175) return "Kit Failed"
if (calculatedRatio in 0.175..0.22) return "Kit Failed"
if (calculatedRatio in 0.22..0.25) return "Kit Failed"
if (calculatedRatio in 0.25..0.35) return "Kit Failed"
if (calculatedRatio > 0.35) return "Kit Failed"
if (calculatedRatio < 0.05) return "Inconclusive. Very low Absorbance - Repeat test with Higher Blood Volume"
if (calculatedRatio in 0.05..0.155) return "Normal"
if (calculatedRatio in 0.155..0.175) return "Negative Borderline. Repeat Test"
if (calculatedRatio in 0.175..0.22) return "Sickle Cell Trait"
if (calculatedRatio in 0.22..0.25) return "Positive for Sickle Cell. HPLC for Confirmation"
if (calculatedRatio in 0.25..0.35) return "Sickle Cell Disease"
if (calculatedRatio > 0.35) return "Inconclusive. Repeat with test with lower volume of blood"
} else {
return "NULL"
return "INVALID"
}
} catch (e: Exception) {
showToast(R.string.error_classification)
Firebase.crashlytics.recordException(e)
return "ERROR"
}
return "INVALID"
}
private fun absorbanceBasedClassification(predictedDenovixRatio: Double?): String {
try {
hemoCubeViewModel.messages.postValue("result classification")
if (predictedDenovixRatio != null) {
if (predictedDenovixRatio in 0.0..0.16) return "Kit Passed"
if (predictedDenovixRatio in 0.16..0.165) return "Kit Passed"
if (predictedDenovixRatio in 0.165..0.235) return "Kit Failed"
if (predictedDenovixRatio in 0.235..0.24) return "Kit Failed"
if (predictedDenovixRatio in 0.24..1.0) return "Kit Failed"
} else {
return "INVALID"
}
} catch (e: Exception) {
showToast("error while performing classification")
Firebase.crashlytics.recordException(e)
return "ERROR"
}
return "NULL"
return "INVALID"
}
private fun showToast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
private fun startBufferProcess() {
private fun showToast(messageResId: Int) {
Toast.makeText(requireContext(), getString(messageResId), Toast.LENGTH_SHORT).show()
}
(activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startBuffer,
private fun startBufferProcess() {
activity?.runOnUiThread {
binding.btnPlacebuffer.visibility = View.GONE
}
(activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.START_BUFFER_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {}
@@ -293,8 +449,11 @@ class HemoCubeBufferCheckFragment : Fragment() {
}
private fun startSampleProcess() {
activity?.runOnUiThread {
binding.btnPlacebuffer.visibility = View.GONE
}
(activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.startSample,
(activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.START_SAMPLE,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {}
@@ -303,9 +462,28 @@ class HemoCubeBufferCheckFragment : Fragment() {
})
}
private fun getDeviceInfo() {
hemoCubeViewModel.progressBar.postValue(true)
(activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
hemoCubeViewModel.messages.postValue(stringData)
binding.tvSubtitle4.text = stringData
}
}
override fun onUsbError(e: Exception?) {
hemoCubeViewModel.progressBar.postValue(false)
}
})
}
private fun fetchResult() {
(activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.getSample,
(activity as HemocubeBufferCheckActivity).mService.sendAndListenToHemoCube(HemoCubeCommands.PRINT_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {}
@@ -320,21 +498,6 @@ class HemoCubeBufferCheckFragment : Fragment() {
return coefficient1 * ratio + coefficient2
}
private fun parseResult(frames: List<String>): String {
val lines = mutableListOf<String>()
for (frame in frames.reversed()) {
if (frame.contains("REND") || frame.contains("RESULT")) lines += frame
if (frame.contains("RESULT")) {
break
}
}
val line = lines.reversed().joinToString("").trim()
if (line.contains("RESULT") && line.split(" ").size == 8) {
return line
}
return ""
}
private fun isSerialValid(s: String): Boolean {
if (s.length != 17) {
binding.nameEditText.error = getString(R.string.invalid_kit)
@@ -342,13 +505,4 @@ class HemoCubeBufferCheckFragment : Fragment() {
}
return true
}
private fun isValidResult(line: String): String {
return if (line.contains("RESULT") && line.split(" ").size == 8) {
line
} else {
""
}
}
}

View File

@@ -1,5 +1,6 @@
package com.example.hpostesting.presentation.buffercheck
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -21,6 +22,7 @@ import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
@@ -73,6 +75,12 @@ open class HemocubeBufferCheckActivity : AppCompatActivity() {
}
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHemocubeBinding.inflate(layoutInflater)
@@ -92,7 +100,7 @@ open class HemocubeBufferCheckActivity : AppCompatActivity() {
} else {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.device_disconnected, Toast.LENGTH_SHORT).show()
}
}
}
@@ -103,7 +111,7 @@ open class HemocubeBufferCheckActivity : AppCompatActivity() {
val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
if (availableDrivers.isEmpty()) {
Toast.makeText(this, "No device found", Toast.LENGTH_SHORT).show()
Toast.makeText(this, R.string.no_device_found, Toast.LENGTH_SHORT).show()
} else {
mDriver = availableDrivers[0]
mConnection = manager.openDevice(mDriver.device)
@@ -117,21 +125,22 @@ open class HemocubeBufferCheckActivity : AppCompatActivity() {
}
@SuppressLint("MutableImplicitPendingIntent")
private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
val mPendingIntent: PendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(
this, 0, Intent(Constants.HOMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HOMOCUBE_USB_PERMISSION),
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HOMOCUBE_USB_PERMISSION)
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}

View File

@@ -0,0 +1,182 @@
package com.example.hpostesting.presentation.calibration
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityCalibrationBinding
@AndroidEntryPoint
class CalibrationActivity: AppCompatActivity() {
private lateinit var binding: ActivityCalibrationBinding
val viewModel: CalibrationViewModel by viewModels()
private var myMenu: Menu? = null
private lateinit var mDriver: UsbSerialDriver
private var mConnection: UsbDeviceConnection? = null
lateinit var mService: UsbService
private val TAG = "Calibration"
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
synchronized(this) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device?.apply {
connectUsb(true)
DataHolder.usbConnected.postValue(true)
}
} else {
onErrorReported("permission denied for device")
DataHolder.usbConnected.postValue(true)
}
}
}
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as UsbService.UsbServiceBinder
mService = binder.getService()
viewModel.isServiceConnected = true
mConnection.let { mService.connect(mDriver, mConnection!!) }
moveToNext()
}
override fun onServiceDisconnected(arg0: ComponentName) {
viewModel.isServiceConnected = false
}
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCalibrationBinding.inflate(layoutInflater)
setContentView(binding.root)
// setSupportActionBar(binding.myToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setupListener()
connectUsb(false)
}
private fun setupListener() {
DataHolder.usbConnected.observe(this) {
Log.d("USB OBSERVE", "HemoCube called -> $it")
if (it) {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24)
} else {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
}
}
}
open fun connectUsb(permissionGranted: Boolean) {
Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
if (availableDrivers.isEmpty()) {
onErrorReported("No Device is Connected")
} else {
mDriver = availableDrivers[0]
mConnection = manager.openDevice(mDriver.device)
if (mConnection == null) {
requestUserPermission(manager, mDriver.device)
} else {
setupService()
}
}
}
fun onErrorReported(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
if (!isFinishing) onBackPressed()
}
private fun moveToNext() {
if (supportFragmentManager.isDestroyed) return
supportFragmentManager.beginTransaction().replace(binding.fgCalibration.id, CalibrationFragment())
.commit()
}
@SuppressLint("MutableImplicitPendingIntent")
private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
val mPendingIntent: PendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}
fun setupService() {
val intent = Intent(this, UsbService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.my_menu, menu)
return true
}
override fun onDestroy() {
super.onDestroy()
if (viewModel.isServiceConnected) {
mService.disconnect()
unbindService(connection)
viewModel.isServiceConnected = false
}
}
}

View File

@@ -0,0 +1,278 @@
package com.example.hpostesting.presentation.calibration
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.model.calibration.CalibrationData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.databinding.FragmentCalibrationBinding
class CalibrationFragment: Fragment() {
private lateinit var binding: FragmentCalibrationBinding
private val calibrationViewModel: CalibrationViewModel by activityViewModels()
private lateinit var sharedPreferences: SharedPreferences
private var currentDeviceData: DeviceData? = null
private var resultData: String = ""
private val messages = MutableLiveData<String>()
private var startListening = MutableLiveData(false)
private lateinit var calibrationData: CalibrationData
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
binding = FragmentCalibrationBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
observeViewModel()
loadSavedCalibration()
}
private fun initViews() {
binding.btnSubmit.visibility = View.GONE
listenToHemoCube()
getDeviceId()
binding.btnSubmit.setOnClickListener {
binding.btnSubmit.visibility = View.GONE
saveCalibration()
}
val etLed1Slope = binding.etLed1Slope
etLed1Slope.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led1Slope = s.toString().toDoubleOrNull() ?: 0.0
}
})
val etLed1Intercept = binding.etLed1Intercept
etLed1Intercept.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led1Intercept = s.toString().toDoubleOrNull() ?: 0.0
}
})
val etLed2Slope = binding.etLed2Slope
etLed2Slope.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led2Slope = s.toString().toDoubleOrNull() ?: 0.0
}
})
val etLed2Intercept = binding.etLed2Intercept
etLed2Intercept.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led2Intercept = s.toString().toDoubleOrNull() ?: 0.0
}
})
val etLed3Slope = binding.etLed3Slope
etLed3Slope.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led3Slope = s.toString().toDoubleOrNull() ?: 0.0
}
})
val etLed3Intercept = binding.etLed3Intercept
etLed3Intercept.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led3Intercept = s.toString().toDoubleOrNull() ?: 0.0
}
})
val etLed4Slope = binding.etLed4Slope
etLed4Slope.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led4Slope = s.toString().toDoubleOrNull() ?: 0.0
}
})
val etLed4Intercept = binding.etLed4Intercept
etLed4Intercept.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
calibrationData.led4Intercept = s.toString().toDoubleOrNull() ?: 0.0
}
})
}
private fun loadSavedCalibration() {
calibrationData = calibrationViewModel.loadSavedCalibrationData()
activity?.runOnUiThread() {
binding.etLed1Slope.text =
Editable.Factory.getInstance().newEditable(calibrationData.led1Slope.toString())
binding.etLed1Intercept.text =
Editable.Factory.getInstance().newEditable(calibrationData.led1Intercept.toString())
binding.etLed2Slope.text =
Editable.Factory.getInstance().newEditable(calibrationData.led2Slope.toString())
binding.etLed2Intercept.text =
Editable.Factory.getInstance().newEditable(calibrationData.led2Intercept.toString())
binding.etLed3Slope.text =
Editable.Factory.getInstance().newEditable(calibrationData.led3Slope.toString())
binding.etLed3Intercept.text =
Editable.Factory.getInstance().newEditable(calibrationData.led3Intercept.toString())
binding.etLed4Slope.text =
Editable.Factory.getInstance().newEditable(calibrationData.led4Slope.toString())
binding.etLed4Intercept.text =
Editable.Factory.getInstance().newEditable(calibrationData.led4Intercept.toString())
}
}
private fun saveCalibration() {
if (calibrationViewModel.saveCalibration(calibrationData)) {
startActivity(Intent(requireActivity(), DashboardActivity::class.java))
}
}
private fun observeViewModel() {
calibrationViewModel.deviceData.observe(viewLifecycleOwner) {
currentDeviceData = it
}
messages.observe(viewLifecycleOwner) {
binding.tvSubtitle4.text = it
}
calibrationViewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
showToast("Auto Dac data uploaded successfully")
}
if (result == "Local") {
showToast("Auto Dac uploading failed, note it down manually")
}
binding.progressBar.visibility = View.GONE
}
}
private fun getDeviceId() {
calibrationViewModel.progressBar.postValue(true)
(activity as CalibrationActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
calibrationViewModel.messages.postValue(stringData)
binding.tvSubtitle4.text = stringData
}
}
override fun onUsbError(e: Exception?) {
calibrationViewModel.progressBar.postValue(false)
}
})
}
private fun listenToHemoCube() {
val fullReadOutput = StringBuilder()
startListening.postValue(true)
try {
(activity as CalibrationActivity).mService.listenToHemoCube(object :
UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
calibrationViewModel.messages.postValue(stringData)
fullReadOutput.append(stringData)
resultData += stringData
binding.tvSubtitle4.text = resultData
if (stringData.contains("SN")) {
val slData = stringData.split(" ")
if (slData.size > 1) {
val hardwareId = slData[1].trim()
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()
}
}
activity?.runOnUiThread {
binding.btnSubmit.visibility = View.VISIBLE
}
}
}
}
override fun onUsbError(e: Exception?) {
calibrationViewModel.progressBar.postValue(false)
}
})
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
}
}
private fun showToast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
}

View File

@@ -0,0 +1,90 @@
package com.example.hpostesting.presentation.calibration
import android.content.Context
import android.content.SharedPreferences
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.hpostesting.data.NetworkStatusLiveData
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.calibration.CalibrationData
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.repository.Repository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class CalibrationViewModel @Inject constructor(
private val repository: Repository,
context: Context
) : ViewModel() {
var isServiceConnected = false
val progressBar = MutableLiveData(false)
val messages = MutableLiveData<String>()
private val sharedPreference: SharedPreferences =
context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
// private val _networkStatusLiveData = NetworkStatusLiveData(context)
val deviceData = MutableLiveData<DeviceData?>()
// val networkStatusLiveData: LiveData<Boolean>
// get() = _networkStatusLiveData
val fireBaseUpload = MutableLiveData<String>()
val savedCalibrationData = MutableLiveData<String>()
fun loadSavedCalibrationData(): CalibrationData {
val calibrationData = CalibrationData()
val sharedPreferenceKeys = arrayOf(
"LED1SLOPE", "LED1INTERCEPT",
"LED2SLOPE", "LED2INTERCEPT",
"LED3SLOPE", "LED3INTERCEPT",
"LED4SLOPE", "LED4INTERCEPT",
"CALIBRATED_AT"
)
with(calibrationData) {
led1Slope = sharedPreference.getString(sharedPreferenceKeys[0], "")?.toDoubleOrNull() ?: 0.0
led1Intercept = sharedPreference.getString(sharedPreferenceKeys[1], "")?.toDoubleOrNull() ?: 0.0
led2Slope = sharedPreference.getString(sharedPreferenceKeys[2], "")?.toDoubleOrNull() ?: 0.0
led2Intercept = sharedPreference.getString(sharedPreferenceKeys[3], "")?.toDoubleOrNull() ?: 0.0
led3Slope = sharedPreference.getString(sharedPreferenceKeys[4], "")?.toDoubleOrNull() ?: 0.0
led3Intercept = sharedPreference.getString(sharedPreferenceKeys[5], "")?.toDoubleOrNull() ?: 0.0
led4Slope = sharedPreference.getString(sharedPreferenceKeys[6], "")?.toDoubleOrNull() ?: 0.0
led4Intercept = sharedPreference.getString(sharedPreferenceKeys[7], "")?.toDoubleOrNull() ?: 0.0
calibratedAt = sharedPreference.getString(sharedPreferenceKeys[8], "") ?: ""
}
return calibrationData
}
fun saveCalibration(calibrationData: CalibrationData): Boolean {
try {
with(sharedPreference.edit()) {
putString("LED1SLOPE", calibrationData.led1Slope.toString())
putString("LED1INTERCEPT", calibrationData.led1Intercept.toString())
putString("LED2SLOPE", calibrationData.led2Slope.toString())
putString("LED2INTERCEPT", calibrationData.led2Intercept.toString())
putString("LED3SLOPE", calibrationData.led3Slope.toString())
putString("LED3INTERCEPT", calibrationData.led3Intercept.toString())
putString("LED4SLOPE", calibrationData.led4Slope.toString())
putString("LED4INTERCEPT", calibrationData.led4Intercept.toString())
putString("CALIBRATED_AT", calibrationData.calibratedAt)
apply()
}
return true
} catch (e: Exception) {
return false
}
}
}

View File

@@ -1,38 +1,121 @@
package com.example.hpostesting.presentation.dashboard
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import androidx.drawerlayout.widget.DrawerLayout
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.NatsManager
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.presentation.jig.JigActivity
import com.google.android.material.navigation.NavigationView
import com.google.firebase.appdistribution.FirebaseAppDistribution
import com.google.firebase.appdistribution.FirebaseAppDistributionException
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.google.firebase.crashlytics.ktx.crashlytics
//import com.google.firebase.appdistribution.FirebaseAppDistribution
//import com.google.firebase.appdistribution.FirebaseAppDistributionException
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityDashboardBinding
import okhttp3.ResponseBody
import java.io.File
interface NatsMessageCallback {
fun onMessageReceived(topic: String, message: String)
}
open interface IDataCollector: NatsMessageCallback {
fun setConnect(connect: Boolean)
fun setResponse(response: String)
}
@AndroidEntryPoint
class DashboardActivity : AppCompatActivity() {
class DashboardActivity : AppCompatActivity(), IDataCollector {
val TAG = "DashboardActivity"
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityDashboardBinding
lateinit var sharedPreferences: SharedPreferences
var responses: String = ""
lateinit var nats: NatsManager
private var downloadId: Long = 0
// TODO: Remove hemocube viewmodel
private val hemocubeViewModel: HemoCubeViewModel by viewModels()
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onMessageReceived(topic: String, message: String) {
// Handle incoming messages from NATS
Log.d(TAG, "Received message on topic $topic: $message")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDashboardBinding.inflate(layoutInflater)
sharedPreferences = this.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
setContentView(binding.root)
setSupportActionBar(binding.appBarDashboard.toolbar)
nats = NatsManager(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
nats.connect()
}
val deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "")
nats.sub("server.hpos.${deviceId}.ping")
nats.pub("server.hpos.${deviceId}.ping", "THIS IS A TEST MSG")
hemocubeViewModel.deviceUpdate.observe(this) { result ->
when (result) {
is Result.Success -> {
// Handle success
val apkUrl = result.data
// val apkUrl = "https://dl.dropboxusercontent.com/s/fi/1c3nn7t0co431hicl3hrt/app-debug.apk?rlkey=e4uf13ty1dpcked614vy1aaqp&dl=0"
initiateUpdate(apkUrl.toString())
Log.d("ApI", "APK URL: $apkUrl")
// Toast.makeText(
// this,
// "APK UPLOAD ${result.data}",
// Toast.LENGTH_SHORT
// ).show()
}
is Result.Error -> {
result.exception.let { message ->
Toast.makeText(this, "An error occurred On Updating App: $message", Toast.LENGTH_LONG)
.show()
}
}
is Result.Loading -> {
}
else -> {
}
}
}
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
@@ -46,6 +129,10 @@ class DashboardActivity : AppCompatActivity() {
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
if (Constants.NAVIGATE_TO_TEST_JIG_DIRECTLY) {
startActivity(Intent(this, JigActivity::class.java))
finish()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -59,6 +146,70 @@ class DashboardActivity : AppCompatActivity() {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
private fun initiateUpdate(responseBody: String) {
val apkUrl = responseBody
if (!isValidHttpUrl(apkUrl)) {
return
}
val request = DownloadManager.Request(Uri.parse(apkUrl))
request.setTitle("App Update")
request.setDescription("Downloading update...")
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
request.setDestinationInExternalFilesDir(this, "Updates", "update.apk")
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadId = downloadManager.enqueue(request)
// Register a BroadcastReceiver to receive the download complete event
// val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
// registerReceiver(downloadReceiver, filter)
}
private fun extractApkUrl(responseBody: ResponseBody): String {
return responseBody.string()
}
private fun isValidHttpUrl(url: String): Boolean {
return url.startsWith("http://") || url.startsWith("https://")
}
private val downloadReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
if (id == downloadId) {
installApk()
}
}
}
private fun installApk() {
val file = File(getExternalFilesDir("Updates"), "update.apk")
file.setReadable(true, false) // Ensure the file is readable
val pInfo = baseContext.packageManager.getPackageInfo(baseContext.packageName, 0)
val uri: Uri = FileProvider.getUriForFile(
this,
"${pInfo}.fileprovider",
file
)
// Create an intent to install the APK
val installIntent = Intent(Intent.ACTION_INSTALL_PACKAGE)
installIntent.data = uri
installIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TOP
installIntent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
// Start the installation
startActivity(installIntent)
Log.d("InstallApk", "Install Intent URI: $uri")
Log.d("InstallApk", "Package Name: $packageName")
}
override fun onDestroy() {
super.onDestroy()
// unregisterReceiver(downloadReceiver)
}
override fun onResume() {
super.onResume()
@@ -99,4 +250,16 @@ class DashboardActivity : AppCompatActivity() {
// Toast.makeText(this, "App Update: Success!", Toast.LENGTH_SHORT).show()
}
}
override fun setConnect(connect: Boolean) {
if(connect){
Log.i("NATS Connection", connect.toString())
}
}
override fun setResponse(response: String) {
responses = responses+response+"\n"
println(responses)
}
}

View File

@@ -1,38 +1,69 @@
package com.example.hpostesting.presentation.dashboard
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.presentation.autodac.AutoDacActivity
import com.example.hpostesting.presentation.buffercheck.HemocubeBufferCheckActivity
import com.example.hpostesting.presentation.calibration.CalibrationActivity
import com.example.hpostesting.presentation.deviceinfo.DeviceActivity
import com.example.hpostesting.presentation.deviceprovision.DeviceProvisionActivity
import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import `in`.sminnovations.hpostesting.databinding.FragmentGalleryBinding
class GalleryFragment : Fragment() {
private var _binding: FragmentGalleryBinding? = null
private lateinit var sharedPreferences: SharedPreferences
private var userid = ""
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private lateinit var sharedPreference: SharedPreferences
private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels()
@SuppressLint("SetTextI18n")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
// val galleryViewModel =
// ViewModelProvider(this).get(GalleryViewModel::class.java)
_binding = FragmentGalleryBinding.inflate(inflater, container, false)
val root: View = binding.root
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
sharedPreference = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
if (sharedPreference.getString(
Constants.USER_ID, ""
) == "FACTORY" && sharedPreference.getString(Constants.DEVICE_ID_API, "").toString()
.isEmpty() && sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "")
.toString().isEmpty()
) {
binding.btnDeviceProvision.visibility = View.VISIBLE
} else {
binding.btnDeviceProvision.visibility = View.VISIBLE
}
binding.btnDeviceProvision.setOnClickListener {
startActivity(Intent(requireContext(), DeviceProvisionActivity::class.java))
}
// val textView: TextView = binding.textGallery
// galleryViewModel.text.observe(viewLifecycleOwner) {
//// textView.text = it
// }
binding.btnSubmit.setOnClickListener {
startActivity(Intent(requireContext(), HemocubeBufferCheckActivity::class.java))
}
@@ -41,6 +72,33 @@ class GalleryFragment : Fragment() {
startActivity(Intent(requireContext(), DiagnosticsActivity::class.java))
}
binding.btnAutoDac.setOnClickListener {
startActivity(Intent(requireContext(), AutoDacActivity::class.java))
}
binding.btnCalibration.setOnClickListener {
startActivity(Intent(requireContext(), CalibrationActivity::class.java))
}
binding.btnDeviceInfo.setOnClickListener {
startActivity(Intent(requireContext(), DeviceActivity::class.java))
}
binding.btnFirefox.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.component = ComponentName("org.mozilla.firefox", "org.mozilla.gecko.BrowserApp")
startActivity(intent)
}
binding.btnFiles.setOnClickListener {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "file/*"
startActivity(intent)
}
userid = sharedPreferences.getString(Constants.USER_ID, "").toString()
binding.tvSubtitle4.text = "Login ID : ${userid}"
return root
}

View File

@@ -8,6 +8,8 @@ import android.content.Intent
import android.content.SharedPreferences
import android.os.BatteryManager
import android.os.Bundle
import android.util.Base64
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -16,11 +18,24 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.api.DeviceCommunicationHandler
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.model.login.LoginRequest
import com.example.hpostesting.data.model.login.LoginResponse
import com.example.hpostesting.data.model.molbioresult.MolbioV2Result
import com.example.hpostesting.data.model.molbioresult.MolbioV2ResultRequest
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.model.patient.HemoCubeTestData
import com.example.hpostesting.data.model.patient.UserData
import com.example.hpostesting.data.model.updates.CheckUpdateRequest
import com.example.hpostesting.data.model.updates.DeviceUpdateRequest
import com.example.hpostesting.presentation.KitScanActivity
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.adapter.OfflineUserListAdapter
import com.example.hpostesting.presentation.adapter.UserListAdapter
import com.example.hpostesting.presentation.assurance.AssuranceControlsActivity
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.presentation.testRight.TestRightViewModel
import com.firebase.ui.firestore.FirestoreRecyclerOptions
@@ -32,9 +47,19 @@ import com.google.firebase.perf.ktx.performance
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHomeBinding
import okhttp3.ResponseBody
import org.json.JSONObject
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
@AndroidEntryPoint
class HomeFragment : Fragment() {
@@ -43,12 +68,19 @@ class HomeFragment : Fragment() {
private val viewModel: TestRightViewModel by activityViewModels()
private val hemoCubeViewModel: HemoCubeViewModel by activityViewModels()
private lateinit var rvAdapter: UserListAdapter
private var batLevel: Int = 0 // Initialize with a default value, or obtain the actual battery level
private var batLevel: Int =
0 // Initialize with a default value, or obtain the actual battery level
private lateinit var adapter: OfflineUserListAdapter
private val homeViewModel: HemoCubeViewModel by activityViewModels()
private var isTokenAvailable = false
private var natsToken: String = ""
private var deviceId: String = ""
private lateinit var sharedPreference: SharedPreferences
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View? {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
@@ -59,8 +91,7 @@ class HomeFragment : Fragment() {
return super.onCreateView(inflater, container, savedInstanceState)
}
sharedPreference =
requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
sharedPreference = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
DataHolder.selectedTest = null
return binding.root
@@ -68,19 +99,81 @@ class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (sharedPreference.getString(Constants.USER_ID, "").toString() == "ADMIN") {
binding.labelQuickCapture.visibility = View.VISIBLE
binding.btnQuickCapture.visibility = View.VISIBLE
}
getDeviceId()
checkUnprocessedCSVData()
viewModel.allUserData.observe(viewLifecycleOwner) { userData ->
deleteIncompleteRegistrations(userData)
}
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userData ->
deleteHemoCubeIncompleteRegistrations(userData)
if (userData.isNotEmpty()) {
val userList = mutableListOf<HemoCubeTestData>()
userData.forEach {
if (it.testStatus == false) {
userList.add(it)
}
}
val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager
val batLevel: Int = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
adapter = OfflineUserListAdapter(binding.root, batLevel)
adapter.differ.submitList(userList)
binding.rvOrderOffline.adapter = adapter
}
}
viewModel.networkStatusLiveData.observe(viewLifecycleOwner) { isConnected ->
hemoCubeViewModel.deviceData.observe(viewLifecycleOwner) { deviceData ->
val devicelist = mutableListOf<DeviceData>()
if (deviceData != null) {
devicelist.add(DeviceData(deviceData.deviceId))
}
}
viewModel.networkStatusLiveData?.observe(viewLifecycleOwner) { isConnected ->
if (isConnected) {
binding.internetAvailableCL.visibility = View.VISIBLE
binding.internetNotAvailableCL.visibility = View.GONE
loadUserData()
setSearch()
checkForLocalDBData()
checkForTokenAndUpdate()
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val resultList = MolbioV2ResultRequest(mutableListOf(MolbioV2Result()))
userDataList.forEach { userData ->
if (!userData.molbioFlag && isTokenAvailable) {
resultList.results?.add(
MolbioV2Result(
rawData = userData,
analysisId = userData._id,
analysisDate = "2024-02-08 16:33:56",
analysisStatus = userData.classificationResult,
thresholds = Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId].toString(),
interpretation = userData.classificationResult,
testId = userData._id,
testTime = "2024-02-08 16:33:56",//userData.testTime,
collectionTime = "2024-02-08 16:33:56",//userData.testTime,
expiryTime = "2024-02-08 16:33:56"//userData.testTime,
)
)
}
if (!userData.molbioFlag && isTokenAvailable) {
userData.molbioFlag = true
hemoCubeViewModel.uploadResult(resultList)
}
if (!userData.localFlag) {
userData.localFlag = true
hemoCubeViewModel.bulkAddResultTestToDb(userData)
}
}
}
} else {
binding.internetAvailableCL.visibility = View.GONE
binding.internetNotAvailableCL.visibility = View.VISIBLE
@@ -90,22 +183,45 @@ class HomeFragment : Fragment() {
hemoCubeViewModel.fireBaseBulkUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
Toast.makeText(
requireContext(),
"Test Results Uploaded Successfully",
Toast.LENGTH_SHORT
requireContext(), R.string.test_upload, Toast.LENGTH_SHORT
).show()
}
if (result == "Error") {
Toast.makeText(requireContext(), "Test Results Upload Failed", Toast.LENGTH_SHORT)
Toast.makeText(requireContext(), R.string.test_upload_failed, Toast.LENGTH_SHORT)
.show()
}
}
binding.btnLogout.setOnClickListener {
logoutUser(requireContext())
}
binding.uploadData.setOnClickListener {
showUploadDialog(requireContext())
}
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userData ->
val btnSaveLocalVisibility =
if (userData.any { it.testStatus == true }) View.VISIBLE else View.GONE
binding.downloadCSV.visibility = btnSaveLocalVisibility
binding.downloadCSV.setOnClickListener {
if (btnSaveLocalVisibility == View.VISIBLE) {
// Execute the action when the button is visible (testStatus is true for at least one user)
showDownloadDialog(requireContext())
} else {
// Handle the case when the button is not visible
Toast.makeText(
requireContext(),
"No test details stored locally",
Toast.LENGTH_SHORT
).show()
}
}
}
binding.btnNewKit.setOnClickListener {
with(sharedPreference.edit()) {
putString(Constants.KIT_NUMBER, "")
@@ -115,15 +231,305 @@ class HomeFragment : Fragment() {
startActivity(Intent(requireContext(), KitScanActivity::class.java))
requireActivity().finish()
}
binding.btnQuickCapture.setOnClickListener {
DataHolder.hemoCubeTestData = HemoCubeTestData()
val i = Intent(
requireContext().applicationContext, AssuranceControlsActivity::class.java
)
startActivity(i)
}
binding.downloadCSV.setOnClickListener {
showDownloadDialog(requireContext())
}
}
private fun checkForTokenAndUpdate() {
var accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString()
var password = sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "").toString()
var userID = sharedPreference.getString(Constants.DEVICE_ID_API, "").toString()
deviceId = sharedPreference.getString(Constants.DEVICE_ID, "").toString()
Log.e("idpass", userID)
Log.e("idpass", password)
Log.e("idpass", deviceId)
if (userID.isNotEmpty() && password.isNotEmpty()) {
if (!isTokenAvailable) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
} else {
if (isTokenExpired(accessToken)) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
} else {
isTokenAvailable = true
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.startPeriodicCheckUpdate()
hemoCubeViewModel.downloadClientCertificate()
}
}
}else if (deviceId.isNotEmpty()) {
fetchDeviceCredentials()
// This code will execute after credentials have been successfully fetched and stored.
userID = sharedPreference.getString("username", "").toString()
password = sharedPreference.getString("password", "").toString()
accessToken = sharedPreference.getString(Constants.ACCESS_TOKEN, "").toString()
if (accessToken.isEmpty()) {
hemoCubeViewModel.login(createLoginRequestData(userID, password))
} else {
// Continue with your existing logic if the token is not empty.
isTokenAvailable = true
hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData())
hemoCubeViewModel.uploadLogs()
hemoCubeViewModel.startPeriodicCheckUpdate()
}
} else {
Toast.makeText(
requireContext(),
"Contact Help and get your device provision done",
Toast.LENGTH_SHORT
).show()
}
hemoCubeViewModel.loginResponse.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
updateTokens(response)
}
is Result.Error -> {
response.exception.let { message ->
Toast.makeText(
activity,
"An error occurred in login: $message",
Toast.LENGTH_LONG
)
.show()
}
}
is Result.Loading -> {
}
else -> {}
}
}
hemoCubeViewModel.uploadLogs.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
// Toast.makeText(
// requireContext(),
// "Log uploaded ${response.data.data?.filename}",
// Toast.LENGTH_SHORT
// ).show()
}
is Result.Error -> {
response.exception.let { message ->
Toast.makeText(
activity,
"An error occurred in uploading logs: $message",
Toast.LENGTH_LONG
)
.show()
}
}
is Result.Loading -> {
}
else -> {}
}
}
hemoCubeViewModel.downloadcertificate.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
val url = response.data
val fileName = "nats_certificate.zip"
val downloadDirectory = "NATS"
val file = downloadFile(url, requireContext(), fileName, downloadDirectory)
Toast.makeText(requireContext(), "NATS certificate Downloaded", Toast.LENGTH_SHORT).show()
val unzipDirectoryPath = requireContext().getExternalFilesDir(null)?.absolutePath + "/$downloadDirectory"
unzip(file.absolutePath, unzipDirectoryPath)
Toast.makeText(requireContext(), "NATS certificate Extracted", Toast.LENGTH_SHORT).show()
}
is Result.Error -> {
response.exception.let { message ->
Toast.makeText(
activity,
"An error occurred in nats download: $message",
Toast.LENGTH_LONG
)
.show()
}
}
is Result.Loading -> {
}
else -> {}
}
}
hemoCubeViewModel.resultUpload.observe(viewLifecycleOwner) {
when (it) {
is Result.Success -> {
it.data.data?.forEach { id ->
id.rawData?.let { it1 ->
hemoCubeViewModel.updateMolbioFlag(
it1._id
)
}
}
}
is Result.Error -> {
binding.btnSubmit.visibility = View.VISIBLE
//Remove this line of code while deploying to IOCL
it.exception.let { message ->
Toast.makeText(activity, "An error occurred: $message", Toast.LENGTH_LONG)
.show()
}
}
else -> {}
}
}
}
private fun isTokenExpired(token: String): Boolean {
val parts = token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val decodedPayload = String(Base64.decode(parts[1], Base64.DEFAULT))
val jsonPayload = JSONObject(decodedPayload)
val exp = jsonPayload.optLong("exp", 0)
val currentTimeSeconds = System.currentTimeMillis() / 1000
return exp <= currentTimeSeconds
}
private fun updateTokens(response: Result.Success<LoginResponse>) {
with(sharedPreference.edit()) {
putString(Constants.ACCESS_TOKEN, response.data.data?.accessToken)
putString(Constants.NATS_TOKEN, response.data.data?.deviceUser?.natsToken)
putString(
Constants.NATS_TOKEN_EXPIRE_DATE, response.data.data?.deviceUser?.natsTokenExpiry
)
apply()
}
isTokenAvailable = true
}
private fun createLoginRequestData(userID: String, password: String): LoginRequest {
val pInfo = requireActivity().packageManager.getPackageInfo(
requireActivity().packageName, 0
)
val version = pInfo.versionName
return LoginRequest(
password = password, serialNumber = userID, username = userID, version = version
)
}
private fun createCheckUpdateRequestData(): CheckUpdateRequest {
val pInfo = requireActivity().packageManager.getPackageInfo(
requireActivity().packageName, 0
)
val version = pInfo.versionName
return CheckUpdateRequest(
currentVersion = version
)
}
private fun createDeviceUpdateRequestData(): DeviceUpdateRequest {
return DeviceUpdateRequest(
serial_no = sharedPreference.getString(Constants.DEVICE_ID, "")
)
}
private fun downloadFile(responseBody: ResponseBody, context: Context, fileName: String, downloadDirectory: String): File {
// Ensure the download directory exists
val fileDir = File(context.getExternalFilesDir(null), downloadDirectory)
if (!fileDir.exists()) {
fileDir.mkdirs()
}
val file = File(fileDir, fileName)
Log.d("Download", "Starting download to $file")
responseBody.byteStream().use { inputStream ->
FileOutputStream(file).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
// After download
Log.d("Download", "Download completed to ${file.absolutePath}")
return file
}
private fun unzip(zipFilePath: String, destDirectory: String) {
val destDir = File(destDirectory)
if (!destDir.exists()) {
destDir.mkdir()
}
ZipInputStream(FileInputStream(zipFilePath)).use { zipIn ->
var entry: ZipEntry? = zipIn.nextEntry
while (entry != null) {
val filePath = destDirectory + File.separator + entry.name
if (!entry.isDirectory) {
extractFile(zipIn, filePath)
} else {
val dir = File(filePath)
dir.mkdir()
}
zipIn.closeEntry()
entry = zipIn.nextEntry
}
}
}
private fun extractFile(zipIn: ZipInputStream, filePath: String) {
BufferedOutputStream(FileOutputStream(filePath)).use { bos ->
val bytesIn = ByteArray(4096)
var read: Int
while (zipIn.read(bytesIn).also { read = it } != -1) {
bos.write(bytesIn, 0, read)
}
}
}
private fun setUserId() {
binding.btnSubmit.setOnClickListener {
val userId = binding.userId.text.toString()
if (userId.length >= 18) {
val userData = UserData(_id = userId)
DataHolder.selectedTest = userData
findNavController().navigate(R.id.action_nav_home_to_mainActivity)
val bloodGroup = binding.etBloodGroup.text
if (userId.length >= 10 && !bloodGroup.equals("Select Blood Group") || !bloodGroup.isNullOrBlank()) {
hemoCubeViewModel.addUser(
HemoCubeTestData(
_id = userId,
bloodGroup = bloodGroup.toString(),
incubationTime = SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss", Locale.getDefault()
).format(Calendar.getInstance().time).toString()
)
)
// val userData = UserData(_id = userId)
// DataHolder.selectedTest = userData
// findNavController().navigate(R.id.action_nav_home_to_mainActivity)
} else {
val errorMessage = getString(R.string.user_id_error_message)
Toast.makeText(requireContext(), errorMessage, Toast.LENGTH_SHORT).show()
@@ -132,6 +538,48 @@ class HomeFragment : Fragment() {
}
}
private fun fetchDeviceCredentials() {
try {
val db = Firebase.firestore
// Ensure deviceId is not null or empty
val deviceId = sharedPreference.getString(Constants.DEVICE_ID, "").takeIf { it!!.isNotBlank() }
?: throw IllegalStateException("Device ID is missing or blank.")
val deviceRef = db.collection("devices").whereEqualTo("deviceId", deviceId)
deviceRef.get()
.addOnSuccessListener { documentSnapshot ->
if (!documentSnapshot.isEmpty) {
val deviceData = documentSnapshot.documents[0].toObject(DeviceData::class.java)
deviceData?.let { data ->
val username = data.username
val password = data.password
val natsToken = data.natsToken
// Log for debugging
Log.d("fetchDeviceCredentials", "Username: $username, Password: $password")
// Save credentials in SharedPreferences
with(sharedPreference.edit()) {
putString("username", username)
putString("password", password)
putString(Constants.NATS_TOKEN, natsToken)
apply()
}
hemoCubeViewModel.login(createLoginRequestData(username, password))
} ?: Log.e("fetchDeviceCredentials", "Failed to parse device data.")
} else {
Log.e("fetchDeviceCredentials", "Document does not exist.")
}
}
.addOnFailureListener { exception ->
Log.e("fetchDeviceCredentials", "Error fetching device data", exception)
}
} catch (e: Exception) {
Log.e("fetchDeviceCredentials", "Error in fetchDeviceCredentials", e)
}
}
private fun loadUserData() {
try {
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
@@ -155,10 +603,16 @@ class HomeFragment : Fragment() {
val bm = requireContext().getSystemService(BATTERY_SERVICE) as BatteryManager
val batLevel: Int = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
rvAdapter = view?.let {
UserListAdapter(requireContext(), hemoCubeViewModel, recyclerViewOptions,
it, batLevel, requireActivity())
}!!
rvAdapter = view?.let {
UserListAdapter(
requireContext(),
hemoCubeViewModel,
recyclerViewOptions,
it,
batLevel,
requireActivity()
)
}!!
binding.rvOrder.adapter = rvAdapter
rvAdapter.startListening()
@@ -176,14 +630,14 @@ class HomeFragment : Fragment() {
private fun logoutUser(context: Context) {
val builder = AlertDialog.Builder(context)
builder.setTitle("Log Out")
builder.setMessage("Do you want to LogOut the Application?")
builder.setPositiveButton("Yes") { dialog, _ ->
builder.setTitle(R.string.log_out)
builder.setMessage(R.string.do_log_out)
builder.setPositiveButton(R.string.yes) { dialog, _ ->
saveUserId("")
findNavController().navigate(R.id.action_nav_home_to_loginFragment)
dialog.dismiss()
}
builder.setNegativeButton("No") { dialog, _ ->
builder.setNegativeButton(R.string.no) { dialog, _ ->
dialog.dismiss()
}
@@ -206,8 +660,9 @@ class HomeFragment : Fragment() {
val partition = dateFormat.format(currentDate)
val searchTerm = partition + search
val searchField = field + "Search"
val query = Firebase.firestore.collection("patientData").orderBy(searchField).startAt(searchTerm)
.endAt(searchTerm + "\uf8ff")
val query =
Firebase.firestore.collection("patientData").orderBy(searchField).startAt(searchTerm)
.endAt(searchTerm + "\uf8ff")
query.get().addOnSuccessListener {
userSearchTrace.stop()
}
@@ -218,7 +673,14 @@ class HomeFragment : Fragment() {
batLevel = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
rvAdapter = view?.let {
UserListAdapter(requireContext(), hemoCubeViewModel, recyclerViewOptions, it, batLevel, requireActivity())
UserListAdapter(
requireContext(),
hemoCubeViewModel,
recyclerViewOptions,
it,
batLevel,
requireActivity()
)
}!!
binding.rvOrder.adapter = rvAdapter
rvAdapter.startListening()
@@ -272,9 +734,24 @@ class HomeFragment : Fragment() {
}
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val uploadDataVisibility = if (userDataList.any { !it.localFlag }) View.VISIBLE else View.GONE
val uploadDataVisibility =
if (userDataList.any { !it.localFlag && !it.molbioFlag && it.testStatus == true }) View.VISIBLE else View.GONE
binding.uploadData.visibility = uploadDataVisibility
}
hemoCubeViewModel.allKitTestData.observe(viewLifecycleOwner) { bufferData ->
val uploadDataVisibility =
if (bufferData.any { !it.localFlag }) View.VISIBLE else View.GONE
binding.uploadData.visibility = uploadDataVisibility
}
}
private fun checkUnprocessedCSVData() {
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val downloadDataVisibility =
if (userDataList.any { !it.isCSVCreated && it.testStatus == true }) View.VISIBLE else View.GONE
binding.downloadCSV.visibility = downloadDataVisibility
}
}
private fun showUploadDialog(context: Context) {
@@ -294,6 +771,23 @@ class HomeFragment : Fragment() {
dialog.show()
}
// private fun showDownloadDialog(context: Context) {
// val builder = AlertDialog.Builder(context)
// builder.setTitle(R.string.download_db_registration_title)
// builder.setMessage(R.string.download_db_registration_message)
//
// builder.setPositiveButton(R.string.downloadcsv) { dialog, _ ->
// downloadLocalDBData(dialog)
// }
//
// builder.setNegativeButton(R.string.cancel) { dialog, _ ->
// dialog.dismiss()
// }
//
// val dialog = builder.create()
// dialog.show()
// }
private fun uploadLocalDBData(dialog: DialogInterface) {
viewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
if (userDataList.isEmpty()) {
@@ -311,16 +805,71 @@ class HomeFragment : Fragment() {
}
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
val resultList = MolbioV2ResultRequest(mutableListOf(MolbioV2Result()))
userDataList.forEach { userData ->
if (!userData.localFlag) {
if (!userData.molbioFlag && isTokenAvailable) {
resultList.results?.add(
MolbioV2Result(
rawData = userData,
analysisId = userData._id,
analysisDate = "2024-02-08 16:33:56", //userData.reportUploadTime,
analysisStatus = userData.classificationResult,
thresholds = Constants.BUFFER_INTENSITY_THRESHOLDS[userData.deviceId].toString(),
interpretation = userData.classificationResult,
testId = userData._id,
testTime = "2024-02-08 16:33:56",//userData.testTime,
collectionTime = "2024-02-08 16:33:56",//userData.testTime,
expiryTime = "2024-02-08 16:33:56",//userData.testTime,
)
)
}
if(!userData.localFlag){
userData.localFlag = true
hemoCubeViewModel.bulkAddResultTestToDb(userData)
}
if (!userData.molbioFlag && isTokenAvailable) {
userData.molbioFlag = true
hemoCubeViewModel.uploadResult(resultList)
}
}
dialog.dismiss()
}
hemoCubeViewModel.allKitTestData.observe(viewLifecycleOwner) { kitDataList ->
kitDataList.forEach { userData ->
if (!userData.localFlag) {
userData.localFlag = true
hemoCubeViewModel.bulkAddResultKitTestToDb(userData)
}
}
dialog.dismiss()
}
}
// private fun downloadLocalDBData(dialog: DialogInterface) {
// hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
// val downloadList = mutableListOf<HemoCubeTestData>()
//
// userDataList.forEach { userData ->
// if (!userData.isCSVCreated) {
// downloadList.add(userData) // Add the userData to downloadList
// }
// }
//
// if (downloadList.isNotEmpty()) {
// // Call ViewModel function to create CSV with filtered data
// hemoCubeViewModel.createCSV(downloadList, requireContext())
// Toast.makeText(requireContext(), "Done", Toast.LENGTH_SHORT).show()
// } else {
// Toast.makeText(requireContext(), "Failed", Toast.LENGTH_SHORT).show()
// }
//
// dialog.dismiss()
// }
// }
private fun deleteIncompleteRegistrations(userDataList: List<UserData>) {
userDataList.forEach { userData ->
if (userData.csvPath.isEmpty()) {
@@ -331,7 +880,7 @@ class HomeFragment : Fragment() {
private fun deleteHemoCubeIncompleteRegistrations(userDataList: List<HemoCubeTestData>) {
userDataList.forEach { userData ->
if (userData.testTime?.isEmpty() == true) {
if (userData._id.isEmpty()) {
hemoCubeViewModel.deleteById(userData._id)
}
}
@@ -341,4 +890,119 @@ class HomeFragment : Fragment() {
super.onDestroyView()
_binding = null
}
}
private fun downloadCsv() {
context?.let { context ->
val success = homeViewModel.getLocalUserDataForCsv(context)
if (success) {
// Provide feedback to the user if needed
Toast.makeText(context, "CSV file downloaded successfully", Toast.LENGTH_SHORT)
.show()
} else {
// Handle the case where CSV file generation failed
Toast.makeText(context, "Failed to download CSV file", Toast.LENGTH_SHORT).show()
}
}
}
private fun showDownloadDialog(context: Context) {
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.download_db_registration_title)
builder.setMessage(R.string.download_db_registration_message)
builder.setPositiveButton(R.string.downloadcsv) { dialog, _ ->
downloadLocalDBData(dialog)
}
builder.setNegativeButton(R.string.cancel) { dialog, _ ->
dialog.dismiss()
}
val dialog = builder.create()
dialog.show()
}
private fun downloadLocalDBData(dialog: DialogInterface) {
var csvDownloaded = false
hemoCubeViewModel.allUserData.observe(viewLifecycleOwner) { userDataList ->
try {
if (!csvDownloaded) {
val downloadList = mutableListOf<HemoCubeTestData>()
userDataList.forEach { userData ->
if (userData.testStatus == true) {
// if (!userData.isCSVCreated) {
downloadList.add(userData) // Add the userData to downloadList
}
// }
}
if (downloadList.isNotEmpty()) {
// Call ViewModel function to create CSV with filtered data
hemoCubeViewModel.createCSV(downloadList, requireContext())
csvDownloaded = true
Toast.makeText(
requireContext(),
"CSV file downloaded successfully",
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
requireContext(),
"No data to download",
Toast.LENGTH_SHORT
)
.show()
}
}
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(requireContext(), "Error downloading CSV file", Toast.LENGTH_SHORT)
.show()
} finally {
dialog.dismiss()
}
}
}
private fun getDeviceId() {
val handler = activity as? DeviceCommunicationHandler
handler?.sendAndListenToDevice(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val receivedData = String(it, Charset.forName("UTF-8"))
// Assuming the device ID is the full content of the received data. Adjust if needed.
deviceId =
extractDeviceId(receivedData) // Implement this method based on your data format.
if (deviceId.isNotEmpty()) {
// Store the deviceId in SharedPreferences
with(sharedPreference.edit()) {
putString(Constants.DEVICE_ID, deviceId)
apply()
}
// Optionally, you can update UI or proceed with further logic now that you have the device ID
activity?.runOnUiThread {
// Update your UI or trigger next steps here
}
}
}
}
override fun onUsbError(e: Exception?) {
// Handle USB communication error
}
})
}
fun extractDeviceId(receivedData: String): String {
// Example based on the format "SNS HPP1-3038 SNE"
val regex = "SNS (\\w+) SNE".toRegex()
val matchResult = regex.find(receivedData)
return matchResult?.groups?.get(1)?.value ?: ""
}
}

View File

@@ -1,49 +1,75 @@
package com.example.hpostesting.presentation.dashboard
import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.hpostesting.presentation.dashboard.ui.slideshow.SlideshowViewModel
import `in`.sminnovations.hpostesting.databinding.FragmentSlideshowBinding
import android.widget.Toast
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.example.hpostesting.data.constant.LanguageManager
import `in`.sminnovations.hpostesting.R
private var isLanguageChanged = false
class SlideshowFragment : Fragment() {
class SlideshowFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
val preferenceScreen = preferenceManager.createPreferenceScreen(requireContext())
private var _binding: FragmentSlideshowBinding? = null
val languagePreference = ListPreference(requireContext())
languagePreference.key = "language_preference"
languagePreference.title = getString(R.string.app_language)
languagePreference.summary = getString(R.string.select_language)
languagePreference.entries = arrayOf("English", "Kannada", "Hindi")
languagePreference.entryValues = arrayOf("en", "kn", "hi")
languagePreference.setDefaultValue("en")
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
languagePreference.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _, newValue ->
val languageCode = newValue as String
updateLanguage(requireContext(), languageCode)
true
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
val slideshowViewModel =
ViewModelProvider(this).get(SlideshowViewModel::class.java)
preferenceScreen.addPreference(languagePreference)
setPreferenceScreen(preferenceScreen)
_binding = FragmentSlideshowBinding.inflate(inflater, container, false)
val root: View = binding.root
// App Version Preference
val appVersionPreference = Preference(requireContext())
appVersionPreference.title = "App Version"
appVersionPreference.summary = getAppVersion(requireContext()) + " [ " + getAppEnvironment(requireContext()) + " ]"
val pInfo = requireActivity().packageManager.getPackageInfo(
requireActivity().packageName, 0
)
val version = pInfo.versionName
binding.textSlideshow.text = version
preferenceScreen.addPreference(languagePreference)
preferenceScreen.addPreference(appVersionPreference)
setPreferenceScreen(preferenceScreen)
// val textView: TextView = binding.textSlideshow
// slideshowViewModel.text.observe(viewLifecycleOwner) {
// textView.text = it
// }
return root
if (isLanguageChanged) {
Toast.makeText(requireContext(), R.string.language_update, Toast.LENGTH_SHORT).show()
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
private fun updateLanguage(context: Context, languageCode: String) {
LanguageManager.persistLanguagePreference(context, languageCode)
LanguageManager.setLocale(context, languageCode)
requireActivity().recreate() // Recreate activity to apply language changes
isLanguageChanged = true
}
private fun getAppVersion(context: Context): String {
return try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
pInfo.versionName
} catch (e: PackageManager.NameNotFoundException) {
"N/A"
}
}
private fun getAppEnvironment(context: Context): String {
return try {
val pInfo = context.packageManager.getPackageInfo(context.packageName, 0)
pInfo.packageName.substringAfterLast('.')
} catch (e: PackageManager.NameNotFoundException) {
"N/A"
}
}
}

View File

@@ -23,7 +23,8 @@ class LoginFragment : Fragment() {
savedInstanceState: Bundle?
): View {
_binding = FragmentLoginBinding.inflate(inflater, container, false)
sharedPreference = requireContext().getSharedPreferences("PREFERENCE_NAME", Context.MODE_PRIVATE)
sharedPreference =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
return binding.root
}
@@ -47,10 +48,10 @@ class LoginFragment : Fragment() {
performLogin(loginId, password)
} else {
if (loginId.isBlank()) {
binding.loginId.error = "Please enter a proper Login ID"
binding.loginId.error = R.string.enter_proper_login_id.toString()
}
if (password.isBlank()) {
binding.password.error = "Please enter a proper password"
binding.password.error = R.string.enter_proper_password.toString()
}
}
}
@@ -72,10 +73,10 @@ class LoginFragment : Fragment() {
navigateToHomeFragment()
return
} else {
Toast.makeText(requireContext(), "Wrong Password", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), R.string.wrong_password, Toast.LENGTH_SHORT).show()
}
}
Toast.makeText(requireContext(), "Wrong UserID", Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), R.string.wrong_user_id, Toast.LENGTH_SHORT).show()
}
private fun saveUserId(userId: String) {

View File

@@ -1,13 +0,0 @@
package com.example.hpostesting.presentation.dashboard.ui.home
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class HomeViewModel : ViewModel() {
private val _text = MutableLiveData<String>().apply {
value = "This is home Fragment"
}
val text: LiveData<String> = _text
}

View File

@@ -0,0 +1,187 @@
package com.example.hpostesting.presentation.deviceinfo
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.SharedPreferences
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.api.DeviceCommunicationHandler
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityDeviceBinding
@Suppress("MemberVisibilityCanBePrivate")
@AndroidEntryPoint
class DeviceActivity : AppCompatActivity(), DeviceCommunicationHandler {
private lateinit var binding: ActivityDeviceBinding
private val deviceViewModel by viewModels<DeviceViewModel>()
private var myMenu: Menu? = null
private lateinit var sharedPreferences: SharedPreferences
private lateinit var mDriver: UsbSerialDriver
private var mConnection: UsbDeviceConnection? = null
lateinit var mService: UsbService
private var deviceId = ""
private val TAG = "Calibration"
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
synchronized(this) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device?.apply {
connectUsb(true)
DataHolder.usbConnected.postValue(true)
}
} else {
onErrorReported("permission denied for device")
DataHolder.usbConnected.postValue(true)
}
}
}
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as UsbService.UsbServiceBinder
mService = binder.getService()
deviceViewModel.isServiceConnected = true
mConnection.let { mService.connect(mDriver, mConnection!!) }
moveToNext()
}
override fun onServiceDisconnected(arg0: ComponentName) {
deviceViewModel.isServiceConnected = false
}
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDeviceBinding.inflate(layoutInflater)
setContentView(binding.root)
// setSupportActionBar(binding.myToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setupListener()
connectUsb(false)
}
private fun setupListener() {
DataHolder.usbConnected.observe(this) {
Log.d("USB OBSERVE", "HemoCube called -> $it")
if (it) {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24)
} else {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
}
}
}
open fun connectUsb(permissionGranted: Boolean) {
Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
if (availableDrivers.isEmpty()) {
onErrorReported("No Device is Connected")
} else {
mDriver = availableDrivers[0]
mConnection = manager.openDevice(mDriver.device)
if (mConnection == null) {
requestUserPermission(manager, mDriver.device)
} else {
setupService()
}
}
}
fun onErrorReported(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
if (!isFinishing) onBackPressed()
}
private fun moveToNext() {
if (supportFragmentManager.isDestroyed) return
supportFragmentManager.beginTransaction().replace(binding.fgDevice.id, DeviceFragment())
.commit()
}
@SuppressLint("MutableImplicitPendingIntent")
private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
val mPendingIntent: PendingIntent
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
mPendingIntent = PendingIntent.getBroadcast(
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
mPendingIntent = PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}
fun setupService() {
val intent = Intent(this, UsbService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.my_menu, menu)
return true
}
override fun onDestroy() {
super.onDestroy()
if (deviceViewModel.isServiceConnected) {
mService.disconnect()
unbindService(connection)
deviceViewModel.isServiceConnected = false
}
}
override fun sendAndListenToDevice(command: HemoCubeCommands, listener: UsbServiceListener) {
mService.sendAndListenToHemoCube(command = HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND, listener)
}
}

View File

@@ -0,0 +1,109 @@
package com.example.hpostesting.presentation.deviceinfo
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.databinding.FragmentDeviceBinding
class DeviceFragment : Fragment() {
private lateinit var binding: FragmentDeviceBinding
private val deviceViewModel: HemoCubeViewModel by activityViewModels()
private lateinit var sharedPreferences: SharedPreferences
private var deviceId = ""
private var startListening = MutableLiveData(false)
private var resultData: String = ""
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
): View {
binding = FragmentDeviceBinding.inflate(inflater, container, false)
sharedPreferences =
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
}
private fun initViews() {
deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString()
listenToHemoCube()
getDeviceId()
}
private fun getDeviceId() {
deviceViewModel.progressBar.postValue(true)
(activity as DeviceActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
object : UsbServiceListener {
@SuppressLint("SetTextI18n")
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
deviceViewModel.messages.postValue(stringData)
binding.tvSubtitle4.text = "Device ID: ${stringData}"
}
}
override fun onUsbError(e: Exception?) {
deviceViewModel.progressBar.postValue(false)
}
})
}
private fun listenToHemoCube() {
val fullReadOutput = StringBuilder()
startListening.postValue(true)
try {
(activity as DeviceActivity).mService.listenToHemoCube(object : UsbServiceListener {
@SuppressLint("SetTextI18n")
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
deviceViewModel.messages.postValue(stringData)
fullReadOutput.append(stringData)
resultData += stringData
binding.tvSubtitle4.text = "Device ID : ${resultData}"
if (stringData.contains("SN")) {
val slData = stringData.split(" ")
if (slData.size > 1) {
val hardwareId = slData[1].trim()
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()
}
// binding.tvSubtitle4.text = hardwareId
}
}
}
}
override fun onUsbError(e: Exception?) {
deviceViewModel.progressBar.postValue(false)
}
})
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
}
}
}

View File

@@ -0,0 +1,26 @@
package com.example.hpostesting.presentation.deviceinfo
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.hpostesting.data.dao.HemoCubeDao
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.repository.Repository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@HiltViewModel
class DeviceViewModel @Inject constructor(
private val hemoCubeDao: HemoCubeDao,
private val repository: Repository,
) : ViewModel() {
var isServiceConnected = false
val progressBar = MutableLiveData(false)
val messages = MutableLiveData<String>()
// private val _networkStatusLiveData = NetworkStatusLiveData(context)
// val allUserData = hemoCubeDao.getAll()
val deviceData = MutableLiveData<DeviceData?>()
// val networkStatusLiveData: LiveData<Boolean>
// get() = _networkStatusLiveData
val fireBaseUpload = MutableLiveData<String>()
val fireBaseBulkUpload = MutableLiveData<String>()
}

View File

@@ -0,0 +1,190 @@
package com.example.hpostesting.presentation.deviceprovision
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbManager
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import android.view.Menu
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.example.hpostesting.data.DataHolder
import com.example.hpostesting.data.api.DeviceCommunicationHandler
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.constant.LanguageManager
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.testRight.UsbService
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialProber
import dagger.hilt.android.AndroidEntryPoint
import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.ActivityAutoDacBinding
@AndroidEntryPoint
class DeviceProvisionActivity : AppCompatActivity(), DeviceCommunicationHandler {
private lateinit var binding: ActivityAutoDacBinding
val viewModel: DeviceProvisionViewModel by viewModels()
private var myMenu: Menu? = null
private lateinit var mDriver: UsbSerialDriver
private var mConnection: UsbDeviceConnection? = null
lateinit var mService: UsbService
private val TAG = "HemoCube"
private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
synchronized(this) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device?.apply {
connectUsb(true)
DataHolder.usbConnected.postValue(true)
}
} else {
onErrorReported("permission denied for device")
DataHolder.usbConnected.postValue(true)
}
}
}
}
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder = service as UsbService.UsbServiceBinder
mService = binder.getService()
viewModel.isServiceConnected = true
mConnection.let { mService.connect(mDriver, mConnection!!) }
moveToNext()
}
override fun onServiceDisconnected(arg0: ComponentName) {
viewModel.isServiceConnected = false
}
}
override fun attachBaseContext(newBase: Context?) {
val languageCode = LanguageManager.getSavedLanguage(newBase!!)
LanguageManager.setLocale(newBase, languageCode)
super.attachBaseContext(newBase)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAutoDacBinding.inflate(layoutInflater)
setContentView(binding.root)
// setSupportActionBar(binding.myToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
setupListener()
connectUsb(false)
}
private fun setupListener() {
DataHolder.usbConnected.observe(this) {
Log.d("USB OBSERVE", "HemoCube called -> $it")
if (it) {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_24)
} else {
myMenu?.get(0)?.icon =
ContextCompat.getDrawable(this, R.drawable.ic_baseline_usb_off_24)
Toast.makeText(this, "Device is Disconnected", Toast.LENGTH_SHORT).show()
}
}
}
fun connectUsb(permissionGranted: Boolean) {
Log.d(TAG, "connectUsb() called, permission variable = $permissionGranted")
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
if (availableDrivers.isEmpty()) {
onErrorReported("No Device is Connected")
} else {
mDriver = availableDrivers[0]
mConnection = manager.openDevice(mDriver.device)
if (mConnection == null) {
requestUserPermission(manager, mDriver.device)
} else {
setupService()
}
}
}
fun onErrorReported(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
if (!isFinishing) onBackPressed()
}
private fun moveToNext() {
if (supportFragmentManager.isDestroyed) return
supportFragmentManager.beginTransaction()
.replace(binding.fgAutoDac.id, DeviceProvisionFragment()).commit()
}
@SuppressLint("MutableImplicitPendingIntent")
private fun requestUserPermission(manager: UsbManager, device: UsbDevice) {
val mPendingIntent: PendingIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
PendingIntent.getBroadcast(
this, 0, Intent(Constants.HEMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE
)
} else {
PendingIntent.getBroadcast(
this,
0,
Intent(Constants.HEMOCUBE_USB_PERMISSION),
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
)
}
val filter = IntentFilter(Constants.HEMOCUBE_USB_PERMISSION)
registerReceiver(broadcastReceiver, filter)
manager.requestPermission(device, mPendingIntent)
}
fun setupService() {
val intent = Intent(this, UsbService::class.java)
bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.my_menu, menu)
return true
}
override fun onDestroy() {
super.onDestroy()
if (viewModel.isServiceConnected) {
mService.disconnect()
unbindService(connection)
viewModel.isServiceConnected = false
}
}
override fun sendAndListenToDevice(command: HemoCubeCommands, listener: UsbServiceListener) {
mService.sendAndListenToHemoCube(command = HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND, listener)
}
}

View File

@@ -0,0 +1,212 @@
package com.example.hpostesting.presentation.deviceprovision
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.constant.Constants
import com.example.hpostesting.data.constant.HemoCubeCommands
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.presentation.UsbServiceListener
import com.example.hpostesting.presentation.dashboard.DashboardActivity
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import `in`.sminnovations.hpostesting.databinding.FragmentDeviceProvisionBinding
class DeviceProvisionFragment : Fragment() {
private var resultData: String = ""
private lateinit var binding: FragmentDeviceProvisionBinding
private val viewModel: DeviceProvisionViewModel by activityViewModels()
private lateinit var sharedPreferences: SharedPreferences
private lateinit var deviceProvisionResponse: String
private var currentDeviceData: DeviceData? = null
private var isOnline = false
val deviceData = MutableLiveData<DeviceData?>()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
binding = FragmentDeviceProvisionBinding.inflate(inflater, container, false)
sharedPreferences = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
observeViewModel()
}
private fun initViews() {
listenToHemoCube()
getDeviceId()
binding.btnSubmit.setOnClickListener {
viewModel.deviceProvision(
DeviceProvisionRequest(
email = Constants.deviceProvisionEmail,
password = Constants.deviceProvisionPassword,
serialNumber = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
uid = sharedPreferences.getString(Constants.DEVICE_ID, "").toString()
)
)
}
}
private fun observeViewModel() {
viewModel.deviceData.observe(viewLifecycleOwner) {
currentDeviceData = it
}
viewModel.deviceProvisionResponse.observe(viewLifecycleOwner) { response ->
when (response) {
is Result.Success -> {
if (response.data.result == "Success") {
with(sharedPreferences.edit()) {
putString(
Constants.DEVICE_ID_API,
response.data.data?.credentials?.username
)
putString(
Constants.DEVICE_PASSWORD_API,
response.data.data?.credentials?.password
)
putString(
Constants.NATS_TOKEN,
response.data.data?.device?.deviceUser?.natsToken
)
response.data.data?.device?.deviceUser?.natsToken?.let {
Log.e("natstoken",
it
)
}
putString(
Constants.NATS_TOKEN_EXPIRE_DATE,
response.data.data?.device?.deviceUser?.natsTokenExpiry
)
apply()
}
startActivity(
Intent(
requireContext(),
DashboardActivity::class.java
)
)
Toast.makeText(
activity, "Device registered successfully", Toast.LENGTH_LONG
).show()
viewModel.addDeviceProvisionDataToDb(
DeviceData(
deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString(),
username = response.data.data?.credentials?.username.toString(),
password = response.data.data?.credentials?.password.toString(),
deviceProvisionResponse = response.data.data.toString(),
natsToken = response.data.data?.device?.deviceUser?.natsToken.toString(),
natsTokenExpiry = response.data.data?.device?.deviceUser?.natsTokenExpiry.toString()
)
)
// viewModel.addDeviceId(DeviceData(deviceId = sharedPreferences.getString(Constants.DEVICE_ID, "").toString()))
Log.e("idpass",response.toString())
Log.e("idpass",response.data.data?.credentials?.username.toString())
Log.e("idpass",deviceProvisionResponse)
} else {
Toast.makeText(
activity,
"An error in device provision: ${response.data.message}",
Toast.LENGTH_LONG
).show()
binding.btnSubmit.visibility = View.VISIBLE
}
}
is Result.Error -> {
response.exception.let { message ->
Toast.makeText(
activity,
"An error occurred in device provision: $message",
Toast.LENGTH_LONG
).show()
}
}
is Result.Loading -> {
binding.btnSubmit.visibility = View.GONE
}
else -> {}
}
}
viewModel.fireBaseUpload.observe(viewLifecycleOwner) { result ->
if (result == "Success") {
Toast.makeText(
activity, "Device provision successfully uploaded to firebase", Toast.LENGTH_LONG
).show()
}
}
}
private fun getDeviceId() {
(activity as DeviceProvisionActivity).mService.sendAndListenToHemoCube(
HemoCubeCommands.DEVICE_CONFIGURATION_COMMAND,
object : UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {}
override fun onUsbError(e: Exception?) {}
})
}
private fun listenToHemoCube() {
val fullReadOutput = StringBuilder()
try {
(activity as DeviceProvisionActivity).mService.listenToHemoCube(object :
UsbServiceListener {
override fun onUsbRead(data: ByteArray?) {
data?.let {
val stringData = String(it)
fullReadOutput.append(stringData)
resultData += stringData
handleUsbData()
}
}
override fun onUsbError(e: Exception?) {}
})
} catch (e: Exception) {
Firebase.crashlytics.recordException(e)
}
}
fun extractV2HardwareId(input: String): String? {
val pattern = Regex("SNS\\s*(.*?)\\s*SNE")
val matchResult: MatchResult? = pattern.find(input)
return matchResult?.groups?.get(1)?.value
}
private fun handleUsbData() {
when {
resultData.contains("SNE") -> {
val hardwareId = extractV2HardwareId(resultData)
if (!hardwareId.isNullOrBlank()) {
with(sharedPreferences.edit()) {
putString(Constants.DEVICE_ID, hardwareId)
apply()
}
activity?.runOnUiThread {
binding.btnSubmit.visibility = View.VISIBLE
}
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
package com.example.hpostesting.presentation.deviceprovision
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.hpostesting.data.Result
import com.example.hpostesting.data.model.Response
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse
import com.example.hpostesting.data.model.patient.DeviceData
import com.example.hpostesting.data.repository.Repository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class DeviceProvisionViewModel @Inject constructor(
private val repository: Repository,
) : ViewModel() {
var isServiceConnected = false
val deviceProvisionResponse = MutableLiveData<Result<DeviceProvisionResponse>>()
val fireBaseUpload = MutableLiveData<String>()
val fireBaseBulkUpload = MutableLiveData<String>()
val deviceData = MutableLiveData<DeviceData?>()
fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest) = viewModelScope.launch {
deviceProvisionResponse.postValue(Result.Loading())
repository.deviceProvision(deviceProvisionRequest).let {
deviceProvisionResponse.postValue(it)
}
}
fun addDeviceId(data: DeviceData) {
viewModelScope.launch {
try {
repository.uploadDeviceId(data)
fireBaseUpload.postValue("Successfully device Id uploaded to firebase")
} catch (e: Exception) {
// Log.e("Testdb", "Exception during data upload: ${e.message}")
fireBaseUpload.postValue("Error")
}
}
}
fun addDeviceProvisionDataToDb(data: DeviceData) {
viewModelScope.launch {
try {
when (val response = repository.getDeviceResponse(data)) {
is Response.Success -> {
// Log.i("Testdb", "Data uploaded to Firestore successfully")
fireBaseUpload.postValue("Successfully device response uploaded to firebase")
}
is Response.Error -> {
// Log.e("Testdb", "Error uploading data to Firestore: $response")
fireBaseUpload.postValue("Error")
}
else -> {}
}
} catch (e: Exception) {
// Log.e("Testdb", "Exception during data upload: ${e.message}")
fireBaseUpload.postValue("Error")
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More