diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a586d9a..153965f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,6 +43,10 @@ android:name="com.example.hpostesting.presentation.autodac.AutoDacActivity" android:exported="false" android:theme="@style/Theme.HPOS.NoActionBar" /> + = android.os.Build.VERSION_CODES.S) { - mPendingIntent = PendingIntent.getBroadcast( + 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 ) } else { - mPendingIntent = PendingIntent.getBroadcast( + PendingIntent.getBroadcast( this, 0, Intent(Constants.HOMOCUBE_USB_PERMISSION), diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt index 5698fd5..bdb2bb2 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/GalleryFragment.kt @@ -13,6 +13,7 @@ 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.deviceprovision.DeviceProvisionActivity import com.example.hpostesting.presentation.diagnostics.DiagnosticsActivity import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel import `in`.sminnovations.hpostesting.databinding.FragmentGalleryBinding @@ -52,7 +53,7 @@ class GalleryFragment : Fragment() { } binding.btnDeviceProvision.setOnClickListener { -// hemoCubeViewModel.deviceProvision() + startActivity(Intent(requireContext(), DeviceProvisionActivity::class.java)) } binding.btnSubmit.setOnClickListener { diff --git a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt index 500924f..3793c62 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/dashboard/HomeFragment.kt @@ -130,17 +130,13 @@ class HomeFragment : Fragment() { showUploadDialog(requireContext()) } binding.btnNewKit.setOnClickListener { -// with(sharedPreference.edit()) { -// putString(Constants.KIT_NUMBER, "") -// putInt(Constants.KIT_COUNT, 0) -// apply() -// } -// startActivity(Intent(requireContext(), KitScanActivity::class.java)) -// requireActivity().finish() - val password = sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "810516d8f4c7").toString() - val userID = sharedPreference.getString(Constants.DEVICE_ID_API, "HCV-000-3001").toString() - hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData()) - hemoCubeViewModel.login(createLoginRequestData(userID, password)) + with(sharedPreference.edit()) { + putString(Constants.KIT_NUMBER, "") + putInt(Constants.KIT_COUNT, 0) + apply() + } + startActivity(Intent(requireContext(), KitScanActivity::class.java)) + requireActivity().finish() } } @@ -149,13 +145,13 @@ class HomeFragment : Fragment() { val natsToken = sharedPreference.getString(Constants.NATS_TOKEN, "").toString() val natsTokenExpireDate = sharedPreference.getString(Constants.NATS_TOKEN_EXPIRE_DATE, "").toString() - val password = sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "810516d8f4c7").toString() - val userID = sharedPreference.getString(Constants.DEVICE_ID_API, "HCV-000-3001").toString() + val password = sharedPreference.getString(Constants.DEVICE_PASSWORD_API, "").toString() + val userID = sharedPreference.getString(Constants.DEVICE_ID_API, "").toString() if (userID.isNotEmpty() && password.isNotEmpty()) { if (accessToken.isEmpty() && natsToken.isEmpty()) { hemoCubeViewModel.login(createLoginRequestData(userID, password)) } else { - if (true) { + if (isTokenExpired(accessToken)) { hemoCubeViewModel.login(createLoginRequestData(userID, password)) } else { hemoCubeViewModel.deviceUpdate(createDeviceUpdateRequestData()) @@ -197,6 +193,7 @@ class HomeFragment : Fragment() { Toast.makeText( requireContext(), response.data.toString(), Toast.LENGTH_SHORT ).show() + } is Result.Error -> { @@ -240,7 +237,7 @@ class HomeFragment : Fragment() { } } - fun isTokenExpired(token: String): Boolean { + 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) diff --git a/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionActivity.kt new file mode 100644 index 0000000..aae6d13 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionActivity.kt @@ -0,0 +1,184 @@ +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.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 DeviceProvisionActivity : AppCompatActivity() { + 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.HOMOCUBE_USB_PERMISSION), PendingIntent.FLAG_MUTABLE + ) + } else { + PendingIntent.getBroadcast( + this, + 0, + Intent(Constants.HOMOCUBE_USB_PERMISSION), + PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE + ) + } + + val filter = IntentFilter(Constants.HOMOCUBE_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/deviceprovision/DeviceProvisionFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionFragment.kt new file mode 100644 index 0000000..e3bcf55 --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionFragment.kt @@ -0,0 +1,168 @@ +package com.example.hpostesting.presentation.deviceprovision + +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 android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +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.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 + 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.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 + ) + 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() + } else { + Toast.makeText( + activity, + "An error occurred 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 -> {} + } + } + } + + + private fun getDeviceId() { + (activity as DeviceProvisionActivity).mService.sendAndListenToHemoCube( + HemoCubeCommands.getDeviceId, + 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) + } + } + + private fun handleUsbData() { + when { + resultData.contains("SNE") -> { + val pattern = Regex("HPP1-\\d{4}-\\d{4}") + val matchResult = pattern.find(resultData) + val hardwareId = matchResult?.value + + if (hardwareId.toString().length == 14) { + with(sharedPreferences.edit()) { + putString(Constants.DEVICE_ID, hardwareId) + apply() + } + activity?.runOnUiThread { + binding.btnSubmit.visibility = View.VISIBLE + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionViewModel.kt new file mode 100644 index 0000000..8c4d0ac --- /dev/null +++ b/app/src/main/java/com/example/hpostesting/presentation/deviceprovision/DeviceProvisionViewModel.kt @@ -0,0 +1,36 @@ +package com.example.hpostesting.presentation.deviceprovision + +import android.content.Context +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.Result +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest +import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse +import com.example.hpostesting.data.repository.DatabaseRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class DeviceProvisionViewModel @Inject constructor( + private val databaseRepository: DatabaseRepository, + context: Context +) : ViewModel() { + + var isServiceConnected = false + private val _networkStatusLiveData = NetworkStatusLiveData(context) + val networkStatusLiveData: LiveData + get() = _networkStatusLiveData + + val deviceProvisionResponse = MutableLiveData>() + + fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest) = viewModelScope.launch { + deviceProvisionResponse.postValue(Result.Loading()) + databaseRepository.deviceProvision(deviceProvisionRequest).let { + deviceProvisionResponse.postValue(it) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt index 1b8e23c..6112ee8 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/hemocube/HemoCubeViewModel.kt @@ -20,8 +20,6 @@ import com.example.hpostesting.data.constant.Constants import com.example.hpostesting.data.dao.HemoCubeBufferDao import com.example.hpostesting.data.dao.HemoCubeDao 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.log.UploadLogsResponse import com.example.hpostesting.data.model.login.LoginRequest import com.example.hpostesting.data.model.login.LoginResponse @@ -73,8 +71,6 @@ class HemoCubeViewModel @Inject constructor( // startPeriodicCheckUpdate() // } - val deviceProvisionResponse = MutableLiveData>() - val loginResponse = MutableLiveData>() val resultUpload = MutableLiveData>() @@ -130,13 +126,6 @@ class HemoCubeViewModel @Inject constructor( } } - fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest) = viewModelScope.launch { - deviceProvisionResponse.postValue(Result.Loading()) - databaseRepository.deviceProvision(deviceProvisionRequest).let { - deviceProvisionResponse.postValue(it) - } - } - fun login(loginRequest: LoginRequest) = viewModelScope.launch { loginResponse.postValue(Result.Loading()) databaseRepository.login(loginRequest).let { @@ -427,10 +416,10 @@ class HemoCubeViewModel @Inject constructor( fun getBatteryTemperature(): Float? { val batteryTemp: Float? = batteryStatus?.let { intent -> - val temperature = intent?.getIntExtra( + val temperature = intent.getIntExtra( BatteryManager.EXTRA_TEMPERATURE, 0 - ) ?: 0 + ) temperature.toFloat() / 10 } diff --git a/app/src/main/res/layout/fragment_device_provision.xml b/app/src/main/res/layout/fragment_device_provision.xml new file mode 100644 index 0000000..fc8afc5 --- /dev/null +++ b/app/src/main/res/layout/fragment_device_provision.xml @@ -0,0 +1,23 @@ + + +