diff --git a/app/src/main/java/com/gatecontrol/android/mail/MailModels.kt b/app/src/main/java/com/gatecontrol/android/mail/MailModels.kt
index 29980096..32c14285 100644
--- a/app/src/main/java/com/gatecontrol/android/mail/MailModels.kt
+++ b/app/src/main/java/com/gatecontrol/android/mail/MailModels.kt
@@ -44,6 +44,9 @@ data class MMe(
const val FEATURE_DRIVE = "drive"
const val FEATURE_FAMILY_CALENDAR = "familyCalendar"
const val FEATURE_IMAGES = "images"
+ /** Since Mail 1.2.0: calendars and contacts (the administrator switched them on). */
+ const val FEATURE_CALENDAR = "calendar"
+ const val FEATURE_CONTACTS = "contacts"
}
}
diff --git a/app/src/main/java/com/gatecontrol/android/navigation/MailNavigation.kt b/app/src/main/java/com/gatecontrol/android/navigation/MailNavigation.kt
index 9ef61f6b..e5a28046 100644
--- a/app/src/main/java/com/gatecontrol/android/navigation/MailNavigation.kt
+++ b/app/src/main/java/com/gatecontrol/android/navigation/MailNavigation.kt
@@ -30,7 +30,11 @@ private class MailNavigation(private val navController: NavHostController) {
draft = { go(Screen.MailCompose.create(draft = it)) },
search = { go(Screen.MailSearch.route) },
filing = { go(Screen.MailFiling.route) },
- addAccount = { go(Screen.MailAddAccount.create()) },
+ addAccount = {
+ // an account is added in the settings: back from adding leads there
+ go(Screen.MailSettings.route)
+ go(Screen.MailAddAccount.create())
+ },
settings = { go(Screen.MailSettings.route) },
account = { go(Screen.MailAccount.create(it)) },
outbox = { go(Screen.MailOutbox.route) },
diff --git a/app/src/main/java/com/gatecontrol/android/ui/mail/MailHomeScreen.kt b/app/src/main/java/com/gatecontrol/android/ui/mail/MailHomeScreen.kt
index 0740eb47..5b87d81d 100644
--- a/app/src/main/java/com/gatecontrol/android/ui/mail/MailHomeScreen.kt
+++ b/app/src/main/java/com/gatecontrol/android/ui/mail/MailHomeScreen.kt
@@ -90,6 +90,7 @@ data class MailNav(
val draft: (Long) -> Unit,
val search: () -> Unit,
val filing: () -> Unit,
+ /** "Konto hinzufügen" of the empty tab: the settings with "Konto hinzufügen" on top (the menu has no "+"). */
val addAccount: () -> Unit,
val settings: () -> Unit,
val account: (Long) -> Unit,
@@ -172,7 +173,6 @@ fun MailHomeScreen(nav: MailNav, viewModel: MailHomeViewModel = hiltViewModel())
onBox = { close(); viewModel.selectBox(it) },
onFiling = { close(); nav.filing() },
onOutbox = { close(); nav.outbox() },
- onAdd = { close(); nav.addAccount() },
onSettings = { close(); nav.settings() },
)
}
@@ -576,10 +576,11 @@ internal object MailFolders {
}
/**
- * The menu (mockup "Seitenmenü"): "Mail", "Alle Posteingänge", "Zum Ablegen" (with Drive), "Postausgang" (when
- * something waits), the accounts with their folders and below "Konto hinzufügen" and the settings. A tap on an
- * account opens or closes its folders (the menu stays, [folds] keeps it for the session); picking its "Posteingang"
- * or a folder closes the menu. Calendar and resources come later (phase 2/3).
+ * The menu (mockups "Seitenmenü" and "Seitenmenü neu"): "Mail", "Alle Posteingänge", "Zum Ablegen" (with Drive),
+ * "Postausgang" (when something waits), the accounts with their folders, the storage bar and the foot: the settings
+ * on the left, contacts and calendar on the right ([MailDrawerFoot]). A tap on an account opens or closes its folders
+ * (the menu stays, [folds] keeps it for the session); picking its "Posteingang" or a folder closes the menu. An
+ * account is added in the settings only (no "+" here since phase 2).
*/
@Composable
private fun MailDrawer(
@@ -593,8 +594,10 @@ private fun MailDrawer(
onBox: (MailLogic.Box) -> Unit,
onFiling: () -> Unit,
onOutbox: () -> Unit,
- onAdd: () -> Unit,
onSettings: () -> Unit,
+ onContacts: (() -> Unit)? = null,
+ onCalendar: (() -> Unit)? = null,
+ area: String = MailDrawerFoot.MAIL,
) {
val extra = GateControlTheme.extraColors
Column(Modifier.fillMaxHeight().padding(start = 12.dp, end = 12.dp, top = 8.dp, bottom = 16.dp)) {
@@ -653,13 +656,50 @@ private fun MailDrawer(
}
}
}
- Row(Modifier.padding(horizontal = 12.dp, vertical = 8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- DrawerIcon(GcIcons.Plus, stringResource(R.string.mail_add_account), onAdd)
- DrawerIcon(GcIcons.Settings, stringResource(R.string.mail_settings), onSettings)
+ MailDrawerFoot(me = me, area = area, onSettings = onSettings, onContacts = onContacts, onCalendar = onCalendar)
+ }
+}
+
+/**
+ * The menu's foot (mockup "Seitenmenü neu"): the settings on the left; contacts and calendar on the right, each only
+ * when the server offers it (`features.contacts` / `features.calendar`) and the app can open it, the active area in the
+ * accent. A tap on the active one goes back to the mail.
+ */
+@Composable
+internal fun MailDrawerFoot(
+ me: MMe?,
+ area: String,
+ onSettings: () -> Unit,
+ onContacts: (() -> Unit)?,
+ onCalendar: (() -> Unit)?,
+) {
+ Row(
+ Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 8.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ DrawerIcon(GcIcons.Settings, stringResource(R.string.mail_settings), on = false, onClick = onSettings)
+ Spacer(Modifier.weight(1f))
+ if (onContacts != null && MailDrawerFoot.contacts(me)) {
+ DrawerIcon(GcIcons.User, stringResource(R.string.mail_contacts), on = area == MailDrawerFoot.CONTACTS, onClick = onContacts)
+ }
+ if (onCalendar != null && MailDrawerFoot.calendar(me)) {
+ DrawerIcon(GcIcons.Calendar, stringResource(R.string.mail_calendar), on = area == MailDrawerFoot.CALENDAR, onClick = onCalendar)
}
}
}
+/** What the menu's foot shows (pure, unit-tested). */
+internal object MailDrawerFoot {
+ const val MAIL = "mail"
+ const val CALENDAR = "calendar"
+ const val CONTACTS = "contacts"
+
+ fun calendar(me: MMe?): Boolean = me?.has(MMe.FEATURE_CALENDAR) == true
+
+ fun contacts(me: MMe?): Boolean = me?.has(MMe.FEATURE_CONTACTS) == true
+}
+
@Composable
private fun DrawerLabel(text: String) {
Text(
@@ -740,18 +780,20 @@ private fun DrawerAccount(a: MAccount, count: Int, on: Boolean, open: Boolean, o
}
}
+/** A square button of the menu's foot (44×44); [on]: the area shown now, in the accent (`aria-pressed`). */
@Composable
-private fun DrawerIcon(icon: androidx.compose.ui.graphics.vector.ImageVector, label: String, onClick: () -> Unit) {
+private fun DrawerIcon(icon: androidx.compose.ui.graphics.vector.ImageVector, label: String, on: Boolean, onClick: () -> Unit) {
val extra = GateControlTheme.extraColors
+ val shape = RoundedCornerShape(12.dp)
Box(
Modifier
.size(44.dp)
- .clip(RoundedCornerShape(12.dp))
- .border(1.dp, extra.border, RoundedCornerShape(12.dp))
- .clickable(role = Role.Button, onClick = onClick)
+ .clip(shape)
+ .then(if (on) Modifier.background(extra.accentBg) else Modifier.border(1.dp, extra.border, shape))
+ .selectable(selected = on, role = Role.Button, onClick = onClick)
.semantics { contentDescription = label },
contentAlignment = Alignment.Center,
) {
- Icon(icon, contentDescription = null, tint = extra.muted, modifier = Modifier.size(20.dp))
+ Icon(icon, contentDescription = null, tint = if (on) extra.accentText else extra.muted, modifier = Modifier.size(20.dp))
}
}
diff --git a/app/src/main/res/values-de/strings_mail.xml b/app/src/main/res/values-de/strings_mail.xml
index 95153741..083bace8 100644
--- a/app/src/main/res/values-de/strings_mail.xml
+++ b/app/src/main/res/values-de/strings_mail.xml
@@ -53,6 +53,8 @@
Konten
Konto hinzufügen
Mail-Einstellungen
+ Kalender
+ Kontakte
%1$s von %2$s belegt
Offline – du siehst den zuletzt geladenen Stand.
%1$s: Anmeldung abgelehnt. Tippen, um dich neu anzumelden.
diff --git a/app/src/main/res/values/strings_mail.xml b/app/src/main/res/values/strings_mail.xml
index 06a6d7d2..639a9dc3 100644
--- a/app/src/main/res/values/strings_mail.xml
+++ b/app/src/main/res/values/strings_mail.xml
@@ -53,6 +53,8 @@
Accounts
Add account
Mail settings
+ Calendar
+ Contacts
%1$s of %2$s used
Offline – showing what was loaded last.
%1$s: sign-in refused. Tap to sign in again.
diff --git a/app/src/test/java/com/gatecontrol/android/ui/mail/MailDrawerFootTest.kt b/app/src/test/java/com/gatecontrol/android/ui/mail/MailDrawerFootTest.kt
new file mode 100644
index 00000000..54949d8a
--- /dev/null
+++ b/app/src/test/java/com/gatecontrol/android/ui/mail/MailDrawerFootTest.kt
@@ -0,0 +1,32 @@
+package com.gatecontrol.android.ui.mail
+
+import com.gatecontrol.android.mail.MLimits
+import com.gatecontrol.android.mail.MMe
+import com.gatecontrol.android.mail.MMicrosoft
+import com.gatecontrol.android.mail.MSettings
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+
+class MailDrawerFootTest {
+
+ private fun me(vararg features: String) = MMe(
+ features = features.toSet(),
+ limits = MLimits(5, 0, 0, 12),
+ microsoft = MMicrosoft(null, "https://login.microsoftonline.com/common", emptyList()),
+ accounts = emptyList(),
+ unreadAll = 0,
+ unreadByAccount = emptyMap(),
+ settings = MSettings.DEFAULT,
+ )
+
+ @Test
+ fun `contacts and calendar only with the server's features`() {
+ assertFalse(MailDrawerFoot.calendar(null))
+ assertFalse(MailDrawerFoot.contacts(null))
+ assertFalse(MailDrawerFoot.calendar(me("mail")))
+ assertFalse(MailDrawerFoot.contacts(me("mail", "calendar")))
+ assertTrue(MailDrawerFoot.calendar(me("mail", "calendar")))
+ assertTrue(MailDrawerFoot.contacts(me("mail", "contacts")))
+ }
+}