|
1 | 1 | package com.google.ai.sample.feature.multimodal |
2 | 2 |
|
| 3 | +import android.app.ActivityManager |
| 4 | +import android.app.ActivityManager.RunningAppProcessInfo |
3 | 5 | import android.content.Context |
4 | 6 | import android.graphics.Bitmap |
5 | 7 | import android.content.BroadcastReceiver |
@@ -43,6 +45,7 @@ import kotlinx.serialization.json.Json |
43 | 45 | import kotlinx.coroutines.Job |
44 | 46 | import kotlinx.coroutines.flow.StateFlow |
45 | 47 | import kotlinx.coroutines.flow.asStateFlow |
| 48 | +import androidx.localbroadcastmanager.content.LocalBroadcastManager |
46 | 49 | // Removed duplicate StateFlow import |
47 | 50 | // Removed duplicate asStateFlow import |
48 | 51 | // import kotlinx.coroutines.isActive // Removed as we will use job.isActive |
@@ -193,30 +196,25 @@ performReasoning( |
193 | 196 | } |
194 | 197 |
|
195 | 198 | init { |
196 | | - // ... other init logic if any ... |
| 199 | + // ... other init logic |
197 | 200 | val context = MainActivity.getInstance()?.applicationContext |
198 | 201 | if (context != null) { |
199 | 202 | val filter = IntentFilter(ScreenCaptureService.ACTION_AI_CALL_RESULT) |
200 | | - // Comment: Specify RECEIVER_NOT_EXPORTED for Android 13 (API 33) and above |
201 | | - // to comply with security requirements for programmatically registered receivers. |
202 | | - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
203 | | - context.registerReceiver(aiResultReceiver, filter, Context.RECEIVER_NOT_EXPORTED) |
204 | | - } else { |
205 | | - context.registerReceiver(aiResultReceiver, filter) |
206 | | - } |
207 | | - Log.d(TAG, "AIResultReceiver registered.") |
| 203 | + LocalBroadcastManager.getInstance(context).registerReceiver(aiResultReceiver, filter) |
| 204 | + Log.d(TAG, "AIResultReceiver registered with LocalBroadcastManager.") |
208 | 205 | } else { |
209 | 206 | Log.e(TAG, "Failed to register AIResultReceiver: applicationContext is null at init.") |
210 | | - // Consider if this state implies a critical failure for the ViewModel's operation. |
211 | | - // For now, just logging. |
212 | 207 | } |
213 | 208 | } |
214 | 209 |
|
215 | 210 | override fun onCleared() { |
216 | 211 | super.onCleared() |
217 | | - MainActivity.getInstance()?.applicationContext?.unregisterReceiver(aiResultReceiver) |
218 | | - Log.d(TAG, "AIResultReceiver unregistered.") |
219 | | - // ... other onCleared logic ... |
| 212 | + val context = MainActivity.getInstance()?.applicationContext |
| 213 | + if (context != null) { |
| 214 | + LocalBroadcastManager.getInstance(context).unregisterReceiver(aiResultReceiver) |
| 215 | + Log.d(TAG, "AIResultReceiver unregistered with LocalBroadcastManager.") |
| 216 | + } |
| 217 | + // ... other onCleared logic |
220 | 218 | } |
221 | 219 |
|
222 | 220 | private fun createChatWithSystemMessage(context: Context? = null): Chat { |
@@ -612,68 +610,78 @@ performReasoning( |
612 | 610 | /** |
613 | 611 | * Process commands found in the AI response |
614 | 612 | */ |
615 | | - private fun processCommands(text: String) { |
616 | | - commandProcessingJob?.cancel() // Cancel any previous command processing |
617 | | - commandProcessingJob = PhotoReasoningApplication.applicationScope.launch(Dispatchers.Main) { |
618 | | - if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) return@launch // Check for cancellation |
619 | | - try { |
620 | | - // Parse commands from the text |
621 | | - val commands = CommandParser.parseCommands(text) |
| 613 | +private fun processCommands(text: String) { |
| 614 | + commandProcessingJob?.cancel() // Cancel any previous command processing |
| 615 | + commandProcessingJob = PhotoReasoningApplication.applicationScope.launch(Dispatchers.Main) { |
| 616 | + if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) return@launch // Check for cancellation |
| 617 | + try { |
| 618 | + // Parse commands from the text |
| 619 | + val commands = CommandParser.parseCommands(text) |
622 | 620 |
|
623 | | - if (commands.isNotEmpty()) { |
624 | | - if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) return@launch |
625 | | - Log.d(TAG, "Found ${commands.size} commands in response") |
| 621 | + // Check if takeScreenshot command is present |
| 622 | + val hasTakeScreenshotCommand = commands.any { it is Command.TakeScreenshot } |
626 | 623 |
|
627 | | - // Update the detected commands |
628 | | - val currentCommands = _detectedCommands.value.toMutableList() |
629 | | - currentCommands.addAll(commands) |
630 | | - _detectedCommands.value = currentCommands |
| 624 | + if (commands.isNotEmpty()) { |
| 625 | + if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) return@launch |
| 626 | + Log.d(TAG, "Found ${commands.size} commands in response") |
631 | 627 |
|
632 | | - // Update status to show commands were detected |
633 | | - val commandDescriptions = commands.joinToString("; ") { command -> |
634 | | - command.toString() |
635 | | - } |
636 | | - _commandExecutionStatus.value = "Commands detected: $commandDescriptions" |
| 628 | + // Update the detected commands |
| 629 | + val currentCommands = _detectedCommands.value.toMutableList() |
| 630 | + currentCommands.addAll(commands) |
| 631 | + _detectedCommands.value = currentCommands |
637 | 632 |
|
638 | | - // Execute the commands |
639 | | - for (command in commands) { |
640 | | - if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) { // Check for cancellation before executing each command |
641 | | - Log.d(TAG, "Command execution stopped before executing: $command") |
| 633 | + // Update status to show commands were detected |
| 634 | + val commandDescriptions = commands.joinToString("; ") { command -> |
| 635 | + command.toString() |
| 636 | + } |
| 637 | + _commandExecutionStatus.value = "Commands detected: $commandDescriptions" |
| 638 | + |
| 639 | + // Execute the commands |
| 640 | + for (command in commands) { |
| 641 | + if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) { // Check for cancellation before executing each command |
| 642 | + Log.d(TAG, "Command execution stopped before executing: $command") |
| 643 | + _commandExecutionStatus.value = "Command execution stopped." |
| 644 | + break // Exit loop if cancelled |
| 645 | + } |
| 646 | + try { |
| 647 | + Log.d(TAG, "Executing command: $command") |
| 648 | + ScreenOperatorAccessibilityService.executeCommand(command) |
| 649 | + // Check immediately after execution attempt if a stop was requested |
| 650 | + if (stopExecutionFlag.get()) { |
| 651 | + Log.d(TAG, "Command execution stopped after attempting: $command") |
642 | 652 | _commandExecutionStatus.value = "Command execution stopped." |
643 | | - break // Exit loop if cancelled |
644 | | - } |
645 | | - try { |
646 | | - Log.d(TAG, "Executing command: $command") |
647 | | - ScreenOperatorAccessibilityService.executeCommand(command) |
648 | | - // Check immediately after execution attempt if a stop was requested |
649 | | - if (stopExecutionFlag.get()) { |
650 | | - Log.d(TAG, "Command execution stopped after attempting: $command") |
651 | | - _commandExecutionStatus.value = "Command execution stopped." |
652 | | - break |
653 | | - } |
654 | | - } catch (e: Exception) { |
655 | | - if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) break // Exit loop if cancelled during error handling |
656 | | - Log.e(TAG, "Error executing command: ${e.message}", e) |
657 | | - _commandExecutionStatus.value = "Error during command execution: ${e.message}" |
| 653 | + break |
658 | 654 | } |
659 | | - } |
660 | | - if (stopExecutionFlag.get()){ |
661 | | - _commandExecutionStatus.value = "Command processing loop was stopped." |
| 655 | + } catch (e: Exception) { |
| 656 | + if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) break // Exit loop if cancelled during error handling |
| 657 | + Log.e(TAG, "Error executing command: ${e.message}", e) |
| 658 | + _commandExecutionStatus.value = "Error during command execution: ${e.message}" |
662 | 659 | } |
663 | 660 | } |
664 | | - } catch (e: Exception) { |
665 | | - if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) return@launch |
666 | | - Log.e(TAG, "Error processing commands: ${e.message}", e) |
667 | | - _commandExecutionStatus.value = "Error during command processing: ${e.message}" |
668 | | - } finally { |
669 | | - if (stopExecutionFlag.get()){ |
670 | | - _commandExecutionStatus.value = "Command processing finished after stop request." |
| 661 | + if (stopExecutionFlag.get()){ |
| 662 | + _commandExecutionStatus.value = "Command processing loop was stopped." |
| 663 | + } |
| 664 | + } |
| 665 | + |
| 666 | + // Toast anzeigen wenn kein takeScreenshot Command gefunden wurde |
| 667 | + if (!hasTakeScreenshotCommand && !text.contains("takeScreenshot()", ignoreCase = true)) { |
| 668 | + val context = MainActivity.getInstance() |
| 669 | + if (context != null) { |
| 670 | + Toast.makeText(context, "The AI stopped Screen Operator", Toast.LENGTH_SHORT).show() |
671 | 671 | } |
672 | | - // Reset flag after processing is complete or stopped to allow future executions |
673 | | - // No, don't reset here. Reset at the beginning of 'reason' or when stop is explicitly cleared. |
| 672 | + } |
| 673 | + |
| 674 | + } catch (e: Exception) { |
| 675 | + if (commandProcessingJob?.isActive != true || stopExecutionFlag.get()) return@launch |
| 676 | + Log.e(TAG, "Error processing commands: ${e.message}", e) |
| 677 | + _commandExecutionStatus.value = "Error during command processing: ${e.message}" |
| 678 | + } finally { |
| 679 | + if (stopExecutionFlag.get()){ |
| 680 | + _commandExecutionStatus.value = "Command processing finished after stop request." |
674 | 681 | } |
675 | 682 | } |
676 | 683 | } |
| 684 | +} |
677 | 685 |
|
678 | 686 | /** |
679 | 687 | * Save chat history to SharedPreferences |
|
0 commit comments