11package com.nlinterface.activities
22
33import android.Manifest
4+ import android.content.Context
45import android.content.Intent
56import android.content.pm.PackageManager
67import android.os.Bundle
@@ -184,6 +185,16 @@ class VoiceOnlyActivity: AppCompatActivity() {
184185 }
185186 }
186187
188+ private fun isValidCommand (label : String ): Boolean {
189+ val supportedCommands = listOf (
190+ " grocery-list" ,
191+ " barcode-scanner" ,
192+ " navigation" ,
193+ " object-and-hand-recognition"
194+ )
195+ return supportedCommands.contains(label)
196+ }
197+
187198 private fun processVoiceInput (command : String ) {
188199 lifecycleScope.launch {
189200 showSpeakingStage()
@@ -194,8 +205,11 @@ class VoiceOnlyActivity: AppCompatActivity() {
194205 // Authenticate with the API
195206 val token = llmConnector.authenticate()
196207
208+ // Add <User> prefix to the command
209+ val prefixedCommand = " <User>$command "
210+
197211 // Send command to the LLM API
198- val apiResponse = llmConnector.sendCommandToLLM(command , token)
212+ val apiResponse = llmConnector.sendCommandToLLM(prefixedCommand , token)
199213
200214 // Parse the response and get label and additional data requirement
201215 val (label, needsAdditionalData) = llmConnector.parseResponse(apiResponse)
@@ -215,21 +229,40 @@ class VoiceOnlyActivity: AppCompatActivity() {
215229 viewModel.sayAndAwait(" Sorry, I encountered an error: ${e.message} " )
216230 e.printStackTrace()
217231 }
218-
219- // Automatically start listening again after processing
220- // showListeningStage()
221- // startListening()
222232 }
223233 }
224234
225235 /* *
226- * Checks if the given label is a valid command that the app can handle
236+ * Gets the current grocery list formatted as a string.
237+ * Format: "[quantity]Item; [quantity]Item2; ..."
238+ * If the list is empty, returns "No items on the grocery list"
227239 */
228- private fun isValidCommand (label : String ): Boolean {
229- val supportedCommands = listOf (
230- " grocery-list" ,
231- )
232- return supportedCommands.contains(label)
240+ private fun getCurrentGroceryList (): String {
241+ // Load grocery list from shared preferences
242+ val sharedPreferences = getSharedPreferences(" grocery_list" , Context .MODE_PRIVATE )
243+ val json = sharedPreferences.getString(" grocery_list" , null )
244+
245+ if (json.isNullOrEmpty()) {
246+ return " No items on the grocery list"
247+ }
248+
249+ try {
250+ val gson = com.google.gson.Gson ()
251+ val type = object : com.google.gson.reflect.TypeToken <ArrayList <com.nlinterface.dataclasses.GroceryItem >>() {}.type
252+ val groceryList: ArrayList < com.nlinterface.dataclasses.GroceryItem > = gson.fromJson(json, type)
253+
254+ if (groceryList.isEmpty()) {
255+ return " No items on the grocery list"
256+ }
257+
258+ // Format as "[1]Item; [1]Item2; ..." (assuming quantity is 1 for all items)
259+ return groceryList.joinToString(" ; " ) { item ->
260+ " [1]${item.itemName} "
261+ }
262+ } catch (e: Exception ) {
263+ e.printStackTrace()
264+ return " No items on the grocery list"
265+ }
233266 }
234267
235268 /* *
@@ -239,21 +272,100 @@ class VoiceOnlyActivity: AppCompatActivity() {
239272 when (label) {
240273 " grocery-list" -> {
241274 if (needsAdditionalData) {
242- viewModel.sayAndAwait(getString(R .string.which_item))
243- // Next voice input will be processed in context of grocery list
275+ // Get the current grocery list
276+ val groceryList = getCurrentGroceryList()
277+ viewModel.say(" Checking your grocery list..." )
278+
279+ // Send the grocery list to the LLM with <App> prefix
280+ val token = LLMAppConnector .getInstance.authenticate()
281+ val prefixedData = " <App>$groceryList "
282+ val apiResponse = LLMAppConnector .getInstance.sendCommandToLLM(prefixedData, token)
283+
284+ // Parse the response
285+ val (response, _) = LLMAppConnector .getInstance.parseResponse(apiResponse)
286+
287+ if (response != null ) {
288+ // Process grocery list updates
289+ processGroceryListUpdates(response)
290+ } else {
291+ viewModel.sayAndAwait(" Sorry, I couldn't process your request." )
292+ }
244293 } else {
245294 viewModel.sayAndAwait(getString(R .string.navigate_to_grocery_list))
246295 val intent = Intent (this @VoiceOnlyActivity, GroceryListActivity ::class .java)
247296 startActivity(intent)
248297 }
249298 }
250- // Add more commands as needed based on strings.xml and support of the LLM
299+ " barcode-scanner" -> {
300+ viewModel.sayAndAwait(getString(R .string.barcode_scanner))
301+ val intent = Intent (this @VoiceOnlyActivity, BarcodeSettingsActivity ::class .java)
302+ startActivity(intent)
303+ }
304+ " navigation" -> {
305+ viewModel.sayAndAwait(getString(R .string.place_details))
306+ val intent = Intent (this @VoiceOnlyActivity, PlaceDetailsActivity ::class .java)
307+ startActivity(intent)
308+ }
309+ " object-and-hand-recognition" -> {
310+ viewModel.sayAndAwait(getString(R .string.classification))
311+ val intent = Intent (this @VoiceOnlyActivity, ClassificationActivity ::class .java)
312+ startActivity(intent)
313+ }
251314 else -> {
252- viewModel.sayAndAwait(" I don't know how to handle: $label " )
315+ viewModel.sayAndAwait(" I don't know how to handle: $label ; additional-data-required= $needsAdditionalData " )
253316 }
254317 }
255318 }
256319
320+ /* *
321+ * Process grocery list updates from LLM response
322+ * Expected format: "[+/-quantity]Item; [quantity]Item2; [-quantity]Item3"
323+ */
324+ private suspend fun processGroceryListUpdates (response : String ) {
325+ viewModel.sayAndAwait(" Updating your grocery list..." )
326+
327+ val itemsToProcess = response.split(" ;" )
328+ val itemsToAdd = mutableListOf<String >()
329+ val itemsToRemove = mutableListOf<String >()
330+
331+ // Parse the response to identify items to add or remove
332+ for (item in itemsToProcess) {
333+ val trimmedItem = item.trim()
334+ if (trimmedItem.isNotEmpty()) {
335+ // Parse the format [+/-quantity]Item
336+ val regex = """ ^\[([+\-]?\d+)](.+)$""" .toRegex()
337+ val matchResult = regex.find(trimmedItem)
338+
339+ if (matchResult != null ) {
340+ val (quantityStr, itemName) = matchResult.destructured
341+
342+ if (quantityStr.startsWith(" +" ) || ! quantityStr.startsWith(" -" )) {
343+ // Add item
344+ itemsToAdd.add(itemName.trim())
345+ } else if (quantityStr.startsWith(" -" )) {
346+ // Remove item
347+ itemsToRemove.add(itemName.trim())
348+ }
349+ }
350+ }
351+ }
352+
353+ // Launch GroceryListActivity and pass the items to add/remove
354+ val intent = Intent (this , GroceryListActivity ::class .java)
355+ intent.putExtra(" ITEMS_TO_ADD" , ArrayList (itemsToAdd))
356+ intent.putExtra(" ITEMS_TO_REMOVE" , ArrayList (itemsToRemove))
357+ intent.putExtra(" FROM_VOICE_COMMAND" , true )
358+ startActivity(intent)
359+
360+ // Give feedback to user
361+ val addMessage = if (itemsToAdd.isNotEmpty())
362+ " Adding ${itemsToAdd.joinToString(" , " )} to your grocery list. " else " "
363+ val removeMessage = if (itemsToRemove.isNotEmpty())
364+ " Removing ${itemsToRemove.joinToString(" , " )} from your grocery list." else " "
365+
366+ viewModel.sayAndAwait(" $addMessage$removeMessage " )
367+ }
368+
257369 private fun startListening () {
258370 viewModel.handleSTTSpeechBegin()
259371 }
0 commit comments