|
1 | 1 | package org.kabiri.android.usbterminal
|
2 | 2 |
|
3 |
| -import android.app.PendingIntent |
4 |
| -import android.content.BroadcastReceiver |
5 |
| -import android.content.Context |
6 | 3 | import android.content.Intent
|
7 |
| -import android.content.IntentFilter |
8 |
| -import android.hardware.usb.UsbDevice |
9 |
| -import android.hardware.usb.UsbDeviceConnection |
10 |
| -import android.hardware.usb.UsbManager |
11 |
| -import android.os.Build |
12 | 4 | import android.os.Bundle
|
| 5 | +import android.text.SpannableString |
| 6 | +import android.text.method.ScrollingMovementMethod |
13 | 7 | import android.util.Log
|
14 | 8 | import android.view.Menu
|
15 | 9 | import android.view.MenuInflater
|
16 | 10 | import android.view.MenuItem
|
17 |
| -import android.widget.Toast |
18 | 11 | import androidx.appcompat.app.AppCompatActivity
|
19 |
| -import com.felhr.usbserial.UsbSerialDevice |
20 |
| -import com.felhr.usbserial.UsbSerialInterface |
| 12 | +import androidx.lifecycle.Observer |
21 | 13 | import kotlinx.android.synthetic.main.activity_main.*
|
22 |
| -import java.io.UnsupportedEncodingException |
23 |
| -import java.nio.charset.Charset |
| 14 | +import org.kabiri.android.usbterminal.viewmodel.MainActivityViewModel |
| 15 | +import org.koin.android.viewmodel.ext.android.viewModel |
24 | 16 |
|
25 | 17 | class MainActivity : AppCompatActivity() {
|
26 | 18 |
|
27 | 19 | companion object {
|
28 | 20 | private const val TAG = "MainActivity"
|
29 |
| - private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" |
30 | 21 | }
|
31 | 22 |
|
32 |
| - private lateinit var usbManager: UsbManager |
33 |
| - private lateinit var connection: UsbDeviceConnection |
34 |
| - private lateinit var serialPort: UsbSerialDevice |
35 |
| - private lateinit var usbReceiver: BroadcastReceiver |
| 23 | + private val viewModel: MainActivityViewModel by viewModel() |
36 | 24 |
|
37 | 25 | override fun onCreate(savedInstanceState: Bundle?) {
|
38 | 26 | super.onCreate(savedInstanceState)
|
39 | 27 | setContentView(R.layout.activity_main)
|
40 | 28 |
|
41 |
| - usbManager = getSystemService(Context.USB_SERVICE) as UsbManager |
42 |
| - usbReceiver = object : BroadcastReceiver() { |
| 29 | + // make the text view scrollable: |
| 30 | + tvOutput.movementMethod = ScrollingMovementMethod() |
43 | 31 |
|
44 |
| - override fun onReceive(context: Context?, intent: Intent?) { |
45 |
| - when (intent?.action) { |
46 |
| - ACTION_USB_PERMISSION -> { |
47 |
| - synchronized(this) { |
48 |
| - val device: UsbDevice? = |
49 |
| - intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) |
| 32 | + // open the device and port when the permission is granted by user. |
| 33 | + viewModel.getGrantedDevice().observe(this, Observer { device -> |
| 34 | + viewModel.openDeviceAndPort(device) |
| 35 | + }) |
50 | 36 |
|
51 |
| - if (intent.getBooleanExtra( |
52 |
| - UsbManager.EXTRA_PERMISSION_GRANTED, |
53 |
| - false |
54 |
| - ) |
55 |
| - ) { |
56 |
| - tvOutput.append("\nPermission granted for ${device?.manufacturerName}") |
57 |
| - device?.apply { |
58 |
| - // setup the device communication. |
59 |
| - connection = usbManager.openDevice(device) |
60 |
| - serialPort = UsbSerialDevice |
61 |
| - .createUsbSerialDevice(device, connection) |
62 |
| - if (::serialPort.isInitialized) serialPort.let { |
63 |
| - if (it.open()) { |
64 |
| - // set connection params. |
65 |
| - it.setBaudRate(9600) |
66 |
| - it.setDataBits(UsbSerialInterface.DATA_BITS_8) |
67 |
| - it.setStopBits(UsbSerialInterface.STOP_BITS_1) |
68 |
| - it.setParity(UsbSerialInterface.PARITY_NONE) |
69 |
| - it.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF) |
70 |
| - it.read { message -> |
71 |
| - // check if the Android version is not 5.1.1 Lollipop |
72 |
| - // before printing the message into output. |
73 |
| - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) { |
74 |
| - Log.e( |
75 |
| - TAG, |
76 |
| - "Lollipop 5.1.1 is not supported to show the serial messages from the Arduino." |
77 |
| - ) |
78 |
| - } else { |
79 |
| - message?.let { |
80 |
| - try { |
81 |
| - val encoded = |
82 |
| - String( |
83 |
| - message, |
84 |
| - Charset.defaultCharset() |
85 |
| - ) |
86 |
| - tvOutput.append(encoded) |
87 |
| - } catch (e: UnsupportedEncodingException) { |
88 |
| - e.printStackTrace() |
89 |
| - tvOutput |
90 |
| - .append("\n${e.localizedMessage}") |
91 |
| - } catch (e: Exception) { |
92 |
| - Toast.makeText( |
93 |
| - this@MainActivity, |
94 |
| - e.localizedMessage, |
95 |
| - Toast.LENGTH_SHORT |
96 |
| - ).show() |
97 |
| - } |
98 |
| - } |
99 |
| - } |
100 |
| - } |
101 |
| - tvOutput.append("\nSerial Connection Opened") |
102 |
| - } else { |
103 |
| - tvOutput.append("\nPort not opened") |
104 |
| - } |
105 |
| - } else { |
106 |
| - tvOutput.append("\nSerial Port was null") |
107 |
| - } |
108 |
| - |
109 |
| - } |
110 |
| - } else { |
111 |
| - tvOutput.append("\npermission denied for device $device") |
112 |
| - } |
113 |
| - } |
114 |
| - } |
115 |
| - UsbManager.ACTION_USB_DEVICE_ATTACHED -> tvOutput.append("\nDevice attached") |
116 |
| - UsbManager.ACTION_USB_DEVICE_DETACHED -> tvOutput.append("\nDevice detached") |
117 |
| - } |
118 |
| - } |
119 |
| - } |
| 37 | + viewModel.getLiveOutput().observe(this, Observer { |
| 38 | + val spannable = SpannableString(it.text) |
| 39 | + spannable.setSpan( |
| 40 | + it.getAppearance(this), |
| 41 | + 0, |
| 42 | + it.text.length, |
| 43 | + SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE) |
| 44 | + tvOutput.append(it.text) |
| 45 | + }) |
120 | 46 |
|
| 47 | + // send the command to device when the button is clicked. |
121 | 48 | btEnter.setOnClickListener {
|
122 | 49 | val input = etInput.text.toString()
|
123 |
| - try { |
124 |
| - if (::serialPort.isInitialized && input.isNotBlank()) { |
125 |
| - serialPort.write(input.toByteArray()) |
126 |
| - tvOutput.append("\n") // this is because the answer might be sent in more than one part. |
127 |
| - etInput.setText("") // clear the terminal input. |
128 |
| - } else tvOutput.append("\nSerialPortNotOpened") |
129 |
| - } catch (e: Exception) { |
130 |
| - tvOutput.append("\n${e.localizedMessage}") |
131 |
| - } |
| 50 | + if (viewModel.serialWrite(input)) |
| 51 | + etInput.setText("") // clear the terminal input. |
| 52 | + else Log.e(TAG, "The message was not sent to Arduino") |
132 | 53 | }
|
133 |
| - |
134 | 54 | }
|
135 | 55 |
|
136 | 56 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
137 | 57 | return when (item.itemId) {
|
138 | 58 | R.id.actionConnect -> {
|
139 |
| - |
140 |
| - val usbDevices = usbManager.deviceList |
141 |
| - if (usbDevices.isNotEmpty()) { |
142 |
| - for (device in usbDevices) { |
143 |
| - val deviceVID = device.value.vendorId |
144 |
| - if (deviceVID == 0x2341) { // Arduino vendor ID |
145 |
| - val permissionIntent = PendingIntent.getBroadcast( |
146 |
| - this, |
147 |
| - 0, |
148 |
| - Intent(ACTION_USB_PERMISSION), |
149 |
| - 0 |
150 |
| - ) |
151 |
| - val filter = IntentFilter(ACTION_USB_PERMISSION) |
152 |
| - registerReceiver(usbReceiver, filter) // register the broadcast receiver |
153 |
| - usbManager.requestPermission(device.value, permissionIntent) |
154 |
| - } else { |
155 |
| - tvOutput.append("\nArduino Device not found") |
156 |
| - connection.close() |
157 |
| - } |
158 |
| - } |
159 |
| - } else { |
160 |
| - tvOutput.append("\nNo USB devices are attached") |
161 |
| - } |
| 59 | + viewModel.askForConnectionPermission() |
162 | 60 | true
|
163 | 61 | }
|
164 | 62 | else -> false
|
|
0 commit comments