|
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