fix: Android Gruppen-Umschalter sichtbar + Menü überlappt Navigationsleiste nicht

Top-Bar-Gruppenumschalter ist jetzt eine tonale Pille (Akzentfarbe im
Gruppenmodus) statt eines flachen Icons – klar als Button erkennbar; das
Dropdown markiert die aktive Auswahl. Gruppen-Verwaltung: Tipp auf eine Gruppe
öffnet direkt deren Ansicht, Verwalten liegt aufs Zahnrad. Menü-Bottom-Sheet
respektiert jetzt das Navigationsleisten-Inset (+ Scroll-Fallback), sodass
Abmelden/Server wechseln nicht mehr hinter den Android-Buttons liegen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guido Schmit
2026-05-31 23:08:42 +02:00
parent 3352aa3be7
commit 7a9bdba557
3 changed files with 52 additions and 12 deletions

View File

@@ -5,6 +5,8 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -16,6 +18,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.FilterList
@@ -36,6 +39,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -45,6 +49,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -243,6 +248,7 @@ fun CalendarScreen(
Overlay.GROUPS -> GroupsScreen(
onClose = { overlay = Overlay.NONE },
onChanged = { vm.loadGroups(); vm.loadWritableCalendars() },
onOpenGroupView = { g -> overlay = Overlay.NONE; vm.switchGroup(g) },
)
Overlay.NONE -> Unit
}
@@ -380,27 +386,42 @@ private fun CompactIcon(
}
}
/** Top-bar switcher: "My calendar" + each group; flips the calendar into the group overlay. */
/**
* Top-bar switcher: "My calendar" + each group; flips the calendar into the
* group overlay. Rendered as a tonal pill so it stands out from the flat icons
* (filled in the accent colour while a group overlay is active).
*/
@Composable
private fun GroupSwitcher(groups: List<Group>, activeGroup: Group?, onSwitchGroup: (Group?) -> Unit) {
var open by remember { mutableStateOf(false) }
val active = activeGroup != null
Box {
IconButton(onClick = { open = true }, modifier = Modifier.size(40.dp)) {
Box(
Modifier
.padding(horizontal = 2.dp)
.size(38.dp)
.clip(CircleShape)
.background(if (active) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surfaceVariant)
.clickable { open = true },
contentAlignment = Alignment.Center,
) {
Icon(
Icons.Filled.People,
contentDescription = tr("groups.title"),
modifier = Modifier.size(22.dp),
tint = if (activeGroup != null) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface,
modifier = Modifier.size(21.dp),
tint = if (active) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurfaceVariant,
)
}
DropdownMenu(expanded = open, onDismissRequest = { open = false }) {
DropdownMenuItem(
text = { Text(tr("group.switch.personal")) },
trailingIcon = { if (!active) Icon(Icons.Filled.Check, contentDescription = null) },
onClick = { open = false; onSwitchGroup(null) },
)
groups.forEach { g ->
DropdownMenuItem(
text = { Text("${g.icon ?: "👥"} ${g.name}") },
trailingIcon = { if (activeGroup?.id == g.id) Icon(Icons.Filled.Check, contentDescription = null) },
onClick = { open = false; onSwitchGroup(g) },
)
}

View File

@@ -21,8 +21,10 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
@@ -66,6 +68,7 @@ private val GROUP_ICONS = listOf(
fun GroupsScreen(
onClose: () -> Unit,
onChanged: () -> Unit,
onOpenGroupView: (Group) -> Unit = {},
vm: GroupsViewModel = hiltViewModel(),
) {
var createOpen by remember { mutableStateOf(false) }
@@ -97,16 +100,22 @@ fun GroupsScreen(
}
items(vm.groups, key = { it.id }) { g ->
Row(
Modifier.fillMaxWidth().clickable { manageId = g.id }.padding(vertical = 12.dp),
Modifier.fillMaxWidth().clickable { onOpenGroupView(g) }.padding(vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(g.icon ?: "👥", style = MaterialTheme.typography.titleMedium)
Text(g.name, modifier = Modifier.weight(1f).padding(start = 12.dp), style = MaterialTheme.typography.bodyLarge)
Text(
tr("groups.member_count", g.memberCount),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Column(Modifier.weight(1f).padding(start = 12.dp)) {
Text(g.name, style = MaterialTheme.typography.bodyLarge)
Text(
tr("groups.member_count", g.memberCount),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
IconButton(onClick = { manageId = g.id }) {
Icon(Icons.Filled.Tune, contentDescription = tr("groups.manage"), tint = MaterialTheme.colorScheme.onSurfaceVariant)
}
Icon(Icons.Filled.ChevronRight, contentDescription = tr("groups.view"), tint = MaterialTheme.colorScheme.onSurfaceVariant)
}
Divider()
}

View File

@@ -6,8 +6,11 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AccountCircle
import androidx.compose.material.icons.filled.Dns
@@ -44,7 +47,14 @@ fun MenuSheet(
onSwitchServer: () -> Unit,
) {
ModalBottomSheet(onDismissRequest = onDismiss) {
Column(Modifier.fillMaxWidth().padding(bottom = 24.dp)) {
// Inset for the system navigation bar so the last rows aren't hidden
// behind the Android nav buttons; scroll as a fallback on short screens.
Column(
Modifier.fillMaxWidth()
.verticalScroll(rememberScrollState())
.navigationBarsPadding()
.padding(bottom = 12.dp),
) {
Text(
"Calendarr",
style = MaterialTheme.typography.titleLarge,