diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c94e89b..9b52fdd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -68,6 +68,7 @@ diff --git a/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt index cc6d7eb..cf9aa27 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/MainActivity.kt @@ -73,8 +73,15 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - sharedPreference = getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) + sharedPreference = getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) + val action = intent?.action + if (action == UsbManager.ACTION_USB_DEVICE_ATTACHED || action == UsbManager.ACTION_USB_DEVICE_DETACHED) { + // If the activity is triggered by USB events, simply finish it + checkAndUpdateUsbConnection() + finish() + return + } binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.myToolbar.title = "Test Type" @@ -150,7 +157,7 @@ class MainActivity : AppCompatActivity() { } (device.productId == 24577 && device.vendorId == 1027) || (device.productId == 8963 && device.vendorId == 1659) || (device.productId == 4614 && device.vendorId == 7111) -> { - changeUI() +// changeUI() DataHolder.deviceType.postValue(Constants.DEVICE_TYPE_TRUEHEME) Constants.DEVICE_TYPE_TRUEHEME } diff --git a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt index 7bcca47..c0657b1 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalActivity.kt @@ -108,10 +108,13 @@ class UsbTerminalActivity : AppCompatActivity() { setContentView(binding.root) // setSupportActionBar(binding.myToolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) + doBoth() + } + + fun doBoth(){ setupListener() connectUsb(false) } - private fun setupListener() { DataHolder.usbConnected.observe(this) { Log.d("USB OBSERVE", "HemoCube called -> $it") @@ -156,6 +159,9 @@ class UsbTerminalActivity : AppCompatActivity() { .commit() } + + + @SuppressLint("MutableImplicitPendingIntent") private fun requestUserPermission(manager: UsbManager, device: UsbDevice) { val mPendingIntent: PendingIntent diff --git a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt index 5525b54..02f244d 100644 --- a/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt +++ b/app/src/main/java/com/example/hpostesting/presentation/usb_teminal/UsbTerminalFragment.kt @@ -16,14 +16,20 @@ package com.example.hpostesting.presentation.usb_teminal import android.content.Context import android.content.SharedPreferences import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.text.InputType import android.text.method.ScrollingMovementMethod +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.MutableLiveData -import com.example.hpostesting.data.constant.HemoCubeCommands +import com.example.hpostesting.data.constant.DataHolder import com.example.hpostesting.presentation.utils.UsbServiceListener import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase @@ -36,49 +42,178 @@ class UsbTerminalFragment : Fragment() { private lateinit var sharedPreferences: SharedPreferences private var startListening = MutableLiveData(false) private var resultData: String = "" + + private var fullCommand: String = "" + private var currentCommandIndex = 0 + private var isCommandInProgress = false + private val commandHistory = StringBuilder() + override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentUsbTerminalBinding.inflate(inflater, container, false) - sharedPreferences = - requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) + sharedPreferences = requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initViews() + observeUsbConnection() } + private fun observeUsbConnection() { + DataHolder.usbConnected.observe(viewLifecycleOwner) { isConnected -> + if (isConnected) { + commandHistory.append("\n[Device Connected]") + listenToHemoCube() + if (isCommandInProgress && currentCommandIndex < fullCommand.length) { + (activity as UsbTerminalActivity).doBoth() + commandHistory.append("\n>> Continuing command: $fullCommand from position $currentCommandIndex") + updateMessageDisplay() + continueCommand() + } + } else { + commandHistory.append("\n[Device Disconnected]") + updateMessageDisplay() + isCommandInProgress = false + } + } + } + override fun onResume() { + super.onResume() + + if (DataHolder.usbConnected.value == true) { + listenToHemoCube() + } else { + commandHistory.append("\n[Waiting for Device Connection]") + updateMessageDisplay() + } + (activity as UsbTerminalActivity).connectUsb(false) + } + + override fun onPause() { + super.onPause() + isCommandInProgress = false + currentCommandIndex = 0 + fullCommand = "" + (activity as UsbTerminalActivity).connectUsb(false) + } + + private fun initViews() { - binding.messageCommand.movementMethod = ScrollingMovementMethod() - usbTerminalViewModel.messages.observe(viewLifecycleOwner) { - binding.messageCommand.text = it - } - binding.sendCommand.setOnClickListener { - callCommand(binding.etCommands.text.trim().toString()) + with(binding) { + messageCommand.movementMethod = ScrollingMovementMethod() + + etCommands.apply { + imeOptions = EditorInfo.IME_ACTION_DONE + setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + text?.toString()?.trim()?.let { command -> + if (command.isNotEmpty()) { + callCommand(command) + text?.clear() + } + } + true + } else false + } + } + + sendCommand.setOnClickListener { + etCommands.text?.toString()?.trim()?.let { command -> + if (command.isNotEmpty()) { + callCommand(command) + etCommands.text?.clear() + } + } + } } + + commandHistory.clear() + commandHistory.append("[Session Started]") + updateMessageDisplay() + listenToHemoCube() callCommand("I") } private fun callCommand(command: String) { + commandHistory.append("\n>> $command") + updateMessageDisplay() + + fullCommand = command + currentCommandIndex = 0 + isCommandInProgress = true + usbTerminalViewModel.progressBar.postValue(true) + continueCommand() + } + private fun continueCommand() { + if (currentCommandIndex >= fullCommand.length) { + // Command execution completed + isCommandInProgress = false + usbTerminalViewModel.progressBar.postValue(false) + return + } + + val currentChar = fullCommand[currentCommandIndex].toString() + commandHistory.append("\nExecuting command: $currentChar") + updateMessageDisplay() + + // Send the command and wait for the full response sequence (activity as UsbTerminalActivity).mService.sendAndListenToHemoCubeTxt( - command, + currentChar, object : UsbServiceListener { + private var responseStage = 0 // 0 for BS, 1 for BC + override fun onUsbRead(data: ByteArray?) { - //nothing + data?.let { + val response = String(it).trim() + commandHistory.append("\nReceived response: $response") + updateMessageDisplay() + + // Check response sequence + when (responseStage) { + 0 -> if (response == "BS") { + responseStage = 1 // Wait for BC + } else { + handleWriteError() + } + 1 -> if (response == "BC") { + responseStage = 0 // Response complete for current command + currentCommandIndex++ // Move to the next character + continueCommand() // Start next command + } else { + handleWriteError() + } + } + } } override fun onUsbError(e: Exception?) { - usbTerminalViewModel.progressBar.postValue(false) + Log.e("USB", "Error: ${e?.message}") + (activity as UsbTerminalActivity).doBoth() + Toast.makeText(context, "USB Write Error: ${e?.localizedMessage}", Toast.LENGTH_SHORT).show() } - }) + + private fun handleWriteError() { + activity?.runOnUiThread { + Toast.makeText(context, "Reconnecting USB...", Toast.LENGTH_SHORT).show() + } + (activity as UsbTerminalActivity).doBoth() + // Optionally reset commands + isCommandInProgress = false + currentCommandIndex = 0 + fullCommand = "" + } + + } + ) } - private fun listenToHemoCube() { + private fun listenToHemoCube() { val fullReadOutput = StringBuilder() startListening.postValue(true) @@ -89,18 +224,51 @@ class UsbTerminalFragment : Fragment() { val stringData = String(it) fullReadOutput.append(stringData) resultData += stringData - usbTerminalViewModel.messages.postValue(resultData) + updateMessageDisplay() } } override fun onUsbError(e: Exception?) { - usbTerminalViewModel.messages.postValue(e!!.localizedMessage) + val errorMessage = e!!.localizedMessage + commandHistory.append("\nError: $errorMessage") + updateMessageDisplay() usbTerminalViewModel.progressBar.postValue(false) + activity?.runOnUiThread { + Toast.makeText(context, "USB Error: ${e.message}", Toast.LENGTH_SHORT).show() + } } }) } catch (e: Exception) { - usbTerminalViewModel.messages.postValue(e.localizedMessage) + commandHistory.append("\nError: ${e.localizedMessage}") + updateMessageDisplay() + usbTerminalViewModel.progressBar.postValue(false) Firebase.crashlytics.recordException(e) } } -} + + private fun updateMessageDisplay() { + val fullDisplay = commandHistory.toString() + "\n" + resultData + binding.messageCommand.text = fullDisplay + + binding.messageCommand.post { + val scrollAmount = binding.messageCommand.layout.getLineTop(binding.messageCommand.lineCount) - binding.messageCommand.height + if (scrollAmount > 0) { + binding.messageCommand.scrollTo(0, scrollAmount) + } + } + } + + private fun clearOutput() { + resultData = "" + commandHistory.clear() + commandHistory.append("[Output Cleared]") + updateMessageDisplay() + } + + override fun onDestroyView() { + super.onDestroyView() + isCommandInProgress = false + currentCommandIndex = 0 + fullCommand = "" + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_usb_terminal.xml b/app/src/main/res/layout/fragment_usb_terminal.xml index 1bab6d3..e214e11 100644 --- a/app/src/main/res/layout/fragment_usb_terminal.xml +++ b/app/src/main/res/layout/fragment_usb_terminal.xml @@ -11,59 +11,45 @@ ~ // from ShanMukha Innovations Pvt. Ltd. --> - + android:orientation="vertical" + android:padding="16dp"> - + + + - - + android:layout_weight="1" + android:hint="Enter command" + android:inputType="text" + android:maxLines="1" + android:minHeight="48dp" /> +