Skip to content

Commit b9260ac

Browse files
Update ChatScreen.kt
1 parent 059495b commit b9260ac

1 file changed

Lines changed: 15 additions & 171 deletions

File tree

  • app/src/main/kotlin/com/google/ai/sample/feature/chat
Lines changed: 15 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,44 @@
1+
// --- START OF FILE ChatScreen.kt ---
12
package com.google.ai.sample.feature.chat
23

3-
import androidx.compose.foundation.layout.Box
4-
import androidx.compose.foundation.layout.Column
5-
import androidx.compose.foundation.layout.Row
6-
import androidx.compose.foundation.layout.Spacer
7-
import androidx.compose.foundation.layout.fillMaxSize
8-
import androidx.compose.foundation.layout.fillMaxWidth
9-
import androidx.compose.foundation.layout.height
10-
import androidx.compose.foundation.layout.padding
11-
import androidx.compose.foundation.layout.requiredSize
12-
import androidx.compose.foundation.lazy.LazyColumn
13-
import androidx.compose.foundation.lazy.items
14-
import androidx.compose.foundation.lazy.rememberLazyListState
15-
import androidx.compose.material.icons.Icons
16-
import androidx.compose.material.icons.filled.Send
17-
import androidx.compose.material.icons.outlined.Person
18-
import androidx.compose.material3.Card
19-
import androidx.compose.material3.CardDefaults
20-
import androidx.compose.material3.CircularProgressIndicator
21-
import androidx.compose.material3.Divider
22-
import androidx.compose.material3.Icon
23-
import androidx.compose.material3.IconButton
24-
import androidx.compose.material3.MaterialTheme
25-
import androidx.compose.material3.OutlinedTextField
26-
import androidx.compose.material3.Text
27-
import androidx.compose.material3.TextButton
28-
import androidx.compose.runtime.Composable
29-
import androidx.compose.runtime.DisposableEffect
30-
import androidx.compose.runtime.LaunchedEffect
31-
import androidx.compose.runtime.collectAsState
32-
import androidx.compose.runtime.getValue
4+
// ... (Imports wie in der letzten Version) ...
335
import androidx.compose.runtime.key // Import für key Composable
34-
import androidx.compose.runtime.mutableStateOf
35-
import androidx.compose.runtime.remember
36-
import androidx.compose.runtime.saveable.rememberSaveable
37-
import androidx.compose.runtime.setValue
38-
import androidx.compose.ui.Alignment
39-
import androidx.compose.ui.Modifier
40-
import androidx.compose.ui.draw.drawBehind
41-
import androidx.compose.ui.graphics.Color
42-
import androidx.compose.ui.platform.LocalContext
43-
import androidx.compose.ui.res.stringResource
44-
import androidx.compose.ui.unit.dp
45-
import androidx.lifecycle.ViewModelProvider // Import für Factory
46-
import androidx.lifecycle.viewmodel.compose.viewModel
47-
import com.google.ai.sample.GenerativeViewModelFactory
48-
import com.google.ai.sample.MainActivity
49-
import com.google.ai.sample.R
50-
import com.google.ai.sample.ScreenOperatorAccessibilityService
51-
import com.google.ai.sample.util.Command
52-
import com.google.ai.sample.util.ModelPreferences
6+
import com.google.ai.sample.util.ModelPreferences // Import für ModelPreferences
537
import android.util.Log
548

559
// Annahme: Participant, ChatMessage, ChatUiState sind in eigenen Dateien definiert
5610

