Skip to content

Commit 20c0b05

Browse files
Add files via upload
1 parent c508287 commit 20c0b05

1 file changed

Lines changed: 0 additions & 319 deletions

File tree

app/src/main/kotlin/com/google/ai/sample/ScreenOperatorAccessibilityService.kt

Lines changed: 0 additions & 319 deletions
Original file line numberDiff line numberDiff line change
@@ -1,322 +1,3 @@
1-
package com.google.ai.sample.feature.chat
2-
3-
import androidx.lifecycle.ViewModel
4-
import androidx.lifecycle.viewModelScope
5-
import com.google.ai.client.generativeai.GenerativeModel
6-
import com.google.ai.client.generativeai.type.asTextOrNull
7-
import com.google.ai.client.generativeai.type.content
8-
import com.google.ai.client.generativeai.type.generationConfig // Import hinzugefügt
9-
import com.google.ai.sample.BuildConfig // Import hinzugefügt
10-
import com.google.ai.sample.MainActivity
11-
import com.google.ai.sample.ScreenOperatorAccessibilityService
12-
import com.google.ai.sample.util.Command
13-
import com.google.ai.sample.util.CommandParser
14-
import kotlinx.coroutines.Dispatchers
15-
import kotlinx.coroutines.flow.MutableStateFlow
16-
import kotlinx.coroutines.flow.StateFlow
17-
import kotlinx.coroutines.flow.asStateFlow
18-
import kotlinx.coroutines.launch
19-
import android.util.Log
20-
21-
class ChatViewModel(
22-
// Das initiale Modell wird weiterhin übergeben
23-
initialGenerativeModel: GenerativeModel
24-
) : ViewModel() {
25-
private val TAG = "ChatViewModel"
26-
27-
// --- NEU: Variable für das aktuell verwendete Modell ---
28-
private var currentGenerativeModel: GenerativeModel = initialGenerativeModel
29-
private set // Nur intern änderbar
30-
31-
// --- GEÄNDERT: Chat wird mit dem aktuellen Modell initialisiert ---
32-
private var chat = currentGenerativeModel.startChat(
33-
history = listOf(
34-
content(role = "user") { text("Hello, I have 2 dogs in my house.") },
35-
content(role = "model") { text("Great to meet you. What would you like to know?") }
36-
)
37-
)
38-
39-
private val _uiState: MutableStateFlow<ChatUiState> =
40-
MutableStateFlow(ChatUiState(chat.history.map { content ->
41-
// Map the initial messages
42-
ChatMessage(
43-
text = content.parts.first().asTextOrNull() ?: "",
44-
participant = if (content.role == "user") Participant.USER else Participant.MODEL,
45-
isPending = false
46-
)
47-
}))
48-
val uiState: StateFlow<ChatUiState> =
49-
_uiState.asStateFlow()
50-
51-
// Keep track of detected commands
52-
private val _detectedCommands = MutableStateFlow<List<Command>>(emptyList())
53-
val detectedCommands: StateFlow<List<Command>> = _detectedCommands.asStateFlow()
54-
55-
// Keep track of command execution status
56-
private val _commandExecutionStatus = MutableStateFlow<String>("")
57-
val commandExecutionStatus: StateFlow<String> = _commandExecutionStatus.asStateFlow()
58-
59-
fun sendMessage(userMessage: String) {
60-
// Add a pending message
61-
_uiState.value.addMessage(
62-
ChatMessage(
63-
text = userMessage,
64-
participant = Participant.USER,
65-
isPending = true
66-
)
67-
)
68-
69-
// Clear previous commands
70-
_detectedCommands.value = emptyList()
71-
_commandExecutionStatus.value = ""
72-
73-
viewModelScope.launch {
74-
try {
75-
// --- GEÄNDERT: Verwendet die aktuelle 'chat'-Instanz ---
76-
val response = chat.sendMessage(userMessage)
77-
78-
_uiState.value.replaceLastPendingMessage()
79-
80-
response.text?.let { modelResponse ->
81-
_uiState.value.addMessage(
82-
ChatMessage(
83-
text = modelResponse,
84-
participant = Participant.MODEL,
85-
isPending = false
86-
)
87-
)
88-
89-
// Process commands in the response
90-
processCommands(modelResponse)
91-
}
92-
} catch (e: Exception) {
93-
Log.e(TAG, "Error sending message: ${e.message}", e) // Logging hinzugefügt
94-
_uiState.value.replaceLastPendingMessage()
95-
_uiState.value.addMessage(
96-
ChatMessage(
97-
text = e.localizedMessage ?: "Unknown error sending message", // Klarere Fehlermeldung
98-
participant = Participant.ERROR
99-
)
100-
)
101-
102-
// Update command execution status
103-
_commandExecutionStatus.value = "Fehler beim Senden: ${e.localizedMessage}"
104-
}
105-
}
106-
}
107-
108-
/**
109-
* Process commands found in the AI response
110-
*/
111-
private fun processCommands(text: String) {
112-
viewModelScope.launch(Dispatchers.Main) {
113-
try {
114-
// Parse commands from the text
115-
// --- HINWEIS: Die Logs im Parser bleiben erhalten ---
116-
val commands = CommandParser.parseCommands(text)
117-
118-
if (commands.isNotEmpty()) {
119-
Log.d(TAG, "Found ${commands.size} commands in response")
120-
121-
// Update the detected commands
122-
val currentCommands = _detectedCommands.value.toMutableList()
123-
currentCommands.addAll(commands)
124-
_detectedCommands.value = currentCommands
125-
126-
// Update status to show commands were detected
127-
val commandDescriptions = commands.map {
128-
when (it) {
129-
is Command.ClickButton -> "Klick auf Button: \"${it.buttonText}\""
130-
is Command.TapCoordinates -> "Tippen auf Koordinaten: (${it.x}, ${it.y})"
131-
is Command.TakeScreenshot -> "Screenshot aufnehmen"
132-
is Command.PressHomeButton -> "Home-Button drücken"
133-
is Command.PressBackButton -> "Zurück-Button drücken"
134-
is Command.ShowRecentApps -> "Übersicht der letzten Apps öffnen"
135-
is Command.ScrollDown -> "Nach unten scrollen"
136-
is Command.ScrollUp -> "Nach oben scrollen"
137-
is Command.ScrollLeft -> "Nach links scrollen"
138-
is Command.ScrollRight -> "Nach rechts scrollen"
139-
is Command.ScrollDownFromCoordinates -> "Nach unten scrollen von Position (${it.x}, ${it.y}) mit Distanz ${it.distance}px und Dauer ${it.duration}ms"
140-
is Command.ScrollUpFromCoordinates -> "Nach oben scrollen von Position (${it.x}, ${it.y}) mit Distanz ${it.distance}px und Dauer ${it.duration}ms"
141-
is Command.ScrollLeftFromCoordinates -> "Nach links scrollen von Position (${it.x}, ${it.y}) mit Distanz ${it.distance}px und Dauer ${it.duration}ms"
142-
is Command.ScrollRightFromCoordinates -> "Nach rechts scrollen von Position (${it.x}, ${it.y}) mit Distanz ${it.distance}px und Dauer ${it.duration}ms"
143-
is Command.OpenApp -> "App öffnen: \"${it.packageName}\""
144-
is Command.WriteText -> "Text schreiben: \"${it.text}\""
145-
is Command.UseHighReasoningModel -> "Wechsle zu leistungsfähigerem Modell (gemini-2.5-pro-exp-03-25)" // Name aktualisiert
146-
is Command.UseLowReasoningModel -> "Wechsle zu schnellerem Modell (gemini-2.0-flash-lite)"
147-
}
148-
}
149-
150-
// Show toast with detected commands
151-
val mainActivity = MainActivity.getInstance()
152-
mainActivity?.updateStatusMessage(
153-
"Befehle erkannt: ${commandDescriptions.joinToString(", ")}",
154-
false
155-
)
156-
157-
// Update status
158-
_commandExecutionStatus.value = "Befehle erkannt: ${commandDescriptions.joinToString(", ")}"
159-
160-
// Check if accessibility service is enabled
161-
val isServiceEnabled = mainActivity?.let {
162-
ScreenOperatorAccessibilityService.isAccessibilityServiceEnabled(it)
163-
} ?: false
164-
165-
if (!isServiceEnabled) {
166-
Log.e(TAG, "Accessibility service is not enabled")
167-
_commandExecutionStatus.value = "Accessibility Service ist nicht aktiviert. Bitte aktivieren Sie den Service in den Einstellungen."
168-
169-
// Prompt user to enable accessibility service
170-
mainActivity?.checkAccessibilityServiceEnabled()
171-
return@launch
172-
}
173-
174-
// Check if service is available
175-
if (!ScreenOperatorAccessibilityService.isServiceAvailable()) {
176-
Log.e(TAG, "Accessibility service is not available")
177-
_commandExecutionStatus.value = "Accessibility Service ist nicht verfügbar. Bitte starten Sie die App neu."
178-
179-
// Show toast
180-
mainActivity?.updateStatusMessage(
181-
"Accessibility Service ist nicht verfügbar. Bitte starten Sie die App neu.",
182-
true
183-
)
184-
return@launch
185-
}
186-
187-
// Execute each command
188-
commands.forEachIndexed { index, command ->
189-
Log.d(TAG, "Executing command: $command")
190-
191-
// Update status to show command is being executed
192-
val commandDescription = when (command) {
193-
is Command.ClickButton -> "Klick auf Button: \"${command.buttonText}\""
194-
is Command.TapCoordinates -> "Tippen auf Koordinaten: (${command.x}, ${command.y})"
195-
is Command.TakeScreenshot -> "Screenshot aufnehmen"
196-
is Command.PressHomeButton -> "Home-Button drücken"
197-
is Command.PressBackButton -> "Zurück-Button drücken"
198-
is Command.ShowRecentApps -> "Übersicht der letzten Apps öffnen"
199-
is Command.ScrollDown -> "Nach unten scrollen"
200-
is Command.ScrollUp -> "Nach oben scrollen"
201-
is Command.ScrollLeft -> "Nach links scrollen"
202-
is Command.ScrollRight -> "Nach rechts scrollen"
203-
is Command.ScrollDownFromCoordinates -> "Nach unten scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
204-
is Command.ScrollUpFromCoordinates -> "Nach oben scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
205-
is Command.ScrollLeftFromCoordinates -> "Nach links scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
206-
is Command.ScrollRightFromCoordinates -> "Nach rechts scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
207-
is Command.OpenApp -> "App öffnen: \"${command.packageName}\""
208-
is Command.WriteText -> "Text schreiben: \"${command.text}\""
209-
is Command.UseHighReasoningModel -> "Wechsle zu leistungsfähigerem Modell (gemini-2.5-pro-exp-03-25)" // Name aktualisiert
210-
is Command.UseLowReasoningModel -> "Wechsle zu schnellerem Modell (gemini-2.0-flash-lite)"
211-
}
212-
213-
_commandExecutionStatus.value = "Führe aus: $commandDescription (${index + 1}/${commands.size})"
214-
215-
// Show toast with command being executed
216-
mainActivity?.updateStatusMessage(
217-
"Führe aus: $commandDescription",
218-
false
219-
)
220-
221-
// Execute the command
222-
ScreenOperatorAccessibilityService.executeCommand(command)
223-
224-
// Add a small delay between commands to avoid overwhelming the system
225-
kotlinx.coroutines.delay(800)
226-
}
227-
228-
// Update status to show all commands were executed
229-
_commandExecutionStatus.value = "Alle Befehle ausgeführt: ${commandDescriptions.joinToString(", ")}"
230-
231-
// Show toast with all commands executed
232-
mainActivity?.updateStatusMessage(
233-
"Alle Befehle ausgeführt",
234-
false
235-
)
236-
}
237-
} catch (e: Exception) {
238-
Log.e(TAG, "Error processing commands: ${e.message}", e)
239-
_commandExecutionStatus.value = "Fehler bei der Befehlsverarbeitung: ${e.message}"
240-
241-
// Show toast with error
242-
val mainActivity = MainActivity.getInstance()
243-
mainActivity?.updateStatusMessage(
244-
"Fehler bei der Befehlsverarbeitung: ${e.message}",
245-
true
246-
)
247-
}
248-
}
249-
}
250-
251-
// --- NEUE FUNKTION zum Aktualisieren des Modells ---
252-
/**
253-
* Updates the GenerativeModel instance used by this ViewModel and restarts the chat.
254-
*
255-
* @param newModelName The name of the new model to use (e.g., "gemini-2.5-pro-exp-03-25").
256-
*/
257-
fun updateGenerativeModel(newModelName: String) {
258-
viewModelScope.launch {
259-
Log.i(TAG, "Updating GenerativeModel to: $newModelName")
260-
try {
261-
val config = generationConfig {
262-
temperature = 0.0f // Behalte deine Standardkonfiguration bei
263-
// Weitere Konfigurationen hier hinzufügen, falls nötig
264-
}
265-
// Erstelle eine NEUE Instanz mit dem neuen Namen
266-
currentGenerativeModel = GenerativeModel(
267-
modelName = newModelName,
268-
apiKey = BuildConfig.apiKey, // Stelle sicher, dass apiKey hier verfügbar ist
269-
generationConfig = config
270-
)
271-
272-
// Speichere die aktuelle History, bevor der Chat neu gestartet wird
273-
val currentHistory = chat.history
274-
275-
// Initialisiere die 'chat'-Instanz neu, damit sie das neue Modell verwendet
276-
chat = currentGenerativeModel.startChat(
277-
history = currentHistory // Übernehme die bisherige History
278-
)
279-
Log.i(TAG, "GenerativeModel and chat instance updated successfully. History preserved.")
280-
281-
// Optional: Füge eine Nachricht zum Chat hinzu, um den Wechsel anzuzeigen
282-
_uiState.value.addMessage(
283-
ChatMessage(
284-
text = "Chat-Modell wurde zu '$newModelName' gewechselt.",
285-
participant = Participant.MODEL, // Oder eine neue Participant.SYSTEM Rolle
286-
isPending = false
287-
)
288-
)
289-
290-
} catch (e: Exception) {
291-
Log.e(TAG, "Failed to update GenerativeModel to $newModelName: ${e.message}", e)
292-
// Optional: Füge eine Fehlermeldung zum Chat hinzu
293-
_uiState.value.addMessage(
294-
ChatMessage(
295-
text = "Fehler beim Wechseln des Chat-Modells zu '$newModelName': ${e.localizedMessage}",
296-
participant = Participant.ERROR,
297-
isPending = false
298-
)
299-
)
300-
_commandExecutionStatus.value = "Fehler beim Modellwechsel: ${e.localizedMessage}"
301-
}
302-
}
303-
}
304-
}
305-
```
306-
307-
--- END OF FILE ChatViewModel.kt.txt ---
308-
309-
--- START OF FILE PhotoReasoningViewModel.kt.txt ---
310-
311-
```kotlin
312-
313-
```
314-
315-
--- END OF FILE PhotoReasoningViewModel.kt.txt ---
316-
317-
--- START OF FILE ScreenOperatorAccessibilityService.kt.txt ---
318-
319-
```kotlin
3201
package com.google.ai.sample
3212

3223
import android.accessibilityservice.AccessibilityService

0 commit comments

Comments
 (0)