diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9bc14ac..b5fbefa 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,6 +48,11 @@ android:name="com.example.hpostesting.presentation.autodac.AutoDacActivity" android:exported="false" android:theme="@style/Theme.HPOS.NoActionBar" /> + >> = mapOf( "HC-V1-005" to listOf( listOf(1.5,0.0), diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt index 62f1f86..55b49b8 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/DashboardActivity.kt @@ -25,6 +25,7 @@ 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 @@ -127,6 +128,11 @@ class DashboardActivity : AppCompatActivity(), IDataCollector { ) 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 { diff --git a/app/src/main/java/com/example/hpostesting/presentation/jig/JigActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/jig/JigActivity.kt new file mode 100644 index 0000000..e6848f1 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/jig/JigActivity.kt @@ -0,0 +1,180 @@ +package com.example.hpostesting.presentation.jig + +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.ActivityJigBinding + +@AndroidEntryPoint +class JigActivity: AppCompatActivity(){ + private lateinit var binding: ActivityJigBinding + val viewModel: JigViewModel by viewModels() + private var myMenu: Menu? = null + + private lateinit var mDriver: UsbSerialDriver + private var mConnection: UsbDeviceConnection? = null + lateinit var mService: UsbService + + private val TAG = "JIG" + + 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 = ActivityJigBinding.inflate(layoutInflater) + setContentView(binding.root) +// setSupportActionBar(binding.myToolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) +// setupListener() +// connectUsb(false) + + moveToNext() + } + + 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.fgJig.id, JigFragment()) + .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 + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/jig/JigFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/jig/JigFragment.kt new file mode 100644 index 0000000..83f9a35 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/jig/JigFragment.kt @@ -0,0 +1,180 @@ +package com.example.hpostesting.presentation.jig + +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import android.text.Editable +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.EditText +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.MutableLiveData +import com.example.hpostesting.data.model.patient.DeviceData +import com.journeyapps.barcodescanner.ScanContract +import com.journeyapps.barcodescanner.ScanIntentResult +import com.zebra.barcode.sdk.sms.ConfigurationUpdateEvent +import com.zebra.scannercontrol.DCSSDKDefs +import com.zebra.scannercontrol.DCSScannerInfo +import com.zebra.scannercontrol.FirmwareUpdateEvent +import com.zebra.scannercontrol.IDcsSdkApiDelegate +import com.zebra.scannercontrol.SDKHandler +import `in`.sminnovations.hpostesting.databinding.FragmentJigBinding + +class JigFragment: Fragment(), IDcsSdkApiDelegate { + private lateinit var binding: FragmentJigBinding + private val jigViewModel: JigViewModel by activityViewModels() + private lateinit var sharedPreferences: SharedPreferences + private var currentDeviceData: DeviceData? = null + private var resultData: String = "" + private val messages = MutableLiveData() + private var startListening = MutableLiveData(false) + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, + ): View { + binding = FragmentJigBinding.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 + + binding.btnScanNow.setOnClickListener { + pullTrigger() + } + } + + private fun observeViewModel() { + + jigViewModel.deviceData.observe(viewLifecycleOwner) { + currentDeviceData = it + } + + messages.observe(viewLifecycleOwner) { + binding.tvScanText.text = it + } + + jigViewModel.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 + } + } + + fun parseData(inputData: List): List> { + val pattern = Regex("([A-Z]+)\\s(\\d+)") + val parsedData = mutableListOf>() + + 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() + } + + var sdkHandler: SDKHandler? = null + var editBarcode: EditText? = null + var mScannerInfoList = ArrayList() + + private val barcodeLauncher = registerForActivityResult( + ScanContract() + ) { result: ScanIntentResult -> + if (result.contents.isNullOrEmpty()) { +// Toast.makeText(this, R.string.cancelled_unable_scan, Toast.LENGTH_LONG).show() + } else { +// Log.d(TAG, result.contents) + processScannedData(result.contents) + } + } + + private fun processScannedData(contents: String) { + binding.tvScanText.setText(contents) + } + + 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 = " 1 " + val outXML = StringBuilder() + val result: DCSSDKDefs.DCSSDK_RESULT = + sdkHandler!!.dcssdkExecuteCommandOpCodeInXMLForScanner( + DCSSDKDefs.DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_PULL_TRIGGER, inXML, outXML, mScannerInfoList[0].scannerID // Ensure you're using the correct scanner ID + ) + if (result == DCSSDKDefs.DCSSDK_RESULT.DCSSDK_RESULT_SUCCESS) { + Log.d("Scanning", "Success") + } else if (result == DCSSDKDefs.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.") + } + } + + override fun dcssdkEventBarcode(barcodeData: ByteArray?, barcodeType: Int, fromScannerID: Int) { + val result = String(barcodeData!!) + Log.d("BARCODE", result) + activity?.runOnUiThread { + val editableResult: Editable = Editable.Factory.getInstance().newEditable(result) + binding.tvScanText.text = editableResult + } + } + + 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?) {} + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/jig/JigViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/jig/JigViewModel.kt new file mode 100644 index 0000000..64970b7 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/jig/JigViewModel.kt @@ -0,0 +1,49 @@ +package com.example.hpostesting.presentation.jig + +import android.content.Context +import android.content.SharedPreferences +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +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 JigViewModel @Inject constructor( + private val hemoCubeDao: HemoCubeDao, + private val repository: Repository, + context: Context +) : ViewModel() { + var isServiceConnected = false + val progressBar = MutableLiveData(false) + val messages = MutableLiveData() + private val sharedPreference: SharedPreferences = + context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) + + val deviceData = MutableLiveData() + val fireBaseUpload = MutableLiveData() + + 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") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_jig.xml b/app/src/main/res/layout/activity_jig.xml new file mode 100644 index 0000000..a0884e4 --- /dev/null +++ b/app/src/main/res/layout/activity_jig.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_jig.xml b/app/src/main/res/layout/fragment_jig.xml new file mode 100644 index 0000000..9967b62 --- /dev/null +++ b/app/src/main/res/layout/fragment_jig.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + +