first design draft
This commit is contained in:
@@ -61,4 +61,6 @@ dependencies {
|
||||
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
|
||||
implementation(libs.androidx.material3.adaptive.navigation3)
|
||||
implementation(libs.kotlinx.serialization.core)
|
||||
|
||||
implementation(libs.bundles.voyager)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.fabisahne.cloudsync
|
||||
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.fabisahne.cloudsync.ui.theme.CloudSyncTheme
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Rule
|
||||
import java.text.NumberFormat
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.fabisahne.cloudsync", appContext.packageName)
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun calculateTip_20PercentTip() {
|
||||
composeTestRule.setContent {
|
||||
CloudSyncTheme {
|
||||
TipTimeLayout()
|
||||
}
|
||||
}
|
||||
composeTestRule.onNodeWithText("Bill Amount")
|
||||
.performTextInput("10")
|
||||
|
||||
val expected = NumberFormat.getCurrencyInstance().format(1.5)
|
||||
composeTestRule.onNodeWithText("Tip Amount: $expected").assertExists(
|
||||
"No node with this text was found."
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,23 +4,22 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import com.fabisahne.cloudsync.ui.screens.HomeScreen
|
||||
import com.fabisahne.cloudsync.ui.theme.CloudSyncTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
CloudSyncTheme {
|
||||
Icon(
|
||||
Icons.Filled.Home,
|
||||
contentDescription = "asdf",
|
||||
)
|
||||
Navigator(HomeScreen)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
app/src/main/java/com/fabisahne/cloudsync/ui/SyncApp.kt
Normal file
2
app/src/main/java/com/fabisahne/cloudsync/ui/SyncApp.kt
Normal file
@@ -0,0 +1,2 @@
|
||||
package com.fabisahne.cloudsync.ui
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
package com.fabisahne.cloudsync.ui
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.fabisahne.cloudsync.ui.navigation
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||
import androidx.navigation3.runtime.NavBackStack
|
||||
import androidx.navigation3.runtime.NavEntry
|
||||
import androidx.navigation3.runtime.NavEntryDecorator
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import androidx.navigation3.runtime.rememberDecoratedNavEntries
|
||||
import androidx.navigation3.runtime.rememberNavBackStack
|
||||
import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator
|
||||
|
||||
@Composable
|
||||
fun rememberNavigationState(
|
||||
startKey: NavKey,
|
||||
topLevelKeys: Set<NavKey>,
|
||||
): NavigationState {
|
||||
val topLevelStack = rememberNavBackStack(startKey)
|
||||
val subStacks = topLevelKeys.associateWith { key -> rememberNavBackStack(key) }
|
||||
|
||||
return remember(startKey, topLevelKeys) {
|
||||
NavigationState(
|
||||
startKey = startKey,
|
||||
topLevelStack = topLevelStack,
|
||||
subStacks = subStacks
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class NavigationState(
|
||||
val startKey: NavKey,
|
||||
val topLevelStack: NavBackStack<NavKey>,
|
||||
val subStacks: Map<NavKey, NavBackStack<NavKey>>
|
||||
) {
|
||||
val currentTopLevelKey: NavKey by derivedStateOf { topLevelStack.last() }
|
||||
|
||||
val topLevelKeys
|
||||
get() = subStacks.keys
|
||||
|
||||
val currentSubStack: NavBackStack<NavKey>
|
||||
get() = subStacks[currentTopLevelKey]
|
||||
?: error("Sub stack for $currentTopLevelKey does not exist")
|
||||
|
||||
val currentKey: NavKey by derivedStateOf { currentSubStack.last() }
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NavigationState.toEntries(
|
||||
entryProvider: (NavKey) -> NavEntry<NavKey>,
|
||||
): SnapshotStateList<NavEntry<NavKey>> {
|
||||
val decoratedEntries = subStacks.mapValues { (_, stack) ->
|
||||
val decorators = listOf<NavEntryDecorator<NavKey>>(
|
||||
rememberSaveableStateHolderNavEntryDecorator(),
|
||||
rememberViewModelStoreNavEntryDecorator(),
|
||||
)
|
||||
rememberDecoratedNavEntries(
|
||||
backStack = stack,
|
||||
entryDecorators = decorators,
|
||||
entryProvider = entryProvider,
|
||||
)
|
||||
}
|
||||
|
||||
return topLevelStack
|
||||
.flatMap { decoratedEntries[it] ?: emptyList() }
|
||||
.toMutableStateList()
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.fabisahne.cloudsync.ui.navigation
|
||||
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
|
||||
class Navigator(val state: NavigationState) {
|
||||
fun navigate(key: NavKey) {
|
||||
when (key) {
|
||||
state.currentTopLevelKey -> clearSubStack()
|
||||
in state.topLevelKeys -> goToTopLevel(key)
|
||||
else -> goToKey(key)
|
||||
}
|
||||
}
|
||||
|
||||
fun goBack() {
|
||||
when (state.currentKey) {
|
||||
state.startKey -> error("You cannot go back from the start route")
|
||||
state.currentTopLevelKey -> state.topLevelStack.removeLastOrNull()
|
||||
else -> state.currentSubStack.removeLastOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun goToKey(key: NavKey) {
|
||||
state.currentSubStack.apply {
|
||||
remove(key)
|
||||
add(key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun goToTopLevel(key: NavKey) {
|
||||
state.topLevelStack.apply {
|
||||
if (key == state.startKey) {
|
||||
clear()
|
||||
} else {
|
||||
remove(key)
|
||||
}
|
||||
add(key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearSubStack() {
|
||||
state.currentSubStack.run {
|
||||
if (size > 1) subList(1, size).clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.fabisahne.cloudsync.ui.navigation
|
||||
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
||||
import cafe.adriel.voyager.navigator.tab.Tab
|
||||
|
||||
|
||||
@Composable
|
||||
fun RowScope.TabNavigationItem(tab: Tab) {
|
||||
val tabNavigator = LocalTabNavigator.current
|
||||
|
||||
NavigationBarItem(
|
||||
selected = tabNavigator.current.key == tab.key,
|
||||
onClick = { tabNavigator.current = tab },
|
||||
label = { Text(tab.options.title) },
|
||||
icon = { Icon(painter = tab.options.icon!!, contentDescription = tab.options.title) }
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.fabisahne.cloudsync.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
|
||||
data class FolderPairScreen(
|
||||
val id: Long
|
||||
) : Screen {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Folder Pair") }
|
||||
)
|
||||
},
|
||||
content = { contentPadding ->
|
||||
Surface(modifier = Modifier.padding(contentPadding)) {
|
||||
Text("ASDF: $id")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.fabisahne.cloudsync.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import cafe.adriel.voyager.navigator.tab.CurrentTab
|
||||
import cafe.adriel.voyager.navigator.tab.TabNavigator
|
||||
import com.fabisahne.cloudsync.ui.navigation.TabNavigationItem
|
||||
import com.fabisahne.cloudsync.ui.tabs.AccountTab
|
||||
import com.fabisahne.cloudsync.ui.tabs.FolderPairTab
|
||||
import com.fabisahne.cloudsync.ui.tabs.HistoryTab
|
||||
import com.fabisahne.cloudsync.ui.tabs.SettingsTab
|
||||
|
||||
|
||||
object HomeScreen : Screen {
|
||||
private val TABS = listOf(
|
||||
HistoryTab,
|
||||
FolderPairTab,
|
||||
AccountTab,
|
||||
SettingsTab,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
TabNavigator(
|
||||
HistoryTab
|
||||
) { tabNavigator ->
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(text = tabNavigator.current.options.title) }
|
||||
)
|
||||
},
|
||||
content = { padding ->
|
||||
Surface(modifier = Modifier.padding(padding)) {
|
||||
CurrentTab()
|
||||
}
|
||||
},
|
||||
bottomBar = {
|
||||
NavigationBar(windowInsets = NavigationBarDefaults.windowInsets) {
|
||||
TABS.forEach {
|
||||
TabNavigationItem(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.fabisahne.cloudsync.ui.tabs
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountBox
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import cafe.adriel.voyager.navigator.tab.Tab
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
|
||||
|
||||
object AccountTab : Tab {
|
||||
override val options: TabOptions
|
||||
@Composable
|
||||
get() {
|
||||
val title = "Accounts"
|
||||
val icon = rememberVectorPainter(Icons.Default.AccountBox)
|
||||
|
||||
return remember {
|
||||
TabOptions(
|
||||
index = 2u,
|
||||
title = title,
|
||||
icon = icon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
Text("Cloud Accounts")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.fabisahne.cloudsync.ui.tabs
|
||||
|
||||
import androidx.compose.foundation.layout.absoluteOffset
|
||||
import androidx.compose.foundation.layout.absolutePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.FolderCopy
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import cafe.adriel.voyager.navigator.tab.Tab
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
|
||||
object FolderPairTab : Tab {
|
||||
override val options: TabOptions
|
||||
@Composable
|
||||
get() {
|
||||
val title = "Folder Pairs"
|
||||
val icon = rememberVectorPainter(Icons.Default.FolderCopy)
|
||||
|
||||
return remember {
|
||||
TabOptions(
|
||||
index = 1u,
|
||||
title = title,
|
||||
icon = icon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
// Text("Folder Pairs")
|
||||
// FloatingActionButton(
|
||||
//
|
||||
// modifier = Modifier.absolute(),
|
||||
// onClick = {}
|
||||
// ) {
|
||||
// Icon(Icons.Default.Add, "Add Pair")
|
||||
// }
|
||||
|
||||
Scaffold(
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
onClick = {}
|
||||
) {
|
||||
Icon(Icons.Default.Add, "Add Pair")
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
Text("Pairs here", Modifier.padding(paddingValues))
|
||||
}
|
||||
}
|
||||
}
|
||||
141
app/src/main/java/com/fabisahne/cloudsync/ui/tabs/HistoryTab.kt
Normal file
141
app/src/main/java/com/fabisahne/cloudsync/ui/tabs/HistoryTab.kt
Normal file
@@ -0,0 +1,141 @@
|
||||
package com.fabisahne.cloudsync.ui.tabs
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.History
|
||||
import androidx.compose.material.icons.outlined.History
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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.rememberVectorPainter
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
|
||||
import cafe.adriel.voyager.navigator.tab.Tab
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
import com.fabisahne.cloudsync.ui.screens.FolderPairScreen
|
||||
import com.fabisahne.cloudsync.ui.theme.CloudSyncTheme
|
||||
import java.time.LocalDate
|
||||
|
||||
object HistoryTab : Tab {
|
||||
override val options: TabOptions
|
||||
@Composable
|
||||
get() {
|
||||
val isSelected = LocalTabNavigator.current.current.key == key
|
||||
val icon = if (isSelected)
|
||||
Icons.Outlined.History
|
||||
else
|
||||
Icons.Default.History
|
||||
return TabOptions(
|
||||
index = 0u,
|
||||
title = "History",
|
||||
icon = rememberVectorPainter(icon),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val navigator = LocalNavigator.currentOrThrow.parent!!
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(10.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(5) { index ->
|
||||
HistoryItem(
|
||||
"Account",
|
||||
"Folder Pair $index",
|
||||
LocalDate.now().minusDays(index.toLong()),
|
||||
isError = index % 2 == 0,
|
||||
onClick = { navigator.push(FolderPairScreen(index.toLong())) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HistoryItem(
|
||||
account: String,
|
||||
pairName: String,
|
||||
date: LocalDate,
|
||||
isError: Boolean = false,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
val (containerColor, contentColor) = if (isError)
|
||||
MaterialTheme.colorScheme.errorContainer to MaterialTheme.colorScheme.onErrorContainer
|
||||
else
|
||||
CardDefaults.cardColors().containerColor to CardDefaults.cardColors().contentColor
|
||||
|
||||
Card(
|
||||
onClick = onClick,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(90.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = containerColor,
|
||||
contentColor = contentColor
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text("$account | $pairName", style = MaterialTheme.typography.titleMedium)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
Text(
|
||||
if (isError) "Error" else "Success",
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Text(date.toString(), style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun HistoryCardPreviewLight() {
|
||||
CloudSyncTheme {
|
||||
HistoryItem(
|
||||
"Account",
|
||||
"Folder Pair",
|
||||
LocalDate.now(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun HistoryCardPreviewDark() {
|
||||
CloudSyncTheme(
|
||||
darkTheme = true
|
||||
) {
|
||||
HistoryItem(
|
||||
"Account",
|
||||
"Folder Pair",
|
||||
LocalDate.now()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.fabisahne.cloudsync.ui.tabs
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountBox
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import cafe.adriel.voyager.navigator.tab.Tab
|
||||
import cafe.adriel.voyager.navigator.tab.TabOptions
|
||||
|
||||
object SettingsTab : Tab {
|
||||
override val options: TabOptions
|
||||
@Composable
|
||||
get() {
|
||||
val title = "Settings"
|
||||
val icon = rememberVectorPainter(Icons.Default.Settings)
|
||||
|
||||
return remember {
|
||||
TabOptions(
|
||||
index = 2u,
|
||||
title = title,
|
||||
icon = icon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content() {
|
||||
Text("Settings")
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.fabisahne.cloudsync.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
@@ -1,30 +1,24 @@
|
||||
package com.fabisahne.cloudsync.ui.theme
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CloudSyncTheme(
|
||||
dynamicColor: Boolean = true,
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor -> {
|
||||
val context = LocalContext.current
|
||||
dynamicDarkColorScheme(context)
|
||||
}
|
||||
|
||||
else -> DarkColorScheme
|
||||
}
|
||||
val colorScheme = if (darkTheme)
|
||||
dynamicDarkColorScheme(context)
|
||||
else
|
||||
dynamicLightColorScheme(context)
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.fabisahne.cloudsync
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
import java.text.NumberFormat
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun calculateTip_20Percent() {
|
||||
val amount = 10.00
|
||||
val tipPercent = 20.00
|
||||
|
||||
val expected = NumberFormat.getCurrencyInstance().format(2)
|
||||
|
||||
assertEquals(expected, calculateTip(amount, tipPercent))
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ lifecycleRuntimeKtx = "2.10.0"
|
||||
activityCompose = "1.12.2"
|
||||
kotlin = "2.0.21"
|
||||
composeBom = "2024.09.00"
|
||||
compileSdk = "36"
|
||||
voyager = "1.1.0-beta03"
|
||||
|
||||
nav3Core = "1.0.0"
|
||||
lifeCycleViewmodelNav3 = "2.10.0"
|
||||
@@ -40,7 +40,15 @@ androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecy
|
||||
kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerializationCore" }
|
||||
androidx-material3-adaptive-navigation3 = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation3", version.ref = "material3AdaptiveNav3" }
|
||||
|
||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
|
||||
voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
|
||||
voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
|
||||
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlinSerialization" }
|
||||
|
||||
[bundles]
|
||||
voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"]
|
||||
Reference in New Issue
Block a user