5711
@Composable
5812
internal fun ChatRoute(
59-
viewModel: ChatViewModel = viewModel(factory = GenerativeViewModelFactory) // ViewModel normal holen
13+
viewModel: ChatViewModel = viewModel(factory = GenerativeViewModelFactory)
6014
) {
6115
val chatUiState by viewModel.uiState.collectAsState()
6216
val commandExecutionStatus by viewModel.commandExecutionStatus.collectAsState()
6317
val detectedCommands by viewModel.detectedCommands.collectAsState()
64-
val currentModelName by viewModel.currentModelName.collectAsState() // Modellnamen beobachten
18+
val currentModelName by viewModel.currentModelName.collectAsState()
6519

6620
val context = LocalContext.current
6721
val mainActivity = context as? MainActivity
6822

69-
val isAccessibilityServiceEnabled = remember {
70-
mutableStateOf(
71-
mainActivity?.let {
72-
ScreenOperatorAccessibilityService.isAccessibilityServiceEnabled(it)
73-
} ?: false
74-
)
75-
}
23+
val isAccessibilityServiceEnabled = remember { /* ... */ mutableStateOf(/* ... */) }
7624

77-
// Effekt für einmalige Aktionen beim Start der Route
7825
LaunchedEffect(Unit) {
7926
Log.d("ChatRoute", "LaunchedEffect(Unit) running.")
8027
mainActivity?.checkAccessibilityServiceEnabled()
81-
// Optional: Lade hier Chat-History, falls ChatViewModel das nicht selbst tut
8228
}
8329

84-
// Key um den Screen legen, um Neukomposition bei Modellwechsel zu erzwingen
8530
key(currentModelName) {
8631
Log.d("ChatRoute", "Recomposing content within key: $currentModelName")
8732
ChatScreen(
88-
uiState = chatUiState, // Wird vom StateFlow beobachtet
33+
uiState = chatUiState,
8934
commandExecutionStatus = commandExecutionStatus,
9035
detectedCommands = detectedCommands,
9136
onMessageSent = { messageText ->
9237
viewModel.sendMessage(messageText)
9338
},
9439
isAccessibilityServiceEnabled = isAccessibilityServiceEnabled.value,
9540
onEnableAccessibilityService = {
96-
isAccessibilityServiceEnabled.value = mainActivity?.let {
97-
ScreenOperatorAccessibilityService.isAccessibilityServiceEnabled(it)
98-
} ?: false
41+
isAccessibilityServiceEnabled.value = mainActivity?.let { /* ... */ } ?: false
9942
mainActivity?.checkAccessibilityServiceEnabled()
10043
}
10144
)
@@ -130,71 +73,17 @@ fun ChatScreen(
13073
) {
13174
// Accessibility Service Status Card
13275
if (!isAccessibilityServiceEnabled) {
133-
Card(
134-
modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
135-
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer)
136-
) {
137-
Column(modifier = Modifier.padding(16.dp)) {
138-
Text(text = "Accessibility Service ist nicht aktiviert", color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.titleMedium)
139-
Spacer(modifier = Modifier.height(8.dp))
140-
Text(text = "Die Klick-Funktionalität benötigt den Accessibility Service...", color = MaterialTheme.colorScheme.error)
141-
Spacer(modifier = Modifier.height(8.dp))
142-
TextButton(onClick = onEnableAccessibilityService) { Text("Accessibility Service aktivieren") }
143-
}
144-
}
76+
// ... (Card-Code) ...
14577
}
14678

14779
// Command Execution Status
14880
if (commandExecutionStatus.isNotEmpty()) {
149-
Card(
150-
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
151-
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer)
152-
) {
153-
Column(modifier = Modifier.padding(16.dp)) {
154-
Text(text = "Befehlsstatus:", style = MaterialTheme.typography.titleMedium)
155-
Spacer(modifier = Modifier.height(4.dp))
156-
Text(text = commandExecutionStatus, color = MaterialTheme.colorScheme.onSecondaryContainer)
157-
}
158-
}
81+
// ... (Card-Code) ...
15982
}
16083

16184
// Detected Commands
16285
if (detectedCommands.isNotEmpty()) {
163-
Card(
164-
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
165-
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.tertiaryContainer)
166-
) {
167-
Column(modifier = Modifier.padding(16.dp)) {
168-
Text(text = "Erkannte Befehle:", style = MaterialTheme.typography.titleMedium)
169-
Spacer(modifier = Modifier.height(4.dp))
170-
detectedCommands.forEachIndexed { index, command ->
171-
val commandText = when (command) {
172-
is Command.ClickButton -> "Klick auf Button: \"${command.buttonText}\""
173-
is Command.TapCoordinates -> "Tippen auf Koordinaten: (${command.x}, ${command.y})"
174-
is Command.TakeScreenshot -> "Screenshot aufnehmen"
175-
is Command.PressHomeButton -> "Home-Button drücken"
176-
is Command.PressBackButton -> "Zurück-Button drücken"
177-
is Command.ShowRecentApps -> "Übersicht der letzten Apps öffnen"
178-
is Command.ScrollDown -> "Nach unten scrollen"
179-
is Command.ScrollUp -> "Nach oben scrollen"
180-
is Command.ScrollLeft -> "Nach links scrollen"
181-
is Command.ScrollRight -> "Nach rechts scrollen"
182-
is Command.ScrollDownFromCoordinates -> "Nach unten scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
183-
is Command.ScrollUpFromCoordinates -> "Nach oben scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
184-
is Command.ScrollLeftFromCoordinates -> "Nach links scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
185-
is Command.ScrollRightFromCoordinates -> "Nach rechts scrollen von Position (${command.x}, ${command.y}) mit Distanz ${command.distance}px und Dauer ${command.duration}ms"
186-
is Command.OpenApp -> "App öffnen: \"${command.packageName}\""
187-
is Command.WriteText -> "Text schreiben: \"${command.text}\""
188-
is Command.UseHighReasoningModel -> "Wechsle zu ${ModelPreferences.HIGH_REASONING_MODEL}"
189-
is Command.UseLowReasoningModel -> "Wechsle zu ${ModelPreferences.LOW_REASONING_MODEL}"
190-
}
191-
Text(text = "${index + 1}. $commandText", color = MaterialTheme.colorScheme.onTertiaryContainer)
192-
if (index < detectedCommands.size - 1) {
193-
Divider(modifier = Modifier.padding(vertical = 4.dp), color = MaterialTheme.colorScheme.onTertiaryContainer.copy(alpha = 0.2f))
194-
}
195-
}
196-
}
197-
}
86+
// ... (Card-Code mit when(command) ...) ...
19887
}
19988

