From 596d69b988c026200c9d217fbcfd5cb8e20821ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Mon, 28 Aug 2023 22:10:49 +0100 Subject: [PATCH 01/14] [63] Base Featute Skeleton implemented --- app/build.gradle | 2 +- build.gradle | 2 +- sdk/build.gradle | 2 +- .../br/com/tick/sdk/database/ExpenseDao.kt | 3 + .../CategorizedExpenseRepository.kt | 2 + .../CategorizedExpensesRepositoryImpl.kt | 4 + ui/build.gradle | 2 +- .../java/br/com/tick/ui/NavigationItem.kt | 4 +- .../main/java/br/com/tick/ui/TeiraScaffold.kt | 70 +++- .../br/com/tick/ui/core/QuickExpenseCard.kt | 25 +- .../br/com/tick/ui/core/TeiraDatePicker.kt | 98 +++++ .../java/br/com/tick/ui/core/TeiraDropdown.kt | 5 +- .../com/tick/ui/core/TeiraNavigationDrawer.kt | 10 +- .../tick/ui/screens/expense/ExpenseScreen.kt | 371 ++++++++++++++++++ .../expense/viewmodels/ExpenseViewModel.kt | 45 +++ .../tick/ui/screens/wallet/ExpensesGrid.kt | 24 +- .../tick/ui/screens/wallet/QuickExpenseBar.kt | 95 +---- .../tick/ui/screens/wallet/WalletScreen.kt | 6 +- ui/src/main/res/drawable/ic_add_photo.xml | 10 + ui/src/main/res/drawable/ic_close.xml | 10 + ui/src/main/res/drawable/ic_location.xml | 10 + .../main/res/values/strings_edit_expense.xml | 20 + ui/src/main/res/values/strings_navigation.xml | 1 + 23 files changed, 693 insertions(+), 128 deletions(-) create mode 100644 ui/src/main/java/br/com/tick/ui/core/TeiraDatePicker.kt create mode 100644 ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt create mode 100644 ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt create mode 100644 ui/src/main/res/drawable/ic_add_photo.xml create mode 100644 ui/src/main/res/drawable/ic_close.xml create mode 100644 ui/src/main/res/drawable/ic_location.xml create mode 100644 ui/src/main/res/values/strings_edit_expense.xml diff --git a/app/build.gradle b/app/build.gradle index fcd1cfe..197fa83 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ plugins { } android { - compileSdk 33 + compileSdk 34 namespace 'br.com.tick.teira' defaultConfig { diff --git a/build.gradle b/build.gradle index 015308e..123a934 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { hiltNavigationComposeVersion = '1.0.0' roomVersion = '2.4.3' dataStoreVersion = '1.0.0' - navigationVersion = '2.5.3' + navigationVersion = '2.7.0' vicoVersion = '1.6.4' composeColorPicker = '0.7.0' firebaseCrashlytics = '18.3.5' diff --git a/sdk/build.gradle b/sdk/build.gradle index 64310f1..35fd2fd 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -6,7 +6,7 @@ plugins { } android { - compileSdk 33 + compileSdk 34 namespace 'br.com.tick.sdk' defaultConfig { diff --git a/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt b/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt index eb70892..93194aa 100644 --- a/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt +++ b/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt @@ -20,4 +20,7 @@ interface ExpenseDao { @Query("SELECT * FROM expense") fun getAllExpenses(): Flow> + + @Query("SELECT * FROM expense WHERE expense_id = :expenseId") + fun getExpense(expenseId: Int): Flow } diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt index a56f300..c0260b5 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt @@ -13,4 +13,6 @@ interface CategorizedExpenseRepository { fun getCategorizedExpenses(): Flow> suspend fun getAccountingCycleExpenses(): Flow> + + fun getCategorizedExpense(expenseId: Int): Flow } diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt index 25889bc..e91fdbe 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt @@ -83,6 +83,10 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( } } + override fun getCategorizedExpense(expenseId: Int): Flow { + return expenseDao.getExpense(expenseId).map { categorize(it) } + } + private suspend fun categorize(expense: Expense): CategorizedExpense { val category = categoryDao.getCategoryById(expense.categoryId) val categoryColorId = category.categoryColorId diff --git a/ui/build.gradle b/ui/build.gradle index 4cd4b31..b5d86e3 100644 --- a/ui/build.gradle +++ b/ui/build.gradle @@ -6,7 +6,7 @@ plugins { } android { - compileSdk 33 + compileSdk 34 namespace 'br.com.tick.ui' defaultConfig { diff --git a/ui/src/main/java/br/com/tick/ui/NavigationItem.kt b/ui/src/main/java/br/com/tick/ui/NavigationItem.kt index dceca90..d6ef86c 100644 --- a/ui/src/main/java/br/com/tick/ui/NavigationItem.kt +++ b/ui/src/main/java/br/com/tick/ui/NavigationItem.kt @@ -1,6 +1,7 @@ package br.com.tick.ui import br.com.tick.ui.NavigationItem.Routes.ANALYSIS +import br.com.tick.ui.NavigationItem.Routes.EDIT_EXPENSE import br.com.tick.ui.NavigationItem.Routes.HISTORY import br.com.tick.ui.NavigationItem.Routes.SETTINGS import br.com.tick.ui.NavigationItem.Routes.WALLET @@ -12,11 +13,12 @@ sealed class NavigationItem(var route: String, var iconResource: Int, var titleR const val WALLET = "wallet" const val ANALYSIS = "analysis" const val HISTORY = "history" + const val EDIT_EXPENSE = "editExpense/" } - object Settings : NavigationItem(SETTINGS, R.drawable.ic_settings, R.string.navigation_item_settings) object Wallet : NavigationItem(WALLET, R.drawable.ic_wallet, R.string.navigation_item_wallet) object Analysis : NavigationItem(ANALYSIS, R.drawable.ic_analysis, R.string.navigation_item_analysis) object History : NavigationItem(HISTORY, R.drawable.ic_history, R.string.navigation_item_history) + object EditExpense : NavigationItem(EDIT_EXPENSE, R.drawable.ic_edit, R.string.navigation_item_edit_expense) } diff --git a/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt b/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt index 37b94e7..cef4892 100644 --- a/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt +++ b/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt @@ -3,6 +3,11 @@ package br.com.tick.ui import android.Manifest import android.content.Context import android.os.Build +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.EaseInOut +import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.material3.DrawerValue @@ -31,10 +36,12 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController +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 androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import br.com.tick.sdk.domain.NotificationPeriodicity @@ -42,6 +49,7 @@ import br.com.tick.sdk.extensions.getPeriodicityTimeDiff import br.com.tick.ui.core.TeiraNavigationDrawer import br.com.tick.ui.extensions.collectAsEffect import br.com.tick.ui.screens.analysis.AnalysisScreen +import br.com.tick.ui.screens.expense.ExpenseScreen import br.com.tick.ui.screens.history.HistoryScreen import br.com.tick.ui.screens.settings.SettingsScreen import br.com.tick.ui.screens.wallet.WalletScreen @@ -53,17 +61,11 @@ import com.google.accompanist.permissions.rememberPermissionState import kotlinx.coroutines.launch import java.util.concurrent.TimeUnit -@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class) +@OptIn(ExperimentalPermissionsApi::class) @Composable fun TeiraScaffold( viewModel: TeiraScaffoldViewModel = hiltViewModel() ) { - val navHostController = rememberNavController() - val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) - val coroutineScope = rememberCoroutineScope() - val navBackStackEntry by navHostController.currentBackStackEntryAsState() - - val currentRoute = navBackStackEntry?.destination?.route val context = LocalContext.current if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -87,6 +89,48 @@ fun TeiraScaffold( setDelayedPeriodicWorker(context, WorkManager.getInstance(context), it) } + val navHostController = rememberNavController() + + NavHost( + navController = navHostController, + startDestination = "home" + ) { + composable("home") { + HomeScreen(navHostController) + } + composable( + route = NavigationItem.EditExpense.route + "{expenseId}", + arguments = listOf(navArgument("expenseId") { type = NavType.IntType }), + enterTransition = { + slideIntoContainer( + animationSpec = tween(400, easing = EaseInOut), + towards = AnimatedContentTransitionScope.SlideDirection.Up + ) + }, + popExitTransition = { + slideOutOfContainer( + animationSpec = tween(400, easing = EaseInOut), + towards = AnimatedContentTransitionScope.SlideDirection.Down + ) + } + ) { navBackStackEntry -> + navBackStackEntry.arguments?.getInt("expenseId")?.let { + ExpenseScreen(navHostController = navHostController, expenseId = it) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun HomeScreen(parentNavController: NavHostController) { + val navHostController = rememberNavController() + val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) + val coroutineScope = rememberCoroutineScope() + val navBackStackEntry by navHostController.currentBackStackEntryAsState() + + val currentRoute = navBackStackEntry?.destination?.route + val context = LocalContext.current TeiraNavigationDrawer( drawerState = drawerState, @@ -147,7 +191,7 @@ fun TeiraScaffold( ) }, selected = currentRoute == navigationItem.route, - onClick = { navigateTo(navHostController, navigationItem.route) } + onClick = { navigateTo(navHostController, navigationItem) } ) } } @@ -156,7 +200,9 @@ fun TeiraScaffold( Box(modifier = Modifier.padding(innerPadding)) { NavHost( navController = navHostController, - startDestination = NavigationItem.Wallet.route + startDestination = NavigationItem.Wallet.route, + enterTransition = { EnterTransition.None }, + exitTransition = { ExitTransition.None } ) { composable(NavigationItem.Settings.route) { SettingsScreen { @@ -164,7 +210,7 @@ fun TeiraScaffold( } } composable(NavigationItem.Wallet.route) { - WalletScreen() + WalletScreen(parentNavController) } composable(NavigationItem.Analysis.route) { AnalysisScreen() @@ -178,8 +224,8 @@ fun TeiraScaffold( } } -private fun navigateTo(navHostController: NavHostController, route: String) { - navHostController.navigate(route) { +private fun navigateTo(navHostController: NavHostController, navigationItem: NavigationItem) { + navHostController.navigate(navigationItem.route) { // Pop up to the start destination of the graph to // avoid building up a large stack of destinations // on the back stack as users select items diff --git a/ui/src/main/java/br/com/tick/ui/core/QuickExpenseCard.kt b/ui/src/main/java/br/com/tick/ui/core/QuickExpenseCard.kt index 7858984..5280d86 100644 --- a/ui/src/main/java/br/com/tick/ui/core/QuickExpenseCard.kt +++ b/ui/src/main/java/br/com/tick/ui/core/QuickExpenseCard.kt @@ -24,12 +24,12 @@ import br.com.tick.ui.screens.wallet.models.ExpenseCard import br.com.tick.ui.theme.spacing import br.com.tick.ui.theme.textStyle -@OptIn(ExperimentalAnimationApi::class) @ExperimentalFoundationApi @Composable fun QuickExpenseCard( expenseCard: ExpenseCard, modifier: Modifier = Modifier, + onQuickActionEdit: (expenseId: Int) -> Unit, onQuickActionDelete: (expenseId: Int) -> Unit ) { var expanded by remember { mutableStateOf(false) } @@ -69,10 +69,12 @@ fun QuickExpenseCard( AnimatedContent( modifier = Modifier.align(Alignment.BottomEnd), - targetState = expanded + targetState = expanded, + label = "" ) { targetExpanded -> if (targetExpanded) { ExpandedCardIcons( + onQuickActionEdit = { onQuickActionEdit(expenseCard.id) }, onQuickActionDelete = { onQuickActionDelete(expenseCard.id) } ) { expanded = !expanded @@ -106,7 +108,7 @@ fun CollapsedCardIcons(modifier: Modifier = Modifier, onExpandIcons: () -> Unit) ) { Icon( modifier = Modifier - .size(24.dp) + .size(26.dp) .clickable { onExpandIcons() }, tint = MaterialTheme.colorScheme.onSurfaceVariant, painter = painterResource(id = R.drawable.ic_slide_left), @@ -119,6 +121,7 @@ fun CollapsedCardIcons(modifier: Modifier = Modifier, onExpandIcons: () -> Unit) fun ExpandedCardIcons( modifier: Modifier = Modifier, onQuickActionDelete: () -> Unit, + onQuickActionEdit: () -> Unit, collapse: () -> Unit ) { Row( @@ -127,7 +130,7 @@ fun ExpandedCardIcons( ) { Icon( modifier = Modifier - .size(24.dp) + .size(26.dp) .clickable { collapse() }, tint = MaterialTheme.colorScheme.onSurfaceVariant, painter = painterResource(id = R.drawable.ic_slide_right), @@ -135,7 +138,9 @@ fun ExpandedCardIcons( ) Icon( - modifier = Modifier.size(24.dp), + modifier = Modifier + .size(26.dp) + .clickable { onQuickActionEdit() }, tint = MaterialTheme.colorScheme.onSurfaceVariant, painter = painterResource(id = R.drawable.ic_edit), contentDescription = "Edit expense" @@ -143,7 +148,7 @@ fun ExpandedCardIcons( Icon( modifier = Modifier - .size(24.dp) + .size(26.dp) .clickable { onQuickActionDelete() }, tint = MaterialTheme.colorScheme.onSurfaceVariant, painter = painterResource(id = R.drawable.ic_delete), @@ -163,8 +168,8 @@ fun QuickExpenseCardPreview() { value = 50.0, category = ExpenseCategory(0, "Category 1", Color.Red.value.toInt()), risk = ExpenseRisk.HIGH - ) - ) { - - } + ), + onQuickActionDelete = {}, + onQuickActionEdit = {} + ) } diff --git a/ui/src/main/java/br/com/tick/ui/core/TeiraDatePicker.kt b/ui/src/main/java/br/com/tick/ui/core/TeiraDatePicker.kt new file mode 100644 index 0000000..26ca3c9 --- /dev/null +++ b/ui/src/main/java/br/com/tick/ui/core/TeiraDatePicker.kt @@ -0,0 +1,98 @@ +package br.com.tick.ui.core + +import android.annotation.SuppressLint +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDefaults +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextDecoration +import br.com.tick.ui.R +import br.com.tick.ui.theme.textStyle +import java.time.Instant +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter + +@SuppressLint("UnrememberedMutableState") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TeiraDatePicker( + modifier: Modifier = Modifier, + color: Color = MaterialTheme.colorScheme.primary, + localDate: LocalDate, + onDateChanged: (LocalDate) -> Unit +) { + var openDialog by remember { mutableStateOf(false) } + + if (openDialog) { + val datePickerState = rememberDatePickerState() + val confirmEnabled = derivedStateOf { datePickerState.selectedDateMillis != null } + + DatePickerDialog( + onDismissRequest = { openDialog = false }, + confirmButton = { + TextButton( + onClick = { + openDialog = false + val localDate = LocalDateTime.ofInstant( + Instant.ofEpochMilli(datePickerState.selectedDateMillis!!), + ZoneId.systemDefault() + ).toLocalDate() + onDateChanged(localDate) + }, + enabled = confirmEnabled.value + ) { + Text(text = stringResource(id = R.string.generic_ok)) + } + }, + dismissButton = { + TextButton( + onClick = { openDialog = false } + ) { + Text(text = stringResource(id = R.string.generic_cancel)) + } + } + ) { + DatePicker( + state = datePickerState, + colors = DatePickerDefaults.colors( + titleContentColor = MaterialTheme.colorScheme.tertiary, + headlineContentColor = MaterialTheme.colorScheme.tertiary, + weekdayContentColor = MaterialTheme.colorScheme.primary, + dayContentColor = MaterialTheme.colorScheme.primary, + disabledDayContentColor = MaterialTheme.colorScheme.secondary, + selectedDayContentColor = MaterialTheme.colorScheme.tertiary, + selectedDayContainerColor = MaterialTheme.colorScheme.onSecondary, + ) + ) + } + } + + Box( + modifier = modifier + ) { + Text( + modifier = Modifier.clickable { openDialog = true }, + text = localDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")), + textDecoration = TextDecoration.Underline, + style = MaterialTheme.textStyle.h4, + color = color + ) + } +} diff --git a/ui/src/main/java/br/com/tick/ui/core/TeiraDropdown.kt b/ui/src/main/java/br/com/tick/ui/core/TeiraDropdown.kt index 5b41470..93bb87d 100644 --- a/ui/src/main/java/br/com/tick/ui/core/TeiraDropdown.kt +++ b/ui/src/main/java/br/com/tick/ui/core/TeiraDropdown.kt @@ -38,7 +38,6 @@ fun TeiraDropdown( lastItemLabel: String? = null, onLastItemSelected: (() -> Unit)? = null ) { - var isDropdownExpanded by remember { mutableStateOf(false) } var selectedItemName by remember { mutableStateOf("") } @@ -53,7 +52,7 @@ fun TeiraDropdown( text = label, modifier = Modifier.align(Alignment.Center), color = borderColor, - style = MaterialTheme.textStyle.h3 + style = MaterialTheme.textStyle.h3small ) DropdownMenu( modifier = Modifier.background(MaterialTheme.colorScheme.onSecondary), @@ -78,7 +77,7 @@ fun TeiraDropdown( text = { Text( text = label, - style = MaterialTheme.textStyle.h3extra, + style = MaterialTheme.textStyle.h3small, color = dropdownItemColor ) } diff --git a/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt b/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt index 2b5db5c..64fc739 100644 --- a/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt +++ b/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.launch fun TeiraNavigationDrawer( drawerState: DrawerState, navBackStackEntry: NavBackStackEntry?, - navigateToRoute: (String) -> Unit, + navigateToRoute: (NavigationItem) -> Unit, content: @Composable () -> Unit ) { val coroutineScope = rememberCoroutineScope() @@ -49,7 +49,7 @@ fun TeiraNavigationDrawer( coroutineScope = coroutineScope, isCurrentRoute = currentRoute == NavigationItem.Settings.route ) { - navigateToRoute(NavigationItem.Settings.route) + navigateToRoute(NavigationItem.Settings) } TeiraNavigationDrawerItem( drawerState = drawerState, @@ -58,7 +58,7 @@ fun TeiraNavigationDrawer( coroutineScope = coroutineScope, isCurrentRoute = currentRoute == NavigationItem.Wallet.route ) { - navigateToRoute(NavigationItem.Wallet.route) + navigateToRoute(NavigationItem.Wallet) } TeiraNavigationDrawerItem( drawerState = drawerState, @@ -67,7 +67,7 @@ fun TeiraNavigationDrawer( coroutineScope = coroutineScope, isCurrentRoute = currentRoute == NavigationItem.Analysis.route ) { - navigateToRoute(NavigationItem.Analysis.route) + navigateToRoute(NavigationItem.Analysis) } TeiraNavigationDrawerItem( drawerState = drawerState, @@ -76,7 +76,7 @@ fun TeiraNavigationDrawer( coroutineScope = coroutineScope, isCurrentRoute = currentRoute == NavigationItem.History.route ) { - navigateToRoute(NavigationItem.History.route) + navigateToRoute(NavigationItem.History) } Spacer(modifier = Modifier.height(MaterialTheme.spacing.extraLarge)) TeiraNavigationDrawerItem( diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt new file mode 100644 index 0000000..a000017 --- /dev/null +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt @@ -0,0 +1,371 @@ +package br.com.tick.ui.screens.expense + +import android.util.Log +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavHostController +import br.com.tick.ui.R +import br.com.tick.ui.core.TeiraDatePicker +import br.com.tick.ui.core.TeiraDropdown +import br.com.tick.ui.screens.expense.viewmodels.ExpenseViewModel +import br.com.tick.ui.screens.shared.AddCategoryDialog +import br.com.tick.ui.theme.spacing +import br.com.tick.ui.theme.textStyle +import java.time.LocalDate + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ExpenseScreen( + navHostController: NavHostController, + expenseViewModel: ExpenseViewModel = hiltViewModel(), + expenseId: Int? = null +) { + var showAddNewCategoryDialog by remember { mutableStateOf(false) } + + val expense by expenseViewModel.categorizedExpense.collectAsStateWithLifecycle() + val categoriesList by expenseViewModel.categories.collectAsStateWithLifecycle() + + LaunchedEffect(key1 = Unit) { + expenseId?.let { expenseViewModel.getExpense(it) } + } + + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.onSecondary), + ) { + Column(modifier = Modifier.fillMaxSize()) { + TopAppBar( + navigationIcon = { + IconButton(onClick = { navHostController.navigateUp() }) { + Icon( + painter = painterResource(id = R.drawable.ic_close), + contentDescription = null + ) + } + }, + title = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround + ) { + if (expense != null) { + Text( + text = stringResource(id = R.string.expense_title), + style = MaterialTheme.textStyle.h2 + ) + Text( + text = stringResource(id = R.string.expense_save), + style = MaterialTheme.textStyle.h2bold + ) + } else { + Text( + text = stringResource(id = R.string.expense_add_title), + style = MaterialTheme.textStyle.h2 + ) + Text( + text = stringResource(id = R.string.expense_add), + style = MaterialTheme.textStyle.h2bold + ) + } + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.secondary, + titleContentColor = MaterialTheme.colorScheme.onSecondary, + navigationIconContentColor = MaterialTheme.colorScheme.onSecondary + ) + ) + + var expenseName by remember { mutableStateOf(TextFieldValue()) } + var expenseCost by remember { mutableStateOf(TextFieldValue()) } + var isInvalidValue by remember { mutableStateOf(false) } + + var selectedCategoryId by remember { mutableIntStateOf(-1) } + val initialCategoryLabel = stringResource(id = R.string.expense_empty_category_title) + var categoryLabel by remember { mutableStateOf(initialCategoryLabel) } + + var localDate by remember { mutableStateOf(LocalDate.now()) } + + if (showAddNewCategoryDialog) { + AddCategoryDialog(onAddNewCategory = { categoryLabel = it }) { + showAddNewCategoryDialog = false + } + } + + + expense?.let { + expenseName = TextFieldValue(it.name) + expenseCost = TextFieldValue(it.expenseValue.toString()) + categoryLabel = it.category.name + selectedCategoryId = it.category.expenseCategoryId + localDate = it.date + Log.d("Tiago", expense.toString()) + } + + val expenseTextFieldColors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = Color.Transparent, + focusedContainerColor = Color.Transparent, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.primary, + focusedLabelColor = MaterialTheme.colorScheme.primary, + unfocusedLabelColor = MaterialTheme.colorScheme.primary, + focusedTextColor = MaterialTheme.colorScheme.secondary, + unfocusedTextColor = MaterialTheme.colorScheme.secondary, + errorBorderColor = MaterialTheme.colorScheme.tertiary, + errorLabelColor = MaterialTheme.colorScheme.tertiary, + errorTextColor = MaterialTheme.colorScheme.tertiary, + errorTrailingIconColor = MaterialTheme.colorScheme.secondary + ) + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(MaterialTheme.spacing.small) + ) { + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = expenseName, + label = { + Text( + text = stringResource(id = R.string.expense_expense_expense), + style = MaterialTheme.textStyle.h2small + ) + }, + onValueChange = { + expenseName = it + }, + colors = expenseTextFieldColors + ) + OutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.smallest), + value = expenseCost, + label = { + Text( + text = stringResource(id = R.string.expense_expense_cost), + style = MaterialTheme.textStyle.h2small + ) + }, + onValueChange = { + expenseCost = it + isInvalidValue = false + try { + expenseCost.text.toDouble() + } catch (exception: NumberFormatException) { + isInvalidValue = true + } + }, + colors = expenseTextFieldColors + ) + Row(modifier = Modifier.padding(top = MaterialTheme.spacing.small)) { + TeiraDropdown( + modifier = Modifier.weight(0.5f), + label = categoryLabel, + borderColor = MaterialTheme.colorScheme.primary, + dropdownItemLabels = categoriesList.map { it.name }, + dropdownItemColors = categoriesList.map { it.color }, + onItemSelected = { + selectedCategoryId = categoriesList[it].expenseCategoryId + categoryLabel = categoriesList[it].name + }, + lastItemLabel = stringResource(id = R.string.expense_add_category), + onLastItemSelected = { + showAddNewCategoryDialog = true + } + ) + + Column( + modifier = Modifier + .weight(0.5f) + .height(56.dp), + verticalArrangement = Arrangement.Bottom, + horizontalAlignment = Alignment.End + ) { + TeiraDatePicker(localDate = localDate) { + localDate = it + } + } + } + + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.medium), + thickness = 1.dp, + color = MaterialTheme.colorScheme.secondary + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.medium), + verticalAlignment = Alignment.Bottom + ) { + Icon( + modifier = Modifier.size(32.dp), + painter = painterResource(id = R.drawable.ic_location), + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) + Text( + modifier = Modifier.padding(start = MaterialTheme.spacing.extraSmall), + text = stringResource(id = R.string.expense_location), + style = MaterialTheme.textStyle.h2, + color = MaterialTheme.colorScheme.onTertiary + ) + Box(modifier = Modifier.fillMaxWidth()) { + Icon( + modifier = Modifier + .size(32.dp) + .align(Alignment.BottomEnd), + painter = painterResource(id = R.drawable.ic_add), + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) + } + } + + Text( + modifier = Modifier.padding(top = MaterialTheme.spacing.extraSmall), + text = "Rua Maria Feliciana, 210, Apartamento 608", + style = MaterialTheme.textStyle.h2small, + color = MaterialTheme.colorScheme.secondary + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.extraLarge), + verticalAlignment = Alignment.Bottom + ) { + Icon( + modifier = Modifier.size(32.dp), + painter = painterResource(id = R.drawable.ic_add_photo), + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) + Text( + modifier = Modifier.padding(start = MaterialTheme.spacing.extraSmall), + text = stringResource(id = R.string.expense_picture), + style = MaterialTheme.textStyle.h2, + color = MaterialTheme.colorScheme.onTertiary + ) + Box(modifier = Modifier.fillMaxWidth()) { + Icon( + modifier = Modifier + .size(32.dp) + .align(Alignment.BottomEnd), + painter = painterResource(id = R.drawable.ic_add), + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) + } + } + + Text( + modifier = Modifier.padding(top = MaterialTheme.spacing.extraSmall), + text = "temp/0/whatever/Teira/1837984.jpg", + style = MaterialTheme.textStyle.h2small, + color = MaterialTheme.colorScheme.secondary + ) + } + + var showDeletionConfirmationDialog by remember { mutableStateOf(false) } + + if (showDeletionConfirmationDialog) { + AlertDialog( + onDismissRequest = { showDeletionConfirmationDialog = false }, + icon = { + Icon( + painter = painterResource(id = R.drawable.ic_priority_high), + contentDescription = null + ) + }, + confirmButton = { + Text( + modifier = Modifier.clickable { + // Remove the expense + showDeletionConfirmationDialog = false + }, + text = stringResource(id = R.string.generic_ok).uppercase() + ) + }, + dismissButton = { + Text( + modifier = Modifier.clickable { + showDeletionConfirmationDialog = false + }, + text = stringResource(id = R.string.generic_cancel).uppercase() + ) + }, + title = { + Text(text = stringResource(id = R.string.expense_delete_title)) + }, + text = { + Text(text = stringResource(id = R.string.expense_delete_description)) + }, + containerColor = MaterialTheme.colorScheme.onSecondary, + titleContentColor = MaterialTheme.colorScheme.tertiary, + textContentColor = MaterialTheme.colorScheme.primary, + iconContentColor = MaterialTheme.colorScheme.onTertiary + ) + } + + Box( + modifier = Modifier + .fillMaxSize() + .padding(MaterialTheme.spacing.extraLarge) + ) { + if (expenseId != null) { + FloatingActionButton( + modifier = Modifier.align(Alignment.BottomEnd), + containerColor = MaterialTheme.colorScheme.tertiary, + onClick = { showDeletionConfirmationDialog = true} + ) { + Image(painter = painterResource(id = R.drawable.ic_delete), contentDescription = null) + } + } + } + } + } +} \ No newline at end of file diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt new file mode 100644 index 0000000..57c6c2f --- /dev/null +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt @@ -0,0 +1,45 @@ +package br.com.tick.ui.screens.expense.viewmodels + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import br.com.tick.sdk.dispatchers.DispatcherProvider +import br.com.tick.sdk.domain.CategorizedExpense +import br.com.tick.sdk.repositories.categorizedexpense.CategorizedExpenseRepository +import br.com.tick.sdk.repositories.expensecategory.ExpenseCategoryRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class ExpenseViewModel @Inject constructor( + categoryRepository: ExpenseCategoryRepository, + private val categorizedExpenseRepository: CategorizedExpenseRepository, + private val dispatcherProvider: DispatcherProvider +) : ViewModel() { + + private val _categorizedExpense = MutableStateFlow(null) + val categorizedExpense = _categorizedExpense.asStateFlow() + + val categories = categoryRepository.getCategories() + .flowOn(dispatcherProvider.io()) + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = listOf() + ) + + fun getExpense(expenseId: Int) { + viewModelScope.launch(dispatcherProvider.io()) { + categorizedExpenseRepository + .getCategorizedExpense(expenseId = expenseId) + .collect { + _categorizedExpense.emit(it) + } + } + } +} diff --git a/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt b/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt index 6a09002..6d69904 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt @@ -17,6 +17,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextDecoration import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavHostController +import br.com.tick.ui.NavigationItem import br.com.tick.ui.R import br.com.tick.ui.core.TeiraEmptyState import br.com.tick.ui.core.TeiraErrorState @@ -33,6 +35,7 @@ import br.com.tick.ui.theme.textStyle @Composable fun ExpensesGrid( modifier: Modifier = Modifier, + navHostController: NavHostController, expensesGridViewModel: ExpensesGridViewModel = hiltViewModel() ) { val expensesListState by remember { @@ -46,12 +49,13 @@ fun ExpensesGrid( AvailableBalanceIndicator(availableBalance) { expensesGridViewModel.toggleAvailableBalanceVisibility() } - Body(modifier, expensesListState, expensesGridViewModel) + Body(modifier, navHostController, expensesListState, expensesGridViewModel) } @Composable fun Body( modifier: Modifier, + navHostController: NavHostController, expensesListState: ExpensesGridStates, expensesGridViewModel: ExpensesGridViewModel ) { @@ -59,7 +63,12 @@ fun Body( is ExpensesGridStates.Empty -> TeiraEmptyState(modifier = modifier.fillMaxSize()) is ExpensesGridStates.Error -> TeiraErrorState(modifier.fillMaxSize()) is ExpensesGridStates.Loading -> TeiraLoadingState(modifier = modifier.fillMaxSize()) - is ExpensesGridStates.Success -> BodyGrid(modifier, expensesListState.expensesList, expensesGridViewModel) + is ExpensesGridStates.Success -> BodyGrid( + modifier = modifier, + expensesList = expensesListState.expensesList, + editExpense = { navHostController.navigate(NavigationItem.EditExpense.route + "$it") }, + removeExpense = { expensesGridViewModel.removeCard(it) } + ) } } @@ -68,7 +77,8 @@ fun Body( fun BodyGrid( modifier: Modifier = Modifier, expensesList: List, - expensesGridViewModel: ExpensesGridViewModel + editExpense: (expenseId: Int) -> Unit, + removeExpense: (expenseId: Int) -> Unit ) { LazyVerticalGrid( modifier = modifier.padding(), @@ -79,10 +89,10 @@ fun BodyGrid( expenseCard = expense, modifier = Modifier .fillMaxWidth() - .padding(MaterialTheme.spacing.extraSmall) - ) { expenseId -> - expensesGridViewModel.removeCard(expenseId) - } + .padding(MaterialTheme.spacing.extraSmall), + onQuickActionEdit = editExpense, + onQuickActionDelete = removeExpense + ) } } } diff --git a/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt b/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt index 6471e82..7dc5818 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt @@ -1,23 +1,17 @@ package br.com.tick.ui.screens.wallet -import android.annotation.SuppressLint +import android.util.Log import androidx.compose.animation.AnimatedContent import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.DatePicker -import androidx.compose.material3.DatePickerDefaults -import androidx.compose.material3.DatePickerDialog -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -27,11 +21,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import br.com.tick.ui.R +import br.com.tick.ui.core.TeiraDatePicker import br.com.tick.ui.core.TeiraDropdown import br.com.tick.ui.core.TeiraFilledTonalButton import br.com.tick.ui.extensions.getLabelResource @@ -39,11 +33,7 @@ import br.com.tick.ui.screens.shared.AddCategoryDialog import br.com.tick.ui.screens.wallet.viewmodels.QuickExpenseBarViewModel import br.com.tick.ui.theme.spacing import br.com.tick.ui.theme.textStyle -import java.time.Instant import java.time.LocalDate -import java.time.LocalDateTime -import java.time.ZoneId -import java.time.format.DateTimeFormatter @OptIn(ExperimentalAnimationApi::class) @Composable @@ -54,7 +44,8 @@ fun QuickExpense( AnimatedContent( modifier = modifier.fillMaxWidth(), - targetState = isExpanded + targetState = isExpanded, + label = "" ) { targetState -> if (targetState) { ExpandedQuickExpense { @@ -77,7 +68,7 @@ fun ExpandedQuickExpense( var expenseName by remember { mutableStateOf(TextFieldValue()) } var expenseValue by remember { mutableStateOf(TextFieldValue()) } var isInvalidValue by remember { mutableStateOf(false) } - var selectedCategoryId by remember { mutableStateOf(-1) } + var selectedCategoryId by remember { mutableIntStateOf(-1) } var localDateTime by remember { mutableStateOf(LocalDate.now()) } var showAddNewCategoryDialog by remember { mutableStateOf(false) } val label = stringResource(id = R.string.wallet_quick_expense_select_category) @@ -199,7 +190,11 @@ fun ExpandedQuickExpense( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.Bottom ) { - QuickExpenseDate(modifier = Modifier.weight(0.5f)) { + TeiraDatePicker( + modifier = Modifier.weight(0.5f), + color = MaterialTheme.colorScheme.onPrimary, + localDate = localDateTime + ) { localDateTime = it } TeiraFilledTonalButton( @@ -209,6 +204,7 @@ fun ExpandedQuickExpense( if (!isInvalidValue) { closeExpandedDialog() val selectedCategory = categoriesList.find { it.expenseCategoryId == selectedCategoryId } + Log.d("Tiago", "Name: $expenseName | Value: $expenseValue | selectedCategory: $selectedCategory | localDateTime: $localDateTime") if (expenseName.text.isNotEmpty() && (expenseValue.text.isNotEmpty() && expenseValue.text.toDouble() != 0.0) && selectedCategory != null @@ -248,72 +244,3 @@ fun ClosedQuickExpense(onClick: () -> Unit) { ) } } - -@SuppressLint("UnrememberedMutableState") -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun QuickExpenseDate( - modifier: Modifier = Modifier, - onDateChanged: (LocalDate) -> Unit -) { - var openDialog by remember { mutableStateOf(false) } - var localDate by remember { mutableStateOf(LocalDate.now()) } - - if (openDialog) { - val datePickerState = rememberDatePickerState() - val confirmEnabled = derivedStateOf { datePickerState.selectedDateMillis != null } - - DatePickerDialog( - onDismissRequest = { openDialog = false }, - confirmButton = { - TextButton( - onClick = { - openDialog = false - localDate = LocalDateTime.ofInstant( - Instant.ofEpochMilli(datePickerState.selectedDateMillis!!), - ZoneId.systemDefault() - ).toLocalDate() - onDateChanged(localDate) - }, - enabled = confirmEnabled.value - ) { - Text(text = stringResource(id = R.string.generic_ok)) - } - }, - dismissButton = { - TextButton( - onClick = { openDialog = false } - ) { - Text(text = stringResource(id = R.string.generic_cancel)) - } - } - ) { - DatePicker( - state = datePickerState, - colors = DatePickerDefaults.colors( - titleContentColor = MaterialTheme.colorScheme.tertiary, - headlineContentColor = MaterialTheme.colorScheme.tertiary, - weekdayContentColor = MaterialTheme.colorScheme.primary, - dayContentColor = MaterialTheme.colorScheme.primary, - disabledDayContentColor = MaterialTheme.colorScheme.secondary, - selectedDayContentColor = MaterialTheme.colorScheme.tertiary, - selectedDayContainerColor = MaterialTheme.colorScheme.onSecondary, - ) - ) - } - } - - Box( - modifier = modifier - ) { - Text( - modifier = Modifier - .align(Alignment.BottomStart) - .clickable { openDialog = true }, - text = localDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")), - textDecoration = TextDecoration.Underline, - style = MaterialTheme.textStyle.h4, - color = MaterialTheme.colorScheme.onPrimary - ) - } -} diff --git a/ui/src/main/java/br/com/tick/ui/screens/wallet/WalletScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/wallet/WalletScreen.kt index 819f2f8..a51ff9d 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/wallet/WalletScreen.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/wallet/WalletScreen.kt @@ -8,10 +8,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.navigation.NavHostController +import androidx.navigation.compose.rememberNavController import br.com.tick.ui.theme.spacing @Composable -fun WalletScreen() { +fun WalletScreen(navHostController: NavHostController) { Column( modifier = Modifier .fillMaxSize() @@ -20,6 +22,6 @@ fun WalletScreen() { verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.small) ) { QuickExpense() - ExpensesGrid() + ExpensesGrid(navHostController = navHostController) } } diff --git a/ui/src/main/res/drawable/ic_add_photo.xml b/ui/src/main/res/drawable/ic_add_photo.xml new file mode 100644 index 0000000..4859261 --- /dev/null +++ b/ui/src/main/res/drawable/ic_add_photo.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/ui/src/main/res/drawable/ic_close.xml b/ui/src/main/res/drawable/ic_close.xml new file mode 100644 index 0000000..9032fa1 --- /dev/null +++ b/ui/src/main/res/drawable/ic_close.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/ui/src/main/res/drawable/ic_location.xml b/ui/src/main/res/drawable/ic_location.xml new file mode 100644 index 0000000..aa209df --- /dev/null +++ b/ui/src/main/res/drawable/ic_location.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/ui/src/main/res/values/strings_edit_expense.xml b/ui/src/main/res/values/strings_edit_expense.xml new file mode 100644 index 0000000..f50cd85 --- /dev/null +++ b/ui/src/main/res/values/strings_edit_expense.xml @@ -0,0 +1,20 @@ + + + + Edit your expense + Select Category + Expense + Save + Add + Expense + Cost + Category + Expense Date + Label + Add new Category + Select a date + Location + Picture + Delete expense + This will erase this expense and it will not be visible again + diff --git a/ui/src/main/res/values/strings_navigation.xml b/ui/src/main/res/values/strings_navigation.xml index 890f3a2..0f648ff 100644 --- a/ui/src/main/res/values/strings_navigation.xml +++ b/ui/src/main/res/values/strings_navigation.xml @@ -5,5 +5,6 @@ Wallet Analysis History + Edit Expense From 81c97a93438f52c2221ce9aa52976eb1382f3c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Tue, 5 Sep 2023 14:00:55 +0100 Subject: [PATCH 02/14] [63] Add/Edit an Expense --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 20 +- build.gradle | 6 + sdk/build.gradle | 1 + .../br/com/tick/sdk/database/ExpenseDao.kt | 4 + .../com/tick/sdk/domain/CategorizedExpense.kt | 6 +- .../CategorizedExpenseRepository.kt | 2 + .../CategorizedExpensesRepositoryImpl.kt | 22 +- ui/build.gradle | 5 + ui/src/main/AndroidManifest.xml | 3 + .../tick/ui/screens/expense/ExpenseScreen.kt | 743 +++++++++++------- .../expense/viewmodels/ExpenseViewModel.kt | 26 + .../br/com/tick/utils/ComposeFileProvider.kt | 30 + ...s_edit_expense.xml => strings_expense.xml} | 13 +- ui/src/main/res/xml/filepaths.xml | 5 + 15 files changed, 610 insertions(+), 278 deletions(-) create mode 100644 ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt rename ui/src/main/res/values/{strings_edit_expense.xml => strings_expense.xml} (74%) create mode 100644 ui/src/main/res/xml/filepaths.xml diff --git a/app/build.gradle b/app/build.gradle index 197fa83..9264847 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,6 +5,7 @@ plugins { id 'kotlin-kapt' id 'com.google.gms.google-services' id 'com.google.firebase.crashlytics' + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' } android { @@ -101,6 +102,7 @@ dependencies { implementation "androidx.hilt:hilt-work:$hiltWorkVersion" implementation "androidx.work:work-runtime-ktx:$workManagerVersion" + kapt "androidx.hilt:hilt-compiler:$hiltCompilerVersion" kapt "com.google.dagger:hilt-compiler:$hiltVersion" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9c1c41a..11f9fbf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,7 @@ android:supportsRtl="true" android:theme="@style/Theme.Teira" tools:targetApi="31"> + - - \ No newline at end of file + + + + + + + + + diff --git a/build.gradle b/build.gradle index 123a934..87a3b1b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ buildscript { composeMaterialVersion = '1.2.0-beta03' composeFoundation = '1.4.0-alpha03' composeUi = '1.4.0-alpha03' + composeUiUtil = '1.5.0' composeMaterial3Version = '1.1.0-rc01' coroutinesVersion = '1.6.4' coreKtxVersion = '1.9.0' @@ -31,6 +32,10 @@ buildscript { hiltWorkVersion = '1.0.0' hiltCompilerVersion = '1.0.0' acompanistPermissionVersion = '0.30.0' + googleMaps = '2.14.0' + playServicesMap = '18.1.0' + playServicesLocation = '21.0.1' + coilVersion = '2.4.0' dependencies { classpath("com.google.dagger:hilt-android-gradle-plugin:$hiltVersion") @@ -47,6 +52,7 @@ plugins { id 'com.android.application' version '7.4.2' apply false id 'com.android.library' version '7.4.2' apply false id 'org.jetbrains.kotlin.android' version '1.8.0' apply false + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false } task clean(type: Delete) { diff --git a/sdk/build.gradle b/sdk/build.gradle index 35fd2fd..29d9406 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -48,6 +48,7 @@ dependencies { kapt "com.google.dagger:hilt-compiler:$hiltVersion" implementation "androidx.datastore:datastore-preferences:$dataStoreVersion" + implementation "com.google.android.gms:play-services-maps:$playServicesMap" // Unit Tests dependencies testImplementation "junit:junit:$jUnitVersion" diff --git a/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt b/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt index 93194aa..4722520 100644 --- a/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt +++ b/sdk/src/main/java/br/com/tick/sdk/database/ExpenseDao.kt @@ -3,6 +3,7 @@ package br.com.tick.sdk.database import androidx.room.Dao import androidx.room.Insert import androidx.room.Query +import androidx.room.Update import br.com.tick.sdk.database.entities.Expense import kotlinx.coroutines.flow.Flow @@ -12,6 +13,9 @@ interface ExpenseDao { @Insert suspend fun addExpense(expense: Expense) + @Update + suspend fun updateExpense(expense: Expense) + @Query("DELETE FROM expense WHERE expense_id = :expenseId") suspend fun removeExpenseById(expenseId: Int) diff --git a/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt b/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt index 02d9eb9..7c093c1 100644 --- a/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt +++ b/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt @@ -1,5 +1,7 @@ package br.com.tick.sdk.domain +import android.net.Uri +import com.google.android.gms.maps.model.LatLng import java.time.LocalDate data class CategorizedExpense( @@ -7,5 +9,7 @@ data class CategorizedExpense( val name: String, val expenseValue: Double, val date: LocalDate, - val category: ExpenseCategory + val category: ExpenseCategory, + val location: LatLng?, + val picture: Uri? ) diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt index c0260b5..46b0f96 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt @@ -8,6 +8,8 @@ interface CategorizedExpenseRepository { suspend fun addExpense(categoryId: Int, name: String, value: Double, expenseDate: LocalDate) + suspend fun updateExpense(expenseId: Int, categoryId: Int, name: String, value: Double, expenseDate: LocalDate) + suspend fun removeExpense(expenseId: Int) fun getCategorizedExpenses(): Flow> diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt index e91fdbe..9025bd6 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt @@ -38,6 +38,24 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( ) } + override suspend fun updateExpense( + expenseId: Int, + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate + ) { + expenseDao.updateExpense( + Expense( + expenseId = expenseId, + categoryId = categoryId, + name = name, + value = value, + date = expenseDate + ) + ) + } + override suspend fun removeExpense(expenseId: Int) { expenseDao.removeExpenseById(expenseId) } @@ -106,7 +124,9 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( name, value, date, - expenseCategory + expenseCategory, + null, + null ) } } diff --git a/ui/build.gradle b/ui/build.gradle index b5d86e3..e01ea00 100644 --- a/ui/build.gradle +++ b/ui/build.gradle @@ -63,6 +63,7 @@ dependencies { implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycleRuntimeCompose" implementation "androidx.compose.ui:ui:$composeUi" + implementation "androidx.compose.ui:ui-util:$composeUiUtil" implementation "androidx.compose.foundation:foundation:$composeFoundation" implementation "androidx.compose.material3:material3:$composeMaterial3Version" @@ -72,6 +73,9 @@ dependencies { debugImplementation "androidx.compose.ui:ui-tooling:$composeVersion" debugImplementation "androidx.compose.ui:ui-test-manifest:$composeVersion" implementation "com.google.accompanist:accompanist-permissions:$acompanistPermissionVersion" + implementation "com.google.maps.android:maps-compose:$googleMaps" + implementation "com.google.android.gms:play-services-maps:$playServicesMap" + implementation "com.google.android.gms:play-services-location:$playServicesLocation" implementation "com.google.dagger:hilt-android:$hiltVersion" implementation "androidx.hilt:hilt-navigation-compose:$hiltNavigationComposeVersion" @@ -83,6 +87,7 @@ dependencies { implementation "com.patrykandpatrick.vico:compose-m3:$vicoVersion" implementation "com.godaddy.android.colorpicker:compose-color-picker:$composeColorPicker" + implementation "io.coil-kt:coil-compose:$coilVersion" // Unit Tests dependencies testImplementation "junit:junit:$jUnitVersion" diff --git a/ui/src/main/AndroidManifest.xml b/ui/src/main/AndroidManifest.xml index cce937e..b626611 100644 --- a/ui/src/main/AndroidManifest.xml +++ b/ui/src/main/AndroidManifest.xml @@ -1,4 +1,7 @@ + + + \ No newline at end of file diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt index a000017..61c1d3d 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt @@ -1,6 +1,12 @@ +@file:OptIn(ExperimentalPermissionsApi::class) + package br.com.tick.ui.screens.expense -import android.util.Log +import android.Manifest +import android.annotation.SuppressLint +import android.net.Uri +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -14,6 +20,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Card import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton @@ -35,6 +42,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue @@ -42,13 +51,30 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController +import br.com.tick.sdk.domain.ExpenseCategory import br.com.tick.ui.R import br.com.tick.ui.core.TeiraDatePicker import br.com.tick.ui.core.TeiraDropdown +import br.com.tick.ui.core.TeiraEmptyState import br.com.tick.ui.screens.expense.viewmodels.ExpenseViewModel import br.com.tick.ui.screens.shared.AddCategoryDialog import br.com.tick.ui.theme.spacing import br.com.tick.ui.theme.textStyle +import br.com.tick.utils.ComposeFileProvider +import coil.compose.AsyncImage +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.isGranted +import com.google.accompanist.permissions.rememberMultiplePermissionsState +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationServices +import com.google.android.gms.maps.model.CameraPosition +import com.google.android.gms.maps.model.LatLng +import com.google.maps.android.compose.GoogleMap +import com.google.maps.android.compose.MapProperties +import com.google.maps.android.compose.MapUiSettings +import com.google.maps.android.compose.Marker +import com.google.maps.android.compose.MarkerState +import com.google.maps.android.compose.rememberCameraPositionState import java.time.LocalDate @OptIn(ExperimentalMaterial3Api::class) @@ -58,314 +84,491 @@ fun ExpenseScreen( expenseViewModel: ExpenseViewModel = hiltViewModel(), expenseId: Int? = null ) { - var showAddNewCategoryDialog by remember { mutableStateOf(false) } + var showDeletionConfirmationDialog by remember { mutableStateOf(false) } val expense by expenseViewModel.categorizedExpense.collectAsStateWithLifecycle() val categoriesList by expenseViewModel.categories.collectAsStateWithLifecycle() + var expenseName by remember { mutableStateOf(TextFieldValue()) } + var expenseValue by remember { mutableStateOf(TextFieldValue()) } + var expenseCategoryLabel by remember { mutableStateOf("") } + var expenseCategoryId by remember { mutableIntStateOf(-1) } + var expenseDate by remember { mutableStateOf(LocalDate.now()) } + var location by remember { mutableStateOf(null) } + var photoUri by remember { mutableStateOf(null) } + + expense?.let { + expenseName = TextFieldValue(it.name) + expenseValue = TextFieldValue(it.expenseValue.toString()) + expenseCategoryLabel = it.category.name + expenseCategoryId = it.category.expenseCategoryId + expenseDate = it.date + location = it.location + photoUri = it.picture + } + LaunchedEffect(key1 = Unit) { expenseId?.let { expenseViewModel.getExpense(it) } } - Box( + Column( modifier = Modifier .fillMaxSize() - .background(MaterialTheme.colorScheme.onSecondary), + .background(MaterialTheme.colorScheme.background) ) { - Column(modifier = Modifier.fillMaxSize()) { - TopAppBar( - navigationIcon = { - IconButton(onClick = { navHostController.navigateUp() }) { - Icon( - painter = painterResource(id = R.drawable.ic_close), - contentDescription = null + TopAppBar( + navigationIcon = { + IconButton(onClick = { navHostController.navigateUp() }) { + Icon( + painter = painterResource(id = R.drawable.ic_close), + contentDescription = null + ) + } + }, + title = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround + ) { + if (expense != null) { + Text( + text = stringResource(id = R.string.expense_title), + style = MaterialTheme.textStyle.h2 + ) + Text( + modifier = Modifier.clickable { + expenseViewModel.handleExpense( + expenseId = expenseId, + categoryId = expenseCategoryId, + name = expenseName.text, + value = expenseValue.text.toDouble(), + expenseDate = expenseDate + ) + }, + text = stringResource(id = R.string.expense_save), + style = MaterialTheme.textStyle.h2bold + ) + } else { + Text( + text = stringResource(id = R.string.expense_add_title), + style = MaterialTheme.textStyle.h2 + ) + Text( + modifier = Modifier.clickable { + expenseViewModel.handleExpense( + expenseId = expenseId, + categoryId = expenseCategoryId, + name = expenseName.text, + value = expenseValue.text.toDouble(), + expenseDate = expenseDate + ) + }, + text = stringResource(id = R.string.expense_add), + style = MaterialTheme.textStyle.h2bold ) } + + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.secondary, + titleContentColor = MaterialTheme.colorScheme.onSecondary, + navigationIconContentColor = MaterialTheme.colorScheme.onSecondary + ) + ) + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(MaterialTheme.spacing.small) + ) { + BaseExpense( + categoriesList = categoriesList, + expenseName = expenseName.text, + onExpenseNameChanged = { expenseName = TextFieldValue(it) }, + expenseValue = expenseValue.text, + onExpenseValueChanged = { expenseValue = TextFieldValue(it.toString()) }, + expenseCategoryLabel = expenseCategoryLabel, + expenseCategoryId = expenseCategoryId, + onExpenseCategoryChanged = { + expenseCategoryId = it.first + expenseCategoryLabel = it.second + }, + expenseDate = expenseDate, + onExpenseDateChanged = { + expenseDate = it + } + ) + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.medium), + thickness = 1.dp, + color = MaterialTheme.colorScheme.secondary + ) + ExpenseLocation(expense?.location) { location = it } + ExpensePhoto(expense?.name, expense?.picture) { photoUri = it } + } + + + if (showDeletionConfirmationDialog) { + AlertDialog( + onDismissRequest = { showDeletionConfirmationDialog = false }, + icon = { + Icon( + painter = painterResource(id = R.drawable.ic_priority_high), + contentDescription = null + ) + }, + confirmButton = { + Text( + modifier = Modifier.clickable { + // Remove the expense + showDeletionConfirmationDialog = false + }, + text = stringResource(id = R.string.generic_ok).uppercase() + ) + }, + dismissButton = { + Text( + modifier = Modifier.clickable { + showDeletionConfirmationDialog = false + }, + text = stringResource(id = R.string.generic_cancel).uppercase() + ) }, title = { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceAround - ) { - if (expense != null) { - Text( - text = stringResource(id = R.string.expense_title), - style = MaterialTheme.textStyle.h2 - ) - Text( - text = stringResource(id = R.string.expense_save), - style = MaterialTheme.textStyle.h2bold - ) - } else { - Text( - text = stringResource(id = R.string.expense_add_title), - style = MaterialTheme.textStyle.h2 - ) - Text( - text = stringResource(id = R.string.expense_add), - style = MaterialTheme.textStyle.h2bold - ) - } - } + Text(text = stringResource(id = R.string.expense_delete_title)) }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.secondary, - titleContentColor = MaterialTheme.colorScheme.onSecondary, - navigationIconContentColor = MaterialTheme.colorScheme.onSecondary - ) + text = { + Text(text = stringResource(id = R.string.expense_delete_description)) + }, + containerColor = MaterialTheme.colorScheme.onSecondary, + titleContentColor = MaterialTheme.colorScheme.tertiary, + textContentColor = MaterialTheme.colorScheme.primary, + iconContentColor = MaterialTheme.colorScheme.onTertiary ) + } - var expenseName by remember { mutableStateOf(TextFieldValue()) } - var expenseCost by remember { mutableStateOf(TextFieldValue()) } - var isInvalidValue by remember { mutableStateOf(false) } + Box( + modifier = Modifier + .fillMaxSize() + .padding(MaterialTheme.spacing.extraLarge) + ) { + if (expenseId != null) { + FloatingActionButton( + modifier = Modifier.align(Alignment.BottomEnd), + containerColor = MaterialTheme.colorScheme.tertiary, + onClick = { showDeletionConfirmationDialog = true } + ) { + Image(painter = painterResource(id = R.drawable.ic_delete), contentDescription = null) + } + } + } + } +} - var selectedCategoryId by remember { mutableIntStateOf(-1) } - val initialCategoryLabel = stringResource(id = R.string.expense_empty_category_title) - var categoryLabel by remember { mutableStateOf(initialCategoryLabel) } +@Composable +fun BaseExpense( + categoriesList: List, + expenseName: String?, + expenseValue: String?, + expenseCategoryLabel: String?, + expenseCategoryId: Int?, + expenseDate: LocalDate?, + onExpenseNameChanged: (String) -> Unit, + onExpenseValueChanged: (Double) -> Unit, + onExpenseCategoryChanged: (Pair) -> Unit, + onExpenseDateChanged: (LocalDate) -> Unit +) { + var name by remember { mutableStateOf(TextFieldValue(expenseName.orEmpty())) } + var value by remember { mutableStateOf(TextFieldValue(expenseValue.orEmpty())) } + var isInvalidValue by remember { mutableStateOf(false) } - var localDate by remember { mutableStateOf(LocalDate.now()) } + var selectedCategoryId by remember { mutableIntStateOf(expenseCategoryId ?: -1) } + val initialCategoryLabel = stringResource(id = R.string.expense_empty_category_title) + var categoryLabel by remember { mutableStateOf(expenseCategoryLabel ?: initialCategoryLabel) } + var showAddNewCategoryDialog by remember { mutableStateOf(false) } - if (showAddNewCategoryDialog) { - AddCategoryDialog(onAddNewCategory = { categoryLabel = it }) { - showAddNewCategoryDialog = false - } - } + var date by remember { mutableStateOf(expenseDate ?: LocalDate.now()) } + val expenseTextFieldColors = OutlinedTextFieldDefaults.colors( + unfocusedContainerColor = Color.Transparent, + focusedContainerColor = Color.Transparent, + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.primary, + focusedLabelColor = MaterialTheme.colorScheme.primary, + unfocusedLabelColor = MaterialTheme.colorScheme.primary, + focusedTextColor = MaterialTheme.colorScheme.secondary, + unfocusedTextColor = MaterialTheme.colorScheme.secondary, + errorBorderColor = MaterialTheme.colorScheme.tertiary, + errorLabelColor = MaterialTheme.colorScheme.tertiary, + errorTextColor = MaterialTheme.colorScheme.tertiary, + errorTrailingIconColor = MaterialTheme.colorScheme.secondary + ) - expense?.let { - expenseName = TextFieldValue(it.name) - expenseCost = TextFieldValue(it.expenseValue.toString()) - categoryLabel = it.category.name - selectedCategoryId = it.category.expenseCategoryId - localDate = it.date - Log.d("Tiago", expense.toString()) - } + if (showAddNewCategoryDialog) { + AddCategoryDialog(onAddNewCategory = { categoryLabel = it }) { + showAddNewCategoryDialog = false + } + } - val expenseTextFieldColors = OutlinedTextFieldDefaults.colors( - unfocusedContainerColor = Color.Transparent, - focusedContainerColor = Color.Transparent, - focusedBorderColor = MaterialTheme.colorScheme.primary, - unfocusedBorderColor = MaterialTheme.colorScheme.primary, - focusedLabelColor = MaterialTheme.colorScheme.primary, - unfocusedLabelColor = MaterialTheme.colorScheme.primary, - focusedTextColor = MaterialTheme.colorScheme.secondary, - unfocusedTextColor = MaterialTheme.colorScheme.secondary, - errorBorderColor = MaterialTheme.colorScheme.tertiary, - errorLabelColor = MaterialTheme.colorScheme.tertiary, - errorTextColor = MaterialTheme.colorScheme.tertiary, - errorTrailingIconColor = MaterialTheme.colorScheme.secondary + Column(modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = name, + label = { + Text( + text = stringResource(id = R.string.expense_expense_expense), + style = MaterialTheme.textStyle.h2small + ) + }, + onValueChange = { + name = it + onExpenseNameChanged(it.text) + }, + colors = expenseTextFieldColors + ) + OutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.smallest), + value = value, + label = { + Text( + text = stringResource(id = R.string.expense_expense_cost), + style = MaterialTheme.textStyle.h2small + ) + }, + onValueChange = { + value = it + isInvalidValue = false + try { + val parsedValue = value.text.toDouble() + onExpenseValueChanged(parsedValue) + } catch (exception: NumberFormatException) { + isInvalidValue = true + } + }, + colors = expenseTextFieldColors + ) + Row(modifier = Modifier.padding(top = MaterialTheme.spacing.small)) { + TeiraDropdown( + modifier = Modifier.weight(0.5f), + label = categoryLabel, + borderColor = MaterialTheme.colorScheme.primary, + dropdownItemLabels = categoriesList.map { it.name }, + dropdownItemColors = categoriesList.map { it.color }, + onItemSelected = { + selectedCategoryId = categoriesList[it].expenseCategoryId + categoryLabel = categoriesList[it].name + onExpenseCategoryChanged(Pair(selectedCategoryId, categoryLabel)) + }, + lastItemLabel = stringResource(id = R.string.expense_add_category), + onLastItemSelected = { + showAddNewCategoryDialog = true + } ) Column( modifier = Modifier - .fillMaxWidth() - .padding(MaterialTheme.spacing.small) + .weight(0.5f) + .height(56.dp), + verticalArrangement = Arrangement.Bottom, + horizontalAlignment = Alignment.End ) { - OutlinedTextField( - modifier = Modifier.fillMaxWidth(), - value = expenseName, - label = { - Text( - text = stringResource(id = R.string.expense_expense_expense), - style = MaterialTheme.textStyle.h2small - ) - }, - onValueChange = { - expenseName = it - }, - colors = expenseTextFieldColors - ) - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(top = MaterialTheme.spacing.smallest), - value = expenseCost, - label = { - Text( - text = stringResource(id = R.string.expense_expense_cost), - style = MaterialTheme.textStyle.h2small - ) - }, - onValueChange = { - expenseCost = it - isInvalidValue = false - try { - expenseCost.text.toDouble() - } catch (exception: NumberFormatException) { - isInvalidValue = true - } - }, - colors = expenseTextFieldColors - ) - Row(modifier = Modifier.padding(top = MaterialTheme.spacing.small)) { - TeiraDropdown( - modifier = Modifier.weight(0.5f), - label = categoryLabel, - borderColor = MaterialTheme.colorScheme.primary, - dropdownItemLabels = categoriesList.map { it.name }, - dropdownItemColors = categoriesList.map { it.color }, - onItemSelected = { - selectedCategoryId = categoriesList[it].expenseCategoryId - categoryLabel = categoriesList[it].name - }, - lastItemLabel = stringResource(id = R.string.expense_add_category), - onLastItemSelected = { - showAddNewCategoryDialog = true - } - ) - - Column( - modifier = Modifier - .weight(0.5f) - .height(56.dp), - verticalArrangement = Arrangement.Bottom, - horizontalAlignment = Alignment.End - ) { - TeiraDatePicker(localDate = localDate) { - localDate = it - } - } + TeiraDatePicker(localDate = date) { + date = it + onExpenseDateChanged(date) } + } + } + } +} - Divider( - modifier = Modifier - .fillMaxWidth() - .padding(top = MaterialTheme.spacing.medium), - thickness = 1.dp, - color = MaterialTheme.colorScheme.secondary - ) +@SuppressLint("MissingPermission") +@Composable +fun ExpenseLocation( + expenseLocation: LatLng?, + onExpenseLocationChanged: (LatLng) -> Unit +) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.medium), + verticalAlignment = Alignment.Bottom + ) { + Icon( + modifier = Modifier.size(32.dp), + painter = painterResource(id = R.drawable.ic_location), + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) + Text( + modifier = Modifier.padding(start = MaterialTheme.spacing.extraSmall), + text = stringResource(id = R.string.expense_location), + style = MaterialTheme.textStyle.h2, + color = MaterialTheme.colorScheme.onTertiary + ) + } - Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = MaterialTheme.spacing.medium), - verticalAlignment = Alignment.Bottom - ) { - Icon( - modifier = Modifier.size(32.dp), - painter = painterResource(id = R.drawable.ic_location), - contentDescription = null, - tint = MaterialTheme.colorScheme.tertiary - ) - Text( - modifier = Modifier.padding(start = MaterialTheme.spacing.extraSmall), - text = stringResource(id = R.string.expense_location), - style = MaterialTheme.textStyle.h2, - color = MaterialTheme.colorScheme.onTertiary - ) - Box(modifier = Modifier.fillMaxWidth()) { - Icon( - modifier = Modifier - .size(32.dp) - .align(Alignment.BottomEnd), - painter = painterResource(id = R.drawable.ic_add), - contentDescription = null, - tint = MaterialTheme.colorScheme.tertiary - ) - } - } + val context = LocalContext.current + if (expenseLocation != null) { + ExpenseLocationMap(location = expenseLocation, onExpenseLocationChanged = onExpenseLocationChanged) + } else { + val locationPermissionState = rememberMultiplePermissionsState( + listOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION) + ) + val hasPermissions = locationPermissionState.permissions.all { it.status.isGranted } + val emptyStateText = if (hasPermissions) { + R.string.expense_location_empty_state_label + } else { + R.string.expense_location_empty_no_permission_state_label + } + var capturedLocation by remember { mutableStateOf(null) } - Text( - modifier = Modifier.padding(top = MaterialTheme.spacing.extraSmall), - text = "Rua Maria Feliciana, 210, Apartamento 608", - style = MaterialTheme.textStyle.h2small, - color = MaterialTheme.colorScheme.secondary - ) + if (capturedLocation != null) { + capturedLocation?.let { + ExpenseLocationMap(location = it, onExpenseLocationChanged = onExpenseLocationChanged) + } + } else { + TeiraEmptyState( + modifier = Modifier + .fillMaxWidth() + .height(160.dp) + .padding(top = MaterialTheme.spacing.small) + .clickable { + if (hasPermissions) { + val locationProvider: FusedLocationProviderClient = + LocationServices.getFusedLocationProviderClient(context) + locationProvider.lastLocation.addOnCompleteListener { + val lastKnownLocation = it.result + capturedLocation = LatLng(lastKnownLocation.latitude, lastKnownLocation.longitude) + } + } else { + locationPermissionState.launchMultiplePermissionRequest() + } + }, + emptyStateLabel = emptyStateText + ) + } + } +} - Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = MaterialTheme.spacing.extraLarge), - verticalAlignment = Alignment.Bottom - ) { - Icon( - modifier = Modifier.size(32.dp), - painter = painterResource(id = R.drawable.ic_add_photo), - contentDescription = null, - tint = MaterialTheme.colorScheme.tertiary - ) - Text( - modifier = Modifier.padding(start = MaterialTheme.spacing.extraSmall), - text = stringResource(id = R.string.expense_picture), - style = MaterialTheme.textStyle.h2, - color = MaterialTheme.colorScheme.onTertiary - ) - Box(modifier = Modifier.fillMaxWidth()) { - Icon( - modifier = Modifier - .size(32.dp) - .align(Alignment.BottomEnd), - painter = painterResource(id = R.drawable.ic_add), - contentDescription = null, - tint = MaterialTheme.colorScheme.tertiary - ) - } - } +@Composable +fun ExpenseLocationMap( + location: LatLng, + onExpenseLocationChanged: (LatLng) -> Unit +) { + val minZoomPossible = 12f + val cameraPositionState = rememberCameraPositionState { + position = CameraPosition.fromLatLngZoom(location, minZoomPossible) + } + val mapProperties by remember { + mutableStateOf( + MapProperties(maxZoomPreference = 21f, minZoomPreference = minZoomPossible) + ) + } + val mapUiSettings by remember { + mutableStateOf( + MapUiSettings(mapToolbarEnabled = false, zoomControlsEnabled = false) + ) + } + val markerState = MarkerState(position = location) - Text( - modifier = Modifier.padding(top = MaterialTheme.spacing.extraSmall), - text = "temp/0/whatever/Teira/1837984.jpg", - style = MaterialTheme.textStyle.h2small, - color = MaterialTheme.colorScheme.secondary - ) + Card( + modifier = Modifier + .fillMaxWidth() + .height(160.dp) + .padding(top = MaterialTheme.spacing.small) + ) { + GoogleMap( + modifier = Modifier.fillMaxSize(), + properties = mapProperties, + uiSettings = mapUiSettings, + cameraPositionState = cameraPositionState, + onMapClick = { + markerState.position = it + onExpenseLocationChanged(it) } + ) { + Marker( + state = markerState, + title = stringResource(id = R.string.expense_expense_expense) + ) + } + } +} - var showDeletionConfirmationDialog by remember { mutableStateOf(false) } +@Composable +fun ExpensePhoto( + expenseName: String?, + expensePhotoUri: Uri?, + onPictureSelected: (Uri) -> Unit +) { + var hasImage by remember { mutableStateOf(expensePhotoUri != null) } + var imageUri by remember { mutableStateOf(expensePhotoUri) } - if (showDeletionConfirmationDialog) { - AlertDialog( - onDismissRequest = { showDeletionConfirmationDialog = false }, - icon = { - Icon( - painter = painterResource(id = R.drawable.ic_priority_high), - contentDescription = null - ) - }, - confirmButton = { - Text( - modifier = Modifier.clickable { - // Remove the expense - showDeletionConfirmationDialog = false - }, - text = stringResource(id = R.string.generic_ok).uppercase() - ) - }, - dismissButton = { - Text( - modifier = Modifier.clickable { - showDeletionConfirmationDialog = false - }, - text = stringResource(id = R.string.generic_cancel).uppercase() - ) - }, - title = { - Text(text = stringResource(id = R.string.expense_delete_title)) - }, - text = { - Text(text = stringResource(id = R.string.expense_delete_description)) - }, - containerColor = MaterialTheme.colorScheme.onSecondary, - titleContentColor = MaterialTheme.colorScheme.tertiary, - textContentColor = MaterialTheme.colorScheme.primary, - iconContentColor = MaterialTheme.colorScheme.onTertiary - ) - } + val context = LocalContext.current + val cameraLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.TakePicture(), + onResult = { success -> + hasImage = success + imageUri?.let(onPictureSelected) + } + ) - Box( - modifier = Modifier - .fillMaxSize() - .padding(MaterialTheme.spacing.extraLarge) - ) { - if (expenseId != null) { - FloatingActionButton( - modifier = Modifier.align(Alignment.BottomEnd), - containerColor = MaterialTheme.colorScheme.tertiary, - onClick = { showDeletionConfirmationDialog = true} - ) { - Image(painter = painterResource(id = R.drawable.ic_delete), contentDescription = null) - } - } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.extraLarge), + verticalAlignment = Alignment.Bottom + ) { + Icon( + modifier = Modifier.size(32.dp), + painter = painterResource(id = R.drawable.ic_add_photo), + contentDescription = null, + tint = MaterialTheme.colorScheme.tertiary + ) + Text( + modifier = Modifier.padding(start = MaterialTheme.spacing.extraSmall), + text = stringResource(id = R.string.expense_picture), + style = MaterialTheme.textStyle.h2, + color = MaterialTheme.colorScheme.onTertiary + ) + } + + if (imageUri != null) { + Card( + modifier = Modifier + .fillMaxWidth() + .height(160.dp) + .padding(top = MaterialTheme.spacing.small) + ) { + if (hasImage && imageUri != null) { + AsyncImage( + model = imageUri, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop, + contentDescription = "Selected image", + ) } } + } else { + TeiraEmptyState( + modifier = Modifier + .fillMaxWidth() + .height(160.dp) + .padding(top = MaterialTheme.spacing.small) + .clickable { + val fileName = expenseName ?: LocalDate + .now() + .toString() + val uri = ComposeFileProvider.getImageUri(fileName, context) + imageUri = uri + cameraLauncher.launch(uri) + }, + emptyStateLabel = R.string.expense_picture_empty_state_label + ) } -} \ No newline at end of file +} diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt index 57c6c2f..9c51d8b 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt @@ -13,12 +13,14 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import java.time.LocalDate import javax.inject.Inject @HiltViewModel class ExpenseViewModel @Inject constructor( categoryRepository: ExpenseCategoryRepository, private val categorizedExpenseRepository: CategorizedExpenseRepository, + private val expenseRepository: CategorizedExpenseRepository, private val dispatcherProvider: DispatcherProvider ) : ViewModel() { @@ -42,4 +44,28 @@ class ExpenseViewModel @Inject constructor( } } } + + fun handleExpense(expenseId: Int? = null, categoryId: Int, name: String, value: Double, expenseDate: LocalDate) { + viewModelScope.launch(dispatcherProvider.io()) { + if (expenseId != null) { + saveExpense(expenseId, categoryId, name, value, expenseDate) + } else { + addExpense(categoryId, name, value, expenseDate) + } + } + } + + private suspend fun addExpense(categoryId: Int, name: String, value: Double, expenseDate: LocalDate) { + expenseRepository.addExpense(categoryId, name, value, expenseDate) + } + + private suspend fun saveExpense( + expenseId: Int, + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate + ) { + expenseRepository.updateExpense(expenseId, categoryId, name, value, expenseDate) + } } diff --git a/ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt b/ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt new file mode 100644 index 0000000..3607837 --- /dev/null +++ b/ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt @@ -0,0 +1,30 @@ +package br.com.tick.utils + +import android.content.Context +import android.net.Uri +import androidx.core.content.FileProvider +import br.com.tick.ui.R +import java.io.File + +class ComposeFileProvider : FileProvider(R.xml.filepaths) { + companion object { + fun getImageUri(fileName: String, context: Context): Uri { + val directory = File(context.filesDir, "images") + directory.mkdirs() + + val file = File.createTempFile( + "${fileName}_", + ".jpg", + directory + ) + + val authority = context.packageName + ".fileprovider" + + return getUriForFile( + context, + authority, + file, + ) + } + } +} diff --git a/ui/src/main/res/values/strings_edit_expense.xml b/ui/src/main/res/values/strings_expense.xml similarity index 74% rename from ui/src/main/res/values/strings_edit_expense.xml rename to ui/src/main/res/values/strings_expense.xml index f50cd85..8d66dcd 100644 --- a/ui/src/main/res/values/strings_edit_expense.xml +++ b/ui/src/main/res/values/strings_expense.xml @@ -2,19 +2,24 @@ Edit your expense - Select Category Expense + Save Add + Expense Cost + Select Category Category - Expense Date - Label Add new Category - Select a date + Expense Date + Location + We need your permission to get your current location + Inform this expense location Picture + Take a picture of your expense + Delete expense This will erase this expense and it will not be visible again diff --git a/ui/src/main/res/xml/filepaths.xml b/ui/src/main/res/xml/filepaths.xml new file mode 100644 index 0000000..57c458e --- /dev/null +++ b/ui/src/main/res/xml/filepaths.xml @@ -0,0 +1,5 @@ + + + + + From 067a19bd63eec3aacdbed6b4f5cb1d8de6bdd702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Thu, 7 Sep 2023 10:24:24 +0100 Subject: [PATCH 03/14] [63] WIP Fixing nested composes --- .../tick/ui/screens/expense/ExpenseScreen.kt | 273 ++++++++++-------- 1 file changed, 146 insertions(+), 127 deletions(-) diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt index 61c1d3d..93b8970 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt @@ -29,15 +29,21 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableDoubleStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -51,6 +57,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController +import br.com.tick.sdk.domain.CategorizedExpense import br.com.tick.sdk.domain.ExpenseCategory import br.com.tick.ui.R import br.com.tick.ui.core.TeiraDatePicker @@ -75,6 +82,7 @@ import com.google.maps.android.compose.MapUiSettings import com.google.maps.android.compose.Marker import com.google.maps.android.compose.MarkerState import com.google.maps.android.compose.rememberCameraPositionState +import kotlinx.coroutines.launch import java.time.LocalDate @OptIn(ExperimentalMaterial3Api::class) @@ -89,115 +97,120 @@ fun ExpenseScreen( val expense by expenseViewModel.categorizedExpense.collectAsStateWithLifecycle() val categoriesList by expenseViewModel.categories.collectAsStateWithLifecycle() - var expenseName by remember { mutableStateOf(TextFieldValue()) } - var expenseValue by remember { mutableStateOf(TextFieldValue()) } - var expenseCategoryLabel by remember { mutableStateOf("") } + var expenseName by remember { mutableStateOf("") } + var expenseValue by remember { mutableDoubleStateOf(0.0) } + var isInvalidValue by remember { mutableStateOf(false) } var expenseCategoryId by remember { mutableIntStateOf(-1) } var expenseDate by remember { mutableStateOf(LocalDate.now()) } var location by remember { mutableStateOf(null) } var photoUri by remember { mutableStateOf(null) } - expense?.let { - expenseName = TextFieldValue(it.name) - expenseValue = TextFieldValue(it.expenseValue.toString()) - expenseCategoryLabel = it.category.name - expenseCategoryId = it.category.expenseCategoryId - expenseDate = it.date - location = it.location - photoUri = it.picture - } + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() LaunchedEffect(key1 = Unit) { expenseId?.let { expenseViewModel.getExpense(it) } } - Column( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colorScheme.background) - ) { - TopAppBar( - navigationIcon = { - IconButton(onClick = { navHostController.navigateUp() }) { - Icon( - painter = painterResource(id = R.drawable.ic_close), - contentDescription = null - ) - } - }, - title = { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceAround - ) { - if (expense != null) { - Text( - text = stringResource(id = R.string.expense_title), - style = MaterialTheme.textStyle.h2 - ) - Text( - modifier = Modifier.clickable { - expenseViewModel.handleExpense( - expenseId = expenseId, - categoryId = expenseCategoryId, - name = expenseName.text, - value = expenseValue.text.toDouble(), - expenseDate = expenseDate - ) - }, - text = stringResource(id = R.string.expense_save), - style = MaterialTheme.textStyle.h2bold - ) - } else { - Text( - text = stringResource(id = R.string.expense_add_title), - style = MaterialTheme.textStyle.h2 - ) - Text( - modifier = Modifier.clickable { - expenseViewModel.handleExpense( - expenseId = expenseId, - categoryId = expenseCategoryId, - name = expenseName.text, - value = expenseValue.text.toDouble(), - expenseDate = expenseDate - ) - }, - text = stringResource(id = R.string.expense_add), - style = MaterialTheme.textStyle.h2bold + Scaffold( + modifier = Modifier.fillMaxSize(), + containerColor = MaterialTheme.colorScheme.background, + topBar = { + TopAppBar( + navigationIcon = { + IconButton(onClick = { navHostController.navigateUp() }) { + Icon( + painter = painterResource(id = R.drawable.ic_close), + contentDescription = null ) } + }, + title = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceAround + ) { + if (expense != null) { + Text( + text = stringResource(id = R.string.expense_title), + style = MaterialTheme.textStyle.h2 + ) + Text( + modifier = Modifier.clickable { + if (isInvalidValue) { + scope.launch { + snackbarHostState + .showSnackbar( + message = "Algo de errado aconteceu. Mudanças não foram salvas.", + duration = SnackbarDuration.Short + ) + } + } else { + expenseViewModel.handleExpense( + expenseId = expenseId, + categoryId = expenseCategoryId, + name = expenseName, + value = expenseValue, + expenseDate = expenseDate + ) + } + }, + text = stringResource(id = R.string.expense_save), + style = MaterialTheme.textStyle.h2bold + ) + } else { + Text( + text = stringResource(id = R.string.expense_add_title), + style = MaterialTheme.textStyle.h2 + ) + Text( + modifier = Modifier.clickable { + expenseViewModel.handleExpense( + expenseId = expenseId, + categoryId = expenseCategoryId, + name = expenseName, + value = expenseValue, + expenseDate = expenseDate + ) + }, + text = stringResource(id = R.string.expense_add), + style = MaterialTheme.textStyle.h2bold + ) + } - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.secondary, - titleContentColor = MaterialTheme.colorScheme.onSecondary, - navigationIconContentColor = MaterialTheme.colorScheme.onSecondary + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.secondary, + titleContentColor = MaterialTheme.colorScheme.onSecondary, + navigationIconContentColor = MaterialTheme.colorScheme.onSecondary + ) ) - ) - - Column( - modifier = Modifier - .fillMaxWidth() - .padding(MaterialTheme.spacing.small) - ) { + }, + floatingActionButton = { + if (expenseId != null) { + FloatingActionButton( + containerColor = MaterialTheme.colorScheme.tertiary, + onClick = { showDeletionConfirmationDialog = true } + ) { + Image( + painter = painterResource(id = R.drawable.ic_delete), + contentDescription = null + ) + } + } + }, + snackbarHost = { SnackbarHost(hostState = snackbarHostState) } + ) { paddingValues -> + Column(modifier = Modifier.fillMaxWidth().padding(paddingValues)) { BaseExpense( + expense = expense, categoriesList = categoriesList, - expenseName = expenseName.text, - onExpenseNameChanged = { expenseName = TextFieldValue(it) }, - expenseValue = expenseValue.text, - onExpenseValueChanged = { expenseValue = TextFieldValue(it.toString()) }, - expenseCategoryLabel = expenseCategoryLabel, - expenseCategoryId = expenseCategoryId, - onExpenseCategoryChanged = { - expenseCategoryId = it.first - expenseCategoryLabel = it.second - }, - expenseDate = expenseDate, - onExpenseDateChanged = { - expenseDate = it - } + onExpenseNameChanged = { expenseName = it }, + onExpenseValueChanged = { expenseValue = it }, + onExpenseCategoryChanged = { expenseCategoryId = it }, + onExpenseDateChanged = { expenseDate = it }, + onInvalidValue = { isInvalidValue = it } ) Divider( modifier = Modifier @@ -249,48 +262,39 @@ fun ExpenseScreen( iconContentColor = MaterialTheme.colorScheme.onTertiary ) } - - Box( - modifier = Modifier - .fillMaxSize() - .padding(MaterialTheme.spacing.extraLarge) - ) { - if (expenseId != null) { - FloatingActionButton( - modifier = Modifier.align(Alignment.BottomEnd), - containerColor = MaterialTheme.colorScheme.tertiary, - onClick = { showDeletionConfirmationDialog = true } - ) { - Image(painter = painterResource(id = R.drawable.ic_delete), contentDescription = null) - } - } - } } } @Composable fun BaseExpense( + expense: CategorizedExpense?, categoriesList: List, - expenseName: String?, - expenseValue: String?, - expenseCategoryLabel: String?, - expenseCategoryId: Int?, - expenseDate: LocalDate?, onExpenseNameChanged: (String) -> Unit, onExpenseValueChanged: (Double) -> Unit, - onExpenseCategoryChanged: (Pair) -> Unit, - onExpenseDateChanged: (LocalDate) -> Unit + onExpenseCategoryChanged: (Int) -> Unit, + onExpenseDateChanged: (LocalDate) -> Unit, + onInvalidValue: (Boolean) -> Unit ) { - var name by remember { mutableStateOf(TextFieldValue(expenseName.orEmpty())) } - var value by remember { mutableStateOf(TextFieldValue(expenseValue.orEmpty())) } + var name by remember { mutableStateOf(TextFieldValue()) } + var value by remember { mutableStateOf(TextFieldValue()) } var isInvalidValue by remember { mutableStateOf(false) } - var selectedCategoryId by remember { mutableIntStateOf(expenseCategoryId ?: -1) } + var selectedCategoryId by remember { mutableIntStateOf(-1) } val initialCategoryLabel = stringResource(id = R.string.expense_empty_category_title) - var categoryLabel by remember { mutableStateOf(expenseCategoryLabel ?: initialCategoryLabel) } + var categoryLabel by remember { mutableStateOf(initialCategoryLabel) } var showAddNewCategoryDialog by remember { mutableStateOf(false) } - var date by remember { mutableStateOf(expenseDate ?: LocalDate.now()) } + var date by remember { mutableStateOf(LocalDate.now()) } + + LaunchedEffect(key1 = expense) { + expense?.let { + name = TextFieldValue(it.name) + value = TextFieldValue(it.expenseValue.toString()) + categoryLabel = it.category.name + selectedCategoryId = it.category.expenseCategoryId + date = it.date + } + } val expenseTextFieldColors = OutlinedTextFieldDefaults.colors( unfocusedContainerColor = Color.Transparent, @@ -340,14 +344,20 @@ fun BaseExpense( style = MaterialTheme.textStyle.h2small ) }, + isError = isInvalidValue, + prefix = { + Text(text = "€") + }, onValueChange = { value = it isInvalidValue = false try { val parsedValue = value.text.toDouble() + onInvalidValue(false) onExpenseValueChanged(parsedValue) } catch (exception: NumberFormatException) { isInvalidValue = true + onInvalidValue(true) } }, colors = expenseTextFieldColors @@ -360,9 +370,8 @@ fun BaseExpense( dropdownItemLabels = categoriesList.map { it.name }, dropdownItemColors = categoriesList.map { it.color }, onItemSelected = { - selectedCategoryId = categoriesList[it].expenseCategoryId categoryLabel = categoriesList[it].name - onExpenseCategoryChanged(Pair(selectedCategoryId, categoryLabel)) + onExpenseCategoryChanged(categoriesList[it].expenseCategoryId) }, lastItemLabel = stringResource(id = R.string.expense_add_category), onLastItemSelected = { @@ -414,10 +423,16 @@ fun ExpenseLocation( val context = LocalContext.current if (expenseLocation != null) { - ExpenseLocationMap(location = expenseLocation, onExpenseLocationChanged = onExpenseLocationChanged) + ExpenseLocationMap( + location = expenseLocation, + onExpenseLocationChanged = onExpenseLocationChanged + ) } else { val locationPermissionState = rememberMultiplePermissionsState( - listOf(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION) + listOf( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION + ) ) val hasPermissions = locationPermissionState.permissions.all { it.status.isGranted } val emptyStateText = if (hasPermissions) { @@ -429,7 +444,10 @@ fun ExpenseLocation( if (capturedLocation != null) { capturedLocation?.let { - ExpenseLocationMap(location = it, onExpenseLocationChanged = onExpenseLocationChanged) + ExpenseLocationMap( + location = it, + onExpenseLocationChanged = onExpenseLocationChanged + ) } } else { TeiraEmptyState( @@ -443,7 +461,8 @@ fun ExpenseLocation( LocationServices.getFusedLocationProviderClient(context) locationProvider.lastLocation.addOnCompleteListener { val lastKnownLocation = it.result - capturedLocation = LatLng(lastKnownLocation.latitude, lastKnownLocation.longitude) + capturedLocation = + LatLng(lastKnownLocation.latitude, lastKnownLocation.longitude) } } else { locationPermissionState.launchMultiplePermissionRequest() From 4409eda8fbcdd98caa76b54086cb754273a1fc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Thu, 14 Sep 2023 21:51:59 +0100 Subject: [PATCH 04/14] [63] Stable Version for Adding and Changing Expenses --- .../database/converters/LatLngConverter.kt | 26 ++ .../database/converters/LocalDateConverter.kt | 2 +- .../com/tick/sdk/database/entities/Expense.kt | 13 +- .../com/tick/sdk/domain/CategorizedExpense.kt | 1 + .../CategorizedExpenseRepository.kt | 23 +- .../CategorizedExpensesRepositoryImpl.kt | 30 +- .../java/br/com/tick/ui/NavigationItem.kt | 17 +- .../main/java/br/com/tick/ui/TeiraScaffold.kt | 12 +- .../tick/ui/screens/expense/ExpenseScreen.kt | 296 ++++++++++++------ .../expense/viewmodels/ExpenseViewModel.kt | 44 ++- .../tick/ui/screens/wallet/ExpensesGrid.kt | 2 +- ui/src/main/res/values/strings_expense.xml | 2 + 12 files changed, 331 insertions(+), 137 deletions(-) create mode 100644 sdk/src/main/java/br/com/tick/sdk/database/converters/LatLngConverter.kt diff --git a/sdk/src/main/java/br/com/tick/sdk/database/converters/LatLngConverter.kt b/sdk/src/main/java/br/com/tick/sdk/database/converters/LatLngConverter.kt new file mode 100644 index 0000000..7bc17e8 --- /dev/null +++ b/sdk/src/main/java/br/com/tick/sdk/database/converters/LatLngConverter.kt @@ -0,0 +1,26 @@ +package br.com.tick.sdk.database.converters + +import androidx.room.TypeConverter +import com.google.android.gms.maps.model.LatLng + +class LatLngConverter { + + companion object { + const val LAT_LNG_SPLITTER = "|" + } + + @TypeConverter + fun toString(location: LatLng?): String? { + if (location == null) return null + + return "${location.latitude}$LAT_LNG_SPLITTER${location.longitude}" + } + + @TypeConverter + fun fromString(location: String?): LatLng? { + if (location == null) return null + + val locationFromString = location.split(LAT_LNG_SPLITTER) + return LatLng(locationFromString[0].toDouble(), locationFromString[1].toDouble()) + } +} diff --git a/sdk/src/main/java/br/com/tick/sdk/database/converters/LocalDateConverter.kt b/sdk/src/main/java/br/com/tick/sdk/database/converters/LocalDateConverter.kt index 8dfba85..7b08eeb 100644 --- a/sdk/src/main/java/br/com/tick/sdk/database/converters/LocalDateConverter.kt +++ b/sdk/src/main/java/br/com/tick/sdk/database/converters/LocalDateConverter.kt @@ -14,4 +14,4 @@ class LocalDateConverter { fun fromLong(value: Long): LocalDate { return LocalDate.ofEpochDay(value) } -} \ No newline at end of file +} diff --git a/sdk/src/main/java/br/com/tick/sdk/database/entities/Expense.kt b/sdk/src/main/java/br/com/tick/sdk/database/entities/Expense.kt index b576ba4..2fd59a3 100644 --- a/sdk/src/main/java/br/com/tick/sdk/database/entities/Expense.kt +++ b/sdk/src/main/java/br/com/tick/sdk/database/entities/Expense.kt @@ -4,11 +4,13 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import androidx.room.TypeConverters +import br.com.tick.sdk.database.converters.LatLngConverter import br.com.tick.sdk.database.converters.LocalDateConverter +import com.google.android.gms.maps.model.LatLng import java.time.LocalDate @Entity -@TypeConverters(LocalDateConverter::class) +@TypeConverters(LocalDateConverter::class, LatLngConverter::class) data class Expense( @PrimaryKey(autoGenerate = true) @@ -25,5 +27,12 @@ data class Expense( val value: Double, @ColumnInfo(name = "date") - val date: LocalDate + val date: LocalDate, + + @ColumnInfo(name = "location") + val location: LatLng?, + + @ColumnInfo(name = "photoUri") + val photoUri: String? + ) diff --git a/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt b/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt index 7c093c1..f0ab975 100644 --- a/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt +++ b/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt @@ -8,6 +8,7 @@ data class CategorizedExpense( val expenseId: Int, val name: String, val expenseValue: Double, + val currencyFormat: CurrencyFormat, val date: LocalDate, val category: ExpenseCategory, val location: LatLng?, diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt index 46b0f96..fc6b9b1 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpenseRepository.kt @@ -1,14 +1,31 @@ package br.com.tick.sdk.repositories.categorizedexpense +import android.net.Uri import br.com.tick.sdk.domain.CategorizedExpense +import com.google.android.gms.maps.model.LatLng import kotlinx.coroutines.flow.Flow import java.time.LocalDate interface CategorizedExpenseRepository { - suspend fun addExpense(categoryId: Int, name: String, value: Double, expenseDate: LocalDate) - - suspend fun updateExpense(expenseId: Int, categoryId: Int, name: String, value: Double, expenseDate: LocalDate) + suspend fun addExpense( + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate, + location: LatLng? = null, + photoUri: Uri? = null + ) + + suspend fun updateExpense( + expenseId: Int, + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate, + location: LatLng? = null, + photoUri: Uri? = null + ) suspend fun removeExpense(expenseId: Int) diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt index 9025bd6..fbead10 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt @@ -1,5 +1,6 @@ package br.com.tick.sdk.repositories.categorizedexpense +import android.net.Uri import br.com.tick.sdk.database.CategoryColorDao import br.com.tick.sdk.database.CategoryDao import br.com.tick.sdk.database.ExpenseDao @@ -8,6 +9,7 @@ import br.com.tick.sdk.database.entities.Expense import br.com.tick.sdk.domain.CategorizedExpense import br.com.tick.sdk.domain.ExpenseCategory import br.com.tick.sdk.domain.getAccountingDateDayOfMonth +import com.google.android.gms.maps.model.LatLng import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first @@ -26,14 +28,18 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( categoryId: Int, name: String, value: Double, - expenseDate: LocalDate + expenseDate: LocalDate, + location: LatLng?, + photoUri: Uri? ) { expenseDao.addExpense( Expense( categoryId = categoryId, name = name, value = value, - date = expenseDate + date = expenseDate, + location = location, + photoUri = photoUri?.toString() ) ) } @@ -43,7 +49,9 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( categoryId: Int, name: String, value: Double, - expenseDate: LocalDate + expenseDate: LocalDate, + location: LatLng?, + photoUri: Uri? ) { expenseDao.updateExpense( Expense( @@ -51,7 +59,9 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( categoryId = categoryId, name = name, value = value, - date = expenseDate + date = expenseDate, + location = location, + photoUri = photoUri?.toString() ) ) } @@ -61,7 +71,7 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( } override fun getCategorizedExpenses(): Flow> { - return expenseDao.getExpenses().map { expenses -> + return expenseDao.getExpenses().filterNotNull().map { expenses -> expenses.map { categorize(it) } } } @@ -81,7 +91,7 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( pivot.withDayOfMonth(userAccountingDayOfMonth) } - return expenseDao.getAllExpenses().map { expenses -> + return expenseDao.getAllExpenses().filterNotNull().map { expenses -> expenses.filter { expense -> val yearDiff = nextAccountingDate.year - expense.date.year val monthDiff = nextAccountingDate.month.value - expense.date.month.value @@ -102,11 +112,12 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( } override fun getCategorizedExpense(expenseId: Int): Flow { - return expenseDao.getExpense(expenseId).map { categorize(it) } + return expenseDao.getExpense(expenseId).filterNotNull().map { categorize(it) } } private suspend fun categorize(expense: Expense): CategorizedExpense { val category = categoryDao.getCategoryById(expense.categoryId) + val user = userDao.getUniqueUser().first() val categoryColorId = category.categoryColorId val color = if (categoryColorId != null) { @@ -123,10 +134,11 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( expenseId, name, value, + user.currency, date, expenseCategory, - null, - null + location, + photoUri?.let { Uri.parse(it) } ) } } diff --git a/ui/src/main/java/br/com/tick/ui/NavigationItem.kt b/ui/src/main/java/br/com/tick/ui/NavigationItem.kt index d6ef86c..455844e 100644 --- a/ui/src/main/java/br/com/tick/ui/NavigationItem.kt +++ b/ui/src/main/java/br/com/tick/ui/NavigationItem.kt @@ -1,24 +1,35 @@ package br.com.tick.ui import br.com.tick.ui.NavigationItem.Routes.ANALYSIS -import br.com.tick.ui.NavigationItem.Routes.EDIT_EXPENSE +import br.com.tick.ui.NavigationItem.Routes.EXPENSE +import br.com.tick.ui.NavigationItem.Routes.EXPENSE_ID_TAG import br.com.tick.ui.NavigationItem.Routes.HISTORY +import br.com.tick.ui.NavigationItem.Routes.HOME import br.com.tick.ui.NavigationItem.Routes.SETTINGS import br.com.tick.ui.NavigationItem.Routes.WALLET sealed class NavigationItem(var route: String, var iconResource: Int, var titleResource: Int) { private object Routes { + const val HOME = "home" const val SETTINGS = "configuration" const val WALLET = "wallet" const val ANALYSIS = "analysis" const val HISTORY = "history" - const val EDIT_EXPENSE = "editExpense/" + const val EXPENSE_ID_TAG = "{expenseId}" + const val EXPENSE = "editExpense/$EXPENSE_ID_TAG" } + object Home : NavigationItem(HOME, R.drawable.ic_wallet, R.string.navigation_item_wallet) object Settings : NavigationItem(SETTINGS, R.drawable.ic_settings, R.string.navigation_item_settings) object Wallet : NavigationItem(WALLET, R.drawable.ic_wallet, R.string.navigation_item_wallet) object Analysis : NavigationItem(ANALYSIS, R.drawable.ic_analysis, R.string.navigation_item_analysis) object History : NavigationItem(HISTORY, R.drawable.ic_history, R.string.navigation_item_history) - object EditExpense : NavigationItem(EDIT_EXPENSE, R.drawable.ic_edit, R.string.navigation_item_edit_expense) + object EditExpense: NavigationItem(EXPENSE, R.drawable.ic_edit, R.string.navigation_item_edit_expense) { + + const val NAVIGATION_EXPENSE_ID_TAG = "expenseId" + fun with(expenseId: Int): String { + return EXPENSE.replace(EXPENSE_ID_TAG, expenseId.toString()) + } + } } diff --git a/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt b/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt index cef4892..306e2d5 100644 --- a/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt +++ b/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt @@ -93,14 +93,16 @@ fun TeiraScaffold( NavHost( navController = navHostController, - startDestination = "home" + startDestination = NavigationItem.Home.route ) { - composable("home") { + composable(NavigationItem.Home.route) { HomeScreen(navHostController) } composable( - route = NavigationItem.EditExpense.route + "{expenseId}", - arguments = listOf(navArgument("expenseId") { type = NavType.IntType }), + route = NavigationItem.EditExpense.route, + arguments = listOf( + navArgument(NavigationItem.EditExpense.NAVIGATION_EXPENSE_ID_TAG) { type = NavType.IntType } + ), enterTransition = { slideIntoContainer( animationSpec = tween(400, easing = EaseInOut), @@ -114,7 +116,7 @@ fun TeiraScaffold( ) } ) { navBackStackEntry -> - navBackStackEntry.arguments?.getInt("expenseId")?.let { + navBackStackEntry.arguments?.getInt(NavigationItem.EditExpense.NAVIGATION_EXPENSE_ID_TAG)?.let { ExpenseScreen(navHostController = navHostController, expenseId = it) } } diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt index 93b8970..8bd8301 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt @@ -5,10 +5,10 @@ package br.com.tick.ui.screens.expense import android.Manifest import android.annotation.SuppressLint import android.net.Uri +import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -63,6 +63,7 @@ import br.com.tick.ui.R import br.com.tick.ui.core.TeiraDatePicker import br.com.tick.ui.core.TeiraDropdown import br.com.tick.ui.core.TeiraEmptyState +import br.com.tick.ui.extensions.getLabelResource import br.com.tick.ui.screens.expense.viewmodels.ExpenseViewModel import br.com.tick.ui.screens.shared.AddCategoryDialog import br.com.tick.ui.theme.spacing @@ -106,7 +107,9 @@ fun ExpenseScreen( var photoUri by remember { mutableStateOf(null) } val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val context = LocalContext.current LaunchedEffect(key1 = Unit) { expenseId?.let { expenseViewModel.getExpense(it) } @@ -141,7 +144,7 @@ fun ExpenseScreen( scope.launch { snackbarHostState .showSnackbar( - message = "Algo de errado aconteceu. Mudanças não foram salvas.", + message = context.getString(R.string.expense_save_invalid_message), duration = SnackbarDuration.Short ) } @@ -151,8 +154,11 @@ fun ExpenseScreen( categoryId = expenseCategoryId, name = expenseName, value = expenseValue, - expenseDate = expenseDate + expenseDate = expenseDate, + location = location, + photoUri = photoUri ) + navHostController.navigateUp() } }, text = stringResource(id = R.string.expense_save), @@ -165,13 +171,26 @@ fun ExpenseScreen( ) Text( modifier = Modifier.clickable { - expenseViewModel.handleExpense( - expenseId = expenseId, - categoryId = expenseCategoryId, - name = expenseName, - value = expenseValue, - expenseDate = expenseDate - ) + if (isInvalidValue) { + scope.launch { + snackbarHostState + .showSnackbar( + message = context.getString(R.string.expense_add_invalid_message), + duration = SnackbarDuration.Short + ) + } + } else { + expenseViewModel.handleExpense( + expenseId = expenseId, + categoryId = expenseCategoryId, + name = expenseName, + value = expenseValue, + expenseDate = expenseDate, + location = location, + photoUri = photoUri + ) + navHostController.navigateUp() + } }, text = stringResource(id = R.string.expense_add), style = MaterialTheme.textStyle.h2bold @@ -202,25 +221,32 @@ fun ExpenseScreen( }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) } ) { paddingValues -> - Column(modifier = Modifier.fillMaxWidth().padding(paddingValues)) { - BaseExpense( - expense = expense, - categoriesList = categoriesList, - onExpenseNameChanged = { expenseName = it }, - onExpenseValueChanged = { expenseValue = it }, - onExpenseCategoryChanged = { expenseCategoryId = it }, - onExpenseDateChanged = { expenseDate = it }, - onInvalidValue = { isInvalidValue = it } - ) - Divider( + expense?.let { + Column( modifier = Modifier .fillMaxWidth() - .padding(top = MaterialTheme.spacing.medium), - thickness = 1.dp, - color = MaterialTheme.colorScheme.secondary - ) - ExpenseLocation(expense?.location) { location = it } - ExpensePhoto(expense?.name, expense?.picture) { photoUri = it } + .padding(paddingValues) + .padding(MaterialTheme.spacing.extraSmall) + ) { + BaseExpense( + expense = expense, + categoriesList = categoriesList, + onExpenseNameChanged = { expenseName = it }, + onExpenseValueChanged = { expenseValue = it }, + onExpenseCategoryChanged = { expenseCategoryId = it }, + onExpenseDateChanged = { expenseDate = it }, + onInvalidValue = { isInvalidValue = it } + ) + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.medium), + thickness = 1.dp, + color = MaterialTheme.colorScheme.secondary + ) + ExpenseLocation(it.location) { location = it } + ExpensePhoto(it.name, it.picture) { photoUri = it } + } } @@ -236,8 +262,9 @@ fun ExpenseScreen( confirmButton = { Text( modifier = Modifier.clickable { - // Remove the expense showDeletionConfirmationDialog = false + navHostController.navigateUp() + expenseId?.let { expenseViewModel.removeExpense(it) } }, text = stringResource(id = R.string.generic_ok).uppercase() ) @@ -289,10 +316,17 @@ fun BaseExpense( LaunchedEffect(key1 = expense) { expense?.let { name = TextFieldValue(it.name) + onExpenseNameChanged(it.name) + value = TextFieldValue(it.expenseValue.toString()) + onExpenseValueChanged(it.expenseValue) + categoryLabel = it.category.name selectedCategoryId = it.category.expenseCategoryId + onExpenseCategoryChanged(it.category.expenseCategoryId) + date = it.date + onExpenseDateChanged(it.date) } } @@ -346,7 +380,9 @@ fun BaseExpense( }, isError = isInvalidValue, prefix = { - Text(text = "€") + if (expense != null) { + Text(text = stringResource(id = expense.currencyFormat.getLabelResource())) + } }, onValueChange = { value = it @@ -399,8 +435,14 @@ fun BaseExpense( @Composable fun ExpenseLocation( expenseLocation: LatLng?, - onExpenseLocationChanged: (LatLng) -> Unit + onExpenseLocationChanged: (LatLng?) -> Unit ) { + var expenseLocationState by remember { mutableStateOf(expenseLocation) } + + expenseLocation?.let { + onExpenseLocationChanged(expenseLocationState) + } + Row( modifier = Modifier .fillMaxWidth() @@ -422,61 +464,79 @@ fun ExpenseLocation( } val context = LocalContext.current - if (expenseLocation != null) { - ExpenseLocationMap( - location = expenseLocation, - onExpenseLocationChanged = onExpenseLocationChanged - ) - } else { - val locationPermissionState = rememberMultiplePermissionsState( - listOf( - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION + when (val location = expenseLocationState) { + null -> { + val locationPermissionState = rememberMultiplePermissionsState( + listOf( + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.ACCESS_FINE_LOCATION + ) ) - ) - val hasPermissions = locationPermissionState.permissions.all { it.status.isGranted } - val emptyStateText = if (hasPermissions) { - R.string.expense_location_empty_state_label - } else { - R.string.expense_location_empty_no_permission_state_label - } - var capturedLocation by remember { mutableStateOf(null) } + val hasPermissions = locationPermissionState.permissions.all { it.status.isGranted } + val emptyStateText = if (hasPermissions) { + R.string.expense_location_empty_state_label + } else { + R.string.expense_location_empty_no_permission_state_label + } - if (capturedLocation != null) { - capturedLocation?.let { - ExpenseLocationMap( - location = it, - onExpenseLocationChanged = onExpenseLocationChanged + var capturedLocation by remember { mutableStateOf(null) } + + if (capturedLocation != null) { + capturedLocation?.let { + ExpenseLocationMap( + location = it, + onDelete = { + expenseLocationState = null + capturedLocation = null + onExpenseLocationChanged(null) + }, + onExpenseLocationChanged = onExpenseLocationChanged + ) + } + } else { + TeiraEmptyState( + modifier = Modifier + .fillMaxWidth() + .height(160.dp) + .padding(top = MaterialTheme.spacing.small) + .clickable { + if (hasPermissions) { + val locationProvider: FusedLocationProviderClient = + LocationServices.getFusedLocationProviderClient(context) + locationProvider.lastLocation.addOnCompleteListener { + val lastKnownLocation = it.result + val location = LatLng(lastKnownLocation.latitude, lastKnownLocation.longitude) + capturedLocation = location + onExpenseLocationChanged(location) + } + } else { + locationPermissionState.launchMultiplePermissionRequest() + } + }, + emptyStateLabel = emptyStateText ) } - } else { - TeiraEmptyState( - modifier = Modifier - .fillMaxWidth() - .height(160.dp) - .padding(top = MaterialTheme.spacing.small) - .clickable { - if (hasPermissions) { - val locationProvider: FusedLocationProviderClient = - LocationServices.getFusedLocationProviderClient(context) - locationProvider.lastLocation.addOnCompleteListener { - val lastKnownLocation = it.result - capturedLocation = - LatLng(lastKnownLocation.latitude, lastKnownLocation.longitude) - } - } else { - locationPermissionState.launchMultiplePermissionRequest() - } - }, - emptyStateLabel = emptyStateText + } + + else -> { + ExpenseLocationMap( + location = location, + onDelete = { + Log.d("Tiago", "OnDelete Clicked") + expenseLocationState = null + onExpenseLocationChanged(null) + }, + onExpenseLocationChanged = onExpenseLocationChanged ) } + } } @Composable fun ExpenseLocationMap( location: LatLng, + onDelete: () -> Unit, onExpenseLocationChanged: (LatLng) -> Unit ) { val minZoomPossible = 12f @@ -501,42 +561,60 @@ fun ExpenseLocationMap( .height(160.dp) .padding(top = MaterialTheme.spacing.small) ) { - GoogleMap( - modifier = Modifier.fillMaxSize(), - properties = mapProperties, - uiSettings = mapUiSettings, - cameraPositionState = cameraPositionState, - onMapClick = { - markerState.position = it - onExpenseLocationChanged(it) + Box { + GoogleMap( + modifier = Modifier.fillMaxSize(), + properties = mapProperties, + uiSettings = mapUiSettings, + cameraPositionState = cameraPositionState, + onMapClick = { + markerState.position = it + onExpenseLocationChanged(it) + } + ) { + Marker( + state = markerState, + title = stringResource(id = R.string.expense_expense_expense) + ) + } + IconButton( + modifier = Modifier.align(Alignment.TopEnd), + onClick = { onDelete() } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_delete), + contentDescription = "", + tint = Color.White + ) } - ) { - Marker( - state = markerState, - title = stringResource(id = R.string.expense_expense_expense) - ) } } } @Composable fun ExpensePhoto( - expenseName: String?, + expenseName: String, expensePhotoUri: Uri?, - onPictureSelected: (Uri) -> Unit + onPictureSelected: (Uri?) -> Unit ) { - var hasImage by remember { mutableStateOf(expensePhotoUri != null) } - var imageUri by remember { mutableStateOf(expensePhotoUri) } val context = LocalContext.current + + var imageUri by remember { mutableStateOf(expensePhotoUri) } + var hasImage by remember { mutableStateOf(expensePhotoUri != null) } + val cameraLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.TakePicture(), onResult = { success -> hasImage = success - imageUri?.let(onPictureSelected) + onPictureSelected(imageUri) } ) + expensePhotoUri?.let { + onPictureSelected(imageUri) + } + Row( modifier = Modifier .fillMaxWidth() @@ -557,20 +635,35 @@ fun ExpensePhoto( ) } - if (imageUri != null) { + if (hasImage) { Card( modifier = Modifier .fillMaxWidth() .height(160.dp) .padding(top = MaterialTheme.spacing.small) ) { - if (hasImage && imageUri != null) { - AsyncImage( - model = imageUri, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop, - contentDescription = "Selected image", - ) + if (imageUri != null) { + Box(modifier = Modifier.fillMaxSize()) { + AsyncImage( + model = imageUri, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop, + contentDescription = "Selected image", + ) + IconButton( + modifier = Modifier.align(Alignment.TopEnd), + onClick = { + hasImage = false + imageUri = null + } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_delete), + contentDescription = "", + tint = Color.White + ) + } + } } } } else { @@ -580,10 +673,7 @@ fun ExpensePhoto( .height(160.dp) .padding(top = MaterialTheme.spacing.small) .clickable { - val fileName = expenseName ?: LocalDate - .now() - .toString() - val uri = ComposeFileProvider.getImageUri(fileName, context) + val uri = ComposeFileProvider.getImageUri(expenseName, context) imageUri = uri cameraLauncher.launch(uri) }, diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt index 9c51d8b..080b127 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt @@ -1,11 +1,13 @@ package br.com.tick.ui.screens.expense.viewmodels +import android.net.Uri import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import br.com.tick.sdk.dispatchers.DispatcherProvider import br.com.tick.sdk.domain.CategorizedExpense import br.com.tick.sdk.repositories.categorizedexpense.CategorizedExpenseRepository import br.com.tick.sdk.repositories.expensecategory.ExpenseCategoryRepository +import com.google.android.gms.maps.model.LatLng import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -20,7 +22,6 @@ import javax.inject.Inject class ExpenseViewModel @Inject constructor( categoryRepository: ExpenseCategoryRepository, private val categorizedExpenseRepository: CategorizedExpenseRepository, - private val expenseRepository: CategorizedExpenseRepository, private val dispatcherProvider: DispatcherProvider ) : ViewModel() { @@ -45,27 +46,50 @@ class ExpenseViewModel @Inject constructor( } } - fun handleExpense(expenseId: Int? = null, categoryId: Int, name: String, value: Double, expenseDate: LocalDate) { + fun handleExpense( + expenseId: Int? = null, + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate, + location: LatLng?, + photoUri: Uri? + ) { viewModelScope.launch(dispatcherProvider.io()) { if (expenseId != null) { - saveExpense(expenseId, categoryId, name, value, expenseDate) + saveExpense(expenseId, categoryId, name, value, expenseDate, location, photoUri) } else { - addExpense(categoryId, name, value, expenseDate) + addExpense(categoryId, name, value, expenseDate, location, photoUri) } } } - private suspend fun addExpense(categoryId: Int, name: String, value: Double, expenseDate: LocalDate) { - expenseRepository.addExpense(categoryId, name, value, expenseDate) - } - private suspend fun saveExpense( expenseId: Int, categoryId: Int, name: String, value: Double, - expenseDate: LocalDate + expenseDate: LocalDate, + location: LatLng?, + photoUri: Uri? + ) { + categorizedExpenseRepository.updateExpense(expenseId, categoryId, name, value, expenseDate, location, photoUri) + } + + private suspend fun addExpense( + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate, + location: LatLng?, + photoUri: Uri? ) { - expenseRepository.updateExpense(expenseId, categoryId, name, value, expenseDate) + categorizedExpenseRepository.addExpense(categoryId, name, value, expenseDate, location, photoUri) + } + + fun removeExpense(expenseId: Int) { + viewModelScope.launch(dispatcherProvider.io()) { + categorizedExpenseRepository.removeExpense(expenseId) + } } } diff --git a/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt b/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt index 6d69904..78ef64e 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt @@ -66,7 +66,7 @@ fun Body( is ExpensesGridStates.Success -> BodyGrid( modifier = modifier, expensesList = expensesListState.expensesList, - editExpense = { navHostController.navigate(NavigationItem.EditExpense.route + "$it") }, + editExpense = { navHostController.navigate(NavigationItem.EditExpense.with(it)) }, removeExpense = { expensesGridViewModel.removeCard(it) } ) } diff --git a/ui/src/main/res/values/strings_expense.xml b/ui/src/main/res/values/strings_expense.xml index 8d66dcd..4f398c8 100644 --- a/ui/src/main/res/values/strings_expense.xml +++ b/ui/src/main/res/values/strings_expense.xml @@ -5,7 +5,9 @@ Expense Save + Can\'t save expense with invalid data Add + Can\'t add expense with invalid data Expense Cost From bdb21c725bcdcd4bdcdae947266c565fd73effe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Thu, 14 Sep 2023 22:01:03 +0100 Subject: [PATCH 05/14] [63] Fixing tests --- .../FakeCategorizedExpenseRepository.kt | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt b/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt index 6cc13d9..146aea6 100644 --- a/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt +++ b/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt @@ -1,8 +1,11 @@ package br.com.tick.sdk.repositories +import android.net.Uri import br.com.tick.sdk.domain.CategorizedExpense +import br.com.tick.sdk.domain.CurrencyFormat import br.com.tick.sdk.domain.ExpenseCategory import br.com.tick.sdk.repositories.categorizedexpense.CategorizedExpenseRepository +import com.google.android.gms.maps.model.LatLng import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import java.time.LocalDate @@ -11,8 +14,45 @@ class FakeCategorizedExpenseRepository : CategorizedExpenseRepository { private val expenses = mutableListOf() - override suspend fun addExpense(categoryId: Int, name: String, value: Double, expenseDate: LocalDate) { - expenses.add(CategorizedExpense(expenses.size, name, value, expenseDate, ExpenseCategory(categoryId, "", 0))) + override suspend fun addExpense( + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate, + location: LatLng?, + photoUri: Uri? + ) { + expenses.add( + CategorizedExpense( + expenses.size, + name, + value, + CurrencyFormat.EURO, + expenseDate, + ExpenseCategory(categoryId, "", 0), + LatLng(0.0, 0.0), + Uri.EMPTY + ) + ) + } + + override suspend fun updateExpense( + expenseId: Int, + categoryId: Int, + name: String, + value: Double, + expenseDate: LocalDate, + location: LatLng?, + photoUri: Uri? + ) { + expenses[expenseId] = expenses[expenseId].copy( + expenseId = expenseId, + name = name, + expenseValue = value, + date = expenseDate, + location = location, + picture = photoUri + ) } override suspend fun removeExpense(expenseId: Int) { @@ -26,4 +66,8 @@ class FakeCategorizedExpenseRepository : CategorizedExpenseRepository { override suspend fun getAccountingCycleExpenses(): Flow> { return flowOf(expenses.take(30)) } + + override fun getCategorizedExpense(expenseId: Int): Flow { + return flowOf(expenses[expenseId]) + } } From f46cd42c31d89f5e86b7890f2ede2dc6d924de17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Sun, 17 Sep 2023 17:10:57 +0100 Subject: [PATCH 06/14] [63] Stable Implementation for Expenses --- .../com/tick/sdk/domain/CategorizedExpense.kt | 1 - .../CategorizedExpensesRepositoryImpl.kt | 2 - .../java/br/com/tick/ui/NavigationItem.kt | 6 +- .../main/java/br/com/tick/ui/TeiraScaffold.kt | 13 ++- .../com/tick/ui/core/TeiraNavigationDrawer.kt | 10 +++ .../tick/ui/screens/expense/ExpenseScreen.kt | 85 ++++++++++++------- .../expense/viewmodels/ExpenseViewModel.kt | 15 ++++ .../tick/ui/screens/wallet/ExpensesGrid.kt | 2 +- .../br/com/tick/utils/ComposeFileProvider.kt | 17 ++-- ui/src/main/res/drawable/ic_expense.xml | 10 +++ ui/src/main/res/values/strings_navigation.xml | 2 +- 11 files changed, 108 insertions(+), 55 deletions(-) create mode 100644 ui/src/main/res/drawable/ic_expense.xml diff --git a/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt b/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt index f0ab975..7c093c1 100644 --- a/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt +++ b/sdk/src/main/java/br/com/tick/sdk/domain/CategorizedExpense.kt @@ -8,7 +8,6 @@ data class CategorizedExpense( val expenseId: Int, val name: String, val expenseValue: Double, - val currencyFormat: CurrencyFormat, val date: LocalDate, val category: ExpenseCategory, val location: LatLng?, diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt index fbead10..6eee0e1 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/categorizedexpense/CategorizedExpensesRepositoryImpl.kt @@ -117,7 +117,6 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( private suspend fun categorize(expense: Expense): CategorizedExpense { val category = categoryDao.getCategoryById(expense.categoryId) - val user = userDao.getUniqueUser().first() val categoryColorId = category.categoryColorId val color = if (categoryColorId != null) { @@ -134,7 +133,6 @@ class CategorizedExpensesRepositoryImpl @Inject constructor( expenseId, name, value, - user.currency, date, expenseCategory, location, diff --git a/ui/src/main/java/br/com/tick/ui/NavigationItem.kt b/ui/src/main/java/br/com/tick/ui/NavigationItem.kt index 455844e..e835510 100644 --- a/ui/src/main/java/br/com/tick/ui/NavigationItem.kt +++ b/ui/src/main/java/br/com/tick/ui/NavigationItem.kt @@ -17,7 +17,7 @@ sealed class NavigationItem(var route: String, var iconResource: Int, var titleR const val ANALYSIS = "analysis" const val HISTORY = "history" const val EXPENSE_ID_TAG = "{expenseId}" - const val EXPENSE = "editExpense/$EXPENSE_ID_TAG" + const val EXPENSE = "editExpense?$EXPENSE_ID_TAG" } object Home : NavigationItem(HOME, R.drawable.ic_wallet, R.string.navigation_item_wallet) @@ -25,10 +25,10 @@ sealed class NavigationItem(var route: String, var iconResource: Int, var titleR object Wallet : NavigationItem(WALLET, R.drawable.ic_wallet, R.string.navigation_item_wallet) object Analysis : NavigationItem(ANALYSIS, R.drawable.ic_analysis, R.string.navigation_item_analysis) object History : NavigationItem(HISTORY, R.drawable.ic_history, R.string.navigation_item_history) - object EditExpense: NavigationItem(EXPENSE, R.drawable.ic_edit, R.string.navigation_item_edit_expense) { + object Expense : NavigationItem(EXPENSE, R.drawable.ic_expense, R.string.navigation_item_expense) { const val NAVIGATION_EXPENSE_ID_TAG = "expenseId" - fun with(expenseId: Int): String { + fun show(expenseId: Int?): String { return EXPENSE.replace(EXPENSE_ID_TAG, expenseId.toString()) } } diff --git a/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt b/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt index 306e2d5..e4ba068 100644 --- a/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt +++ b/ui/src/main/java/br/com/tick/ui/TeiraScaffold.kt @@ -99,9 +99,12 @@ fun TeiraScaffold( HomeScreen(navHostController) } composable( - route = NavigationItem.EditExpense.route, + route = NavigationItem.Expense.route, arguments = listOf( - navArgument(NavigationItem.EditExpense.NAVIGATION_EXPENSE_ID_TAG) { type = NavType.IntType } + navArgument(NavigationItem.Expense.NAVIGATION_EXPENSE_ID_TAG) { + type = NavType.IntType + defaultValue = -1 + } ), enterTransition = { slideIntoContainer( @@ -116,8 +119,9 @@ fun TeiraScaffold( ) } ) { navBackStackEntry -> - navBackStackEntry.arguments?.getInt(NavigationItem.EditExpense.NAVIGATION_EXPENSE_ID_TAG)?.let { - ExpenseScreen(navHostController = navHostController, expenseId = it) + navBackStackEntry.arguments?.getInt(NavigationItem.Expense.NAVIGATION_EXPENSE_ID_TAG)?.let { + val expenseId = if (it == -1) null else it + ExpenseScreen(navHostController = navHostController, expenseId = expenseId) } } } @@ -137,6 +141,7 @@ private fun HomeScreen(parentNavController: NavHostController) { TeiraNavigationDrawer( drawerState = drawerState, navBackStackEntry = navBackStackEntry, + navigateToParentRoute = { navigateTo(parentNavController, it) }, navigateToRoute = { navigateTo(navHostController, it) } ) { Scaffold( diff --git a/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt b/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt index 64fc739..1f9d907 100644 --- a/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt +++ b/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.launch fun TeiraNavigationDrawer( drawerState: DrawerState, navBackStackEntry: NavBackStackEntry?, + navigateToParentRoute: (NavigationItem) -> Unit, navigateToRoute: (NavigationItem) -> Unit, content: @Composable () -> Unit ) { @@ -78,6 +79,15 @@ fun TeiraNavigationDrawer( ) { navigateToRoute(NavigationItem.History) } + TeiraNavigationDrawerItem( + drawerState = drawerState, + painter = painterResource(id = NavigationItem.Expense.iconResource), + text = stringResource(id = NavigationItem.Expense.titleResource), + coroutineScope = coroutineScope, + isCurrentRoute = currentRoute == NavigationItem.Expense.route + ) { + navigateToParentRoute(NavigationItem.Expense) + } Spacer(modifier = Modifier.height(MaterialTheme.spacing.extraLarge)) TeiraNavigationDrawerItem( drawerState = drawerState, diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt index 8bd8301..8ecce5f 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt @@ -58,6 +58,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import br.com.tick.sdk.domain.CategorizedExpense +import br.com.tick.sdk.domain.CurrencyFormat import br.com.tick.sdk.domain.ExpenseCategory import br.com.tick.ui.R import br.com.tick.ui.core.TeiraDatePicker @@ -96,6 +97,7 @@ fun ExpenseScreen( var showDeletionConfirmationDialog by remember { mutableStateOf(false) } val expense by expenseViewModel.categorizedExpense.collectAsStateWithLifecycle() + val currency by expenseViewModel.currency.collectAsStateWithLifecycle() val categoriesList by expenseViewModel.categories.collectAsStateWithLifecycle() var expenseName by remember { mutableStateOf("") } @@ -221,32 +223,31 @@ fun ExpenseScreen( }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) } ) { paddingValues -> - expense?.let { - Column( + Column( + modifier = Modifier + .fillMaxWidth() + .padding(paddingValues) + .padding(MaterialTheme.spacing.extraSmall) + ) { + BaseExpense( + expense = expense, + currencyFormat = currency, + categoriesList = categoriesList, + onExpenseNameChanged = { expenseName = it }, + onExpenseValueChanged = { expenseValue = it }, + onExpenseCategoryChanged = { expenseCategoryId = it }, + onExpenseDateChanged = { expenseDate = it }, + onInvalidValue = { isInvalidValue = it } + ) + Divider( modifier = Modifier .fillMaxWidth() - .padding(paddingValues) - .padding(MaterialTheme.spacing.extraSmall) - ) { - BaseExpense( - expense = expense, - categoriesList = categoriesList, - onExpenseNameChanged = { expenseName = it }, - onExpenseValueChanged = { expenseValue = it }, - onExpenseCategoryChanged = { expenseCategoryId = it }, - onExpenseDateChanged = { expenseDate = it }, - onInvalidValue = { isInvalidValue = it } - ) - Divider( - modifier = Modifier - .fillMaxWidth() - .padding(top = MaterialTheme.spacing.medium), - thickness = 1.dp, - color = MaterialTheme.colorScheme.secondary - ) - ExpenseLocation(it.location) { location = it } - ExpensePhoto(it.name, it.picture) { photoUri = it } - } + .padding(top = MaterialTheme.spacing.medium), + thickness = 1.dp, + color = MaterialTheme.colorScheme.secondary + ) + ExpenseLocation(expense?.location) { location = it } + ExpensePhoto(expense?.name, expense?.picture) { photoUri = it } } @@ -295,6 +296,7 @@ fun ExpenseScreen( @Composable fun BaseExpense( expense: CategorizedExpense?, + currencyFormat: CurrencyFormat, categoriesList: List, onExpenseNameChanged: (String) -> Unit, onExpenseValueChanged: (Double) -> Unit, @@ -330,6 +332,13 @@ fun BaseExpense( } } + LaunchedEffect(key1 = categoriesList) { + selectedCategoryId = categoriesList.find { categories -> + categories.name == categoryLabel + }?.expenseCategoryId ?: -1 + onExpenseCategoryChanged(selectedCategoryId) + } + val expenseTextFieldColors = OutlinedTextFieldDefaults.colors( unfocusedContainerColor = Color.Transparent, focusedContainerColor = Color.Transparent, @@ -380,9 +389,7 @@ fun BaseExpense( }, isError = isInvalidValue, prefix = { - if (expense != null) { - Text(text = stringResource(id = expense.currencyFormat.getLabelResource())) - } + Text(text = stringResource(id = currencyFormat.getLabelResource())) }, onValueChange = { value = it @@ -439,8 +446,11 @@ fun ExpenseLocation( ) { var expenseLocationState by remember { mutableStateOf(expenseLocation) } - expenseLocation?.let { - onExpenseLocationChanged(expenseLocationState) + LaunchedEffect(key1 = expenseLocation) { + expenseLocation?.let { + expenseLocationState = it + onExpenseLocationChanged(expenseLocationState) + } } Row( @@ -593,7 +603,7 @@ fun ExpenseLocationMap( @Composable fun ExpensePhoto( - expenseName: String, + expenseName: String?, expensePhotoUri: Uri?, onPictureSelected: (Uri?) -> Unit ) { @@ -611,8 +621,12 @@ fun ExpensePhoto( } ) - expensePhotoUri?.let { - onPictureSelected(imageUri) + LaunchedEffect(key1 = expensePhotoUri) { + expensePhotoUri?.let { + imageUri = it + hasImage = true + onPictureSelected(imageUri) + } } Row( @@ -655,6 +669,7 @@ fun ExpensePhoto( onClick = { hasImage = false imageUri = null + onPictureSelected(null) } ) { Icon( @@ -673,7 +688,11 @@ fun ExpensePhoto( .height(160.dp) .padding(top = MaterialTheme.spacing.small) .clickable { - val uri = ComposeFileProvider.getImageUri(expenseName, context) + val uri = ComposeFileProvider.getImageUri( + expenseName ?: LocalDate + .now() + .toString(), context + ) imageUri = uri cameraLauncher.launch(uri) }, diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt index 080b127..598180a 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt @@ -1,18 +1,22 @@ package br.com.tick.ui.screens.expense.viewmodels import android.net.Uri +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import br.com.tick.sdk.dispatchers.DispatcherProvider import br.com.tick.sdk.domain.CategorizedExpense +import br.com.tick.sdk.domain.CurrencyFormat import br.com.tick.sdk.repositories.categorizedexpense.CategorizedExpenseRepository import br.com.tick.sdk.repositories.expensecategory.ExpenseCategoryRepository +import br.com.tick.sdk.repositories.user.UserRepository import com.google.android.gms.maps.model.LatLng import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import java.time.LocalDate @@ -21,6 +25,7 @@ import javax.inject.Inject @HiltViewModel class ExpenseViewModel @Inject constructor( categoryRepository: ExpenseCategoryRepository, + userRepository: UserRepository, private val categorizedExpenseRepository: CategorizedExpenseRepository, private val dispatcherProvider: DispatcherProvider ) : ViewModel() { @@ -36,7 +41,17 @@ class ExpenseViewModel @Inject constructor( initialValue = listOf() ) + val currency = userRepository.getUser() + .flowOn(dispatcherProvider.io()) + .map { it.currency } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = CurrencyFormat.EURO + ) + fun getExpense(expenseId: Int) { + Log.d("Tiago", "ExpenseID $expenseId") viewModelScope.launch(dispatcherProvider.io()) { categorizedExpenseRepository .getCategorizedExpense(expenseId = expenseId) diff --git a/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt b/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt index 78ef64e..e2a33a3 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/wallet/ExpensesGrid.kt @@ -66,7 +66,7 @@ fun Body( is ExpensesGridStates.Success -> BodyGrid( modifier = modifier, expensesList = expensesListState.expensesList, - editExpense = { navHostController.navigate(NavigationItem.EditExpense.with(it)) }, + editExpense = { navHostController.navigate(NavigationItem.Expense.show(it)) }, removeExpense = { expensesGridViewModel.removeCard(it) } ) } diff --git a/ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt b/ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt index 3607837..65e72a7 100644 --- a/ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt +++ b/ui/src/main/java/br/com/tick/utils/ComposeFileProvider.kt @@ -5,6 +5,7 @@ import android.net.Uri import androidx.core.content.FileProvider import br.com.tick.ui.R import java.io.File +import java.time.LocalDate class ComposeFileProvider : FileProvider(R.xml.filepaths) { companion object { @@ -12,19 +13,15 @@ class ComposeFileProvider : FileProvider(R.xml.filepaths) { val directory = File(context.filesDir, "images") directory.mkdirs() - val file = File.createTempFile( - "${fileName}_", - ".jpg", - directory - ) + val file = try { + File.createTempFile("${fileName}_", ".jpg", directory) + } catch (exception: IllegalArgumentException) { + File.createTempFile(LocalDate.now().toString(), ".jpg", directory) + } val authority = context.packageName + ".fileprovider" - return getUriForFile( - context, - authority, - file, - ) + return getUriForFile(context, authority, file) } } } diff --git a/ui/src/main/res/drawable/ic_expense.xml b/ui/src/main/res/drawable/ic_expense.xml new file mode 100644 index 0000000..1b5c39c --- /dev/null +++ b/ui/src/main/res/drawable/ic_expense.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/ui/src/main/res/values/strings_navigation.xml b/ui/src/main/res/values/strings_navigation.xml index 0f648ff..a80aa68 100644 --- a/ui/src/main/res/values/strings_navigation.xml +++ b/ui/src/main/res/values/strings_navigation.xml @@ -5,6 +5,6 @@ Wallet Analysis History - Edit Expense + Expense From 17b0266ee914dc7c211155726be827f72f681c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Sun, 17 Sep 2023 17:31:52 +0100 Subject: [PATCH 07/14] [63] Expense Feature Final Touches --- .../main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt | 3 +-- .../com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt | 1 - .../main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt index 8ecce5f..f0be342 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/ExpenseScreen.kt @@ -133,7 +133,7 @@ fun ExpenseScreen( title = { Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceAround + horizontalArrangement = Arrangement.SpaceBetween ) { if (expense != null) { Text( @@ -532,7 +532,6 @@ fun ExpenseLocation( ExpenseLocationMap( location = location, onDelete = { - Log.d("Tiago", "OnDelete Clicked") expenseLocationState = null onExpenseLocationChanged(null) }, diff --git a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt index 598180a..94b9bd7 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/expense/viewmodels/ExpenseViewModel.kt @@ -51,7 +51,6 @@ class ExpenseViewModel @Inject constructor( ) fun getExpense(expenseId: Int) { - Log.d("Tiago", "ExpenseID $expenseId") viewModelScope.launch(dispatcherProvider.io()) { categorizedExpenseRepository .getCategorizedExpense(expenseId = expenseId) diff --git a/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt b/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt index 7dc5818..53d30c1 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/wallet/QuickExpenseBar.kt @@ -204,7 +204,7 @@ fun ExpandedQuickExpense( if (!isInvalidValue) { closeExpandedDialog() val selectedCategory = categoriesList.find { it.expenseCategoryId == selectedCategoryId } - Log.d("Tiago", "Name: $expenseName | Value: $expenseValue | selectedCategory: $selectedCategory | localDateTime: $localDateTime") + if (expenseName.text.isNotEmpty() && (expenseValue.text.isNotEmpty() && expenseValue.text.toDouble() != 0.0) && selectedCategory != null From d561c1912c9eedc9239421c66c7819bc91fdaafd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Sun, 17 Sep 2023 17:45:49 +0100 Subject: [PATCH 08/14] [63] Fixing Tests --- .../tick/sdk/repositories/FakeCategorizedExpenseRepository.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt b/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt index 146aea6..bf60c86 100644 --- a/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt +++ b/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeCategorizedExpenseRepository.kt @@ -2,7 +2,6 @@ package br.com.tick.sdk.repositories import android.net.Uri import br.com.tick.sdk.domain.CategorizedExpense -import br.com.tick.sdk.domain.CurrencyFormat import br.com.tick.sdk.domain.ExpenseCategory import br.com.tick.sdk.repositories.categorizedexpense.CategorizedExpenseRepository import com.google.android.gms.maps.model.LatLng @@ -27,7 +26,6 @@ class FakeCategorizedExpenseRepository : CategorizedExpenseRepository { expenses.size, name, value, - CurrencyFormat.EURO, expenseDate, ExpenseCategory(categoryId, "", 0), LatLng(0.0, 0.0), From 3d39f02f0e6fef923b53a8f3c0f0fafc56c609e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Sun, 17 Sep 2023 18:24:49 +0100 Subject: [PATCH 09/14] [63] Adding Maps API key to secrets --- .github/workflows/build_artifact_and_release_firebase.yml | 5 +++++ .github/workflows/pull_request_unit_test.yml | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/.github/workflows/build_artifact_and_release_firebase.yml b/.github/workflows/build_artifact_and_release_firebase.yml index 6807b7d..50b7b9d 100644 --- a/.github/workflows/build_artifact_and_release_firebase.yml +++ b/.github/workflows/build_artifact_and_release_firebase.yml @@ -22,6 +22,11 @@ jobs: distribution: 'temurin' java-version: '17' cache: 'gradle' + + - name: Update Secrets + env: + MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }} + run: echo 'MAPS_API_KEY=${{ secrets.MAPS_API_KEY }}' > ./local.properties - name: Build the Release AAB run: ./gradlew bundleRelease diff --git a/.github/workflows/pull_request_unit_test.yml b/.github/workflows/pull_request_unit_test.yml index 9780570..361e8c4 100644 --- a/.github/workflows/pull_request_unit_test.yml +++ b/.github/workflows/pull_request_unit_test.yml @@ -17,5 +17,9 @@ jobs: java-version: '17' cache: 'gradle' + - name: Update Secrets + env: + MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }} + run: echo 'MAPS_API_KEY=${{ secrets.MAPS_API_KEY }}' > ./local.properties - name: Unit tests run: ./gradlew testQa From 1f6066303c40ae8b9da803e6cdb04e29d2b48030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Sun, 17 Sep 2023 18:27:24 +0100 Subject: [PATCH 10/14] [63] More eddits to yml --- .github/workflows/build_artifact_and_release_firebase.yml | 4 ++-- .github/workflows/pull_request_unit_test.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_artifact_and_release_firebase.yml b/.github/workflows/build_artifact_and_release_firebase.yml index 50b7b9d..93f0eb4 100644 --- a/.github/workflows/build_artifact_and_release_firebase.yml +++ b/.github/workflows/build_artifact_and_release_firebase.yml @@ -22,11 +22,11 @@ jobs: distribution: 'temurin' java-version: '17' cache: 'gradle' - + - name: Update Secrets env: MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }} - run: echo 'MAPS_API_KEY=${{ secrets.MAPS_API_KEY }}' > ./local.properties + run: echo 'MAPS_API_KEY=${{ secrets.MAPS_API_KEY }}' > ./local.properties - name: Build the Release AAB run: ./gradlew bundleRelease diff --git a/.github/workflows/pull_request_unit_test.yml b/.github/workflows/pull_request_unit_test.yml index 361e8c4..ae38c7d 100644 --- a/.github/workflows/pull_request_unit_test.yml +++ b/.github/workflows/pull_request_unit_test.yml @@ -20,6 +20,6 @@ jobs: - name: Update Secrets env: MAPS_API_KEY: ${{ secrets.MAPS_API_KEY }} - run: echo 'MAPS_API_KEY=${{ secrets.MAPS_API_KEY }}' > ./local.properties + run: echo 'MAPS_API_KEY=${{ secrets.MAPS_API_KEY }}' > ./local.properties - name: Unit tests run: ./gradlew testQa From 4eb4f3959dd8a033cb2325654fe7b905b4fcf219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Sun, 17 Sep 2023 21:48:22 +0100 Subject: [PATCH 11/14] [64] Most Expensive Categories Fix --- .../com/tick/ui/core/TeiraNavigationDrawer.kt | 130 ++++++++++-------- .../ui/screens/analysis/AnalysisScreen.kt | 4 +- .../ui/screens/analysis/FinancialHealth.kt | 1 + .../screens/analysis/MostExpensiveCategory.kt | 36 ++++- .../states/MostExpensiveCategoriesStates.kt | 2 +- .../viewmodels/AnalysisScreenViewModel.kt | 34 ++++- .../main/java/br/com/tick/ui/theme/Spacing.kt | 7 + ui/src/main/res/values/strings_analysis.xml | 2 +- 8 files changed, 146 insertions(+), 70 deletions(-) diff --git a/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt b/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt index 1f9d907..3549735 100644 --- a/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt +++ b/ui/src/main/java/br/com/tick/ui/core/TeiraNavigationDrawer.kt @@ -1,7 +1,13 @@ package br.com.tick.ui.core +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +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.material3.DismissibleDrawerSheet import androidx.compose.material3.DismissibleNavigationDrawer import androidx.compose.material3.DrawerState @@ -13,13 +19,16 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.navigation.NavBackStackEntry import br.com.tick.ui.NavigationItem import br.com.tick.ui.R import br.com.tick.ui.theme.spacing +import br.com.tick.ui.theme.textStyle import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -31,7 +40,6 @@ fun TeiraNavigationDrawer( navigateToRoute: (NavigationItem) -> Unit, content: @Composable () -> Unit ) { - val coroutineScope = rememberCoroutineScope() val currentRoute = navBackStackEntry?.destination?.route @@ -42,60 +50,73 @@ fun TeiraNavigationDrawer( drawerContainerColor = MaterialTheme.colorScheme.surface, drawerContentColor = MaterialTheme.colorScheme.onSurface ) { - Spacer(modifier = Modifier.height(MaterialTheme.spacing.large)) - TeiraNavigationDrawerItem( - drawerState = drawerState, - painter = painterResource(id = NavigationItem.Settings.iconResource), - text = stringResource(id = NavigationItem.Settings.titleResource), - coroutineScope = coroutineScope, - isCurrentRoute = currentRoute == NavigationItem.Settings.route - ) { - navigateToRoute(NavigationItem.Settings) - } - TeiraNavigationDrawerItem( - drawerState = drawerState, - painter = painterResource(id = NavigationItem.Wallet.iconResource), - text = stringResource(id = NavigationItem.Wallet.titleResource), - coroutineScope = coroutineScope, - isCurrentRoute = currentRoute == NavigationItem.Wallet.route + Column( + modifier = Modifier.fillMaxSize().padding(MaterialTheme.spacing.large), + verticalArrangement = Arrangement.SpaceBetween ) { - navigateToRoute(NavigationItem.Wallet) + Column { + Text( + modifier = Modifier.padding(MaterialTheme.spacing.large), + text = stringResource(id = R.string.app_name), + style = MaterialTheme.textStyle.h1extra, + color = MaterialTheme.colorScheme.tertiary + ) + Spacer(modifier = Modifier.height(MaterialTheme.spacing.large)) + TeiraNavigationDrawerItem( + drawerState = drawerState, + painter = painterResource(id = NavigationItem.Settings.iconResource), + text = stringResource(id = NavigationItem.Settings.titleResource), + isCurrentRoute = currentRoute == NavigationItem.Settings.route + ) { + navigateToRoute(NavigationItem.Settings) + } + TeiraNavigationDrawerItem( + drawerState = drawerState, + painter = painterResource(id = NavigationItem.Wallet.iconResource), + text = stringResource(id = NavigationItem.Wallet.titleResource), + isCurrentRoute = currentRoute == NavigationItem.Wallet.route + ) { + navigateToRoute(NavigationItem.Wallet) + } + TeiraNavigationDrawerItem( + drawerState = drawerState, + painter = painterResource(id = NavigationItem.Analysis.iconResource), + text = stringResource(id = NavigationItem.Analysis.titleResource), + isCurrentRoute = currentRoute == NavigationItem.Analysis.route + ) { + navigateToRoute(NavigationItem.Analysis) + } + Spacer( + modifier = Modifier + .padding(horizontal = MaterialTheme.spacing.medium, vertical = MaterialTheme.spacing.small) + .fillMaxWidth() + .height(0.5.dp) + .background(MaterialTheme.colorScheme.tertiary) + ) + TeiraNavigationDrawerItem( + drawerState = drawerState, + painter = painterResource(id = NavigationItem.History.iconResource), + text = stringResource(id = NavigationItem.History.titleResource), + isCurrentRoute = currentRoute == NavigationItem.History.route + ) { + navigateToRoute(NavigationItem.History) + } + TeiraNavigationDrawerItem( + drawerState = drawerState, + painter = painterResource(id = NavigationItem.Expense.iconResource), + text = stringResource(id = NavigationItem.Expense.titleResource), + isCurrentRoute = currentRoute == NavigationItem.Expense.route + ) { + navigateToParentRoute(NavigationItem.Expense) + } + } + TeiraNavigationDrawerItem( + drawerState = drawerState, + painter = painterResource(id = R.drawable.ic_clear), + text = stringResource(id = R.string.generic_close), + isCurrentRoute = false + ) } - TeiraNavigationDrawerItem( - drawerState = drawerState, - painter = painterResource(id = NavigationItem.Analysis.iconResource), - text = stringResource(id = NavigationItem.Analysis.titleResource), - coroutineScope = coroutineScope, - isCurrentRoute = currentRoute == NavigationItem.Analysis.route - ) { - navigateToRoute(NavigationItem.Analysis) - } - TeiraNavigationDrawerItem( - drawerState = drawerState, - painter = painterResource(id = NavigationItem.History.iconResource), - text = stringResource(id = NavigationItem.History.titleResource), - coroutineScope = coroutineScope, - isCurrentRoute = currentRoute == NavigationItem.History.route - ) { - navigateToRoute(NavigationItem.History) - } - TeiraNavigationDrawerItem( - drawerState = drawerState, - painter = painterResource(id = NavigationItem.Expense.iconResource), - text = stringResource(id = NavigationItem.Expense.titleResource), - coroutineScope = coroutineScope, - isCurrentRoute = currentRoute == NavigationItem.Expense.route - ) { - navigateToParentRoute(NavigationItem.Expense) - } - Spacer(modifier = Modifier.height(MaterialTheme.spacing.extraLarge)) - TeiraNavigationDrawerItem( - drawerState = drawerState, - painter = painterResource(id = R.drawable.ic_clear), - text = stringResource(id = R.string.generic_close), - coroutineScope = coroutineScope, - isCurrentRoute = false - ) } }, content = content @@ -107,10 +128,11 @@ private fun TeiraNavigationDrawerItem( drawerState: DrawerState, painter: Painter, text: String, - coroutineScope: CoroutineScope, isCurrentRoute: Boolean, onDrawerItemClick: (() -> Unit)? = null ) { + val coroutineScope = rememberCoroutineScope() + NavigationDrawerItem( icon = { Icon(painter = painter, contentDescription = null) diff --git a/ui/src/main/java/br/com/tick/ui/screens/analysis/AnalysisScreen.kt b/ui/src/main/java/br/com/tick/ui/screens/analysis/AnalysisScreen.kt index cc8b8c6..ccbb679 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/analysis/AnalysisScreen.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/analysis/AnalysisScreen.kt @@ -26,12 +26,12 @@ fun AnalysisScreen() { MostExpensiveCategory( modifier = Modifier .fillMaxWidth() - .padding(top = MaterialTheme.spacing.large) + .padding(top = MaterialTheme.spacing.small) ) FinancialHealthComposable( modifier = Modifier .fillMaxWidth() - .padding(top = MaterialTheme.spacing.large) + .padding(top = MaterialTheme.spacing.small) ) } } diff --git a/ui/src/main/java/br/com/tick/ui/screens/analysis/FinancialHealth.kt b/ui/src/main/java/br/com/tick/ui/screens/analysis/FinancialHealth.kt index 003f8ce..9dec2af 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/analysis/FinancialHealth.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/analysis/FinancialHealth.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel +import br.com.tick.sdk.domain.CurrencyFormat import br.com.tick.ui.R import br.com.tick.ui.core.TeiraNoAvailableDataState import br.com.tick.ui.screens.analysis.states.FinancialHealth diff --git a/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt b/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt index 456eac7..35223cd 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt @@ -1,10 +1,14 @@ package br.com.tick.ui.screens.analysis +import androidx.compose.foundation.background 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.Spacer 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.layout.size import androidx.compose.material3.Card @@ -23,8 +27,10 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import br.com.tick.sdk.domain.CurrencyFormat import br.com.tick.ui.R import br.com.tick.ui.core.TeiraNoAvailableDataState +import br.com.tick.ui.extensions.getLabelResource import br.com.tick.ui.screens.analysis.models.MostExpensiveCategory import br.com.tick.ui.screens.analysis.states.MostExpensiveCategoriesStates import br.com.tick.ui.screens.analysis.viewmodels.AnalysisScreenViewModel @@ -38,10 +44,11 @@ fun MostExpensiveCategory( ) { val mostExpensiveCategoriesStates by viewModel.mostExpenseCategoryList .collectAsState(initial = MostExpensiveCategoriesStates.NoDataAvailable) + val currency by viewModel.currency.collectAsState() Column(modifier = modifier.fillMaxWidth()) { when (val state = mostExpensiveCategoriesStates) { - is MostExpensiveCategoriesStates.Full -> MostExpensiveCategoryBody(modifier, state) + is MostExpensiveCategoriesStates.Full -> MostExpensiveCategoryBody(modifier, currency, state) MostExpensiveCategoriesStates.NoDataAvailable -> TeiraNoAvailableDataState(modifier) } } @@ -50,6 +57,7 @@ fun MostExpensiveCategory( @Composable private fun MostExpensiveCategoryBody( modifier: Modifier = Modifier, + currencyFormat: CurrencyFormat, mostExpensiveCategoriesState: MostExpensiveCategoriesStates.Full ) { Column(modifier = modifier.fillMaxWidth()) { @@ -59,7 +67,9 @@ private fun MostExpensiveCategoryBody( style = MaterialTheme.textStyle.h2 ) Row( - modifier = modifier.fillMaxWidth().padding(top = MaterialTheme.spacing.medium), + modifier = modifier + .fillMaxWidth() + .padding(top = MaterialTheme.spacing.medium), horizontalArrangement = Arrangement.SpaceEvenly ) { mostExpensiveCategoriesState.mostExpensiveCategories.forEach { mostExpensiveCategory -> @@ -67,9 +77,11 @@ private fun MostExpensiveCategoryBody( Color(it) } ?: MaterialTheme.colorScheme.secondary + val currencyLabel = stringResource(id = currencyFormat.getLabelResource()) + CategoryCard( label = mostExpensiveCategory.categoryName, - subLabel = mostExpensiveCategory.amount.toString(), + subLabel = "$currencyLabel${mostExpensiveCategory.amount}", color = categoryCardColor ) } @@ -83,7 +95,7 @@ fun CategoryCard(modifier: Modifier = Modifier, label: String, subLabel: String, modifier = modifier .size(80.dp) .padding(MaterialTheme.spacing.smallest), - colors = CardDefaults.cardColors(containerColor = color) + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface) ) { Column( modifier = Modifier.fillMaxSize(), @@ -93,15 +105,24 @@ fun CategoryCard(modifier: Modifier = Modifier, label: String, subLabel: String, Text( text = label, style = MaterialTheme.textStyle.h2bold, - color = MaterialTheme.colorScheme.onSecondary, + color = MaterialTheme.colorScheme.onTertiary, maxLines = 1, overflow = TextOverflow.Ellipsis ) Text( text = subLabel, - style = MaterialTheme.textStyle.h3small, - color = MaterialTheme.colorScheme.onSecondary + style = MaterialTheme.textStyle.h3, + color = MaterialTheme.colorScheme.onTertiary ) + Box(modifier = Modifier.fillMaxSize()) { + Spacer( + modifier = Modifier + .height(6.dp) + .background(color) + .align(Alignment.BottomCenter) + .fillMaxWidth() + ) + } } } } @@ -110,6 +131,7 @@ fun CategoryCard(modifier: Modifier = Modifier, label: String, subLabel: String, @Composable fun MostExpensiveCategoryBodyPreview() { MostExpensiveCategoryBody( + currencyFormat = CurrencyFormat.EURO, mostExpensiveCategoriesState = MostExpensiveCategoriesStates.Full( listOf(MostExpensiveCategory("Test", Color.Red.toArgb(), 56.0)) ) diff --git a/ui/src/main/java/br/com/tick/ui/screens/analysis/states/MostExpensiveCategoriesStates.kt b/ui/src/main/java/br/com/tick/ui/screens/analysis/states/MostExpensiveCategoriesStates.kt index 9b7f670..852185e 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/analysis/states/MostExpensiveCategoriesStates.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/analysis/states/MostExpensiveCategoriesStates.kt @@ -13,7 +13,7 @@ sealed class MostExpensiveCategoriesStates { Full( mostExpensiveCategories .sortedByDescending { it.amount } - .take(5) + .take(4) ) } } diff --git a/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt b/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt index f6d7c2f..8037d97 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt @@ -1,7 +1,10 @@ package br.com.tick.ui.screens.analysis.viewmodels import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import br.com.tick.sdk.dispatchers.DispatcherProvider +import br.com.tick.sdk.domain.CurrencyFormat +import br.com.tick.sdk.repositories.user.UserRepository import br.com.tick.ui.screens.analysis.states.AnalysisGraphStates import br.com.tick.ui.screens.analysis.states.FinancialHealth import br.com.tick.ui.screens.analysis.states.MostExpensiveCategoriesStates @@ -10,8 +13,15 @@ import br.com.tick.ui.screens.analysis.usecases.FetchLastMonthExpenses import br.com.tick.ui.screens.analysis.usecases.GetMostExpensiveCategories import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -19,6 +29,7 @@ class AnalysisScreenViewModel @Inject constructor( private val fetchLastMonthExpenses: FetchLastMonthExpenses, private val getMostExpensiveCategories: GetMostExpensiveCategories, private val calculateFinancialHealthSituation: CalculateFinancialHealthSituation, + userRepository: UserRepository, private val dispatcherProvider: DispatcherProvider ) : ViewModel() { @@ -36,10 +47,23 @@ class AnalysisScreenViewModel @Inject constructor( } }.flowOn(dispatcherProvider.io()) - val financialHealthSituation: Flow - get() = flow { + val currency = userRepository.getUser() + .flowOn(dispatcherProvider.io()) + .map { it.currency } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = CurrencyFormat.EURO + ) + + private val _financialHealthSituation = MutableStateFlow(FinancialHealth.NoDataAvailable) + val financialHealthSituation: Flow = _financialHealthSituation + + init { + viewModelScope.launch(dispatcherProvider.io()) { calculateFinancialHealthSituation().collect { - emit(it) + _financialHealthSituation.emit(it) } - }.flowOn(dispatcherProvider.io()) -} \ No newline at end of file + } + } +} diff --git a/ui/src/main/java/br/com/tick/ui/theme/Spacing.kt b/ui/src/main/java/br/com/tick/ui/theme/Spacing.kt index c827474..d8fdc03 100644 --- a/ui/src/main/java/br/com/tick/ui/theme/Spacing.kt +++ b/ui/src/main/java/br/com/tick/ui/theme/Spacing.kt @@ -37,6 +37,13 @@ data class TeiraTextStyle( lineHeight = 24.sp, letterSpacing = 3.sp ), + val h1extra: TextStyle = TextStyle( + fontFamily = Mulish, + fontWeight = FontWeight.Bold, + fontSize = 26.sp, + lineHeight = 24.sp, + letterSpacing = 3.sp + ), val h2: TextStyle = TextStyle( fontFamily = Mulish, fontWeight = FontWeight.Medium, diff --git a/ui/src/main/res/values/strings_analysis.xml b/ui/src/main/res/values/strings_analysis.xml index 6cfd3c3..428e752 100644 --- a/ui/src/main/res/values/strings_analysis.xml +++ b/ui/src/main/res/values/strings_analysis.xml @@ -5,7 +5,7 @@ Most expensive categories Financial Health - Your expenses is %1\$.2f percent of your income. %2\$s! + Your expenses is %1$.2f%% of your income. %2$s! You are safe! You need to be cautious. From 23869fc8a6597e0283ee1b3852d7f396fc2b49ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Sun, 17 Sep 2023 22:44:00 +0100 Subject: [PATCH 12/14] [64] Changed Color specs --- .../ui/screens/analysis/MostExpensiveCategory.kt | 2 +- ui/src/main/java/br/com/tick/ui/theme/Color.kt | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt b/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt index 35223cd..5ab9931 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/analysis/MostExpensiveCategory.kt @@ -75,7 +75,7 @@ private fun MostExpensiveCategoryBody( mostExpensiveCategoriesState.mostExpensiveCategories.forEach { mostExpensiveCategory -> val categoryCardColor = mostExpensiveCategory.color?.let { Color(it) - } ?: MaterialTheme.colorScheme.secondary + } ?: MaterialTheme.colorScheme.surface val currencyLabel = stringResource(id = currencyFormat.getLabelResource()) diff --git a/ui/src/main/java/br/com/tick/ui/theme/Color.kt b/ui/src/main/java/br/com/tick/ui/theme/Color.kt index 71f61b5..b1d5997 100644 --- a/ui/src/main/java/br/com/tick/ui/theme/Color.kt +++ b/ui/src/main/java/br/com/tick/ui/theme/Color.kt @@ -2,13 +2,13 @@ package br.com.tick.ui.theme import androidx.compose.ui.graphics.Color -val TeiraPrimaryColor = Color(0xFF6d7c8e) -val TeiraSecondaryColor = Color(0xFF6a6b68) -val TeiraTertiaryColor = Color(0xFFa45c3c) +val TeiraPrimaryColor = Color(0xFF6D788E) +val TeiraSecondaryColor = Color(0xFF989996) +val TeiraTertiaryColor = Color(0xFFCA5E2F) val TeiraOnPrimaryColor = Color(0xFFEFF1E9) -val TeiraOnSecondaryColor = Color(0xFFDCDDD9) -val TeiraOnTertiaryColor = Color(0xFF0c0c0c) +val TeiraOnSecondaryColor = Color(0xFFEFF1E9) +val TeiraOnTertiaryColor = Color(0xFF474444) -val TeiraSurfaceColor = Color(0xFFCFD1CA) +val TeiraSurfaceColor = Color(0xFFF2F8EE) val TeiraSurfaceVariantColor = Color(0xFFA0AF7B) \ No newline at end of file From b57633ee1d877ffab7b89c328ff9473185aa7159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Mon, 18 Sep 2023 20:08:22 +0100 Subject: [PATCH 13/14] [64] Fixing tests --- .../sdk/repositories/FakeUserRepository.kt | 1 + .../viewmodels/AnalysisScreenViewModel.kt | 3 ++- .../main/java/br/com/tick/ui/theme/Color.kt | 2 +- .../viewmodels/AnalysisScreenViewModelTest.kt | 27 ++++++++++--------- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeUserRepository.kt b/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeUserRepository.kt index 7bd4630..fe76b24 100644 --- a/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeUserRepository.kt +++ b/sdk/src/qa/java/br/com/tick/sdk/repositories/FakeUserRepository.kt @@ -17,6 +17,7 @@ class FakeUserRepository: UserRepository { } override fun getUser() = user + override suspend fun setInitialUser() { user.tryEmit(User.initial()) } diff --git a/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt b/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt index 8037d97..44a274d 100644 --- a/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt +++ b/ui/src/main/java/br/com/tick/ui/screens/analysis/viewmodels/AnalysisScreenViewModel.kt @@ -15,6 +15,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow @@ -57,7 +58,7 @@ class AnalysisScreenViewModel @Inject constructor( ) private val _financialHealthSituation = MutableStateFlow(FinancialHealth.NoDataAvailable) - val financialHealthSituation: Flow = _financialHealthSituation + val financialHealthSituation: StateFlow = _financialHealthSituation init { viewModelScope.launch(dispatcherProvider.io()) { diff --git a/ui/src/main/java/br/com/tick/ui/theme/Color.kt b/ui/src/main/java/br/com/tick/ui/theme/Color.kt index b1d5997..736af20 100644 --- a/ui/src/main/java/br/com/tick/ui/theme/Color.kt +++ b/ui/src/main/java/br/com/tick/ui/theme/Color.kt @@ -10,5 +10,5 @@ val TeiraOnPrimaryColor = Color(0xFFEFF1E9) val TeiraOnSecondaryColor = Color(0xFFEFF1E9) val TeiraOnTertiaryColor = Color(0xFF474444) -val TeiraSurfaceColor = Color(0xFFF2F8EE) +val TeiraSurfaceColor = Color(0xFFeeeeee) val TeiraSurfaceVariantColor = Color(0xFFA0AF7B) \ No newline at end of file diff --git a/ui/src/test/java/br/com/tick/ui/viewmodels/AnalysisScreenViewModelTest.kt b/ui/src/test/java/br/com/tick/ui/viewmodels/AnalysisScreenViewModelTest.kt index 884670c..92eb82c 100644 --- a/ui/src/test/java/br/com/tick/ui/viewmodels/AnalysisScreenViewModelTest.kt +++ b/ui/src/test/java/br/com/tick/ui/viewmodels/AnalysisScreenViewModelTest.kt @@ -41,6 +41,7 @@ class AnalysisScreenViewModelTest { fetchLastMonthExpenses, getMostExpensiveCategories, calculateFinancialHealthSituation, + userRepository, FakeDispatcher() ) } @@ -49,20 +50,24 @@ class AnalysisScreenViewModelTest { fun `When user made an expense, financial health should reflect this change`() = runTest { val expenseRepository = FakeCategorizedExpenseRepository() val userRepository: UserRepository = FakeUserRepository() + + userRepository.setMonthlyIncome(1500.0) + expenseRepository.addExpense( + categoryId = 1, + name = "Something", + value = 15.0, + expenseDate = LocalDate.now(), + location = null, + photoUri = null + ) + val analysisScreenViewModel = getViewModel( categorizedExpenseRepository = expenseRepository, userRepository = userRepository ) - userRepository.setMonthlyIncome(1500.0) - expenseRepository.addExpense(0, "Name_1", 10.0, LocalDate.now()) - analysisScreenViewModel.financialHealthSituation.test { - val financialHealthState = awaitItem() - Truth.assertThat(financialHealthState).isInstanceOf(FinancialHealth.Situation::class.java) - Truth.assertThat( - (financialHealthState as FinancialHealth.Situation).percentageOfCompromisedIncome - ).isGreaterThan(0) + Truth.assertThat(awaitItem()).isInstanceOf(FinancialHealth.Situation::class.java) } } @@ -70,16 +75,14 @@ class AnalysisScreenViewModelTest { fun `When user has no expenses, financial health should have no available data`() = runTest { val expenseRepository = FakeCategorizedExpenseRepository() val userRepository: UserRepository = FakeUserRepository() + val analysisScreenViewModel = getViewModel( categorizedExpenseRepository = expenseRepository, userRepository = userRepository ) - userRepository.setMonthlyIncome(1500.0) - analysisScreenViewModel.financialHealthSituation.test { - val financialHealthState = awaitItem() - Truth.assertThat(financialHealthState).isInstanceOf(FinancialHealth.NoDataAvailable::class.java) + Truth.assertThat(awaitItem()).isInstanceOf(FinancialHealth.NoDataAvailable::class.java) } } From 29468fcb2fa3f947724f37367c24d928dbe5156f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20D=C3=A1vila?= Date: Mon, 18 Sep 2023 21:31:39 +0100 Subject: [PATCH 14/14] [64] Final Touches --- app/src/main/AndroidManifest.xml | 4 +- .../drawable/teira_launcher_background.xml | 74 ++++++++++++++++++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 -- .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 -- .../res/mipmap-anydpi-v26/teira_launcher.xml | 5 ++ .../teira_launcher_round.xml | 5 ++ app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 1404 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 2898 -> 0 bytes .../main/res/mipmap-hdpi/teira_launcher.webp | Bin 0 -> 2968 bytes .../teira_launcher_foreground.webp | Bin 0 -> 9310 bytes .../res/mipmap-hdpi/teira_launcher_round.webp | Bin 0 -> 4722 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 982 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 1772 -> 0 bytes .../main/res/mipmap-mdpi/teira_launcher.webp | Bin 0 -> 2038 bytes .../teira_launcher_foreground.webp | Bin 0 -> 5600 bytes .../res/mipmap-mdpi/teira_launcher_round.webp | Bin 0 -> 2930 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 1900 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 3918 -> 0 bytes .../main/res/mipmap-xhdpi/teira_launcher.webp | Bin 0 -> 4578 bytes .../teira_launcher_foreground.webp | Bin 0 -> 12958 bytes .../mipmap-xhdpi/teira_launcher_round.webp | Bin 0 -> 6812 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 2884 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 5914 -> 0 bytes .../res/mipmap-xxhdpi/teira_launcher.webp | Bin 0 -> 6676 bytes .../teira_launcher_foreground.webp | Bin 0 -> 20240 bytes .../mipmap-xxhdpi/teira_launcher_round.webp | Bin 0 -> 10594 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 3844 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 7778 -> 0 bytes .../res/mipmap-xxxhdpi/teira_launcher.webp | Bin 0 -> 10000 bytes .../teira_launcher_foreground.webp | Bin 0 -> 23238 bytes .../mipmap-xxxhdpi/teira_launcher_round.webp | Bin 0 -> 14862 bytes app/src/main/teira_launcher-playstore.png | Bin 0 -> 51616 bytes .../java/br/com/tick/sdk/database/UserDao.kt | 2 +- .../br/com/tick/sdk/domain/ExpenseRisk.kt | 5 +- .../repositories/user/UserRepositoryImpl.kt | 9 ++- 35 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 app/src/main/res/drawable/teira_launcher_background.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/teira_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/teira_launcher_round.xml delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-hdpi/teira_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/teira_launcher_foreground.webp create mode 100644 app/src/main/res/mipmap-hdpi/teira_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/teira_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/teira_launcher_foreground.webp create mode 100644 app/src/main/res/mipmap-mdpi/teira_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/teira_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/teira_launcher_foreground.webp create mode 100644 app/src/main/res/mipmap-xhdpi/teira_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/teira_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/teira_launcher_foreground.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/teira_launcher_round.webp delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp delete mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/teira_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/teira_launcher_foreground.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/teira_launcher_round.webp create mode 100644 app/src/main/teira_launcher-playstore.png diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 11f9fbf..2927f81 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,9 +7,9 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher" + android:icon="@mipmap/teira_launcher" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@mipmap/teira_launcher" android:supportsRtl="true" android:theme="@style/Theme.Teira" tools:targetApi="31"> diff --git a/app/src/main/res/drawable/teira_launcher_background.xml b/app/src/main/res/drawable/teira_launcher_background.xml new file mode 100644 index 0000000..ca3826a --- /dev/null +++ b/app/src/main/res/drawable/teira_launcher_background.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index eca70cf..0000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index eca70cf..0000000 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/teira_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/teira_launcher.xml new file mode 100644 index 0000000..aeb4c0d --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/teira_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/teira_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/teira_launcher_round.xml new file mode 100644 index 0000000..aeb4c0d --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/teira_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78ecd372343283f4157dcfd918ec5165bb3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1404 zcmV-?1%vuhNk&F=1pok7MM6+kP&il$0000G0000-002h-06|PpNX!5L00Dqw+t%{r zzW2vH!KF=w&cMnnN@{whkTw+#mAh0SV?YL=)3MimFYCWp#fpdtz~8$hD5VPuQgtcN zXl<@<#Cme5f5yr2h%@8TWh?)bSK`O z^Z@d={gn7J{iyxL_y_%J|L>ep{dUxUP8a{byupH&!UNR*OutO~0{*T4q5R6@ApLF! z5{w?Z150gC7#>(VHFJZ-^6O@PYp{t!jH(_Z*nzTK4 zkc{fLE4Q3|mA2`CWQ3{8;gxGizgM!zccbdQoOLZc8hThi-IhN90RFT|zlxh3Ty&VG z?Fe{#9RrRnxzsu|Lg2ddugg7k%>0JeD+{XZ7>Z~{=|M+sh1MF7~ zz>To~`~LVQe1nNoR-gEzkpe{Ak^7{{ZBk2i_<+`Bq<^GB!RYG+z)h;Y3+<{zlMUYd zrd*W4w&jZ0%kBuDZ1EW&KLpyR7r2=}fF2%0VwHM4pUs}ZI2egi#DRMYZPek*^H9YK zay4Iy3WXFG(F14xYsoDA|KXgGc5%2DhmQ1gFCkrgHBm!lXG8I5h*uf{rn48Z!_@ z4Bk6TJAB2CKYqPjiX&mWoW>OPFGd$wqroa($ne7EUK;#3VYkXaew%Kh^3OrMhtjYN?XEoY`tRPQsAkH-DSL^QqyN0>^ zmC>{#F14jz4GeW{pJoRpLFa_*GI{?T93^rX7SPQgT@LbLqpNA}<@2wH;q493)G=1Y z#-sCiRNX~qf3KgiFzB3I>4Z%AfS(3$`-aMIBU+6?gbgDb!)L~A)je+;fR0jWLL-Fu z4)P{c7{B4Hp91&%??2$v9iRSFnuckHUm}or9seH6 z>%NbT+5*@L5(I9j@06@(!{ZI?U0=pKn8uwIg&L{JV14+8s2hnvbRrU|hZCd}IJu7*;;ECgO%8_*W Kmw_-CKmY()leWbG diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1ba5cf3ee31b3ecc1ced89044a1f3b7a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2898 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 diff --git a/app/src/main/res/mipmap-hdpi/teira_launcher.webp b/app/src/main/res/mipmap-hdpi/teira_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..6959ebb6eaca886a7096db97623172a8b8e3aa8a GIT binary patch literal 2968 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 O_z9;(z&}4K{2vv=E@b!s literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-hdpi/teira_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/teira_launcher_foreground.webp new file mode 100644 index 0000000000000000000000000000000000000000..9ae94db56a1f451a74e575f00283121473c4d9ec GIT binary patch literal 9310 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 Zx-t%vA>bKj+vyFIm1l@!3;e==;D5)yh}ZxC literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-hdpi/teira_launcher_round.webp b/app/src/main/res/mipmap-hdpi/teira_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..6c54deea0d239b7df0509282abb50c0fcc212f46 GIT binary patch literal 4722 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 Ax&QzG literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64e58ba64d180ce43ee13bf9a17835fbca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 982 zcmV;{11bDcNk&G_0{{S5MM6+kP&il$0000G0000l001ul06|PpNU8t;00Dqo+t#w^ z^1csucXz7-Qrhzl9HuHB%l>&>1tG2^vb*E&k^T3$FG1eQZ51g$uv4V+kI`0<^1Z@N zk?Jjh$olyC%l>)Xq;7!>{iBj&BjJ`P&$fsCfpve_epJOBkTF?nu-B7D!hO=2ZR}

C%4 zc_9eOXvPbC4kzU8YowIA8cW~Uv|eB&yYwAObSwL2vY~UYI7NXPvf3b+c^?wcs~_t{ ze_m66-0)^{JdOMKPwjpQ@Sna!*?$wTZ~su*tNv7o!gXT!GRgivP}ec?5>l1!7<(rT zds|8x(qGc673zrvYIz;J23FG{9nHMnAuP}NpAED^laz3mAN1sy+NXK)!6v1FxQ;lh zOBLA>$~P3r4b*NcqR;y6pwyhZ3_PiDb|%n1gGjl3ZU}ujInlP{eks-#oA6>rh&g+!f`hv#_%JrgYPu z(U^&XLW^QX7F9Z*SRPpQl{B%x)_AMp^}_v~?j7 zapvHMKxSf*Mtyx8I}-<*UGn3)oHd(nn=)BZ`d$lDBwq_GL($_TPaS{UeevT(AJ`p0 z9%+hQb6z)U9qjbuXjg|dExCLjpS8$VKQ55VsIC%@{N5t{NsW)=hNGI`J=x97_kbz@ E0Of=7!TQj4N+cqN`nQhxvX7dAV-`K|Ub$-q+H-5I?Tx0g9jWxd@A|?POE8`3b8fO$T))xP* z(X?&brZw({`)WU&rdAs1iTa0x6F@PIxJ&&L|dpySV!ID|iUhjCcKz(@mE z!x@~W#3H<)4Ae(4eQJRk`Iz3<1)6^m)0b_4_TRZ+cz#eD3f8V;2r-1fE!F}W zEi0MEkTTx}8i1{`l_6vo0(Vuh0HD$I4SjZ=?^?k82R51bC)2D_{y8mi_?X^=U?2|F{Vr7s!k(AZC$O#ZMyavHhlQ7 zUR~QXuH~#o#>(b$u4?s~HLF*3IcF7023AlwAYudn0FV~|odGH^05AYPEfR)8p`i{n zwg3zPVp{+wOsxKc>)(pMupKF!Y2HoUqQ3|Yu|8lwR=?5zZuhG6J?H`bSNk_wPoM{u zSL{c@pY7+c2kck>`^q1^^gR0QB7Y?KUD{vz-uVX~;V-rW)PDcI)$_UjgVV?S?=oLR zf4}zz{#*R_{LkiJ#0RdQLNC^2Vp%JPEUvG9ra2BVZ92(p9h7Ka@!yf9(lj#}>+|u* z;^_?KWdzkM`6gqPo9;;r6&JEa)}R3X{(CWv?NvgLeOTq$cZXqf7|sPImi-7cS8DCN zGf;DVt3Am`>hH3{4-WzH43Ftx)SofNe^-#|0HdCo<+8Qs!}TZP{HH8~z5n`ExcHuT zDL1m&|DVpIy=xsLO>8k92HcmfSKhflQ0H~9=^-{#!I1g(;+44xw~=* zxvNz35vfsQE)@)Zsp*6_GjYD};Squ83<_?^SbALb{a`j<0Gn%6JY!zhp=Fg}Ga2|8 z52e1WU%^L1}15Ex0fF$e@eCT(()_P zvV?CA%#Sy08_U6VPt4EtmVQraWJX` zh=N|WQ>LgrvF~R&qOfB$!%D3cGv?;Xh_z$z7k&s4N)$WYf*k=|*jCEkO19{h_(%W4 zPuOqbCw`SeAX*R}UUsbVsgtuG?xs(#Ikx9`JZoQFz0n*7ZG@Fv@kZk`gzO$HoA9kN z8U5{-yY zvV{`&WKU2$mZeoBmiJrEdzUZAv1sRxpePdg1)F*X^Y)zp^Y*R;;z~vOv-z&)&G)JQ{m!C9cmziu1^nHA z`#`0c>@PnQ9CJKgC5NjJD8HM3|KC(g5nnCq$n0Gsu_DXk36@ql%npEye|?%RmG)

FJ$wK}0tWNB{uH;AM~i diff --git a/app/src/main/res/mipmap-mdpi/teira_launcher.webp b/app/src/main/res/mipmap-mdpi/teira_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..ff400ec0cad46362a252890c0cc96c752b0bde59 GIT binary patch literal 2038 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070fe34c611c42c0d3ad3013a0dce358be0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1900 zcmV-y2b1_xNk&Fw2LJ$9MM6+kP&il$0000G0001A003VA06|PpNH75a00DqwTbm-~ zullQTcXxO9ki!OCRx^i?oR|n!<8G0=kI^!JSjFi-LL*`V;ET0H2IXfU0*i>o6o6Gy zRq6Ap5(_{XLdXcL-MzlN`ugSdZY_`jXhcENAu)N_0?GhF))9R;E`!bo9p?g?SRgw_ zEXHhFG$0{qYOqhdX<(wE4N@es3VIo$%il%6xP9gjiBri+2pI6aY4 zJbgh-Ud|V%3O!IcHKQx1FQH(_*TK;1>FQWbt^$K1zNn^cczkBs=QHCYZ8b&l!UV{K z{L0$KCf_&KR^}&2Fe|L&?1I7~pBENnCtCuH3sjcx6$c zwqkNkru);ie``q+_QI;IYLD9OV0ZxkuyBz|5<$1BH|vtey$> z5oto4=l-R-Aaq`Dk0}o9N0VrkqW_#;!u{!bJLDq%0092{Ghe=F;(kn} z+sQ@1=UlX30+2nWjkL$B^b!H2^QYO@iFc0{(-~yXj2TWz?VG{v`Jg zg}WyYnwGgn>{HFaG7E~pt=)sOO}*yd(UU-D(E&x{xKEl6OcU?pl)K%#U$dn1mDF19 zSw@l8G!GNFB3c3VVK0?uyqN&utT-D5%NM4g-3@Sii9tSXKtwce~uF zS&Jn746EW^wV~8zdQ1XC28~kXu8+Yo9p!<8h&(Q({J*4DBglPdpe4M_mD8AguZFn~ ztiuO~{6Bx?SfO~_ZV(GIboeR9~hAym{{fV|VM=77MxDrbW6`ujX z<3HF(>Zr;#*uCvC*bpoSr~C$h?_%nXps@A)=l_;({Fo#6Y1+Zv`!T5HB+)#^-Ud_; zBwftPN=d8Vx)*O1Mj+0oO=mZ+NVH*ptNDC-&zZ7Hwho6UQ#l-yNvc0Cm+2$$6YUk2D2t#vdZX-u3>-Be1u9gtTBiMB^xwWQ_rgvGpZ6(C@e23c!^K=>ai-Rqu zhqT`ZQof;9Bu!AD(i^PCbYV%yha9zuoKMp`U^z;3!+&d@Hud&_iy!O-$b9ZLcSRh? z)R|826w}TU!J#X6P%@Zh=La$I6zXa#h!B;{qfug}O%z@K{EZECu6zl)7CiNi%xti0 zB{OKfAj83~iJvmpTU|&q1^?^cIMn2RQ?jeSB95l}{DrEPTW{_gmU_pqTc)h@4T>~& zluq3)GM=xa(#^VU5}@FNqpc$?#SbVsX!~RH*5p0p@w z;~v{QMX0^bFT1!cXGM8K9FP+=9~-d~#TK#ZE{4umGT=;dfvWi?rYj;^l_Zxywze`W z^Cr{55U@*BalS}K%Czii_80e0#0#Zkhlij4-~I@}`-JFJ7$5{>LnoJSs??J8kWVl6|8A}RCGAu9^rAsfCE=2}tHwl93t0C?#+jMpvr7O3`2=tr{Hg$=HlnjVG^ewm|Js0J*kfPa6*GhtB>`fN!m#9J(sU!?(OSfzY*zS(FJ<-Vb zfAIg+`U)YaXv#sY(c--|X zEB+TVyZ%Ie4L$gi#Fc++`h6%vzsS$pjz9aLt+ZL(g;n$Dzy5=m=_TV(3H8^C{r0xd zp#a%}ht55dOq?yhwYPrtp-m1xXp;4X;)NhxxUpgP%XTLmO zcjaFva^}dP3$&sfFTIR_jC=2pHh9kpI@2(6V*GQo7Ws)`j)hd+tr@P~gR*2gO@+1? zG<`_tB+LJuF|SZ9tIec;h%}}6WClT`L>HSW?E{Hp1h^+mlbf_$9zA>!ug>NALJsO{ mU%z=YwVD?}XMya)Bp;vlyE5&E_6!fzx9pwrdz474!~g(M6R?N? diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956b3acdc11f40ce2bb3f6efbd845cc243f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3918 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 Ac>n+a literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/teira_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/teira_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..715e6ff1c0609b409606fe0c215c5149a9bd9bf8 GIT binary patch literal 6812 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 S%*3dC9|;9axR7~&>i+?idmvK) literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77f9f036a47549d47db79c16788749dca10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2884 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�(p-{x$&=IACIfoAR!za)F-^da-t3#0Dycnp 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 G@&5yc7XlXm literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/teira_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/teira_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..98add3a726cf25533b8fc37d0e5dc5a2713a7c6a GIT binary patch literal 10594 zcmW++cRbba7k@t2#kH>y*S=)ju66B^acyN}Cz6@S&fXW1y~gxd1a4^Z)A^b zg@nlddHv2`&+Ghgp4U0gdCocSbB^&fEiKC{0Nm76Gc-4p(=)g@i=`4TBlYE+FZZRJ zD)|(a7plxtp?Hr}AjPL%D$ex#P9tSmpIy4|C;7AV1{zdUN7j6wGxxR8dfA;9oKzOK)%V%N;OR<2$E~)>R%)2ktt6?p7 z-o>zb*buhGh_DBIQ|0-0%Il5Kj82g$C_QaAr>kTiYTTRdGtYLkTU~o~9>ru58Eg3b z_E>{;1?!TKGHfQn)M)4Y((;w~eP92N%UG+^LU4lB^jdUU5SQ%|bw&8&Dc9VI znbLLGeF|ANzCVfeYa}`eMRU^DmTxSCNalB@TuJcg_e9?Ly?^2mrWQnE!X^Gm78(E` z-PKFdHbbW2Ts>y)lEI`aagZ|DAMqY7Zf0xz`37S5zGgQ^90SSxuww8F?Li}@ys^U( z)cFB#DtWQ7NjakH1;W(qy~47OnWnS?cJkk?fFHKeFtowQt40( za!c}$&CU!1Ic5&V9M%;!5vq6JwZaBT@~S2q?oMU#z25(5gN3P{!J`4v?hK(r&!zTb zRw-|0vcY=OeMNzLxy2p+j-cQB4xd$w(#IIb!k!$$nvY}NuTM7kyM?T@{$7=xYOB4k z^~Sxk%`cI5?>Hd9(ORw(54o4coru2ct{j{1fKxQw<_FK7RkM#VxfuIXF0o5%HVKT0P&0 zKxdASF4T%hNJ@TlM2%?welwb(=rYxy_r=v}^)%GRF@e8MDbO_8+DnjZ&u%1_*M#lS z0Ld>4t{zOv1gQ~J;d2Q@Jv~Cw|Wp>U#Qot zd?53s$gcnR%!fNFHdN!kPR$;;9w3m1?)>cQN)&v0Q*W#z+xoCdTkIs0#U}79bBw$3 zexOqJ3sIL%@hm=81~vZjN$Hi)_198g>w_2Ol$GcHy8MM^j|baSx(@&budS_2>9SDl z023gl>a=!r+@$UaCResL76eC*g_yb+ky3=&$idvRj_sl8lH7`Ed;vA4GnnUU(oDnuGagqM}lD&?p%4 zwo4{iYOVY;AB7n&6#EDQX|=-SCgiMM^_ozZ4wD)iCn+OTxoKLDWyYk{EqG3idd`P! zcPdAQdY+}xqP9&`lx?aW`LTfd@20k;w=m-v*m#zzZ;hLtnk(X$CFSt*^bD;c=+{+D z(PQ5}nBhP~veF6L85KBHa#?PoBs&?u@pWPcAv((Q#w?hE!v|0!@8=AM)O2gQRlpx4ICp!F)-c@-ZWm zDjEI8BZ*aAi0i#9=FgjDuJr^pq|0DfObCsnt>Mf>$6Z&uY)5m6sE}-_D#)7tp<>$S zGK|nnaz3KMowJQ7+!c*Dqdzj&sk`p!C+AMgAR6P_aezn+`dQf@$4tzh;QExfy?TD` z8S#!bp6K|Px#a7&K42) zV1^U`;}7tFh)bLqF0oC};?aN=0fJ7AivNa~7B`v$H!AK#EeV;-kcs@uT$Qp#i}|Dj zR6{dqwpyO_I}J4|G(U;x<|pF1s8_}CaAhuO(}SwTi1jvTNptIOB2@L~Pjt;4$a(ZN z=~j^zd39vesr-(l3~YyiB~*TX_+9`Djkl8R%VJrI_YH;kWfy!hP2A>TfIs-X18WMe zvHPW%tszR{%Qyc$7&S!N=)0cn&=l|XpYb$I$6EEvR#m7w%XZB(^1@T6{01s63jk2v zHIMFJJsp<`JR-HB9j~aBfhSwR#G5@}?C&mwmRYxrSKt1!pc03Bf4d;f&h77|u=RXp zg>8j>4C=PxZFj2)qh$taAlcsm6gGN+xXyD`b+ts(= zz_!m@0}kTFLGMZ9Y3MPHVd^2h%lP@+vv+hp6`vsnW3$8Bq=Tbt9M^aoR7F^2LFFTq}U`^(&ZDjoFkS2A^P&1nR~ zrFp(+6MIMm1|RfU+Y|Rrwc30x0pD&;7GJWS+GXAL!;0HOlBz1sFZBTwWun6>4}q zs@5KWC|(R&n-iuBGlU4J525ed)n_}SE`3}~PU6;I_3CzI-I1S%P}0hX<6f zuQO1l=XvPW=%Tr_*`@bAC#&bjW22Y^);=N3Ctcpex6Q+=!cd)-r@ z+@iE2vx~lj5X54Ou0hyhk9t8I1sVkd7(mn^wv13^hL0(Yol+TE7$77)7LLQu-z!gM zM^N`Z;s3A@Oor~Q?u2jmBdm3ePmm0LYAkaMYb{@@gnWr5no2-eTwF`0r4j)irQL&W zCA?j!*pd&GW*dmN?bZ*S<32lTYCMxHezl9ZwQYe*kW{$W;4% zipGr>FF83|FfO1d7Pu*qJzy!KtT{(B`0@V#u8P8Ffy;Y+1$-C3n;IfY#R+ihblUq8 zpJ1YTwlE3vzT|)a!KlGDKp&u~-n4E!s}RFG$U}n>!M}3m4g}l&4*yy6R4tCWOt)fW z@gxb|OCZJJ7$g)~B5(2T|JE+LF|V!sSWCrEMa3Z~*n;()X#xy%`yl897#JPZnZ*(@ zrYo+$ADl8Ub$>iR-?K^LJ{_0~{5PLtW2?gk1BCvhAGr5OV2iLr@N~~IG1}vvo}^WMVH?`h4_#aIXy z_&{6T6pf4Gyk$EJY;6~Rrt3gebCb`k<=a}rS&nie{#C`w4ah=IufYt0ip!WZ`7-zs zpa=#Y);yvmq@f+PuAu4Hkb{y4_*4GNpJjh=r!ne>|EUriv-$VTPjR{8$|ble65yE! zrQrnA3zsWKynx+1E~~rkYh>m2RZW!vd5t_FC8x782e%5-?KF^%;)|;-7U*PI4LtL% z?5$BbD(0DMnrmsf+w$@r-!+TuMP@bdL=?xIoK=iTf!mtAqB5D%dCe1_XDEec=N$6I zs}X1j1V2R-DASWeI`DY^M>R^K%43x!Q8oV76?y(W^>o6fjczj6y_KVw5#S-<@CQF! zj3Kj-pa#7JS}5}O&G96JKu*kc0;wa|#^j(NU<*KQ5=Tye7?Q9f;|qY9>u38)8JkG5 zNBJi^Ds5cO-_}`xHy;4r{U6>MGx_nRqcb!Cib#?(J0SR_yhd zd5$mC;f*!}m%tMR9UKc+LGwRd<@>*`jXuFil0nG$YjO!p?Qk+6CGf{~9@z2X8yW2; zHQ*G6RB07j&}6#5okSJx4lHCHJWnn^Vw(AyWLC zvJB0Xr&Y~9|K@v+4*5vxpO{0!;zf2K2L%X}013zdL62aHeyJ*)zEoVnRP?jVf1+Np z;+0AeO_pr9YQ^~8RNIN_?US9adt%&UEbo_=tw;D^nnt{!X!$lDJAo44U0ys`dpSKe zI_pJ$4(hEg%Xhknqj~&imZ+4_DYDE*?#BgHe*sjNs+7Hpo*7)OHl(x z04U}IX#mrZ9ex-YW;bG3rq=oPURU+~2X${-51O{_B>Y>QQ%upn5!!~I9{tO(t~J@m zd{EngYD)u6a*fADIo?O?CbWUSoW$uXA9jomg-3MJ4j0NIvuI8Xvi zBLJnts0(}cYCTW&?^sIwK9E*@_%ji1|7q7@Z}j_r{LG|;C>WdxP{NAsOe%O zQ2^N`UIqaj6kaOk!fDddUb`9jJ}r5w#NwUw^g!9VcjeD+C3*Ri#Or@W;DmmHD9F3c zB4EhGPmT+Fjq<_)By}HwmY}AtgV8my-mA5KdBCz6Eb}$9#p%> zHt}osI=vh2^tz3(ee{X=iIoXolK3n=%}t?Fs4n~F(em|)dRF-&1jQNyaD=Mi#elHDv|;Q_ z#9)t&m;t85z2rewrCh*yZ?4--hvq-B_{w9Eo#enTELO8&hr|4y$7_!7ir+9k|NK%S zcqiQn&mDSl`;GYm)WhMO`fTL)-^E#9XV|RKuJL4h2q-8)nXmkd zD@A0}8+UucI|*vfg-vv)vKr=&cUZ#vzt|`>t)1Ld*kS)CU$f=lNfGE3653wX`Y%86 zY<^-+p~?MAK@U@fX{>p7_cR9k3A7U!zG>^lte4dzQOVZnw@>`a2)mDB_%07V&uX=z&1ADC3 zb11f#%<{e(q&aZgjo&ih_t9wKpJkOW-}Vlb{l&!jsnE1vz+2`KRaQlhRhZ*X+X07} zmz+>;&Vl1Xv9m86cE2c*?5N$A0rYnolfwznL%iWPK)_xzv=kXh(9CixkS~7IpUGUA z?13f}Y4|;<73GT^C~LA-aqh`vcx+B#aYMm&sgu|h9|dXZKz4qtLM$zNsag>qx!sk1 z#vodqzd1$(rhR$tmmEJ^mJ};dP6yANtPvwctRWioTWz4s>$Db(ux|=*d_4r>e?sKB z`rJnkQ&!x9HQtvd0kSTV)@{ibwDytuOt6uk~}Auw@X1t8AC8(YT(2V@xh&?V@NsoHRntpk)n zV2CuPKzNWfd|1qJ5_!D&fH1zj5%D-~^eo&|P#2$1@J1|vDXSjXMnPBw{A<9!C7(dQ42I*}vGGx*uUJm|b>RPEGWC->EfvcJvNX_HL zg<^#PsVTtn=ET*RSka({*w{WP5Kcp$V4uKVeGo^0Sn5rQ8B8XjBkiU?GsZc)xwWZQTzfPTZ_i%gfRb z!NR@r7_b1rh7^xYfkEF13S>;zxgi{y2On=mo=#<0^nLob5Qk5AWmwaABk$$t=jIfq z8drcg1=%Nj%iwqFv|BtBY5g*j*fmM>SK@uQYOf^}0XE9gy)J%%7f5=N6nDM-3)fYI z=9$tAq57p4o_{YU#mLb?fp+zdqe|80~ua< zsLYD>BOr0TL>z!fN(Yk4o}{sA=|%k}sPz;b|IPMG6y$O+B1Y;CqE z@9N#wztb$rfx(oaxpxAFS&m~`IoHm5>lVL_WmT+v$vQqrKK4LF=Oo;+wFDZZ-~AAH zTh>4gfC9E;Ff({EhePux*NGdkRKY7nk6IsRX(5}fK`nV*hth2LVc~Lqz|OMgQ!{s~ zSVP8KfI;iTf&JlOV~Qv!3XYeNk45NN`O{x=u&O1Jo$4Q9d$wN)eiO|8W)W7h&Duh{ z$sPe6-Q(IyJ=-?fzxVe{Pgp`?cnIe*m@o{0(QEY_3Ua{P{r?wn<-srs^zb_VJX(I9 zC)+)d%Wo0|wB;3bx~k6qMu{big&y`zxI30SEfbM|KH(k`bV@sbU0VBiaGWsv1#Z>B z(O;dcH?6&bHg=mo-}q@}bIf_59CQF*p@P^8L(U&-M0AvNsLvhU&3EN4box3~GpPAx zgltcfoxsCt$U}-R!4NL+7rfHmoI2fEr`Xu#mV@in@l&*N=oxm-vrbz(dsdeR3*hR! zZ$9LNtRBwEiiXlGHs%yKxFwg9#_ic%L!hq0O-S%N2;)H16$-K}E?W&@h>s~)CoS)b z$3`s!otx*=IxcmkgMGpHSzNwM@bsLL*VmGOm7e4B=03h|x8SIRB1!@QcoEil_gR1% z;q`6EkcI-QI)u$XtO!0n8F)3vJ-+W;=pv?BqpJ%{h00_4EgEH;y(JVI$8_`al;b9O zG04lh8hmg-LdpcvAaU3eR1NOpsWfGS95e+e(~|DGE~CG#*#x~(QBZjp`?-~QZ}j-M z>5=HFLGf+-aaK*YqKYCKJTssjln3CF6NrUub2J~qqTzo$~KZ~9g-+gWRkiXYJ4G((>&!Ocp4g{s!76@o)Se7-ajM*{Df>kNq#J>4; z*N0znUxRI)bGL*={9amcY+iD9b-l?T#0R;Q)+V2Ymp!9)d(( z;wQ`cbk{bnOFvIk?WP*ML^{vEu={?{!k;^H(MJpKPXaygZNp-Fr)P8I9#1*Aw&afY z&IY>Ie#&4+CRO2?R2WDvxs?UU2>GPTl@ii4G)UjX6%#~p_rM-rS`)4oT}c^u9So}Y9+!AmiG8;@xy2|`GN(Or{DRwkaNsE&(Ono1N~s~q zPrH}w4x^&qhry;+n~8|698~qWT-N%UgUX4=AYIr{D#dREr>76uI&|QWaH8*ElD*v^wt|^G zE}{R4p-bw`{W<@hSK+gdYPLdKPHX1t2FF?seIid&PUxnaEpB_lLD%!55EO`|ad7zd zNLG)m1EGU@e3zv};;MzXLa+{2hkoVj&-sWW<$|VPx}_1~KR!6`E$lgUowYQavps%# z&HV3YMGFLghybTZpg?tW<7}}h)|M0x;RuljXLbkK=aUikDnTBaC8;E9H(Jh5XJQsl zPaY369x8B?kG@wAh(!Zu5R?M@)A6yO?Quw4a^N8#$bJ z?Q>tndb9iTlp1i-M)H3fw)2hb5&FdQ*6;MHfv6%`WZg4s|MBy`{cW6siW%3;OR%aefx8b6Vm-} z0dC?yq>lP`ch6dP^CIe|8#>xjc}qN^26F~bvDe^Gnj{f7DDTjYdOV+IJfaxNKl``u zYN29)!yTom=U<;!e`(mN30eI4n^QoD_02jOVa{P8aZ{Cs@iCy~p@R7lSm*1Fa{a~! zb5iCBYvnaJcjK22YFzU#3H{_K;tFXgoc6i1d&6B;@#D!iN>-qQa2X6;;ej1V9zV#^W*IQ{b zY5&EORsXgN?dB;ZXl2u0c-472nW@0T?7!j`DH5%|_R@Jbc(w?OxM`CyI1S zeDsc6o|@e&m+bqiUaa74c}Io}Ag}^*J|FfkiP2ScNF=KZV?MBx42%n-=#-V)oZ>2- zJ1-+3HL&nl{o!tbM$sdeS-+k8Nh8E4Z*Ew;cpKGHjC)Hpznb4Ng#&f7BQ=4SLHdf- zj@l9jN~e=C?cUR@UvPVcw_L~|wMX5!`MX6l9eb2X@;{!_FvJs%L8Q3PI9lB4b}bEd zSsBFe5#%#gY84Xg=^;(JS2@DjwyGX;mN=x>1vJoc^bjTav#s6}v>8Wi(E`Q)aiYm^ zioHkxpMa|_u$5)AIuwFl-b7(z3PbjHtDo*orP6bubogKfg0RQ}zZ;zawLt^M1aECy z!bL|hvZIsmNB}=2%rfiyFlUBU2M#k3f<*#&YyWwO-A4yUERvWRHqBzMSiJ#7YN3%{ z!9W#RU8gc3qHs?O@Avl&RL1D|@e@w;vQ&zr)Lu!}=ETe-45m7-3J7S1$nlQ>;a9$j z(l`$Hzd>_mp;=0}o9$?C36uFU1ztvrTEArAK;9M~P78e9d&Z|=Xc;An= z#8dOBoFZ0>MQ+-Fj3j_7y*^>oenL4u{tkc;irm%#zbTVjTN{p78a~1if&mJ;w78=o zR5ZXyh+5kD6$Wi99Fz=gU1IalmAaq)WmoWHQutoDN9(Y%FRp~oGV_7)1t4JX0Y8b% zd9<`R#T@)aHGww3n3;7da_sc}rJJFy6MUdd8AFCwqNQ<`I>OPdP_Mn;I2IY}-Xyoy z5)yio_2O0@DQHwrVfq70-k>KLYH@U{etcION+X>l+@TDCCoA2bjJ69T3`4w#6rF1n z0+}+aJrAmbdsaWV;Rg3gtWZOQhdJ(Inxe65v8b>Ox(g!kcE}aS^}7TYbe?%d--w7m zD89EEf48nj$lSecNBBwr=RuZxicd(*aM*$zUTv3vuz`vh0h6p!D9#xq+sg`*pHC;gc06 zb+gLB0l|N8ySHm6GB^aVuQSj_#mr{qM#f4V8|yx%d9&KzPdOa?s(@ zkLlU4fau%tx&ob2kNIb4t+ihh)ZnIlosHsTSUl=-8RIu2EHX|F4KUBi8>4!csrE-j z>F#izpHCb$X^Z)c?Sz*5BrcCP)^%}(RAQv2#pwwagYGxp_7ptxSQye*Sstz9p0x@4 z8L9i#i6b>Q$XD`~{iN3LB{O~ozK3cOd}RP65lh5yWckfMOjtwcnXbbvudlI*H1cYs zyys}`Ih#@nSXA*@G&mi5WOMw!qii+l4#mx>mX_y>ZDJW1o>P<)N*qHz@T@Zw={B0C zzKu(GE^H%lo)di#{9t>|XDTyduU#-AtmT(aVXl>U_NvKKE8!jwdckq~L{4)iwykPs zuxIaa^;9X~GdFF|@2;GwquMFKxpy8a!!xt9?}JXzbt(n&&A7MnO#^k;2K~uOE?J7V zOdz83J$^bRk?CY9V+RE@-tbn4HfZ$?X|+krCUj9TfBm`rRJq}Ne>(hhZs)aNM9(9Y z16Pkv2@jNW%{GbFlC8*7L1CXqheCHDFmnN0GXTl*Y3t0L%mg#BhztU55 zcm5JDSB0d!7i$X<{i?Eh_EWM+w}JLcTky~H8{z#MTd(al^X`)|QNm4a9g{OZ_a}8Y zbQhlaD*n+QeKMA9vN?L(lPxBCz^y#z(RMWZH#t@Lx5!PgAXv;DW#!S2RJz|u1-Vc5 zZB9}nBQM279k3>=m3R?oWX1~uBIJ~AF|*Wokw)U@=l{xjz725+-#cl#vD#Y6Kz0drNqB(r|xm?tJD6Feis i=oA<%r5Flh8gj;8FtL}x#UTCy^+*B028O(>Nd5;@t=|{` literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d6427e6fa1074b79ccd52ef67ac15c5637e85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3844 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

UmY2RIYtNPVDpE$%vda+HD#3m z&VuXJ{BK&Qe+rBa7eq}Q(bq|tn(RrJAk|ztj2(i{d>nmQnM?;HF2k&9sA6up5tmjl z7lySlzMbifH17-m-Lwa_F&e7nOH?ESi3#ckR3tsM+jsck3`oG!uMS}|eAwVXv>}qxwq?QY%QJ0}r@^;fhuUA9W z*BVl>TGo&N004@xSiwDUXUvp51sVmqO3m)=B55aPwf@0=e}cN+$-BdKxY`YrT_4)0 z_d10#i44Q*rFr8MC>*)v$EJvz``(pb{e&*6k+b zsMz%($|1+8hn8c2?P(l@;Rb&CsZeYoCI3?2!LqjbwPXW3z4G$Qfj=cT5Yb%vY0(AX oeb?AaKtwrnc|$|zzw9vfvn^aJJ!zd)XFXqqy0000001=f@-~a#s diff --git a/app/src/main/res/mipmap-xxxhdpi/teira_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/teira_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..fc29b2c4c648a2564ebb2a8b8c31248c0d9649e3 GIT binary patch literal 10000 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 Ua0qU1I$mo-DUkCGiNGIEkpq%*Rz8n%oS z%8atd&-2&w*E7D)^Ll;1&*$@gE?+P*YPJKw`mDZ%m4%u~^+{i@s>K_m>|}{{XNY#! zk)Exl{5{sKm>+zP`E*Plu(7YN6Edpn!)H23UfJr-Ftw`NH@1n7W@@TS<_+iGj&)MDt}`nNyP$ zYouTNF?Mi;QxZFB)|lx!n;L*-bysrpPDsfSewC7>j=KE1cNrumJ}+%$PQkEh%3{v; zP_Xbsj!T$_4jHFT@hBR$_h&#u4WHTw=?mY=LE0W2|v7K$iHX6Z1Q(0(o7U? z1)D0=P`}1t;)>Hf3XuyYw$$qGWxo6OZ6WrQ*~`N@4p>MGtJ1>`CD;htM7F1P@jrsQ zsA1gwQ;S0XF|q#VE6Z}q;IR@fF)i*qYxTXO7iUTL#d&XExA|Ijd(?+<=hsXmI90AmD8)nyO(%3e3(V{0kE0~s62|?^WmCW4 zB~h-f#lYLZ?-e0LS&jVM{fdLl`9pWDXAsl(JY6$5xjvy7w#Fi7CZ}TfWBGd_iQYjx zk8D|jLK9pMS{HO7kRU_&V?*?x98@+V>wjv{4_Ns`{AI0s>p4az8(?+o{WzU0ZqUQ_OJ+*7LS%goC(T;1LFdQoMYb_DF{ABxZ+16v_pS!eWnIzZj;@Oz& zqzy$M1-mbFzPv2-Hnvv8GpCbZ;kv2io|U?rHFsvG;SI-YY?p*7ms1KnB_A&j!j=EJ zdwO27-gS_<^m^}d|G6{R5CLVzoa?p4FA`D*()12D976W)%U#I0I#K6q{4Rw_d|SNn zwZ~?fUD5D((e};sTOt=?+r?fz%&6KB8cb3>EAJz!$98I4=c(D}$txy6cB1a8eck2# zeDAfuQ!kw+zZG!UNbQKT{GiC0gWZ6TkOsD!Zfmt3$BFC%B-q+Oi0DL_??m0tOE;Dx zeLOrqJ1Uvl;?DTJxow-jBjvKT407x6cqY&Jdl@XB+5!Swh(* z?;f)(hc~?PzmOq)A;X99LWL}B^VsR_@E5xWFZtQmosRcexK&QQq52Y`D*N#d-5?XU z<5tjH`z->9yxX`|6_$O7s*;M&nUZNulO1p` zLj4sd1F&;_)Sk~unvcvOCjX}l1APyLHTR{Za1m6}4Uw73$O!rE2Ini-%E&F!_LyR( za%T76z}Si$%kLrjk>OXq6`#zh?cJh5MwDH9cxYDYZ(jB;K>xt(mo1D|T!GkfBf>ei37uH&Z9 z3Mx5S$8FexE^FuYO=gj55U0)QNZu+7ISq&@Zl~%oZ}kKw%0~s`@Al?WJw2eLVi$lN zJ#;(G%T%+aKOm`EF%jOTfThGJu`7k3Mx*z(2A2!QO)_|L-{HNPO<*JvH~P%6Q}*e} zqRH|&?j$jHVUxl=8N!>EZy!aQjA#eynea^o$=QVM=vS321Y)%1QCPif*QTIW`zvvo zb%3$uf`;v{?2^W)emMV^W0~wWqYR#1y}hlDr_&4WeG~S<0tkto$%2jmOkl&T|&%~seW>uy`taECeP)%tnDN2<97@4-lr<-7Y<33${R ziO3@-#yizpCZYNl&g6ujU0Hs7mOS8(jNQHtB(Uf=DJ@=r?q7^6yV9@R2gSJ?{w4zBMGN2KuK@6+3DEGTalZf|rm@P%ax;xHu2>YY ze*108>@yfS_AjgilAhagjguR23dlL;NPWX1#^jth z_OQNu?hR%>kSx4SErbmqTqjl3W||AV*X+?p2@!O0s&{8|_+d0=rzsc?Uc`u~$Q?xw zX58&IDde~b=`uo#0)-N-7h=w*9Z|9J_{AUz)$yj8eI~u%#f-hu7lRr0;$zPP6rcw^ zzO6}s-I<(n7hi7q*4a>SAgj?#TeV+ZEev1S$_`;#WI~ddhmuu`&xxHj5&buXeeMm! zKq<{4<1Q-wwb&{4>rWv#-Xa93wk*6T_7Mpn2uREQEKXqox0?Ur&V?k6fZm0}}Mq~J3q5-600KO~x z!W}MtHw=tqFn|BPdYe1}&=?fD*OLo08s#SbQHm{m;td)zp9Khw`tAj0@ zt?Mdad=RSy_F9qULpFik4h^Pik7{0&G@M&4WJwhgLU-J>0QQdxJ_= zcXocN?}Q1C{C6&Z#2|ok-N3PEV7H*@G2J*a$8+c4;8jgu=nb`1I#+Y0fwh|HqaV@7 zht@|wAN3u|vHYc6jW20qLcD@vb@6ySn4!f0(wqNcAZXwO;J&`vKU+&2lS0BC?L`Du ztu5c#-YFKE-P%54f0X|yO=>aNp}WRiZFYqjlY{ys4J}d-(+?6c@|1Uh%jcdpGD#A? z0YO#46j5?Y*NPsLi@Vv?SxHiyg9yb+ah`6GU5q3puo3I79VMtcDtMIn{^nNC?n*>$ zKerCF2tWt~9sw8zAGP=gS(KyfJ+xds+TJX%J}8*#q^lsMet0k*b>~`IW19VuW$2&7 z5d-B3HSM>Iyg5IJy76cV!gX#%O#3{j07L|lg}37H=S}lK*8XkJorI>`_oMqPTbnHl zYmsx@kKVU$AAgB%+T>d^KiI2CQxs|o+w$4@IZ!jwRaj7)8Hr)^aseHIt#bp9=E8-B zN9wBfROya)Mqp{Dv$p>l=sSFpM)&l5fs&vkfgU@h@bTn&ZXU-Y_(+9N2JrH=K$lfZksp_@V@H_thAMi^_<82zuM)2)z)f7r8I(|@R0Ixi zkXRdW!I@VP72IDv`r5t1|H~m-odsA@mtW7~I0cK4cJ|Y#>;3#s;OO5HqiWT$>ISpi za9Bm)6TwAEvL{ZsNSu)X0**ggE#&EJud0tMZLG}f9+D#|#Jj?P8iZmkgM=%f2r)d; z&(CUd7WQ(D2`XZ2Fv-dxZjI~d&$2J3w#?GSAi$6ZPX3e%0eH+&iZhg3Hgnx9*$2&# z1tntLLSpT_=n`L525Fh$;h=!#EAF?FZjDdLRm4yKoAA_@YpnmqQs=*Py)mNc(+DV9 zAkl5LBoZM|8?e69zx5~3Q7biMq;h39Qte1f6mYjNb3VVyGg&){qY?fE4LhOn~(;WQ-9#$WQkHe$X1q%WR;yQZx6nf(fE>var-=(eE# z$t?czmY2s%kLxfdD`U63&B9S66iFUmuI$qQaTESaIgEPK&hOQW2e>Z3(4$J3h*%*2 zvU6tZ!kt@}xjYbQ=epzF%tnzD`z??E`8z(ETu>2eOnq3Dw8g5$UPBwSG{>&Z!uZsi z&HPz}9FNsp-_N|U5PoO_D;kLp<4^t=h{SiHf-Sq5*L}QM$ zQuIa@hEK6fgu@bO#isxtI9}T8H`RSLqBk}CG)6brZlmiFI(abp5-(WRM8DZp5ue`B z^Zc~ja>nY`&)l4xz?y)~f2~JzSGUc#cbBGw_Nvxyl|=sa7OEJ@``2Db=W-X}W;ZIu zFDb)CAUz`_%}xaY^&q@-3#!ihQ{m|>beWri>Mck+80-P2+#(eU1nD+*^c5gI1Nfti ztNVAjy@4CuA_G|}$m7de)24P`COZ$Al>TcdC|4haJyTw(9=crmkkFR5%!g5RyH(q9 zI5W|lyH_vd_};GMP<;o}{GJYX&I|l~OQ6%nijghamv8q>&52-*IV<$~4H?kjaeVqU zH@o!6Z#+zG<+ra$8+OhpGmro#yM-YiG1Jm=mRsR-M(qJbCtD20K3Frd;-uRJBk=kU zIAz{|`_z2{oynGtZoQi13HquuZ&|JMUwmf!t>I2pD}^7c4&OHG9v^7DKO9>*NG2aN z*bNMljb}F_YisFfZq%u*s=A1eu;XCL#Si|^pOu6>FBv1h#VxPUBp z&}Te@Az2}KdV~dKGQcilZ_F0fVAs+P4GA! zDCrDIdC)`;TYnlH4%*VxFTR)$c!36#M^p z_m)|&%|<$r=aTc37k|wupZ~H~Hylv+z=uyz$FbrAP-%;W+k*VJEo95wf~PC5Jvat& z%RD%8VS0&3nA0>;?~HcW zZxg&ugI~oO&zfsN+seAxsa$w~^^bnwb90e%C!+4nyAZxD;$YFRo}$`ojekG7qxYGo z$!mLqEp28{$)GxZp#22jLq5nKtnuu9DlpqFktd(Zxau5TshAnqRR8k9@kVRss$1>t zo}7D;4Nk{}no3aIRL`9MqI=}=SZA8k@$W~6hhc})$8qONp7n{%()t8d<}mdho)-1& zEZy`FL-u{CxtR z&OT8KG}HCAJu^1J@HK%zzsz*pt2_FBI%#Cmza-(D8a+2B>7PS`$EQlE;LlD0f}l~- z_VGD&kTNDYJM9;4xZb?4P8F)oY-00v->ZsMLBWxVnd(Br*;zivp}LGqyPn(pKWdV-0YmGz{S$zbMg z5f~Rx(ZJFa01*cayI$*b@jP|e%(ksz36BsEt-dmFfsgMuX6?+*yI)*EeDcP(LNCLC z#Bku~%z2;n8*T10=DB7f6zeaU<|-%+8ETQs4l>CmFK_li5YiBQ7YqHt{HtKSplN_(4Ma^4uu8u|Bc3gP?N4veq}mqgF%i_! zC)*IoCx80--xEqq2cwR*=U9gNPvk)(T@f>+0!Qu_jwp<_S&4~v{wD3EQ-8VVq3r%^ zGdz?4kJA#jExG!US#@h*=1qDLG%2#wY0wI7E-pjKVnR$4yR33AEFLIu@E1W1)%Ztl zHGkM_T;OKXiaYyUGfVj29aIwJeJ1ot0Dqv1xAvpzRuXT6Q1Wbz-Ht+vd^#U?mjex2 z0EQrG|5GNHeR+n|0VmBWGLs$O**V3^i;<(Qd2am8V1e>Je~&-~C)D`ZI{o@fWj@!T z#6)IB{?_Tg1SitDN*FM5WyhE-ItC%0v_x2< zEqaSCcJN@)ttV%+vpL|p^0x?=)y|;cmHYWmP$vf*xPsQ?`g1E=%i1}jH4I+@KG2U7 zt7Kx8>yV~jl+ay@gq&U`Q7sm;S@~BlWNrQk4l_nf!zbT)iMO(=+^yDYj2XK^+og!Y zWZ!G-t`l}PZjTmA4pCd~K#CePhKc!!(LY&?ElaqJ=`Cyz_Ww{GmZSD+wDV zEBW|xl$;|I&AB6kWDq~a!h4_hi#QVH`kwD0GWH|D75#Z3(}^Yxd%i%Ym;K!|_=V3@ zst924{pIy)Z~zKmyV9qigs7;`ZfF*nIVClk*bFQ?EuvcEk0BSezI{F$Kl zajQWK zL7ny0CCJ>5MY71+fR^pM=Rzi{c|aaQ;^6dY%0T1w+`{>gXG6KnrneuB9y9PD4V|!Q zL4$f1!+ue;qm>VjUeQKXE#Wi?k_TiKR(%Pr3^o1k_B6yRB@#|uA0k)~D~l(xkbfg& z@h*%giY_dW5kgRe5`e2#he7Cg9ATu|p2`!RqM-SyOFIzmM>Jk(Kffbaly{+|$^+?j6&;u7@Ov>+7QfT$qR3zgo58QPc)6N6#9#~>aeh|mfL`hYwg`H;xv5P zvYdTN9)pglZ^wJtJQn{r?Ru8KYo#c#dHMUuL#5f-VR5yd1IAqJr(2`6re z*<3&wu)ib#;ugOAy9rMLoKgh%sZ-Oa39K~^N@UV(@qGCN=33myA_BdSwtx--u@>td zf^kxU&(6m#fZ3uqgai2A1crrh)-zzzpFls|&qmqpq`~TA`#S6OtrP`mx&y8#?L_`OaoQ>c!1(2q(en?94p%0x&U5s<1!_Lar)?O#oH>@S{i zL-x5@I^Fw^r4>btvp}I+B4mKdkdcMwzEv-77ieK%YH*n{8jQ5s{D3n|h2in%ha2x@ zbD>BQ{Rer$9W#-zwC$YH-I)d!Qnk2llhB=!%L9&XE4KgMA?rIsL%I~b&#~v^*z%x^ zKRigVfLZJ*%lG{ty3mWO(RKPlu@}%`l^1-kq#t@HF|IvkPR@a3t4It6_q(XA4pls3 zH-5dHL$B(_!JvY$%)Lrt;-nxkim}FH%L0n0kgeRgkin*$Gqw;MQXk^UZqbLG@}t8| z-C4{246wXD^lli&*$F&prY%=&MjuKN!U3X?s3~YGXC-2q_31@xep=`N7!Da{0VQ=T zM@B%10h)xnk%;SeGY$E2^jaLI_~J}V9yb@!F&FqhAwqfu^wyQFs%xDF7>2}`T<#-_ zaJ_2lI&^eX)|NuQ+fKx@wJ1Yg{yQ^}zjva(eY|&HJ2*+>9^|SP)B=L~2brObjt9Og zm`YLLr$LAnr?_C&p=Mns7qd~)_*O#vYoHeD`3(WPQS19?B5QTr?3X%H)s;@8wOmK-`EtdS5`Cb zMMQbKJTWi4Pb@e4WzxHUgVRu%HR&v?CDF~&qYevb029UV1UHjnAF?|Gk&}=cS`I7? zXWg_y^YetC_LHpA4$y4!7qL|E^7~GS>oXSG5vS4r-IX17b_Ghn#lIwVy@za8>K)xj zc@p4vzI_~tVfnMymiNI1(x5zbHQK4Bv2=Oe>{HSR^A$RX0W$eq0pzClCqdVc#hjZp zCI032OVW!7qy)1bJ`%G=Ec30M9*D&x%YeC!a7b-y2e1I>S7_inY&+K%D$aUMdIlqA zhACw+)o$+GJ_gI`zMyFxyl^P{`rA}LOv1Y<6PtJNt*>sRXIZ}P%jUmz zyu9Xg)g1fZ8Y$7_nBZ)7%N!=sP`nJRNE44t%*lS$(brn)mq3tXM2UT%hl1@}t8$@4 zL-(ZOxj?GW7W_t{D)Entanll3^H;mYR`X4xDD5JayQ*$Z5BKg7u?rzS=MpJL33(1l zlK}lP`%$;8pL%eUR80^tf_IbBFpwwYUvL#;Du~f$c#Dfev^T)iew^d~Hr#N_yrSwj z&0(eev`um`f+7qXt-riV{P6%q>}nB2<|xQxZ*&1~@b~r?!7ZoGsn6tzcf#w@1}4c6 z>@NhO6M$w@3sWAs=zX}Ujy2;9BLbZW!NlPlc+ z?!7m9^wxkaymClHEP%^xhlGBk| z|8iMDuMO3NiGWOooHwEx3XfXH)*nV5{nb5I zJvy=;Xx@u@t2oA%WYJvAq9z4;#Eb$I4A}d1o9CB`WH`XNfHO!JcN=YRC*_vup!(eq zhLCzqkaD6>#q2j!+GrvhPVQ~ra1SnJy-f&p<&;O5R;aSTCW!XAj~4XD2P|-(ovD%) z+*<2A98>f>&B&V?{)I`TPha8y*g%Nr7Z>t!Fc6|5l2nKK6>EjdPOcMf+oc#&<_-rP zFWE3=W#1^MWpgG7SJg+C8TB*6|oh znZv(@)+mw2@E+)HxI0sg$S3CXMFgXo5)m$#+vq#<`g>i4&y7ZBwaPxE@kfc+c+v~K z59_Tt)=9;}bw4t@9zbwO!lQT&h48W8kGaMf(VM-JR%9dEF6kRVnlyxiRZwrl(#sYH ziJZ7+Vs3ajB)rgueFT}$@UZWG#up5B$`dJ9_F>qV2!GA`b%|WW2#awh%_ef!=`jT* zc*$VdZ%8mmCqMjj@huZ}Ho}Lfst&O*&=tY?#>TVLiw~MINs?YL5wHNJ{=?`dU!UJC zS%+q;R7*wAAWV3CK!*G_-a`&4($oQ<^kj&|2m3NVXOExh`EJ2*3yDSN8!bIrOy!cT zdvsvT2-?bCl=+mZ(%y2OW`Aa|ubjTCH^Vj%f+CG!0EFh%kv?33^WZ>R5{17p$h%bf zb;aN~3w;ozZ`W%Y<5w|j%#B(c$r9*fl;wK1*zAdq3?;3C`}A7e=apZ0or2O78PB|< zPCq+9vNXS+R!`d^J`xQ1qs#O@N@ifKUsd-P3#|>naX6g+ttNj@EzhW?x7V5ldpGIc zsrjDkEg~2QuugfXNXv>^^Y8a+fE&CdUQ$g$V6CW3+#DMZK7)D~zvsmRb8nnd-O$KZ zetjdx(}H;M%WIEhx+$}U`899HDlXzXVG{$!$Wy1jCagFIUi~(-nm!oE4kj!7Ot(}f zoCUJ89OheB{wT}!k&{-xe^`nvs~y;_{7!8`kwoyLj(tyVyuJ3m`Onn!q4|c;n(o?R zfO|C{89a0tL@=()V##klPFywjK+@*R%ty(-52^=S=AQwzHCi_32do_)gPkdUfHW5l z&gfqLTX6vY0L5{oU%!BSC1yPmi^dk~$7TsrYb7U-Z&<|K8Qbj_H;U?07FZxx@~U`lSE*EISKI zpihSjRQg6UiG)VjmTYd1O(}PVQ(+b+@|U?SWjc^(`f-830Wut2z%p-)LyCvIk?nF7P=;Co4ISQ?5CeLKDU*E0kjR)iqs zbW2cWgP-&yyS5?541?N*PI_WI+id zBPfKJJ?7WN96YS?>-CM>iklQ^fe>J|81Oug3_<-#s69scs8{8BUD z8)k8`t>z)oY!ex12LbQ|p(*JEUec`(E>I$ra3)_>Nqa%G_6><1(zbhq8%XM~MM?6n zkyk`Jwcp)RGZ5H-o@PkI2s2$sH-D9zDx4TEcinZER|IDRO@`7yYy;yhPvi$E8&CUe zFbA#mvT`6vBac;`N3wygQoN*3!b(ym z{G|G&CAL7@q!36-?7}I!bLMMf{eg4!q4Va^MOEDam*;XrSVNCA@~tY7B*mRS9Q^#T zG+uX*IPXwU=72dd$prx+7COTN#|OY&8B5)-3Jg;UpVXFnFWq@K5=jlNE>OJJ$D##3 z4ymi{XJiGac_7|6&CZvMY<(MFYDl{{J2Ei66KbrOVrApZmg~Xj$nCJ&WF0WT4mUW- zjwn3E7wD@{|IXfwZaGSjyg6^X@Td9D%(t8c#qrO@;wJJ5iw2QbA3USOY1;lVFy9_J z98HTplwUsn=NU4reeGi^Bf9f2{#*S$Dd-l)tM@7OvNCdkT;pu>1ku7Aydr&np7KM9 z9URQjjOYO?(S(WvUzM7f_aOar{pTJ%jBIZ9_Zd*tf<3(bb~ZE`U|~i2e9Lt2#xqa- zidi{+uQqbL8yNX+G~-Orc`Wx0L{WBkO|LdIh6o}O zGRq`I8u3QREy3nxxgSk=`SpR}I+2G#b%(=p5eJ@+c4yT>3Wh3;ccZ%Kq$G;;Ht$f9 zPw8uW=njSt+91!{=#7CZM8M!tCyqdHkzPMVjA*9kGsZ&J(=4oR!TmpGL1A#8-eC;) z=N<~zRPAgA7D8>Cd~3jScO4G9Z4-9C1T9Krl?aXsYEc&yFxh!=0QEjmV21~yq(SIs5-MLH3}T^HRRUva=WV<`y>W1` zyD2wIj-(3la4zK|X*3W+D1yKwlZg@U7g>(hmUQ0_{5j-za_SclnjV^!JD6VmMTx&! zL1p}inh9D&ytM;Y6!}(dV<4RHDbi`c410*2nAtqOSzc`^o>{o7HCLN<^vh{oCVJs- z^~Z^Ee)UavTFy__d;%J0x#x$;j;}^A z8MD!#q!Z{SuTK~aeh~RT11$jxgpnR-3@auUrv~^6?eD?{j@$j1YCRmhX1@>9?2Yv` zV@b>IN57WtL@w>~Z(kVMpHQ$G_W$PI@e??`&pjh`x_^{=XO{I~QTOo9deQ3dK9c79nV*k#oFMr0 zYXGAa9i>*LkfYqmnZms1AZ28?JP1(M_es8WpFNQ{{juVJq)@Mra#x}7)r9Dyhwh5a z%gukU4jg^>!&J(=687>vzL^UPmBwF#!z_p-+3R{316s}sYGQKM#;D2@g0H*D-y6Tg zpYXi0pj>GFsOgjaP-;PXXDVG0NIoIn zsy^I+@)2OJJSQU&pzLvtHr|IH4IG1z zE2Td;RT4K`7=Wd$G%;NdiJ)Euu(eE-a-8)d)RyXhf@Q3VJg(`-wsNy<$*IH-12+y% zD1mIv(?xeD`iX3)*<YBeJ#H(Wa_d01tpZ6r!_VsaxJV)I2PnW8Xs*2xJF& z-Miabz~EDIpzZXeoq_`q5?uURu?wxGugjm{Q<-RhAd)1t9${6HSvctEX|?qo);_4; z!K;3|7lF3GnAv2_N4P=pl3!E-eQW1q{yfO}NDZRZ?6(`B6dBbP^Iq8Kkt_*8y9e<; zN7!K=W+O4c2)8{kB-|bDo6b<cSGgQ&)?pgVW7w2LSIHo4#>iDE^%bCyd)jWG% zAgi5fQNamg6yP^1$*v^?cmpix0Hq!rtF4P#K)EO5Mc+M1YvFTNdg`;UT4IY$rePSr zTgI0C_qYSy4g9X()%8gpG`UUyS1yzf;e)GQtGf2s{NS)!s)lng(pcMHC@0?LZeWs5 zxMe08&KW%St`%x3wwS?~{e@jR{~M#G1v2^-V*h3gu;@KY6qR=~Al~~Aa8NH_{4y!s z#6w~w)%E}+g3(K6y~N28?N^DE4{+h)6|uh#y8z1q)Shw8-S0`ZT$V*15BfL&V=9Fs zI@ojFPVByT?o}+e(K6O1rE{zrie+#%%{X{1SuYuK-cc$pZ7b7@{^)&BqT~(A&W|32 z7vwLT-X$(1IzqQUU?4>~+epmBwE4N&fmToZ9IY-UmbYgU1!o|q+zhVk^i8kwz!GlA z@M$J^rL<*-IQoR6gE0nQ5kdI1?e3!SGXDg#nV3cX=fx~Pn5>NKD(7x1Yzj7bf8g{k zMsM)lqZuQmIUqFQDb80LUoVE$P@lMj-yQlC9;yF`DzD1)6`W-97w3wvGmA}%RISO)IopSw0wO$q1@;`$l72qcU)=V^ikvZ`bJWY_R zw7`N%3%PX}o>Z9tvtaK0%sWaw?@eDRV?@@lvtuJc2*0yiB);}sey}>+^2fnOnk?tj zLz5=WaL8B*lj*44DK@I^n4TV2dwNj8)a$0pA;tr-0ToyheQY3S1rQ0KL!Rkic$UO~ zMtKp1FIZe1LL}BJx`o3r!Sujh1e~s5CFUxOGJLZbEG+5;@LE0g=gWCffRMD_G~E*# z>vp%Ao{_w8IE4~sT5$DOi!Tx|7#Y?(|S`Jup0@#bs zk*@)A(A7V_1!*_NVM(11P1^m^E_VQ~Ll#|K2q$bN7F^qhp(}w!aPAVN=JeHbhzbfL zs1dP2GrFe*7{$|uf2T7hJxJjjy{W(LC+@i7hFB*P53L0Tvp*mIjF-8j>BH`vPb{df zxqv1hh)0TF9_m!U#t>sA{o^q0E}cp>s0k5A?zQaqiM+w5tzI1O*#^<=072C4Mn#az zw#22)gg}5+29^nGYtM7I$kZv7JIZJhClRl6>Ledf|e zoM@{H#F}^c)q{HM*=32Euol}ohPn=`Xn^vv;bneDq_>8v)0K>J_%T6MW(^3~Sb@JN z64r;!8({>pzlK>rDI^d#J%~z0)D8FlOoWiuRod=jU!Ea*pCFq|>x3+11D<#I%60gr zA%R9$MV`q2{e9|A)n-`a5prSS{m=F3f;R2d_k|;!OalYX+uK3-7p|&@uHZ_|?|@TR zGvQ0sV=sC@HpssDdw&4G_0jH8^xr#^IbT)|ACb4o|FmSxQ_&zu^Do+kqy7{HJ-qPY z=Un*hg{6|s@86pLJUo(1<-Gh>vJ1Z#D&|ovQf)wv7wa1M4k;+qy*}GlW2iPup~Vzr3wYeiP()du?;- z7vCeD?+m*aIvY z)s`RXX)o^1&mYYn?d%`#{~0?ztSxAYHqenMViv9$X1;W}MsILnO;Go5iLTCGrL|7; z=I+3OW)%od8ENPkjW!q$Ql7TsS7K74H8XmsreY#K0ZCH-=g(|2iy{7m*QWe zJM@|R=-4^-bGO9x_+agLH+p?RH}cv@QvH{!{|Xi8Pl&?S1$8bire~YCs3C(2&*Ni_ zUE=<@e)UW9pzn7sF+prA!64|M#kYi=&k0f`=15NB=DAYr(AiB)^1*^-NRnv04+En&obrhZnZgk2-g2IK}(pjD#85g{xY**78 z249f5D-5CP)k^C1GFX73B8mcv28UzOSUX^E!`a0AgVHn#ilE7qb3)A38m9LJYTmP? zu35TYQuzR%_{p`FCI77DUAFmeBg^E)Kl;C=HLzp^zp@{@gyHvo5fg(UlBr3$7K|-6G?2XY3#f&>JlXclyzVqhc z4R5o~PVC43OLcd-#tC8i(I$_0f+k8NrJwCAB5rPMp(igw2so27bsaP$kyw(w+J1u5h49_&^)ikLsCO+bwTvwS>+gvjmp;Qo|K}SkOS8 z1l0g;?pP#(_F1sW#I$jgSvk04KGtW^^0E&WipD?Hj||oL+a8u`5O#3_xm3B~;Ad-p zU$8J**9OHJ`SSgA+*ub*Rb?z&n@|>OYBKdkfVYunlP`Rm{t=F4lGk-P4qIwB2f;Tf4l%tPe~dYjQmy1C%XL@SbxA*hB(Z z)-CTR9rs2NSx*?q07=Lq?a1he_RfU)Lubg&v-b&BKH}CnCSvSnjD*{H@1OkhT@UV| zZ%N;_uMxWM(+=H=QWFpiVbS?J+Z#f+HuH^9*y388f^z~Op-92&Cg?_k0}loKR4+Sd zZ+Cg%Ud8Sq=uPfE9sRGEl#6Ci{!xU%(CzB34sc+NI2)QyD+Tf zkpi$PCT#q4LemD4;OYzwOAA$#)tYWi{rb{2VzH>IIsGXoM^LpH9sl>?8^BPu;T@z+ z0|vpsXp{@Rwy28ri}2Q8`_=GAd2fhFxeuPRx7$jEIg!;PW`yIU z>P9bc10o5aENV_(^rszi1sd@w>i#~J=@g0$!yGT(#FabSvN*IYjiy#SuWdPD^e}a?ZBob$Gabwh3 zgctHhr~oA_w0MvO9j4Xra$i+o=)Gcw5A zrQwPl$zqiYyh^k#Xjk~}`g}Lr5bG51lh{0fB%T@|@Wg(eI)*a<Q)E)cq$ zkN287tC(3RTeJXO$6|Ex?XD6t!=Z`t2d4djmj(ofR(>)O${$79b={A4xKuAcYg$TQ z$a3+{_Xk^Q)=H*NfS`F;SZ?Q`0B^gJD{uqZFuFH-HP!`#A?Q;~yjo1pexEc(?NUFC z#qjrQ|K9U=X0MMX{|S}@emoqV)u6(nqF)_|gXwcQl zqP@qZ|J)53&(v`VF#2rGj>XA?KQyps;q=5^;TqY(-US^qtLdi5Pm8(%N0Mj(vaU9q zo3qc8A;g_(OLimT0~*Ev|qWcz!}8qfQ=4RQta*o!=9tD&1p#uUZh`G$US*53xk=B(*Y@NxNd& zR%_&uOU=CNABzyvNR$Ms>7h5>e+7ax{ZM0mDeC&s(evjn@=DMCz33say*YbUev9dZ z^gP$Pz?)}2#0wvkNt`V=(Gl?0`MUB;v(%)7>~y&`#EWLL>-LO4W4#!PCOy9oE51f~ zxht6B9{I}is5>*3khY-MEwp|BSqjrRKKScvc-zSWiMXn_Lp(NqPTw)N` zKyr<;q{dWZ`PxTMn>L`Rz*Z3zYKkn9`k6~xHyfLBRfGUSB@7cO`Z!-eisr-{1cQTlJrU!c|2^aMRI#S>U;sPe-) z%#67Dm(yzJSDp!Km(GJGV7drCB!|KuJ;??r|X#b*gZ-8hCES=Z#<)7c(`1b<}= zSDQaXf7$yN#U$&6R1rYsOmZJ`9ccUB0Q%`wVh@Zl&y4Amn4V<_O*`2#6T}8bh#N}u zX!zye8&47oET`?3OJEpM?+6e@ShGzL8}j<;KU@*`qNo1MN51w zF)qSfxLJ?e;$4E8*98GbSAUM7gTPQFDWq)G zbmCnogl95;3p>#`ib|fzfX#Ms&siN^0 z7|uAMey1x|;QV0oGtslU&F||ziLk7G*}XFSra%5Jp7}ZJO@3O;C<{~i{((%uX*ndR z=?XntDYa$Fj*$rtc-uiEBT5-fE83MlAq`V%Ta*NHvwr^I6EaZLC*O;SER0uhkb)uM z05$qlRh83=+ID)7xc-Y5P>~(jY|`)i%+rwnYdH6RCfNUvUpttYjUgIClVgsX!yL;- zxAS4$B&TXa4xyG)I*&OOO663cNl8V`F?Yy}oEp}N2{n}{Q%Wc4@ZIN!?|*PT9@pXh zy58^S>yX$WQd#bPz#t>S%rsBa#I<8@CoPwA1OxPAZ$Z zKuzAs7fNjA_Y}$66dP7K#m7{tnvGR$K67H9AucX$BX~6(z2bKw;6&$$29;gaD)9Y6 zr6V*7uGI<-mL9eSU<>%%Pf4`i2^T<`J>EYRCv138mfk<~9-QEJp z9=V#*6xZ2L>0nxs@=+(9lbb%#I)F{n@MxY0@clUsW);7zvuu8b(&vZ1fvarwGm2*< zlB`7vZ?&NS!Bg%NDER4j8q9vaiiE+iSioXhw`;cR0}_nH+E##~2d^te?`q>`KL7^R zNL!nj@JRuD4RP{6*B$^z@K|umn9lwE3UC%F=e!&sgT3vEfQX`lev-60PzwTAx$hDC$b!obY?u^$JK&aVrR4pXmNzF*kq{FL9G1s zVKv&2Ou;|d9g+5WT6sPD82a^S~bXN#6%So*f*q( zE<=g7r_-A!3rKp+rKI$@p0N_#W9dZma#PRc__5)RH##p24+!VgR&k808Y8=uaU8T| z?ptl@hsT-@>3@E4|68=b+z(>S6GjrYYRW#9J)GTrXS0<_=kW8>*S2M5=(?HqDqj_L z=2ob@if*Kyj65(%bxd+ePY9ww*+tZN>2%;dMMx}ywE$$+K8dPemt8eA;3|YB?CCev zx+R^Ck17o8T6}lMHBscS%i&nRxAqBzLyB049Y{6k-}hb3M*dDjAIMVF*ehj1`4{@Q z4$7c$Sx&7}JzwlD2WF3Xv6qdVpL14m;4rPXK|Uu1JM7o)s+c($hxLm+Wo`Sw^ub2G zt@PUW3UhSs-2hd9P5JrW>jwW8b=g2>cRe6d&p`cj?JWzBtRUVmU#>Mby=J2~P9@^R5 zGjpOuW^p^Ml-Ew}iO*g9j4p~1CR~6(-|B-{rb!$af`eEeAS_@pWxq(GAPFal#oMHe ze-J^4b1uHReEY$4HG0}AqHovY&ivF*wYbg!gSI}m`Aa7Jw%#?#q*YSxHbF2N4(X4@ zjLobK!@4V<$|w1r9ii@J!slDmI2jhWAzU1IqyAM|CS|OV@?uEraEQ_z%2i?8&97Gl z@YpWF%SZ0}Z&1ffM2zyp0YtZ0^UXR~5(d}tEYk00cgTxN57J!vz)PJyS9jb|e;{P< zO=LfdICgY)al`A^&k&8|^mThHtuErD3`ot7(NKJfv(xSI^H9!a(^xQQ4$dr*UhC7m zwgRn`w0ozX-c`e949lEIy(YWavFG=h+eku^7ub(HTibcpS0q(_`rTCiB~L8*=0{CRkQ3Hnb)!cK64r8!%|(_x70V zW26Sw^#|fZUh?ro9ULJ2sJFo)jyth;?E8?m*WdG&A@x2af`6ieTPp06J3lHXbLqPP zOQ&~AjRjUvq72*S^A$QQrO4r0s$rVdM`HlxaUrlHCn+GbT1H~t*8S9noGbmUnT{+# zbyi&V(}bB{vO0CdCnzBS<+Vd^d@>I0w{G76r?83T*}@!GCA1N~!t6VVgO6CB2?tg< zVz}}Pbc2w;ue@f`hpdZjwtg&eAz0{{lyqi-SV#m~x!gjp;_wd+_DCGQKr$9RCY!u)X+ zGXnnuUAK91{Q0i=p2@_3OSTtlQ?Ysf%`}O(%UnZcB8xe??KD!d1^HH{KHE`cS1aZ{ z!1jJEp1t^JQS#&gvh%cLBS)r@_x>p{Q{7lT(+vK?I(@FHWCtdpt7^-1=S>uSkPiMe*@|2aS08j#VYN1@+wowy zaWxS@9ti3euX0lAByaAyZbzt_TI51-98GPGl-@NkjXi-l@`&BlnzO?%UaPgOqo?D} zY}xO-Y4<H{}_JKg%w z^=Wo2$2?iI`Yc~@?CVQPH`dQn;S8XN_VTZ0tz32ZWe=)8st%r_%Hm0H-7S5%uLL|& zEVNNH$Wi<@x6-g^#wBR|#Cx@@+<7TnyeE5Nn!>zI1}|Sigbf))E~F@_1%WpeA}IJ5 z|8-y2KJwAtuE+h+g@54X~|eHDVcW$xl@$UCdU(K_dFZxROegExO#WQ#lxlO|Oh7RC(k(z(9*6k5nB+fK{HV>KNeb9a3yMyPfxTe9Qb+ebK-_7Z8EA3>V(XIb_pKh zzfc@JpVfq0B=|Fo_5>>h#%5cojkPz0VV*$6{{eVUErb&i0kHTyso~F342K!!W$ZX> z22S;Wyy1)D12Yg^R*-D~l#;H6q;oV9*>fsS)}OP`WPm356hRF4&cMz zEG?V;=-5dWOp5_?0_6o?!yLI7mYB*+us@M?y$2e*mDNH3C?1VQ#YXNZkvY5V8enYz z5KA^tF~bFXD6oK^@I;ZFB8s5`OowO58jzymzjfxOOy zpwn2_o?~^*6}E@Dxh3w^Ul4=6SDmbJJvT=vyk6`-IyJYeF>T|f@IgPRh>GLvihsvg z5(U!y<{aABy}0p2Gwo62?<2k4XuudGfsH7hN&r&EAM|z9hm@10Pl_VN3$0ej>KR<8 zCIGHFK+_y{2f|{Ma=0BeSG1ZpASuUM)RxbP!^;juoeKqrA+ZoSmed5Av0l<=goy6l zZWj}sM%K?#6%?`?p4-pCFC9R|TODS}rZ0<0XAX7lgoSo6y_xPA5FQfcSW;_%H%dp= zKL}N$0c>cTn=tcH9f*bTUR6GeLSXznVLb3Oa}yQ^s>O9@3&p+-pa9Tc%Jk|p;t<~G z{F68&PyCL%54_=37^;+q+#|hvHBJ$V)5zs&R)h8)vSGutC^3YFpa%;OElrhadzy== zO>Cwt!8Y`I|LJSLkbk$g9)Bb0+CA#-JkfTUxuShKS#%&Yromiub1cN#!+2fFP08FN zS#1~fW#9qh+>vvD#eIYKPWL|AE2P4B7X>eb*&ia}pOjs;z5nN@(6EltdEuM+_>KGl znR-S))U9eQDQD#HEbQpf?(rk)K!N20mIYO&0#`tg+Ek5*J7jnl+86ke^D&DR#%-6c zwcRIf`2+2qc6R@L&SGNrzWFPzDVxSbqsT1mzX(0Cr)*{Ay#|(O_~k^pw_@7Gx0-6> zoxL8x_2;Vkgs$eR5e;x1g6nsJ&`bOJD<;Ene(4_%?I*K?qQB8LoZhn2+6*C{`tCRp1{q{m zJh|#%rY@tm0NXA4&;BS1&RdK4xV1XUJ_FML35a%w6wr zqQwDDBpa?+Dg8!hCDe~a zLOkNBMAV{DM;cdQ`uJ`^cH@`@9iA1uw}04$pLQ(wv%EZa_6b?^qFN?`_eE4q&qD$j6c$F!wxq_;hO{*R zI)N?~^gKTL1dWliWjg(r@#WkeR&@4zzQKAos(Oo>bMXhXVTIH@?>BROwLeLsA`g*Wejn#cb`Tia4`gq}&{ z8^B+nS(Lc!fz~DGKQqp&HJ4XZygj(YP}>4aP(K%p zaXsRi%{d={GcF22^>4%E`xb>XuOFgZtkVdrL7%JV!YP?btl1LCGla8|8?p?6CtyGU zb1c@y>K^OS@G9D08HUW7kMkeo4TC{x$(^&_qAE8Vf&g-`aknGK7Vb# z(;YH^-|}MCejCfxwl#JmhUFeD$3m=aDev!XVI#_9w4>TqYIiUF`qeYJffyn1TA;u1 z4tMg>nmGFmh5Ef%GvK_>*;lrhsFQo33}?yy|C1xrcU#>ESKp6yJ%-&t?8m)g<8F2+ zI2;ScOS{HCT&GX)RU$F+^8Bp^j49G(AMT|%2folN$^M<}p{Ht?=<|LjH)8Xric`(^ zyTmu#Qi)G_uQOd#AJyfpz6N?K!P!k#BtfCZxiNRc3kSbWV~%Yo~_#vxfVnkZ?41DpWKY1N^l335|B8eD~y zr5v#kr z2&K#N0L!5dcp^$n)e%&UoXi^#Y#5a|17U(UQ2={RLCOl?SgdsH$kr%GUL0<{Pn^E7 zH^h$ns3=X8fiV`+2SpqQG;dV<0Y9g1eA9KRt^T9Z)?m(SPU|C zdYh#6x*Cm+`a&*5*CJS$tcNz(aX_cjC$h$zK+!G}IXUneVyCfdDR$ze`sdmD&u=Q4 zs|HZa0&Y?EeD>4O%n1QrYV`^YQ}4xX2d^?n{c1V=ToD04Ia`<}7i=QGLBr??|3;q- zx#<8tYfG3P1_jGMR8K2vwhDq*YOLIN0@=R*d z?UaS+i^ttQxXaKfrk-}rJ&Ve{t&lsK%Va}Dga?o3zmyU{7FTPyYbAbY?@>U^;2a)E z`W6)C^wNxA4#ENq&s=T~h2St$!84HX%u&2<3lNX(Tah1&(JMu1SR~c6M}p8 zyn-w}OftxsH0VBe{0QAHvOUre4HOJ#U0yJzBm}X4E(ByI)0L$%kdCH1>%ZGW6gPrE zfy+7o#p((b_*!K35kM5cL{T0)%pQ9p*K-5B%C|D0-1T%qAwfO?0jyZN>fDpV6- zZh^csm-gQCtu%uINUTA|()PP~FXICZg{FcCI$V!D&utd2+#hf8qD6eT53;J zw;Ih8$pno)m=bHR@}&o&tXqofOCh=G$mS&7n+6$5m|+6=DRz|xfAzqnJ0i>(`VRB2?uqmS?_Zl6F_xRQTTd#|DbmR+J^gONrV84(1$deZVnl$l z7{b6zOx2`>b+m4zD2EKFDsGM#YyQx>uv>_&Vhq**Ne9SOxWf$seUMkJHo8g5f}0WB zR`zd3AEM{QDCi^;J!+nAE#>=3qhIP()TdxG40~}Zd|f}>GmI-OtWG$bk(5EGp*42J z@?w{Mfi$~+XFT&sPj!Mu<>t<@p1^9Iz+^9zSv*vBAexUYsSlc=CR|CTw5rL4HoDk`< zq|}CWc%#+`iVPd?jU@q2@AqTtf?}V3ESiV99ih~v;b6fMvISn06$@jSHJ8vY-LD<{ zlK6XYhwkmcv|XZGe$xHelavT(-!&S;vI4GztPZob?OaxO?YWZ#cnjM4@|GxAKPYxI z%?bIB&`J5SCGf{N9ynD=8_h%Y>x9bPy%*E|5|8xL5*rtL3wqg> zp|T3~Vx}x-h#szQG8Ys*t~Tl#o&T*xW%vwgmdA9@@$Gv?(*=WRw`}90J{neH&d9pH z(27Q5?j6h^Mu0a8(WMuLRHF;SZq#^^CEo~w;j#H09f`WhR%&6Ky=NM2US!4cBaRg4 z)R!MA2ZXK?C&$NN5XLh8;*4=U)EUyx>G&F;KuNd+_oMh1$N&ho+4X5w@9lFw9Pmq(9q zR1?U+((!uJW~lu746d)t;pQ-C2dt7pcU*%PXEUiMn(2oZe5%KimKocgGs-;q1_^r>lhS>)`(YqP(5T literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/teira_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/teira_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..e04235e20270a8880c41fa4918b2cb45d5c094b6 GIT binary patch literal 14862 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 + fun getUniqueUser(): Flow @Insert(onConflict = OnConflictStrategy.ABORT) fun setInitialUser(user: User) diff --git a/sdk/src/main/java/br/com/tick/sdk/domain/ExpenseRisk.kt b/sdk/src/main/java/br/com/tick/sdk/domain/ExpenseRisk.kt index 217f72f..17e06ea 100644 --- a/sdk/src/main/java/br/com/tick/sdk/domain/ExpenseRisk.kt +++ b/sdk/src/main/java/br/com/tick/sdk/domain/ExpenseRisk.kt @@ -1,8 +1,5 @@ package br.com.tick.sdk.domain -/** - * This class holds business logic to determine weather an expense value is categorized as certain threshold. - */ enum class ExpenseRisk(private val percentageThreshold: Double) { HIGHEST(20.0), HIGH(10.0), @@ -12,7 +9,7 @@ enum class ExpenseRisk(private val percentageThreshold: Double) { companion object { fun getRiskFromValue(monthlyIncome: Double, expenseValue: Double): ExpenseRisk { - require(expenseValue > 0) { "There's no way to calculate risks for a negative value. What is that anyway?" } + require(expenseValue >= 0) { "There's no way to calculate risks for a negative value. What is that anyway?" } return when ((expenseValue * 100) / monthlyIncome) { in 0.0..LOWEST.percentageThreshold -> LOWEST diff --git a/sdk/src/main/java/br/com/tick/sdk/repositories/user/UserRepositoryImpl.kt b/sdk/src/main/java/br/com/tick/sdk/repositories/user/UserRepositoryImpl.kt index 3393554..9b8110c 100644 --- a/sdk/src/main/java/br/com/tick/sdk/repositories/user/UserRepositoryImpl.kt +++ b/sdk/src/main/java/br/com/tick/sdk/repositories/user/UserRepositoryImpl.kt @@ -6,6 +6,8 @@ import br.com.tick.sdk.domain.AccountingDate import br.com.tick.sdk.domain.CurrencyFormat import br.com.tick.sdk.domain.NotificationPeriodicity import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import javax.inject.Inject class UserRepositoryImpl @Inject constructor( @@ -13,11 +15,14 @@ class UserRepositoryImpl @Inject constructor( ) : UserRepository { override fun getUser(): Flow { - return userDao.getUniqueUser() + return userDao.getUniqueUser().filterNotNull() } override suspend fun setInitialUser() { - userDao.setInitialUser(User.initial()) + val user = userDao.getUniqueUser().first() + if (user == null) { + userDao.setInitialUser(User.initial()) + } } override suspend fun setMonthlyIncome(newValue: Double) {