diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
index b268ef36..495ef5f9 100644
--- a/.idea/deploymentTargetSelector.xml
+++ b/.idea/deploymentTargetSelector.xml
@@ -4,6 +4,14 @@
+
+
+
+
+
+
+
+
diff --git a/app/build.gradle b/app/build.gradle
index 25f74eca..82726210 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,7 +17,7 @@ android {
minSdk 24
targetSdk 34
versionCode 350
- versionName "3.5.0"
+ versionName "3.6.0-dev"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
@@ -94,8 +94,6 @@ dependencies {
implementation "androidx.compose.animation:animation"
implementation "androidx.compose.runtime:runtime-livedata"
implementation "androidx.compose.material3:material3"
- // Accompanist compose.
- implementation "com.google.accompanist:accompanist-systemuicontroller:0.28.0"
// Material theme for main activity.
implementation 'com.google.android.material:material:1.11.0'
// Android 12+ splash API.
diff --git a/app/src/main/java/com/starry/greenstash/MainActivity.kt b/app/src/main/java/com/starry/greenstash/MainActivity.kt
index 39e8cab8..69887670 100644
--- a/app/src/main/java/com/starry/greenstash/MainActivity.kt
+++ b/app/src/main/java/com/starry/greenstash/MainActivity.kt
@@ -27,6 +27,7 @@ package com.starry.greenstash
import android.os.Bundle
import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
@@ -43,11 +44,10 @@ import androidx.core.content.ContextCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.compose.rememberNavController
-import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.starry.greenstash.ui.navigation.NavGraph
import com.starry.greenstash.ui.screens.other.AppLockedScreen
import com.starry.greenstash.ui.screens.settings.SettingsViewModel
-import com.starry.greenstash.ui.screens.settings.ThemeMode
+import com.starry.greenstash.ui.theme.AdjustEdgeToEdge
import com.starry.greenstash.ui.theme.GreenStashTheme
import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.toToast
@@ -75,6 +75,8 @@ class MainActivity : AppCompatActivity() {
mainViewModel.isLoading.value
}
+ enableEdgeToEdge() // enable edge to edge for the activity.
+
// refresh reminders
mainViewModel.refreshReminders()
@@ -137,15 +139,10 @@ class MainActivity : AppCompatActivity() {
private fun setAppContents(showAppContents: State) {
setContent {
GreenStashTheme(settingsViewModel = settingsViewModel) {
- val systemUiController = rememberSystemUiController()
- systemUiController.setNavigationBarColor(
- color = MaterialTheme.colorScheme.background,
- darkIcons = settingsViewModel.getCurrentTheme() == ThemeMode.Light
- )
-
- systemUiController.setStatusBarColor(
- color = MaterialTheme.colorScheme.surface,
- darkIcons = settingsViewModel.getCurrentTheme() == ThemeMode.Light
+ // fix status bar icon color in dark mode.
+ AdjustEdgeToEdge(
+ activity = this,
+ themeState = settingsViewModel.getCurrentTheme()
)
Surface(
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeAppBars.kt b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeAppBars.kt
index fcaabd68..b6d63abe 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeAppBars.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeAppBars.kt
@@ -27,9 +27,12 @@ package com.starry.greenstash.ui.screens.home.composables
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBars
+import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
@@ -42,9 +45,9 @@ 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.Surface
import androidx.compose.material3.Text
-import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -161,7 +164,10 @@ private fun SearchAppBar(
onSearchClicked: (String) -> Unit,
) {
Surface(
- modifier = Modifier.fillMaxWidth(), color = MaterialTheme.colorScheme.surface
+ modifier = Modifier
+ .fillMaxWidth()
+ .windowInsetsPadding(insets = WindowInsets.statusBars),
+ color = MaterialTheme.colorScheme.surface
) {
OutlinedTextField(
modifier = Modifier
@@ -182,7 +188,7 @@ private fun SearchAppBar(
Icon(
imageVector = Icons.Default.Search,
contentDescription = null,
- tint = MaterialTheme.colorScheme.onSurface
+ tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
)
}
},
@@ -197,7 +203,7 @@ private fun SearchAppBar(
Icon(
imageVector = Icons.Filled.Close,
contentDescription = null,
- tint = MaterialTheme.colorScheme.onSurface
+ tint = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
)
}
},
@@ -207,11 +213,13 @@ private fun SearchAppBar(
keyboardActions = KeyboardActions(onSearch = {
onSearchClicked(text)
}),
- colors = TextFieldDefaults.colors(
- focusedContainerColor = Color.Transparent,
- unfocusedContainerColor = MaterialTheme.colorScheme.primaryContainer.copy(0.3f),
- disabledContainerColor = Color.Transparent,
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = MaterialTheme.colorScheme.surfaceContainer,
+ unfocusedContainerColor = MaterialTheme.colorScheme.surfaceContainer,
cursorColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
+ focusedTextColor = MaterialTheme.colorScheme.onSurface,
+ focusedBorderColor = Color.Transparent,
+ unfocusedBorderColor = Color.Transparent
),
shape = RoundedCornerShape(24.dp)
)
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDrawer.kt b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDrawer.kt
index b75c5438..c39e77cb 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDrawer.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDrawer.kt
@@ -25,14 +25,24 @@
package com.starry.greenstash.ui.screens.home.composables
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
+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.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DrawerState
+import androidx.compose.material3.DrawerValue
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -40,6 +50,7 @@ import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.NavigationDrawerItem
import androidx.compose.material3.NavigationDrawerItemDefaults
import androidx.compose.material3.Text
+import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -47,49 +58,80 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
+import androidx.navigation.compose.rememberNavController
+import coil.compose.AsyncImage
import com.starry.greenstash.R
import com.starry.greenstash.ui.navigation.DrawerScreens
+import com.starry.greenstash.ui.screens.settings.ThemeMode
+import com.starry.greenstash.ui.screens.settings.composables.AboutLinks
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.launch
+
@Composable
-fun HomeDrawer(drawerState: DrawerState, navController: NavController) {
+fun HomeDrawer(drawerState: DrawerState, navController: NavController, themeMode: ThemeMode) {
val items = listOf(DrawerScreens.Home, DrawerScreens.Backups, DrawerScreens.Settings)
val selectedItem = remember { mutableStateOf(items[0]) }
val view = LocalView.current
+ val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
ModalDrawerSheet(
- modifier = Modifier.width(280.dp),
- drawerShape = RoundedCornerShape(4.dp)
+ modifier = Modifier.width(295.dp),
+ drawerShape = RoundedCornerShape(topEnd = 14.dp, bottomEnd = 14.dp),
+ drawerTonalElevation = 2.dp,
) {
- Spacer(Modifier.height(14.dp))
-
- Text(
- text = Utils.getGreeting(),
- modifier = Modifier.padding(start = 16.dp, top = 12.dp),
- fontSize = 24.sp,
- fontWeight = FontWeight.SemiBold,
- fontFamily = greenstashFont,
- color = MaterialTheme.colorScheme.onSurface
- )
+ Row(
+ modifier = Modifier
+ .height(140.dp)
+ .fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Spacer(modifier = Modifier.width(20.dp))
+ Box(
+ modifier = Modifier
+ .size(60.dp)
+ .background(
+ color = if (themeMode == ThemeMode.Light) MaterialTheme.colorScheme.onSurface
+ else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.15f),
+ shape = CircleShape
+ )
+ ) {
+ AsyncImage(
+ model = R.drawable.ic_launcher_foreground,
+ contentDescription = stringResource(id = R.string.app_name),
+ modifier = Modifier.fillMaxSize(),
+ )
+ }
+
+ Spacer(modifier = Modifier.width(18.dp))
+ Text(
+ text = stringResource(id = R.string.app_name),
+ fontFamily = greenstashFont,
+ fontSize = 22.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
- .padding(top = 16.dp, bottom = 16.dp),
+ .padding(bottom = 16.dp),
thickness = 0.5.dp,
- color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.2f)
+ color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f)
)
items.forEach { item ->
@@ -102,8 +144,7 @@ fun HomeDrawer(drawerState: DrawerState, navController: NavController) {
},
label = {
Text(
- text = stringResource(id = item.nameResId),
- fontFamily = greenstashFont
+ text = stringResource(id = item.nameResId), fontFamily = greenstashFont
)
},
selected = item == selectedItem.value,
@@ -122,17 +163,142 @@ fun HomeDrawer(drawerState: DrawerState, navController: NavController) {
Spacer(modifier = Modifier.height(4.dp))
}
+ HorizontalDivider(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 14.dp, bottom = 14.dp),
+ thickness = 0.5.dp,
+ color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.4f)
+ )
+
+ // Non-navigational items in the drawer ========================================
+
+ NavigationDrawerItem(
+ modifier = Modifier
+ .width(280.dp)
+ .padding(NavigationDrawerItemDefaults.ItemPadding),
+ selected = false,
+ onClick = {
+ view.weakHapticFeedback()
+ onRatingClick(context)
+ },
+ icon = {
+ Icon(
+ imageVector = ImageVector.vectorResource(id = R.drawable.ic_nav_rating),
+ contentDescription = null
+ )
+ },
+ label = {
+ Text(
+ text = stringResource(id = R.string.drawer_rating),
+ fontFamily = greenstashFont
+ )
+ },
+ )
+
+ NavigationDrawerItem(
+ modifier = Modifier
+ .width(280.dp)
+ .padding(NavigationDrawerItemDefaults.ItemPadding),
+ selected = false,
+ onClick = {
+ view.weakHapticFeedback()
+ onShareClick(context)
+ },
+ icon = {
+ Icon(
+ imageVector = ImageVector.vectorResource(id = R.drawable.ic_nav_share),
+ contentDescription = null
+ )
+ },
+ label = {
+ Text(
+ text = stringResource(id = R.string.drawer_share),
+ fontFamily = greenstashFont
+ )
+ },
+ )
+
+ NavigationDrawerItem(
+ modifier = Modifier
+ .width(280.dp)
+ .padding(NavigationDrawerItemDefaults.ItemPadding),
+ selected = false,
+ onClick = {
+ view.weakHapticFeedback()
+ onPrivacyClick(context)
+ },
+ icon = {
+ Icon(
+ imageVector = ImageVector.vectorResource(id = R.drawable.ic_nav_privacy),
+ contentDescription = null
+ )
+ },
+ label = {
+ Text(
+ text = stringResource(id = R.string.drawer_privacy),
+ fontFamily = greenstashFont
+ )
+ },
+ )
+
Spacer(Modifier.weight(1f))
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
text = stringResource(id = R.string.drawer_footer_text),
modifier = Modifier.padding(bottom = 18.dp),
- fontSize = 12.sp,
+ fontSize = 11.sp,
fontFamily = greenstashFont,
- color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f)
+ color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.69f)
)
}
}
+}
+
+private fun onRatingClick(context: Context) {
+ try {
+ context.startActivity(
+ Intent(
+ Intent.ACTION_VIEW,
+ Uri.parse("market://details?id=${context.packageName}")
+ )
+ )
+ } catch (e: ActivityNotFoundException) {
+ Utils.openWebLink(
+ context = context,
+ url = "https://play.google.com/store/apps/details?id=${context.packageName}"
+ )
+ }
+}
+
+private fun onShareClick(context: Context) {
+ val shareMessage =
+ context.getString(
+ R.string.drawer_share_message,
+ "https://play.google.com/store/apps/details?id=${context.packageName}"
+ ).trimIndent()
+ val shareIntent = Intent(Intent.ACTION_SEND).apply {
+ type = "text/plain"
+ putExtra(Intent.EXTRA_TEXT, shareMessage)
+ }
+ context.startActivity(Intent.createChooser(shareIntent, null))
+}
+
+private fun onPrivacyClick(context: Context) {
+ Utils.openWebLink(
+ context = context,
+ url = AboutLinks.PrivacyPolicy.url
+ )
+}
+
+@Preview
+@Composable
+private fun HomeDrawerPV() {
+ HomeDrawer(
+ drawerState = rememberDrawerState(initialValue = DrawerValue.Open),
+ navController = rememberNavController(),
+ themeMode = ThemeMode.Light
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeScreen.kt
index f67940fa..5825fe04 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeScreen.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeScreen.kt
@@ -77,10 +77,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.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
@@ -100,6 +98,7 @@ import com.airbnb.lottie.compose.rememberLottieComposition
import com.psoffritti.taptargetcompose.TapTargetCoordinator
import com.psoffritti.taptargetcompose.TapTargetStyle
import com.psoffritti.taptargetcompose.TextDefinition
+import com.starry.greenstash.MainActivity
import com.starry.greenstash.R
import com.starry.greenstash.database.core.GoalWithTransactions
import com.starry.greenstash.ui.navigation.Screens
@@ -108,6 +107,7 @@ import com.starry.greenstash.ui.screens.home.FilterSortType
import com.starry.greenstash.ui.screens.home.HomeViewModel
import com.starry.greenstash.ui.screens.home.SearchWidgetState
import com.starry.greenstash.ui.theme.greenstashFont
+import com.starry.greenstash.utils.getActivity
import com.starry.greenstash.utils.isScrollingUp
import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.delay
@@ -118,19 +118,19 @@ import java.util.Locale
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(navController: NavController) {
+ val context = LocalContext.current
val viewModel: HomeViewModel = hiltViewModel()
- val allGoalState = viewModel.goalsList.observeAsState(emptyList())
+ val settingsVM = (context.getActivity() as MainActivity).settingsViewModel
+ val allGoalState = viewModel.goalsList.observeAsState(emptyList())
val showFilterSheet = remember { mutableStateOf(false) }
val filterSheetState = rememberModalBottomSheetState()
-
val drawerState = rememberDrawerState(DrawerValue.Closed)
val searchWidgetState by viewModel.searchWidgetState
val searchTextState by viewModel.searchTextState
val lazyListState = rememberLazyListState()
-
val snackBarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
@@ -153,7 +153,11 @@ fun HomeScreen(navController: NavController) {
drawerState = drawerState,
gesturesEnabled = drawerState.isOpen,
drawerContent = {
- HomeDrawer(drawerState = drawerState, navController = navController)
+ HomeDrawer(
+ drawerState = drawerState,
+ navController = navController,
+ themeMode = settingsVM.getCurrentTheme()
+ )
},
) {
val showTapTargets = remember { mutableStateOf(false) }
@@ -469,7 +473,7 @@ private fun FilterButton(text: String, isSelected: Boolean, onClick: () -> Unit)
textColor = MaterialTheme.colorScheme.onSecondaryContainer
}
- val haptic = LocalHapticFeedback.current
+ val view = LocalView.current
Card(
modifier = Modifier
@@ -478,7 +482,7 @@ private fun FilterButton(text: String, isSelected: Boolean, onClick: () -> Unit)
colors = CardDefaults.cardColors(containerColor = buttonColor),
shape = RoundedCornerShape(14.dp),
onClick = {
- haptic.performHapticFeedback(HapticFeedbackType.LongPress)
+ view.weakHapticFeedback()
onClick()
}
) {
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/EditTransactionSheet.kt b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/EditTransactionSheet.kt
index ce94b4f6..1f0e0a67 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/EditTransactionSheet.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/EditTransactionSheet.kt
@@ -25,25 +25,26 @@
package com.starry.greenstash.ui.screens.info.composables
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.selection.selectableGroup
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
-import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
-import androidx.compose.material3.RadioButton
+import androidx.compose.material3.SegmentedButton
+import androidx.compose.material3.SegmentedButtonDefaults
+import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.material3.Text
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
@@ -52,7 +53,6 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
@@ -132,39 +132,59 @@ fun EditTransactionSheet(
.fillMaxWidth()
.padding(8.dp)
) {
- OutlinedCard(
+ SingleChoiceSegmentedButtonRow(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 18.dp, vertical = 6.dp)
) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .selectableGroup(),
- horizontalArrangement = Arrangement.SpaceEvenly,
- ) {
- Row(verticalAlignment = Alignment.CenterVertically) {
- RadioButton(
- selected = selectedTransactionType == TransactionType.Deposit.name,
- onClick = { onTransactionTypeSelected(TransactionType.Deposit.name) },
- )
+ SegmentedButton(
+ selected = selectedTransactionType == TransactionType.Deposit.name,
+ onClick = { onTransactionTypeSelected(TransactionType.Deposit.name) },
+ shape = RoundedCornerShape(topStart = 14.dp, bottomStart = 14.dp),
+ label = {
Text(
- text = TransactionType.Deposit.name,
- fontFamily = greenstashFont
+ text = TransactionType.Deposit.name, fontFamily = greenstashFont
)
- }
+ },
+ icon = {
+ if (selectedTransactionType == TransactionType.Deposit.name) {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(16.dp)
+ )
+ }
+ },
+ colors = SegmentedButtonDefaults.colors(
+ activeContentColor = MaterialTheme.colorScheme.onPrimary,
+ activeContainerColor = MaterialTheme.colorScheme.primary,
+ )
+ )
- Row(verticalAlignment = Alignment.CenterVertically) {
- RadioButton(
- selected = selectedTransactionType == TransactionType.Withdraw.name,
- onClick = { onTransactionTypeSelected(TransactionType.Withdraw.name) },
- )
+ SegmentedButton(
+ selected = selectedTransactionType == TransactionType.Withdraw.name,
+ onClick = { onTransactionTypeSelected(TransactionType.Withdraw.name) },
+ shape = RoundedCornerShape(topEnd = 14.dp, bottomEnd = 14.dp),
+ label = {
Text(
text = TransactionType.Withdraw.name,
fontFamily = greenstashFont
)
- }
- }
+ },
+ icon = {
+ if (selectedTransactionType == TransactionType.Withdraw.name) {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(16.dp)
+ )
+ }
+ },
+ colors = SegmentedButtonDefaults.colors(
+ activeContentColor = MaterialTheme.colorScheme.onPrimary,
+ activeContainerColor = MaterialTheme.colorScheme.primary,
+ )
+ )
}
DateTimeCard(
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/input/composables/InputScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/input/composables/InputScreen.kt
index 1594b255..5615665b 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/input/composables/InputScreen.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/input/composables/InputScreen.kt
@@ -375,7 +375,7 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
GoalPriorityMenu(
selectedPriority = viewModel.state.priority,
- obPriorityChanged = { newValue ->
+ onPriorityChanged = { newValue ->
viewModel.updatePriority(newValue)
}
)
@@ -630,7 +630,7 @@ private fun IconPickerCard(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-private fun GoalPriorityMenu(selectedPriority: String, obPriorityChanged: (String) -> Unit) {
+private fun GoalPriorityMenu(selectedPriority: String, onPriorityChanged: (String) -> Unit) {
Card(
modifier = Modifier.fillMaxWidth(0.86f), colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
@@ -658,7 +658,7 @@ private fun GoalPriorityMenu(selectedPriority: String, obPriorityChanged: (Strin
) {
SegmentedButton(
selected = selectedPriority == GoalPriority.High.name,
- onClick = { obPriorityChanged(GoalPriority.High.name) },
+ onClick = { onPriorityChanged(GoalPriority.High.name) },
shape = RoundedCornerShape(topStart = 14.dp, bottomStart = 14.dp),
label = {
Text(
@@ -677,12 +677,14 @@ private fun GoalPriorityMenu(selectedPriority: String, obPriorityChanged: (Strin
colors = SegmentedButtonDefaults.colors(
activeContentColor = MaterialTheme.colorScheme.onPrimary,
activeContainerColor = MaterialTheme.colorScheme.primary,
+ inactiveContainerColor = MaterialTheme.colorScheme.primaryContainer,
+ inactiveContentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
)
SegmentedButton(
selected = selectedPriority == GoalPriority.Normal.name,
- onClick = { obPriorityChanged(GoalPriority.Normal.name) },
+ onClick = { onPriorityChanged(GoalPriority.Normal.name) },
shape = RectangleShape,
label = {
Text(
@@ -701,12 +703,14 @@ private fun GoalPriorityMenu(selectedPriority: String, obPriorityChanged: (Strin
colors = SegmentedButtonDefaults.colors(
activeContentColor = MaterialTheme.colorScheme.onPrimary,
activeContainerColor = MaterialTheme.colorScheme.primary,
+ inactiveContainerColor = MaterialTheme.colorScheme.primaryContainer,
+ inactiveContentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
)
SegmentedButton(
selected = selectedPriority == GoalPriority.Low.name,
- onClick = { obPriorityChanged(GoalPriority.Low.name) },
+ onClick = { onPriorityChanged(GoalPriority.Low.name) },
shape = RoundedCornerShape(topEnd = 14.dp, bottomEnd = 14.dp),
label = {
Text(
@@ -725,6 +729,8 @@ private fun GoalPriorityMenu(selectedPriority: String, obPriorityChanged: (Strin
colors = SegmentedButtonDefaults.colors(
activeContentColor = MaterialTheme.colorScheme.onPrimary,
activeContainerColor = MaterialTheme.colorScheme.primary,
+ inactiveContainerColor = MaterialTheme.colorScheme.primaryContainer,
+ inactiveContentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
)
}
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/AboutScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/AboutScreen.kt
index 921a73f8..956833af 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/AboutScreen.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/AboutScreen.kt
@@ -25,10 +25,6 @@
package com.starry.greenstash.ui.screens.settings.composables
-import android.content.ActivityNotFoundException
-import android.content.Context
-import android.content.Intent
-import android.net.Uri
import android.os.Build
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
@@ -60,6 +56,7 @@ import androidx.navigation.NavController
import com.starry.greenstash.BuildConfig
import com.starry.greenstash.R
import com.starry.greenstash.ui.theme.greenstashFont
+import com.starry.greenstash.utils.Utils
sealed class AboutLinks(val url: String) {
data object ReadMe : AboutLinks("https://github.com/Pool-Of-Tears/GreenStash")
@@ -109,35 +106,35 @@ fun AboutScreen(navController: NavController) {
SettingsItem(title = stringResource(id = R.string.about_readme_title),
description = stringResource(id = R.string.about_readme_desc),
icon = Icons.AutoMirrored.Filled.Notes,
- onClick = { openWebLink(context, AboutLinks.ReadMe.url) }
+ onClick = { Utils.openWebLink(context, AboutLinks.ReadMe.url) }
)
}
item {
SettingsItem(title = stringResource(id = R.string.about_privacy_title),
description = stringResource(id = R.string.about_privacy_desc),
icon = Icons.Filled.PrivacyTip,
- onClick = { openWebLink(context, AboutLinks.PrivacyPolicy.url) }
+ onClick = { Utils.openWebLink(context, AboutLinks.PrivacyPolicy.url) }
)
}
item {
SettingsItem(title = stringResource(id = R.string.about_gh_issue_title),
description = stringResource(id = R.string.about_gh_issue_desc),
icon = ImageVector.vectorResource(id = R.drawable.ic_about_gh_issue),
- onClick = { openWebLink(context, AboutLinks.GithubIssues.url) }
+ onClick = { Utils.openWebLink(context, AboutLinks.GithubIssues.url) }
)
}
item {
SettingsItem(title = stringResource(id = R.string.about_telegram_title),
description = stringResource(id = R.string.about_telegram_desc),
icon = ImageVector.vectorResource(id = R.drawable.ic_about_telegram),
- onClick = { openWebLink(context, AboutLinks.Telegram.url) }
+ onClick = { Utils.openWebLink(context, AboutLinks.Telegram.url) }
)
}
item {
SettingsItem(title = stringResource(id = R.string.about_support_title),
description = stringResource(id = R.string.about_support_desc),
icon = Icons.Filled.Favorite,
- onClick = { openWebLink(context, AboutLinks.Sponsor.url) }
+ onClick = { Utils.openWebLink(context, AboutLinks.Sponsor.url) }
)
}
item {
@@ -153,15 +150,6 @@ fun AboutScreen(navController: NavController) {
}
}
-fun openWebLink(context: Context, url: String) {
- val uri: Uri = Uri.parse(url)
- val intent = Intent(Intent.ACTION_VIEW, uri)
- try {
- context.startActivity(intent)
- } catch (exc: ActivityNotFoundException) {
- exc.printStackTrace()
- }
-}
fun getVersionReport(): String {
val versionName = BuildConfig.VERSION_NAME
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/GoalCardStyle.kt b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/GoalCardStyle.kt
index 26cfd5b6..77e173f5 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/GoalCardStyle.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/GoalCardStyle.kt
@@ -174,7 +174,7 @@ fun GoalCardStyle(navController: NavController) {
savedAmount = "$1,000.00",
daysLeftText = "12 days left",
goalProgress = 0.8f,
- goalIcon = ImageVector.vectorResource(id = R.drawable.ic_nav_backups),
+ goalIcon = ImageVector.vectorResource(id = R.drawable.ic_nav_rating),
onDepositClicked = {},
onWithdrawClicked = {},
onInfoClicked = {},
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsScreen.kt
index 385bf297..4ccbf1b7 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsScreen.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsScreen.kt
@@ -34,14 +34,17 @@ import androidx.compose.foundation.layout.Spacer
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.foundation.lazy.LazyColumn
import androidx.compose.foundation.selection.selectable
import androidx.compose.foundation.selection.selectableGroup
-import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
-import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.BrightnessMedium
+import androidx.compose.material.icons.filled.Contrast
+import androidx.compose.material.icons.filled.Info
+import androidx.compose.material.icons.filled.LocalPolice
+import androidx.compose.material.icons.filled.Lock
+import androidx.compose.material.icons.filled.Palette
import androidx.compose.material.icons.filled.Style
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
@@ -53,23 +56,17 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.RadioButton
import androidx.compose.material3.RadioButtonDefaults
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SheetState
-import androidx.compose.material3.Switch
-import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
-import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -100,10 +97,6 @@ import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.getActivity
import com.starry.greenstash.utils.toToast
import com.starry.greenstash.utils.weakHapticFeedback
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
import java.util.concurrent.Executor
@@ -158,11 +151,9 @@ fun SettingsScreen(navController: NavController) {
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DisplaySettings(viewModel: SettingsViewModel, navController: NavController) {
val context = LocalContext.current
- val sheetState = rememberModalBottomSheetState()
val showThemeSheet = remember { mutableStateOf(false) }
// Theme related values.
@@ -194,14 +185,25 @@ private fun DisplaySettings(viewModel: SettingsViewModel, navController: NavCont
SettingsCategory(title = stringResource(id = R.string.display_settings_title))
SettingsItem(title = stringResource(id = R.string.theme_setting),
description = themeValue,
- icon = ImageVector.vectorResource(id = R.drawable.ic_settings_theme),
+ icon = Icons.Filled.BrightnessMedium,
onClick = { showThemeSheet.value = true })
+ SettingsItem(
+ title = stringResource(id = R.string.amoled_theme_setting),
+ description = stringResource(id = R.string.amoled_theme_desc),
+ icon = Icons.Filled.Contrast,
+ switchState = amoledThemeValue,
+ onCheckChange = { newValue ->
+ amoledThemeValue.value = newValue
+ viewModel.setAmoledTheme(newValue)
+ }
+ )
+
SettingsItem(title = stringResource(id = R.string.material_you_setting),
description = stringResource(
id = R.string.material_you_setting_desc
),
- icon = ImageVector.vectorResource(id = R.drawable.ic_settings_material_you),
+ icon = Icons.Filled.Palette,
switchState = materialYouValue,
onCheckChange = { newValue ->
materialYouValue.value = newValue
@@ -224,36 +226,24 @@ private fun DisplaySettings(viewModel: SettingsViewModel, navController: NavCont
onClick = { navController.navigate(Screens.GoalCardStyle.route) })
if (showThemeSheet.value) {
- ThemeBottomSheet(
+ ThemePickerDialog(
themeValue = themeValue,
- amoledThemeValue = amoledThemeValue.value,
- sheetState = sheetState,
- showThemeSheet = showThemeSheet,
+ showThemeDialog = showThemeSheet,
onThemeChange = { newTheme ->
viewModel.setTheme(newTheme)
- },
- onAmoledThemeChange = { newValue ->
- amoledThemeValue.value = newValue
- viewModel.setAmoledTheme(newValue)
}
)
}
}
}
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
-private fun ThemeBottomSheet(
+private fun ThemePickerDialog(
themeValue: String,
- amoledThemeValue: Boolean,
- sheetState: SheetState,
- showThemeSheet: MutableState,
+ showThemeDialog: MutableState,
onThemeChange: (ThemeMode) -> Unit,
- onAmoledThemeChange: (Boolean) -> Unit,
) {
- val view = LocalView.current
val context = LocalContext.current
- val coroutineScope = rememberCoroutineScope()
val themeRadioOptions = listOf(
stringResource(id = R.string.theme_dialog_option1),
stringResource(id = R.string.theme_dialog_option2),
@@ -263,161 +253,75 @@ private fun ThemeBottomSheet(
mutableStateOf(themeValue)
}
- ModalBottomSheet(sheetState = sheetState, onDismissRequest = {
- coroutineScope.launch {
- sheetState.hide()
- delay(300)
- showThemeSheet.value = false
- }
- }) {
- Column(
- modifier = Modifier.fillMaxWidth()
-
- ) {
- Card(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 14.dp),
- colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp)
- ),
- shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
+ if (showThemeDialog.value) {
+ AlertDialog(onDismissRequest = {
+ showThemeDialog.value = false
+ }, title = {
+ Text(
+ text = stringResource(id = R.string.theme_dialog_title),
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }, text = {
+ Column(
+ modifier = Modifier.selectableGroup(),
+ verticalArrangement = Arrangement.Center,
) {
- Column(
- modifier = Modifier
- .selectableGroup()
- .padding(top = 6.dp),
- verticalArrangement = Arrangement.Center,
- ) {
- themeRadioOptions.forEach { text ->
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .height(46.dp)
- .selectable(
- selected = (text == selectedThemeOption),
- onClick = { onThemeOptionSelected(text) },
- role = Role.RadioButton,
- )
- .padding(start = 14.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- RadioButton(
+ themeRadioOptions.forEach { text ->
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(46.dp)
+ .selectable(
selected = (text == selectedThemeOption),
- onClick = null,
- colors = RadioButtonDefaults.colors(
- selectedColor = MaterialTheme.colorScheme.primary,
- unselectedColor = MaterialTheme.colorScheme.inversePrimary,
- disabledSelectedColor = Color.Black,
- disabledUnselectedColor = Color.Black
- ),
- )
- Text(
- text = text,
- modifier = Modifier.padding(start = 16.dp),
- color = MaterialTheme.colorScheme.onSurface,
- fontFamily = greenstashFont,
- )
- }
+ onClick = { onThemeOptionSelected(text) },
+ role = Role.RadioButton,
+ ),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ RadioButton(
+ selected = (text == selectedThemeOption),
+ onClick = null,
+ colors = RadioButtonDefaults.colors(
+ selectedColor = MaterialTheme.colorScheme.primary,
+ unselectedColor = MaterialTheme.colorScheme.inversePrimary,
+ disabledSelectedColor = Color.Black,
+ disabledUnselectedColor = Color.Black
+ ),
+ )
+ Text(
+ text = text,
+ modifier = Modifier.padding(start = 16.dp),
+ color = MaterialTheme.colorScheme.onSurface,
+ fontFamily = greenstashFont
+ )
}
}
}
-
- Card(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 14.dp, vertical = 4.dp),
- colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp)
- ),
- shape = RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp)
- ) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(4.dp)
- ) {
- Text(
- text = stringResource(id = R.string.amoled_theme_setting),
- fontFamily = greenstashFont,
- modifier = Modifier.padding(start = 14.dp, top = 10.dp)
- )
- Spacer(modifier = Modifier.weight(1f))
- Switch(
- checked = amoledThemeValue,
- onCheckedChange = {
- view.weakHapticFeedback()
- onAmoledThemeChange(it)
- },
- thumbContent = if (amoledThemeValue) {
- {
- Icon(
- imageVector = Icons.Filled.Check,
- contentDescription = null,
- modifier = Modifier.size(SwitchDefaults.IconSize),
- )
- }
- } else {
- null
- },
- modifier = Modifier.padding(end = 14.dp)
- )
- }
- }
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = 8.dp, bottom = 20.dp, end = 14.dp)
- ) {
- Spacer(modifier = Modifier.weight(1f))
- TextButton(onClick = {
- coroutineScope.launch {
- sheetState.hide()
- delay(300)
- showThemeSheet.value = false
- }
- }) {
- Text(
- stringResource(id = R.string.cancel), fontFamily = greenstashFont
- )
- }
-
- FilledTonalButton(
- onClick = {
- coroutineScope.launch {
- sheetState.hide()
- delay(100)
- showThemeSheet.value = false
-
- withContext(Dispatchers.Main) {
- when (selectedThemeOption) {
- context.getString(R.string.theme_dialog_option1) -> {
- onThemeChange(ThemeMode.Light)
- }
-
- context.getString(R.string.theme_dialog_option2) -> {
- onThemeChange(ThemeMode.Dark)
- }
-
- context.getString(R.string.theme_dialog_option3) -> {
- onThemeChange(ThemeMode.Auto)
- }
- }
- }
+ }, confirmButton = {
+ FilledTonalButton(
+ onClick = {
+ showThemeDialog.value = false
+ onThemeChange(
+ when (selectedThemeOption) {
+ context.getString(R.string.theme_dialog_option1) -> ThemeMode.Light
+ context.getString(R.string.theme_dialog_option2) -> ThemeMode.Dark
+ else -> ThemeMode.Auto
}
- }, colors = ButtonDefaults.filledTonalButtonColors(
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer
- ), shape = RoundedCornerShape(16.dp)
- ) {
- Text(
- stringResource(id = R.string.theme_dialog_apply_button),
- fontFamily = greenstashFont
)
- }
+ }, colors = ButtonDefaults.filledTonalButtonColors(
+ containerColor = MaterialTheme.colorScheme.primary,
+ contentColor = MaterialTheme.colorScheme.onPrimary
+ )
+ ) {
+ Text(stringResource(id = R.string.theme_dialog_apply_button))
}
- }
+ }, dismissButton = {
+ TextButton(onClick = {
+ showThemeDialog.value = false
+ }) {
+ Text(stringResource(id = R.string.cancel))
+ }
+ })
}
}
@@ -564,7 +468,7 @@ private fun SecuritySettings(viewModel: SettingsViewModel) {
SettingsCategory(title = stringResource(id = R.string.security_settings_title))
SettingsItem(title = stringResource(id = R.string.app_lock_setting),
description = stringResource(id = R.string.app_lock_setting_desc),
- icon = ImageVector.vectorResource(id = R.drawable.ic_settings_app_lock),
+ icon = Icons.Filled.Lock,
switchState = appLockSwitch,
onCheckChange = { newValue ->
appLockSwitch.value = newValue
@@ -620,11 +524,11 @@ private fun MiscSettings(navController: NavController) {
SettingsCategory(title = stringResource(id = R.string.misc_setting_title))
SettingsItem(title = stringResource(id = R.string.license_setting),
description = stringResource(id = R.string.license_setting_desc),
- icon = ImageVector.vectorResource(id = R.drawable.ic_settings_osl),
+ icon = Icons.Filled.LocalPolice,
onClick = { navController.navigate(Screens.OSLScreen.route) })
SettingsItem(title = stringResource(id = R.string.app_info_setting),
description = stringResource(id = R.string.app_info_setting_desc),
- icon = ImageVector.vectorResource(id = R.drawable.ic_settings_about),
+ icon = Icons.Filled.Info,
onClick = { navController.navigate(Screens.AboutScreen.route) })
}
Spacer(modifier = Modifier.height(2.dp)) // Last item padding.
diff --git a/app/src/main/java/com/starry/greenstash/ui/theme/Theme.kt b/app/src/main/java/com/starry/greenstash/ui/theme/Theme.kt
index 360ac07c..a3f0ab41 100644
--- a/app/src/main/java/com/starry/greenstash/ui/theme/Theme.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/theme/Theme.kt
@@ -27,6 +27,9 @@ package com.starry.greenstash.ui.theme
import android.content.Context
import android.os.Build
+import androidx.activity.SystemBarStyle
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
@@ -35,6 +38,7 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
@@ -155,6 +159,32 @@ private fun getColorScheme(
}
}
+/**
+ * Helper composable function to fix the status bar icons on dark theme
+ * when using edge-to-edge mode.
+ * @param activity: MainActivity to enable edge-to-edge status bar.
+ * @param themeState: ThemeMode to check the current theme.
+ */
+@Composable
+fun AdjustEdgeToEdge(activity: AppCompatActivity, themeState: ThemeMode) {
+ LaunchedEffect(themeState) {
+ if (themeState == ThemeMode.Dark) {
+ activity.enableEdgeToEdge(
+ statusBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT),
+ navigationBarStyle = SystemBarStyle.dark(android.graphics.Color.TRANSPARENT)
+ )
+ } else {
+ activity.enableEdgeToEdge()
+ }
+ }
+}
+
+/**
+ * GreenStashTheme composable function to apply the theme to the app.
+ * @param darkTheme: Boolean to check if the theme is dark.
+ * @param settingsViewModel: SettingsViewModel to observe the theme settings.
+ * @param content: @Composable function to apply the theme to the content.
+ */
@Composable
fun GreenStashTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
diff --git a/app/src/main/java/com/starry/greenstash/utils/Utils.kt b/app/src/main/java/com/starry/greenstash/utils/Utils.kt
index 175194d2..82b2e789 100644
--- a/app/src/main/java/com/starry/greenstash/utils/Utils.kt
+++ b/app/src/main/java/com/starry/greenstash/utils/Utils.kt
@@ -25,6 +25,10 @@
package com.starry.greenstash.utils
+import android.content.ActivityNotFoundException
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
import android.os.Build
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
@@ -33,17 +37,22 @@ import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.text.NumberFormat
-import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.Currency
-import java.util.Date
import java.util.Locale
import java.util.TimeZone
+/**
+ * A collection of utility functions.
+ */
object Utils {
- /** Validate number from text field. */
+ /** Get validated number from the text.
+ *
+ * @param text The text to validate
+ * @return The validated number
+ */
fun getValidatedNumber(text: String): String {
val filteredChars = text.filterIndexed { index, c ->
c.isDigit() || (c == '.' && index != 0
@@ -60,7 +69,11 @@ object Utils {
}
}
- /** Round decimal (double) to 2 digits */
+ /** Round the decimal number to two decimal places.
+ *
+ * @param number The number to round
+ * @return The rounded number
+ */
fun roundDecimal(number: Double): Double {
val locale = DecimalFormatSymbols(Locale.US)
val df = DecimalFormat("#.##", locale)
@@ -68,7 +81,12 @@ object Utils {
return df.format(number).toDouble()
}
- /** Convert double into currency format */
+ /** Format currency based on the currency code.
+ *
+ * @param amount The amount to format
+ * @param currencyCode The currency code
+ * @return The formatted currency
+ */
fun formatCurrency(amount: Double, currencyCode: String): String {
val nf = NumberFormat.getCurrencyInstance().apply {
currency = Currency.getInstance(currencyCode)
@@ -82,7 +100,19 @@ object Utils {
}
/**
- * https://developer.android.com/reference/androidx/biometric/BiometricPrompt.PromptInfo.Builder#setAllowedAuthenticators(int)
+ * Get the authenticators based on the Android version.
+ *
+ * For Android 9 and 10, the authenticators are BIOMETRIC_WEAK and DEVICE_CREDENTIAL.
+ *
+ * For Android 11 and above, the authenticators are BIOMETRIC_STRONG and DEVICE_CREDENTIAL.
+ *
+ * For Android versions below 9, the authenticators are BIOMETRIC_STRONG and DEVICE_CREDENTIAL although they are not supported,
+ * they don't result in any error unlike in Android 9 and 10.
+ *
+ * See https://developer.android.com/reference/androidx/biometric/BiometricPrompt.PromptInfo.Builder#setAllowedAuthenticators(int)
+ * for more information.
+ *
+ * @return The authenticators based on the Android version.
*/
fun getAuthenticators() = if (Build.VERSION.SDK_INT == 28 || Build.VERSION.SDK_INT == 29) {
BIOMETRIC_WEAK or DEVICE_CREDENTIAL
@@ -90,18 +120,12 @@ object Utils {
BIOMETRIC_STRONG or DEVICE_CREDENTIAL
}
- fun getGreeting(): String {
- val currentTime = System.currentTimeMillis()
- val simpleDateFormat = SimpleDateFormat("HH", Locale.US)
-
- return when (simpleDateFormat.format(Date(currentTime)).toInt()) {
- in 0..11 -> "Good Morning!"
- in 12..16 -> "Good Afternoon!"
- in 17..20 -> "Good Evening!"
- else -> "Good Night!"
- }
- }
+ /** Get the epoch time from the LocalDateTime.
+ *
+ * @param dateTime The LocalDateTime object
+ * @return The epoch time
+ */
fun getEpochTime(dateTime: LocalDateTime): Long {
val timeZone = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ZoneId.systemDefault()
@@ -111,4 +135,19 @@ object Utils {
return dateTime.atZone(timeZone).toInstant().toEpochMilli()
}
+ /** Open the web link in the browser.
+ *
+ * @param context The context
+ * @param url The URL to open
+ */
+ fun openWebLink(context: Context, url: String) {
+ val uri: Uri = Uri.parse(url)
+ val intent = Intent(Intent.ACTION_VIEW, uri)
+ try {
+ context.startActivity(intent)
+ } catch (exc: ActivityNotFoundException) {
+ exc.printStackTrace()
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/starry/greenstash/widget/configuration/WidgetConfigActivity.kt b/app/src/main/java/com/starry/greenstash/widget/configuration/WidgetConfigActivity.kt
index 533c0d01..bbddd703 100644
--- a/app/src/main/java/com/starry/greenstash/widget/configuration/WidgetConfigActivity.kt
+++ b/app/src/main/java/com/starry/greenstash/widget/configuration/WidgetConfigActivity.kt
@@ -29,6 +29,7 @@ import android.appwidget.AppWidgetManager
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
@@ -83,11 +84,10 @@ import com.airbnb.lottie.compose.LottieCompositionResult
import com.airbnb.lottie.compose.LottieCompositionSpec
import com.airbnb.lottie.compose.animateLottieCompositionAsState
import com.airbnb.lottie.compose.rememberLottieComposition
-import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.starry.greenstash.MainActivity
import com.starry.greenstash.R
import com.starry.greenstash.ui.screens.settings.SettingsViewModel
-import com.starry.greenstash.ui.screens.settings.ThemeMode
+import com.starry.greenstash.ui.theme.AdjustEdgeToEdge
import com.starry.greenstash.ui.theme.GreenStashTheme
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.widget.GoalWidget
@@ -103,21 +103,16 @@ class WidgetConfigActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ enableEdgeToEdge() // Enable edge-to-edge mode for the activity.
settingsViewModel = ViewModelProvider(this)[SettingsViewModel::class.java]
setContent {
GreenStashTheme(settingsViewModel = settingsViewModel) {
- val systemUiController = rememberSystemUiController()
- systemUiController.setNavigationBarColor(
- color = MaterialTheme.colorScheme.background,
- darkIcons = settingsViewModel.getCurrentTheme() == ThemeMode.Light
+ // fix status bar icon color in dark mode.
+ AdjustEdgeToEdge(
+ activity = this,
+ themeState = settingsViewModel.getCurrentTheme()
)
-
- systemUiController.setStatusBarColor(
- color = MaterialTheme.colorScheme.surfaceColorAtElevation(4.dp),
- darkIcons = settingsViewModel.getCurrentTheme() == ThemeMode.Light
- )
-
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
diff --git a/app/src/main/res/drawable/ic_nav_backups.xml b/app/src/main/res/drawable/ic_nav_backups.xml
index 7c945841..65bc34fd 100644
--- a/app/src/main/res/drawable/ic_nav_backups.xml
+++ b/app/src/main/res/drawable/ic_nav_backups.xml
@@ -7,8 +7,9 @@
+ android:pathData="M20.5348 3.46447C19.0704 2 16.7133 2 11.9993 2C7.28525 2 4.92823 2 3.46377 3.46447C2.70628 4.22195 2.3406 5.21824 2.16406 6.65598C2.69473 6.06532 3.33236 5.57328 4.04836 5.20846C4.82984 4.81027 5.66664 4.6488 6.59316 4.5731C7.48829 4.49997 8.58971 4.49998 9.93646 4.5H14.0621C15.4089 4.49998 16.5103 4.49997 17.4054 4.5731C18.332 4.6488 19.1688 4.81027 19.9502 5.20846C20.6662 5.57328 21.3039 6.06532 21.8345 6.65598C21.658 5.21824 21.2923 4.22195 20.5348 3.46447Z" />
+ android:fillType="evenOdd"
+ android:pathData="M2 14C2 11.1997 2 9.79961 2.54497 8.73005C3.02433 7.78924 3.78924 7.02433 4.73005 6.54497C5.79961 6 7.19974 6 10 6H14C16.8003 6 18.2004 6 19.27 6.54497C20.2108 7.02433 20.9757 7.78924 21.455 8.73005C22 9.79961 22 11.1997 22 14C22 16.8003 22 18.2004 21.455 19.27C20.9757 20.2108 20.2108 20.9757 19.27 21.455C18.2004 22 16.8003 22 14 22H10C7.19974 22 5.79961 22 4.73005 21.455C3.78924 20.9757 3.02433 20.2108 2.54497 19.27C2 18.2004 2 16.8003 2 14ZM12.5303 10.4697C12.3897 10.329 12.1989 10.25 12 10.25C11.8011 10.25 11.6103 10.329 11.4697 10.4697L8.96967 12.9697C8.67678 13.2626 8.67678 13.7374 8.96967 14.0303C9.26256 14.3232 9.73744 14.3232 10.0303 14.0303L11.25 12.8107V17C11.25 17.4142 11.5858 17.75 12 17.75C12.4142 17.75 12.75 17.4142 12.75 17V12.8107L13.9697 14.0303C14.2626 14.3232 14.7374 14.3232 15.0303 14.0303C15.3232 13.7374 15.3232 13.2626 15.0303 12.9697L12.5303 10.4697Z" />
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_nav_privacy.xml b/app/src/main/res/drawable/ic_nav_privacy.xml
new file mode 100644
index 00000000..ea64cfab
--- /dev/null
+++ b/app/src/main/res/drawable/ic_nav_privacy.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_nav_rating.xml b/app/src/main/res/drawable/ic_nav_rating.xml
new file mode 100644
index 00000000..7c945841
--- /dev/null
+++ b/app/src/main/res/drawable/ic_nav_rating.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_nav_share.xml b/app/src/main/res/drawable/ic_nav_share.xml
new file mode 100644
index 00000000..1cfd748b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_nav_share.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_settings_about.xml b/app/src/main/res/drawable/ic_settings_about.xml
deleted file mode 100644
index e3d03104..00000000
--- a/app/src/main/res/drawable/ic_settings_about.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_settings_app_lock.xml b/app/src/main/res/drawable/ic_settings_app_lock.xml
deleted file mode 100644
index cfe42f4f..00000000
--- a/app/src/main/res/drawable/ic_settings_app_lock.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_settings_material_you.xml b/app/src/main/res/drawable/ic_settings_material_you.xml
deleted file mode 100644
index 31bd991d..00000000
--- a/app/src/main/res/drawable/ic_settings_material_you.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_settings_osl.xml b/app/src/main/res/drawable/ic_settings_osl.xml
deleted file mode 100644
index 52b259cc..00000000
--- a/app/src/main/res/drawable/ic_settings_osl.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_settings_sort_order.xml b/app/src/main/res/drawable/ic_settings_sort_order.xml
deleted file mode 100644
index 84b8a695..00000000
--- a/app/src/main/res/drawable/ic_settings_sort_order.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_settings_theme.xml b/app/src/main/res/drawable/ic_settings_theme.xml
deleted file mode 100644
index fab1abd7..00000000
--- a/app/src/main/res/drawable/ic_settings_theme.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index f2cf6d6d..de44a5d0 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -11,7 +11,16 @@
Inicio
Respaldo
Ajustes
+ Calificarnos
+ Compartir
+ Privacidad
Hecho con ❤️ en India.
+
+ ¡Hola! Echa un vistazo a esta increíble aplicación llamada GreenStash.
+ Facilita la planificación y gestión de tus metas de ahorro, ayudándote a establecer el hábito de ahorrar dinero para cosas aún mejores.
+ Descárgala ahora desde la Play Store: %s
+
+
Desbloquear GreenStash
@@ -140,10 +149,12 @@
Configuración
Pantalla
Tema predeterminado
+ Cambiar tema
Claro
Oscuro
Predeterminado del sistema
Tema Oscuro
+ Activar tema AMOLED negro
Aplicar
Cambia el tema según tú fondo de pantalla (A12 en adelante)
Esta característica es sólo para dispositivos con Android 12 en adelante.
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 52d71229..16c69080 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -12,7 +12,16 @@
Главная
Резервные копии
Настройки
+ Оценить нас
+ Поделиться
+ Конфиденциальность
Сделано с ❤️ в Индии.
+
+ Привет! Посмотри это удивительное приложение под названием GreenStash.
+ Оно делает планирование и управление твоими финансовыми целями легким и приятным, помогая установить привычку экономить деньги для еще более лучших вещей.
+ Скачай его сейчас из Play Store: %s
+
+
Разблокировать GreenStash
@@ -141,10 +150,12 @@
Настройки
Дисплей
Тема по умолчанию
+ Изменить тему
Светлая
Тёмная
Как в системе
Чёрная тема
+ Включить черную тему AMOLED
Применить
Переключение темы в соответствии с вашими обоями (только для A12+)
Эта функция доступна только для устройств под управлением Android 12 или более поздней версии.
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index c6cd7735..e97e9151 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -11,7 +11,16 @@
Ana Sayfa
Yedekler
Ayarlar
+ Bizi Değerlendir
+ Paylaş
+ Gizlilik
❤️ Hindistan\'da yapıldı.
+
+ Merhaba! GreenStash adlı bu harika uygulamaya göz at.
+ Tasarruf hedeflerinizi planlamanızı ve yönetmenizi kolaylaştırır, daha iyi şeyler için para biriktirme alışkanlığınızı kazanmanıza yardımcı olur.
+ Şimdi Play Store\'dan indirin: %s
+
+
GreenStash\'in Kilidini Aç
@@ -142,10 +151,12 @@
Ayarlar
Görünüm
Varsayılan Tema
+ Temayı Değiştir
Açık
Koyu
Sistem Varsayılanı
Siyah Tema
+ Siyah AMOLED temasını etkinleştir
Uygula
Material You
Temayı duvar kağıdınıza göre değiştirin (Yalnızca A12+)
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index d173913a..951c6809 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -11,7 +11,15 @@
主页
备份
设置
+ 给我们评分
+ 分享
+ 隐私
用❤️制作于印度。
+
+ 嘿!看看这款名为GreenStash的神奇应用吧。它让你规划和管理储蓄目标变得轻松愉快,帮助你养成为更好的事物储蓄的习惯
+ 立即从Play Store下载:%s
+
+
解锁GreenStash
@@ -141,10 +149,12 @@
设置
显示
默认主题
+ 修改主题
明亮
暗色
系统默认
黑色主题
+ 启用黑色AMOLED主题
应用
自适应
根据壁纸切换合适的主题(仅Android12+)
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index c9f8522e..130cb650 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -11,6 +11,9 @@
首頁
備份
設定
+ 給我們評分
+ 分享
+ 隱私
在印度製作,充滿 ❤️。
@@ -19,6 +22,10 @@
驗證成功!
驗證失敗!
驗證錯誤:%s
+
+ 嘿!看看這款名為GreenStash的神奇應用吧。它讓你規劃和管理儲蓄目標變得輕鬆愉快,幫助你養成為更好的事物儲蓄的習慣
+ 立即從Play Store下載:%s
+
您好!請選擇您的預設貨幣以開始。
@@ -141,10 +148,12 @@
設定
顯示
預設主題
+ 更改主題
亮色
暗色
系統預設
黑色主題
+ 啟用黑色AMOLED主題
套用
Material You
根據您的桌布切換主題(僅限 Android 12+)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9370e618..3b707d31 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -11,7 +11,15 @@
Home
Backups
Settings
+ Rate Us
+ Share
+ Privacy
Made with ❤️ in India.
+
+ Hey! Check out this amazing app called GreenStash.
+ It makes planning and managing your savings goals a breeze, helping you establish the habit of saving money for better things.
+ Download it now from the Play Store: %s
+
Unlock GreenStash
@@ -141,10 +149,12 @@
Settings
Display
Default Theme
+ Change Theme
Light
Dark
System Default
Black Theme
+ Enable black AMOLED theme
Apply
Material You
Switch theme according to your wallpaper (A12+ Only)