20089
LazyColumn(
@@ -203,7 +92,6 @@ fun ChatScreen(
20392
.fillMaxWidth()
20493
.weight(1f)
20594
) {
206-
// Verwende einen stabileren Key für die LazyColumn Items
20795
items(uiState.messages, key = { message -> message.hashCode() + message.isPending.hashCode() }) { message ->
20896
when (message.participant) {
20997
Participant.USER -> UserChatBubble(text = message.text, isPending = message.isPending)
@@ -219,59 +107,15 @@ fun ChatScreen(
219107
.padding(top = 8.dp),
220108
verticalAlignment = Alignment.CenterVertically
221109
) {
222-
OutlinedTextField(
223-
value = userMessage,
224-
onValueChange = { userMessage = it },
225-
placeholder = { Text(stringResource(R.string.chat_label)) },
226-
modifier = Modifier.weight(1f).padding(end = 8.dp)
227-
)
228-
IconButton(
229-
onClick = {
230-
if (userMessage.isNotBlank()) {
231-
onMessageSent(userMessage)
232-
userMessage = ""
233-
}
234-
}
235-
) {
236-
Icon(Icons.Default.Send, contentDescription = stringResource(R.string.action_send), tint = MaterialTheme.colorScheme.primary)
237-
}
110+
// ... (OutlinedTextField und IconButton) ...
238111
}
239112
}
240113
}
241114

242115
// --- Chat Bubbles (unverändert) ---
243116
@Composable
244-
fun UserChatBubble(text: String, isPending: Boolean) {
245-
Row(modifier = Modifier.padding(vertical = 8.dp, horizontal = 8.dp).fillMaxWidth(), verticalAlignment = Alignment.Top) {
246-
Spacer(modifier = Modifier.weight(1f))
247-
Card(shape = MaterialTheme.shapes.medium, colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer), modifier = Modifier.weight(4f)) {
248-
Column(modifier = Modifier.padding(all = 16.dp)) {
249-
Text(text = text, color = MaterialTheme.colorScheme.onPrimaryContainer)
250-
if (isPending) { CircularProgressIndicator(modifier = Modifier.padding(top = 8.dp).requiredSize(16.dp), strokeWidth = 2.dp) }
251-
}
252-
}
253-
}
254-
}
117+
fun UserChatBubble(text: String, isPending: Boolean) { /* ... */ }
255118
@Composable
256-
fun ModelChatBubble(text: String, isPending: Boolean) {
257-
Row(modifier = Modifier.padding(vertical = 8.dp, horizontal = 8.dp).fillMaxWidth(), verticalAlignment = Alignment.Top) {
258-
Card(shape = MaterialTheme.shapes.medium, colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondaryContainer), modifier = Modifier.weight(4f)) {
259-
Row(modifier = Modifier.padding(all = 16.dp)) {
260-
Icon(Icons.Outlined.Person, contentDescription = "AI Assistant", tint = MaterialTheme.colorScheme.onSecondaryContainer, modifier = Modifier.requiredSize(24.dp).drawBehind { drawCircle(color = Color.White) }.padding(end = 8.dp))
261-
Column {
262-
Text(text = text, color = MaterialTheme.colorScheme.onSecondaryContainer)
263-
if (isPending) { CircularProgressIndicator(modifier = Modifier.padding(top = 8.dp).requiredSize(16.dp), strokeWidth = 2.dp) }
264-
}
265-
}
266-
}
267-
Spacer(modifier = Modifier.weight(1f))
268-
}
269-
}
119+
fun ModelChatBubble(text: String, isPending: Boolean) { /* ... */ }
270120
@Composable
271-
fun ErrorChatBubble(text: String) {
272-
Box(modifier = Modifier.padding(vertical = 8.dp, horizontal = 8.dp).fillMaxWidth()) {
273-
Card(shape = MaterialTheme.shapes.medium, colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer), modifier = Modifier.fillMaxWidth()) {
274-
Text(text = text, color = MaterialTheme.colorScheme.error, modifier = Modifier.padding(all = 16.dp))
275-
}
276-
}
277-
}
121+
fun ErrorChatBubble(text: String) { /* ... */ }

0 commit comments

Comments
 (0)