diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt
index 2e9fe2f..f2cdeda 100644
--- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt
+++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/folderlist/FolderListScreen.kt
@@ -36,10 +36,14 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vaultdrop.mobile.R
import com.vaultdrop.mobile.data.local.entity.FolderEntity
+import com.vaultdrop.mobile.ui.navigation.FloatingNavBar
+import com.vaultdrop.mobile.ui.navigation.NavTab
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FolderListScreen(
+ selectedTab: NavTab,
+ onTabSelected: (NavTab) -> Unit,
onOpenFolder: (String) -> Unit,
viewModel: FolderListViewModel = hiltViewModel(),
) {
@@ -56,6 +60,9 @@ fun FolderListScreen(
},
)
},
+ bottomBar = {
+ FloatingNavBar(selected = selectedTab, onSelect = onTabSelected)
+ },
) { padding ->
FolderListContent(
uiState = uiState,
diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/FloatingNavBar.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/FloatingNavBar.kt
new file mode 100644
index 0000000..190d395
--- /dev/null
+++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/FloatingNavBar.kt
@@ -0,0 +1,121 @@
+@file:OptIn(ExperimentalMaterial3Api::class)
+
+package com.vaultdrop.mobile.ui.navigation
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Folder
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material.icons.outlined.Folder
+import androidx.compose.material.icons.outlined.Search
+import androidx.compose.material.icons.outlined.Settings
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.vaultdrop.mobile.R
+
+enum class NavTab(val route: String) {
+ FILES(Routes.FILES),
+ SEARCH(Routes.SEARCH),
+ SETTINGS(Routes.SETTINGS),
+}
+
+private data class TabItem(
+ val tab: NavTab,
+ val filled: ImageVector,
+ val outlined: ImageVector,
+ val labelRes: Int,
+)
+
+private val TAB_ITEMS = listOf(
+ TabItem(NavTab.FILES, Icons.Filled.Folder, Icons.Outlined.Folder, R.string.nav_files),
+ TabItem(NavTab.SEARCH, Icons.Filled.Search, Icons.Outlined.Search, R.string.nav_search),
+ TabItem(NavTab.SETTINGS, Icons.Filled.Settings, Icons.Outlined.Settings, R.string.nav_settings),
+)
+
+@Composable
+fun FloatingNavBar(
+ selected: NavTab,
+ onSelect: (NavTab) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ Box(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(start = 20.dp, end = 20.dp, bottom = 16.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ Surface(
+ shape = RoundedCornerShape(30.dp),
+ color = MaterialTheme.colorScheme.surface,
+ shadowElevation = 8.dp,
+ modifier = Modifier.widthIn(max = 400.dp).fillMaxWidth(),
+ ) {
+ Row(
+ modifier = Modifier.padding(vertical = 8.dp, horizontal = 8.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ TAB_ITEMS.forEach { item ->
+ val isActive = item.tab == selected
+ val icon = if (isActive) item.filled else item.outlined
+ val tint = if (isActive) MaterialTheme.colorScheme.primary
+ else MaterialTheme.colorScheme.onSurfaceVariant
+
+ Surface(
+ onClick = { onSelect(item.tab) },
+ shape = RoundedCornerShape(20.dp),
+ color = if (isActive) MaterialTheme.colorScheme.primaryContainer
+ else MaterialTheme.colorScheme.surface,
+ modifier = Modifier.weight(1f),
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 6.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ tint = tint,
+ modifier = Modifier.size(22.dp),
+ )
+ Text(
+ text = stringResource(item.labelRes),
+ color = tint,
+ fontSize = 11.sp,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ fontWeight = if (isActive) FontWeight.Bold else FontWeight.Medium,
+ modifier = Modifier.padding(top = 2.dp),
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt
index f4effa2..4d2abf9 100644
--- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt
+++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt
@@ -1,34 +1,74 @@
package com.vaultdrop.mobile.ui.navigation
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
+import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.vaultdrop.mobile.ui.folderdetail.FolderDetailScreen
import com.vaultdrop.mobile.ui.folderlist.FolderListScreen
+import com.vaultdrop.mobile.ui.search.SearchScreen
+import com.vaultdrop.mobile.ui.settings.SettingsScreen
object Routes {
const val FILES = "files"
+ const val SEARCH = "search"
+ const val SETTINGS = "settings"
const val FOLDER_DETAIL = "folder/{folderResourceId}"
const val ARG_FOLDER = "folderResourceId"
fun folder(folderResourceId: String): String = "folder/$folderResourceId"
}
+private fun String?.toNavTab(): NavTab = when (this) {
+ Routes.FILES -> NavTab.FILES
+ Routes.SEARCH -> NavTab.SEARCH
+ Routes.SETTINGS -> NavTab.SETTINGS
+ else -> NavTab.FILES
+}
+
@Composable
fun NavGraph() {
val navController = rememberNavController()
+
+ val backStackEntry by navController.currentBackStackEntryAsState()
+ val selectedTab = backStackEntry?.destination?.route.toNavTab()
+
+ val onTabSelected: (NavTab) -> Unit = { tab ->
+ if (tab.route != selectedTab.route) {
+ navController.navigate(tab.route) {
+ popUpTo(Routes.FILES) { inclusive = true }
+ launchSingleTop = true
+ }
+ }
+ }
+
NavHost(
navController = navController,
startDestination = Routes.FILES,
) {
composable(Routes.FILES) {
FolderListScreen(
+ selectedTab = selectedTab,
+ onTabSelected = onTabSelected,
onOpenFolder = { id -> navController.navigate(Routes.folder(id)) },
)
}
+ composable(Routes.SEARCH) {
+ SearchScreen(
+ selectedTab = selectedTab,
+ onTabSelected = onTabSelected,
+ )
+ }
+ composable(Routes.SETTINGS) {
+ SettingsScreen(
+ selectedTab = selectedTab,
+ onTabSelected = onTabSelected,
+ )
+ }
composable(
route = Routes.FOLDER_DETAIL,
arguments = listOf(
@@ -45,4 +85,4 @@ fun NavGraph() {
)
}
}
-}
\ No newline at end of file
+}
diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/search/SearchScreen.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/search/SearchScreen.kt
new file mode 100644
index 0000000..edbd8d9
--- /dev/null
+++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/search/SearchScreen.kt
@@ -0,0 +1,42 @@
+package com.vaultdrop.mobile.ui.search
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import com.vaultdrop.mobile.R
+import com.vaultdrop.mobile.ui.navigation.FloatingNavBar
+import com.vaultdrop.mobile.ui.navigation.NavTab
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SearchScreen(
+ selectedTab: NavTab,
+ onTabSelected: (NavTab) -> Unit,
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(title = { Text(stringResource(R.string.search)) })
+ },
+ bottomBar = {
+ FloatingNavBar(selected = selectedTab, onSelect = onTabSelected)
+ },
+ ) { padding ->
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Text(
+ text = stringResource(R.string.search),
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+}
diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/settings/SettingsScreen.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/settings/SettingsScreen.kt
new file mode 100644
index 0000000..e063c46
--- /dev/null
+++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/settings/SettingsScreen.kt
@@ -0,0 +1,42 @@
+package com.vaultdrop.mobile.ui.settings
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import com.vaultdrop.mobile.R
+import com.vaultdrop.mobile.ui.navigation.FloatingNavBar
+import com.vaultdrop.mobile.ui.navigation.NavTab
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingsScreen(
+ selectedTab: NavTab,
+ onTabSelected: (NavTab) -> Unit,
+) {
+ Scaffold(
+ topBar = {
+ TopAppBar(title = { Text(stringResource(R.string.settings)) })
+ },
+ bottomBar = {
+ FloatingNavBar(selected = selectedTab, onSelect = onTabSelected)
+ },
+ ) { padding ->
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center,
+ ) {
+ Text(
+ text = stringResource(R.string.settings),
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
+}
diff --git a/mobile-kotlin/app/src/main/res/values-en/strings.xml b/mobile-kotlin/app/src/main/res/values-en/strings.xml
index 7931844..dddd31f 100644
--- a/mobile-kotlin/app/src/main/res/values-en/strings.xml
+++ b/mobile-kotlin/app/src/main/res/values-en/strings.xml
@@ -14,6 +14,13 @@
Refresh
Back
+
+ Files
+ Search
+ Settings
+ Search
+ Settings
+
Sign in
Sign in to your account
diff --git a/mobile-kotlin/app/src/main/res/values/strings.xml b/mobile-kotlin/app/src/main/res/values/strings.xml
index d7fdb10..8349df7 100644
--- a/mobile-kotlin/app/src/main/res/values/strings.xml
+++ b/mobile-kotlin/app/src/main/res/values/strings.xml
@@ -15,6 +15,13 @@
Actualiser
Retour
+
+ Fichiers
+ Recherche
+ Réglages
+ Recherche
+ Réglages
+
Connexion
Connecte-toi à ton compte