@@ -27,161 +27,168 @@ import javax.inject.Inject
2727/* *
2828 * Created by Ali Kabiri on 12.04.20.
2929 */
30+ @Suppress(" ktlint:standard:backing-property-naming" )
3031@HiltViewModel
3132internal class MainActivityViewModel
32- @Inject constructor (
33- private val arduinoUseCase: IArduinoUseCase ,
34- private val usbUseCase: IUsbUseCase ,
35- private val resourceProvider: IResourceProvider ,
36- ): ViewModel () {
37-
38- private val _infoMessageFlow = MutableStateFlow (" " )
39- val infoMessage: StateFlow <String >
40- get() = _infoMessageFlow
41-
42- private val _errorMessageFlow = MutableStateFlow (" " )
43- val errorMessage: StateFlow <String >
44- get() = _errorMessageFlow
45-
46- private val _outputLive = MutableStateFlow (" " )
47- val output: StateFlow <String >
48- get() = _outputLive
49-
50- val output2 = SnapshotStateList <OutputText >()
51-
52- internal fun startObservingUsbDevice () {
53- // Subscribe to USB device changes.
54- viewModelScope.launch {
55- usbUseCase.usbDevice.collect { device ->
56- _infoMessageFlow .value = " device discovered: ${device?.vendorId} \n "
57- // TODO: DROID-17 - check if this line is required after DROID-17 is done
58- device?.let { openDeviceAndPort(it) }
33+ @Inject
34+ constructor (
35+ private val arduinoUseCase: IArduinoUseCase ,
36+ private val usbUseCase: IUsbUseCase ,
37+ private val resourceProvider: IResourceProvider ,
38+ ) : ViewModel () {
39+ private val _infoMessageFlow = MutableStateFlow (" " )
40+ val infoMessage: StateFlow <String >
41+ get() = _infoMessageFlow
42+
43+ private val _errorMessageFlow = MutableStateFlow (" " )
44+ val errorMessage: StateFlow <String >
45+ get() = _errorMessageFlow
46+
47+ private val _outputLive = MutableStateFlow (" " )
48+ val output: StateFlow <String >
49+ get() = _outputLive
50+
51+ val output2 = SnapshotStateList <OutputText >()
52+
53+ internal fun startObservingUsbDevice () {
54+ // Subscribe to USB device changes.
55+ viewModelScope.launch {
56+ usbUseCase.usbDevice.collect { device ->
57+ _infoMessageFlow .value = " device discovered: ${device?.vendorId} \n "
58+ // TODO: DROID-17 - check if this line is required after DROID-17 is done
59+ device?.let { openDeviceAndPort(it) }
60+ }
5961 }
6062 }
61- }
6263
63- fun connect () {
64- val usbDeviceList = usbUseCase.scanForUsbDevices()
65- if (usbDeviceList.isEmpty()) {
66- _errorMessageFlow .value =
67- resourceProvider.getString(R .string.helper_error_usb_devices_not_attached)
68- return // no usb devices found
69- }
70- val device = usbDeviceList.firstOrNull { it.isOfficialArduinoBoard() || it.isCloneArduinoBoard() }
71- if (device == null ) {
72- _errorMessageFlow .value =
73- resourceProvider.getString(R .string.helper_error_arduino_device_not_found)
74- _infoMessageFlow .value =
75- resourceProvider.getString(R .string.helper_error_connecting_anyway)
76-
77- // request permission for the unknown device anyways
78- return usbUseCase.requestPermission(usbDeviceList.first())
79- }
80- when (device.getArduinoType()) {
81- OFFICIAL -> {
82- usbUseCase.requestPermission(device)
64+ fun connect () {
65+ val usbDeviceList = usbUseCase.scanForUsbDevices()
66+ if (usbDeviceList.isEmpty()) {
67+ _errorMessageFlow .value =
68+ resourceProvider.getString(R .string.helper_error_usb_devices_not_attached)
69+ return // no usb devices found
8370 }
84-
85- else -> {
71+ val device = usbDeviceList.firstOrNull { it.isOfficialArduinoBoard() || it.isCloneArduinoBoard() }
72+ if (device == null ) {
73+ _errorMessageFlow .value =
74+ resourceProvider.getString(R .string.helper_error_arduino_device_not_found)
8675 _infoMessageFlow .value =
8776 resourceProvider.getString(R .string.helper_error_connecting_anyway)
88- usbUseCase.requestPermission(device)
89- }
90- }
91- }
92-
93- fun connectIfAlreadyHasPermission () = viewModelScope.launch {
94- val usbDevice = usbUseCase.usbDevice.firstOrNull() ? : return @launch
95- usbUseCase.hasPermission(usbDevice)
96- // TODO: DROID-17 - Fix hasPermission return value not being used here.
97- openDeviceAndPort(usbDevice)
98- }
99-
100- fun disconnect () {
101- usbUseCase.disconnect()
102- arduinoUseCase.disconnect()
103- }
104-
105- private fun openDeviceAndPort (device : UsbDevice ) = viewModelScope.launch {
106- arduinoUseCase.openDeviceAndPort(device)
107- }
10877
109- fun serialWrite (command : String ): Boolean {
110- _outputLive .value = " ${output.value} \n $command \n "
111- return arduinoUseCase.serialWrite(command)
112- }
113-
114- /* *
115- * Transforms the outputs from ArduinoHelper into spannable text
116- * and merges them in one single flow
117- */
118- suspend fun getLiveOutput (): StateFlow <OutputText > {
119-
120- val infoOutput: Flow <OutputText > = infoMessage.map {
121- _outputLive .value = _outputLive .value + it
122- val outputText = OutputText (it, OutputText .OutputType .TYPE_INFO )
123- output2.add(outputText)
124- return @map outputText
125- }
126-
127- val errorOutput: Flow <OutputText > = errorMessage.map {
128- _outputLive .value = _outputLive .value + it
129- val outputText = OutputText (it, OutputText .OutputType .TYPE_ERROR )
130- output2.add(outputText)
131- return @map outputText
78+ // request permission for the unknown device anyways
79+ return usbUseCase.requestPermission(usbDeviceList.first())
80+ }
81+ when (device.getArduinoType()) {
82+ OFFICIAL -> {
83+ usbUseCase.requestPermission(device)
84+ }
85+
86+ else -> {
87+ _infoMessageFlow .value =
88+ resourceProvider.getString(R .string.helper_error_connecting_anyway)
89+ usbUseCase.requestPermission(device)
90+ }
91+ }
13292 }
13393
134- val usbInfoOutput: Flow <OutputText > = usbUseCase.infoMessageFlow.map {
135- _outputLive .value = _outputLive .value + it
136- val outputText = OutputText (it, OutputText .OutputType .TYPE_INFO )
137- output2.add(outputText)
138- return @map outputText
139- }
94+ fun connectIfAlreadyHasPermission () =
95+ viewModelScope.launch {
96+ val usbDevice = usbUseCase.usbDevice.firstOrNull() ? : return @launch
97+ usbUseCase.hasPermission(usbDevice)
98+ // TODO: DROID-17 - Fix hasPermission return value not being used here.
99+ openDeviceAndPort(usbDevice)
100+ }
140101
141- val arduinoDefaultOutput: Flow <OutputText > = arduinoUseCase.messageFlow.map {
142- _outputLive .value = _outputLive .value + it
143- val outputText = OutputText (it, OutputText .OutputType .TYPE_NORMAL )
144- output2.add(outputText)
145- return @map outputText
102+ fun disconnect () {
103+ usbUseCase.disconnect()
104+ arduinoUseCase.disconnect()
146105 }
147106
148- val arduinoInfoOutput: Flow <OutputText > = arduinoUseCase.infoMessageFlow.map {
149- _outputLive .value = _outputLive .value + it
150- val outputText = OutputText (it, OutputText .OutputType .TYPE_INFO )
151- output2.add(outputText)
152- return @map outputText
153- }
107+ private fun openDeviceAndPort (device : UsbDevice ) =
108+ viewModelScope.launch {
109+ arduinoUseCase.openDeviceAndPort(device)
110+ }
154111
155- val arduinoErrorOutput: Flow <OutputText > = arduinoUseCase.errorMessageFlow.map {
156- _outputLive .value = _outputLive .value + it
157- val outputText = OutputText (it, OutputText .OutputType .TYPE_ERROR )
158- output2.add(outputText)
159- return @map outputText
112+ fun serialWrite (command : String ): Boolean {
113+ _outputLive .value = " ${output.value} \n $command \n "
114+ return arduinoUseCase.serialWrite(command)
160115 }
161116
162- return combine(
163- infoOutput,
164- errorOutput,
165- arduinoDefaultOutput,
166- arduinoInfoOutput,
167- arduinoErrorOutput,
168- ) { info, error, arduinoDefault, arduinoInfo, arduinoError ->
169- // Prioritize error output over info, then normal.
170- when {
171- error.text.isNotEmpty() -> error
172- info.text.isNotEmpty() -> info
173- arduinoError.text.isNotEmpty() -> arduinoError
174- arduinoInfo.text.isNotEmpty() -> arduinoInfo
175- else -> arduinoDefault
176- }
177- }.combine(usbInfoOutput) { outputText, usbInfo ->
178- // Prioritize USB info output over the rest.
179- if (usbInfo.text.isNotEmpty()) {
180- usbInfo
181- } else {
182- outputText
183- }
117+ /* *
118+ * Transforms the outputs from ArduinoHelper into spannable text
119+ * and merges them in one single flow
120+ */
121+ suspend fun getLiveOutput (): StateFlow <OutputText > {
122+ val infoOutput: Flow <OutputText > =
123+ infoMessage.map {
124+ _outputLive .value = _outputLive .value + it
125+ val outputText = OutputText (it, OutputText .OutputType .TYPE_INFO )
126+ output2.add(outputText)
127+ return @map outputText
128+ }
129+
130+ val errorOutput: Flow <OutputText > =
131+ errorMessage.map {
132+ _outputLive .value = _outputLive .value + it
133+ val outputText = OutputText (it, OutputText .OutputType .TYPE_ERROR )
134+ output2.add(outputText)
135+ return @map outputText
136+ }
137+
138+ val usbInfoOutput: Flow <OutputText > =
139+ usbUseCase.infoMessageFlow.map {
140+ _outputLive .value = _outputLive .value + it
141+ val outputText = OutputText (it, OutputText .OutputType .TYPE_INFO )
142+ output2.add(outputText)
143+ return @map outputText
144+ }
145+
146+ val arduinoDefaultOutput: Flow <OutputText > =
147+ arduinoUseCase.messageFlow.map {
148+ _outputLive .value = _outputLive .value + it
149+ val outputText = OutputText (it, OutputText .OutputType .TYPE_NORMAL )
150+ output2.add(outputText)
151+ return @map outputText
152+ }
153+
154+ val arduinoInfoOutput: Flow <OutputText > =
155+ arduinoUseCase.infoMessageFlow.map {
156+ _outputLive .value = _outputLive .value + it
157+ val outputText = OutputText (it, OutputText .OutputType .TYPE_INFO )
158+ output2.add(outputText)
159+ return @map outputText
160+ }
161+
162+ val arduinoErrorOutput: Flow <OutputText > =
163+ arduinoUseCase.errorMessageFlow.map {
164+ _outputLive .value = _outputLive .value + it
165+ val outputText = OutputText (it, OutputText .OutputType .TYPE_ERROR )
166+ output2.add(outputText)
167+ return @map outputText
168+ }
169+
170+ return combine(
171+ infoOutput,
172+ errorOutput,
173+ arduinoDefaultOutput,
174+ arduinoInfoOutput,
175+ arduinoErrorOutput,
176+ ) { info, error, arduinoDefault, arduinoInfo, arduinoError ->
177+ // Prioritize error output over info, then normal.
178+ when {
179+ error.text.isNotEmpty() -> error
180+ info.text.isNotEmpty() -> info
181+ arduinoError.text.isNotEmpty() -> arduinoError
182+ arduinoInfo.text.isNotEmpty() -> arduinoInfo
183+ else -> arduinoDefault
184+ }
185+ }.combine(usbInfoOutput) { outputText, usbInfo ->
186+ // Prioritize USB info output over the rest.
187+ if (usbInfo.text.isNotEmpty()) {
188+ usbInfo
189+ } else {
190+ outputText
191+ }
192+ }.stateIn(viewModelScope)
184193 }
185- .stateIn(viewModelScope)
186194 }
187- }
0 commit comments