diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml
index 608684fc..0c0c3383 100644
--- a/.idea/deploymentTargetDropDown.xml
+++ b/.idea/deploymentTargetDropDown.xml
@@ -3,20 +3,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/app/src/main/java/com/starry/greenstash/MainActivity.kt b/app/src/main/java/com/starry/greenstash/MainActivity.kt
index a3586b81..39e8cab8 100644
--- a/app/src/main/java/com/starry/greenstash/MainActivity.kt
+++ b/app/src/main/java/com/starry/greenstash/MainActivity.kt
@@ -152,6 +152,9 @@ class MainActivity : AppCompatActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
+ val navController = rememberNavController()
+ val startDestination by mainViewModel.startDestination
+
Crossfade(
targetState = showAppContents,
label = "AppLockCrossFade",
@@ -159,9 +162,7 @@ class MainActivity : AppCompatActivity() {
) { showAppContents ->
// show app contents only if user has authenticated.
if (showAppContents.value) {
- val navController = rememberNavController()
- val screen by mainViewModel.startDestination
- NavGraph(navController = navController, screen)
+ NavGraph(navController = navController, startDestination)
} else {
// show app locked screen if user has not authenticated.
AppLockedScreen(onAuthRequest = {
diff --git a/app/src/main/java/com/starry/greenstash/ui/common/DateTimeCard.kt b/app/src/main/java/com/starry/greenstash/ui/common/DateTimeCard.kt
index 5d73b94b..0f8d28aa 100644
--- a/app/src/main/java/com/starry/greenstash/ui/common/DateTimeCard.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/common/DateTimeCard.kt
@@ -39,6 +39,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
@@ -46,6 +47,7 @@ import androidx.compose.ui.unit.dp
import com.starry.greenstash.R
import com.starry.greenstash.ui.screens.settings.DateStyle
import com.starry.greenstash.ui.theme.greenstashFont
+import com.starry.greenstash.utils.weakHapticFeedback
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@@ -55,11 +57,15 @@ fun DateTimeCard(
dateTimeStyle: () -> DateStyle,
onClick: () -> Unit
) {
+ val view = LocalView.current
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 18.dp, vertical = 8.dp)
- .clickable { onClick() }
+ .clickable {
+ view.weakHapticFeedback()
+ onClick()
+ }
) {
Row(
modifier = Modifier
diff --git a/app/src/main/java/com/starry/greenstash/ui/common/TipCard.kt b/app/src/main/java/com/starry/greenstash/ui/common/TipCard.kt
new file mode 100644
index 00000000..a117431e
--- /dev/null
+++ b/app/src/main/java/com/starry/greenstash/ui/common/TipCard.kt
@@ -0,0 +1,125 @@
+/**
+ * MIT License
+ *
+ * Copyright (c) [2022 - Present] Stɑrry Shivɑm
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+package com.starry.greenstash.ui.common
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.expandVertically
+import androidx.compose.animation.shrinkVertically
+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.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Lightbulb
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.starry.greenstash.ui.theme.greenstashFont
+
+@Composable
+fun TipCard(
+ modifier: Modifier = Modifier,
+ icon: ImageVector = Icons.Filled.Lightbulb,
+ description: String,
+ showTipCard: Boolean,
+ onDismissRequest: () -> Unit
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateContentSize(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ AnimatedVisibility(
+ visible = showTipCard,
+ enter = expandVertically(),
+ exit = shrinkVertically()
+ ) {
+ Card(
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(bottom = 10.dp),
+ shape = RoundedCornerShape(16.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.secondaryContainer
+ )
+ ) {
+ Column(
+ modifier = Modifier.padding(16.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ modifier = Modifier.size(32.dp)
+ )
+ Spacer(modifier = Modifier.width(16.dp))
+ Text(
+ text = description,
+ style = MaterialTheme.typography.titleSmall,
+ fontFamily = greenstashFont,
+ )
+ }
+ Spacer(modifier = Modifier.height(16.dp))
+ Button(
+ onClick = { onDismissRequest() },
+ modifier = Modifier.align(Alignment.End)
+ ) {
+ Text(text = "OK")
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun TipCardPV() {
+ TipCard(
+ description = "This is a tip",
+ showTipCard = true,
+ onDismissRequest = {}
+ )
+}
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/backups/composables/BackupScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/backups/composables/BackupScreen.kt
index c408112c..841dcfd5 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/backups/composables/BackupScreen.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/backups/composables/BackupScreen.kt
@@ -64,6 +64,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -73,6 +74,7 @@ import coil.compose.AsyncImage
import com.starry.greenstash.R
import com.starry.greenstash.ui.screens.backups.BackupViewModel
import com.starry.greenstash.ui.theme.greenstashFont
+import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.launch
import java.io.InputStreamReader
import java.io.Reader
@@ -82,6 +84,7 @@ import java.nio.charset.StandardCharsets
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BackupScreen(navController: NavController) {
+ val view = LocalView.current
val context = LocalContext.current
val viewModel = hiltViewModel()
@@ -102,7 +105,10 @@ fun BackupScreen(navController: NavController) {
fontFamily = greenstashFont
)
}, navigationIcon = {
- IconButton(onClick = { navController.navigateUp() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ navController.navigateUp()
+ }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/dwscreen/composables/DWScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/dwscreen/composables/DWScreen.kt
index b754a15e..a079230f 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/dwscreen/composables/DWScreen.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/dwscreen/composables/DWScreen.kt
@@ -60,6 +60,7 @@ 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
@@ -88,6 +89,7 @@ import com.starry.greenstash.ui.screens.dwscreen.DWViewModel
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.validateAmount
+import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@@ -99,6 +101,7 @@ import java.time.LocalDateTime
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DWScreen(goalId: String, transactionTypeName: String, navController: NavController) {
+ val view = LocalView.current
val context = LocalContext.current
val viewModel: DWViewModel = hiltViewModel()
@@ -141,7 +144,10 @@ fun DWScreen(goalId: String, transactionTypeName: String, navController: NavCont
fontFamily = greenstashFont
)
}, navigationIcon = {
- IconButton(onClick = { navController.navigateUp() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ navController.navigateUp()
+ }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/GoalLazyItem.kt b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/GoalLazyItem.kt
index 4519916e..cba270d3 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/GoalLazyItem.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/GoalLazyItem.kt
@@ -36,6 +36,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.starry.greenstash.MainActivity
@@ -50,6 +51,8 @@ import com.starry.greenstash.utils.GoalTextUtils
import com.starry.greenstash.utils.ImageUtils
import com.starry.greenstash.utils.Utils
import com.starry.greenstash.utils.getActivity
+import com.starry.greenstash.utils.strongHapticFeedback
+import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.launch
@@ -71,6 +74,7 @@ fun GoalLazyColumnItem(
}
val openDeleteDialog = remember { mutableStateOf(false) }
+ val localView = LocalView.current
when (goalCardStyle) {
GoalCardStyle.Classic -> {
@@ -90,6 +94,7 @@ fun GoalLazyColumnItem(
goalProgress = progressPercent.toFloat() / 100,
goalImage = item.goal.goalImage,
onDepositClicked = {
+ localView.weakHapticFeedback()
if (item.getCurrentlySavedAmount() >= item.goal.targetAmount) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.goal_already_achieved))
@@ -104,6 +109,7 @@ fun GoalLazyColumnItem(
}
},
onWithdrawClicked = {
+ localView.weakHapticFeedback()
if (item.getCurrentlySavedAmount() == 0f.toDouble()) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.withdraw_button_error))
@@ -118,6 +124,7 @@ fun GoalLazyColumnItem(
}
},
onInfoClicked = {
+ localView.weakHapticFeedback()
navController.navigate(
Screens.GoalInfoScreen.withGoalId(
goalId = item.goal.goalId.toString()
@@ -125,13 +132,17 @@ fun GoalLazyColumnItem(
)
},
onEditClicked = {
+ localView.weakHapticFeedback()
navController.navigate(
Screens.InputScreen.withGoalToEdit(
goalId = item.goal.goalId.toString()
)
)
},
- onDeleteClicked = { openDeleteDialog.value = true }
+ onDeleteClicked = {
+ localView.strongHapticFeedback()
+ openDeleteDialog.value = true
+ }
)
}
@@ -162,6 +173,7 @@ fun GoalLazyColumnItem(
goalProgress = progressPercent.toFloat() / 100,
goalIcon = goalIcon,
onDepositClicked = {
+ localView.weakHapticFeedback()
if (item.getCurrentlySavedAmount() >= item.goal.targetAmount) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.goal_already_achieved))
@@ -176,6 +188,7 @@ fun GoalLazyColumnItem(
}
},
onWithdrawClicked = {
+ localView.weakHapticFeedback()
if (item.getCurrentlySavedAmount() == 0f.toDouble()) {
coroutineScope.launch {
snackBarHostState.showSnackbar(context.getString(R.string.withdraw_button_error))
@@ -190,6 +203,7 @@ fun GoalLazyColumnItem(
}
},
onInfoClicked = {
+ localView.weakHapticFeedback()
navController.navigate(
Screens.GoalInfoScreen.withGoalId(
goalId = item.goal.goalId.toString()
@@ -197,6 +211,7 @@ fun GoalLazyColumnItem(
)
},
onEditClicked = {
+ localView.weakHapticFeedback()
navController.navigate(
Screens.InputScreen.withGoalToEdit(
goalId = item.goal.goalId.toString()
@@ -204,6 +219,7 @@ fun GoalLazyColumnItem(
)
},
onDeleteClicked = {
+ localView.strongHapticFeedback()
openDeleteDialog.value = true
}
)
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 039e99f3..fcaabd68 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
@@ -50,6 +50,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.input.ImeAction
@@ -58,6 +59,7 @@ import androidx.compose.ui.unit.dp
import com.starry.greenstash.R
import com.starry.greenstash.ui.screens.home.SearchWidgetState
import com.starry.greenstash.ui.theme.greenstashFont
+import com.starry.greenstash.utils.weakHapticFeedback
@Composable
@@ -73,7 +75,8 @@ fun HomeAppBar(
) {
Crossfade(
targetState = searchWidgetState,
- animationSpec = tween(durationMillis = 300), label = "searchbar cross-fade"
+ animationSpec = tween(durationMillis = 300),
+ label = "searchbar cross-fade"
) {
when (it) {
SearchWidgetState.CLOSED -> {
@@ -104,6 +107,7 @@ private fun DefaultAppBar(
onFilterClicked: () -> Unit,
onSearchClicked: () -> Unit,
) {
+ val view = LocalView.current
TopAppBar(
title = {
Text(
@@ -114,7 +118,10 @@ private fun DefaultAppBar(
)
},
navigationIcon = {
- IconButton(onClick = { onMenuClicked() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ onMenuClicked()
+ }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = stringResource(id = R.string.menu_button_desc)
@@ -122,14 +129,20 @@ private fun DefaultAppBar(
}
},
actions = {
- IconButton(onClick = { onFilterClicked() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ onFilterClicked()
+ }) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_menu_filter),
contentDescription = stringResource(id = R.string.filter_button_desc),
modifier = Modifier.size(22.dp)
)
}
- IconButton(onClick = { onSearchClicked() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ onSearchClicked()
+ }) {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = stringResource(id = R.string.search_button_desc)
@@ -148,8 +161,7 @@ private fun SearchAppBar(
onSearchClicked: (String) -> Unit,
) {
Surface(
- modifier = Modifier.fillMaxWidth(),
- color = MaterialTheme.colorScheme.surface
+ modifier = Modifier.fillMaxWidth(), color = MaterialTheme.colorScheme.surface
) {
OutlinedTextField(
modifier = Modifier
@@ -166,9 +178,7 @@ private fun SearchAppBar(
},
singleLine = true,
leadingIcon = {
- IconButton(
- onClick = {}
- ) {
+ IconButton(onClick = {}) {
Icon(
imageVector = Icons.Default.Search,
contentDescription = null,
@@ -177,15 +187,13 @@ private fun SearchAppBar(
}
},
trailingIcon = {
- IconButton(
- onClick = {
- if (text.isNotEmpty()) {
- onTextChange("")
- } else {
- onCloseClicked()
- }
+ IconButton(onClick = {
+ if (text.isNotEmpty()) {
+ onTextChange("")
+ } else {
+ onCloseClicked()
}
- ) {
+ }) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = null,
@@ -196,17 +204,16 @@ private fun SearchAppBar(
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Search
),
- keyboardActions = KeyboardActions(
- onSearch = {
- onSearchClicked(text)
- }
- ),
+ keyboardActions = KeyboardActions(onSearch = {
+ onSearchClicked(text)
+ }),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = MaterialTheme.colorScheme.primaryContainer.copy(0.3f),
disabledContainerColor = Color.Transparent,
cursorColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f),
),
- shape = RoundedCornerShape(24.dp))
+ shape = RoundedCornerShape(24.dp)
+ )
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDialogs.kt b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDialogs.kt
index beab8d83..fb71eedb 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDialogs.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/home/composables/HomeDialogs.kt
@@ -36,8 +36,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
-import androidx.compose.ui.hapticfeedback.HapticFeedbackType
-import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import com.starry.greenstash.R
import com.starry.greenstash.ui.theme.greenstashFont
@@ -48,10 +46,8 @@ fun HomeDialogs(
openDeleteDialog: MutableState,
onDeleteConfirmed: () -> Unit,
) {
- val haptic = LocalHapticFeedback.current
-
if (openDeleteDialog.value) {
- haptic.performHapticFeedback(HapticFeedbackType.LongPress)
+
AlertDialog(onDismissRequest = {
openDeleteDialog.value = false
}, title = {
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 87bd5772..b75c5438 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
@@ -47,6 +47,7 @@ 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.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
@@ -57,12 +58,15 @@ import com.starry.greenstash.R
import com.starry.greenstash.ui.navigation.DrawerScreens
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) {
val items = listOf(DrawerScreens.Home, DrawerScreens.Backups, DrawerScreens.Settings)
val selectedItem = remember { mutableStateOf(items[0]) }
+
+ val view = LocalView.current
val coroutineScope = rememberCoroutineScope()
ModalDrawerSheet(
@@ -104,6 +108,7 @@ fun HomeDrawer(drawerState: DrawerState, navController: NavController) {
},
selected = item == selectedItem.value,
onClick = {
+ view.weakHapticFeedback()
selectedItem.value = item
coroutineScope.launch {
drawerState.close()
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 24fe001d..1b8f09f2 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
@@ -81,6 +81,7 @@ 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
import androidx.compose.ui.text.style.TextOverflow
@@ -108,6 +109,7 @@ 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.isScrollingUp
+import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Locale
@@ -386,6 +388,8 @@ private fun HomeExtendedFAB(
) {
val isFabVisible = lazyListState.isScrollingUp()
val density = LocalDensity.current
+ val view = LocalView.current
+
AnimatedVisibility(
visible = isFabVisible,
enter = slideInVertically {
@@ -399,7 +403,10 @@ private fun HomeExtendedFAB(
) {
ExtendedFloatingActionButton(
modifier = modifier.padding(end = 10.dp, bottom = 12.dp),
- onClick = { navController.navigate(Screens.InputScreen.route) },
+ onClick = {
+ view.weakHapticFeedback()
+ navController.navigate(Screens.InputScreen.route)
+ },
elevation = FloatingActionButtonDefaults.elevation(8.dp)
) {
Row {
@@ -443,7 +450,7 @@ private fun FilterSheetContent(viewModel: HomeViewModel) {
}
}
}
-
+
Spacer(modifier = Modifier.height(16.dp))
}
}
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt
index fdab54db..282481a6 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/GoalInfoScreen.kt
@@ -55,10 +55,8 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
-import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.surfaceColorAtElevation
@@ -66,12 +64,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -92,20 +92,24 @@ import com.starry.greenstash.database.goal.GoalPriority.High
import com.starry.greenstash.database.goal.GoalPriority.Low
import com.starry.greenstash.database.goal.GoalPriority.Normal
import com.starry.greenstash.ui.common.ExpandableTextCard
+import com.starry.greenstash.ui.common.TipCard
import com.starry.greenstash.ui.screens.info.InfoViewModel
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.ui.theme.greenstashNumberFont
import com.starry.greenstash.utils.GoalTextUtils
import com.starry.greenstash.utils.Utils
+import com.starry.greenstash.utils.weakHapticFeedback
+import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GoalInfoScreen(goalId: String, navController: NavController) {
+ val view = LocalView.current
+ val context = LocalContext.current
val viewModel: InfoViewModel = hiltViewModel()
val state = viewModel.state
- val context = LocalContext.current
val snackBarHostState = remember { SnackbarHostState() }
@@ -125,7 +129,10 @@ fun GoalInfoScreen(goalId: String, navController: NavController) {
fontFamily = greenstashFont
)
}, navigationIcon = {
- IconButton(onClick = { navController.navigateUp() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ navController.navigateUp()
+ }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
@@ -183,30 +190,32 @@ fun GoalInfoScreen(goalId: String, navController: NavController) {
Spacer(modifier = Modifier.height(6.dp))
}
if (goalData.transactions.isNotEmpty()) {
- TransactionItems(
- goalData.getOrderedTransactions(),
- currencySymbol,
- viewModel
- )
// Show tooltip for swipe functionality.
+ val showTransactionSwipeTip = remember { mutableStateOf(false) }
LaunchedEffect(key1 = true) {
if (viewModel.shouldShowTransactionTip()) {
- val result = snackBarHostState.showSnackbar(
- message = context.getString(R.string.info_transaction_onboarding_tip),
- actionLabel = context.getString(R.string.ok),
- duration = SnackbarDuration.Indefinite
- )
-
- when (result) {
- SnackbarResult.ActionPerformed -> {
- viewModel.transactionTipDismissed()
- }
-
- SnackbarResult.Dismissed -> {}
- }
+ delay(800) // Don't show immediately.
+ showTransactionSwipeTip.value = true
}
}
+ TipCard(
+ modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp),
+ description = stringResource(id = R.string.info_transaction_swipe_tip),
+ showTipCard = showTransactionSwipeTip.value,
+ onDismissRequest = {
+ showTransactionSwipeTip.value = false
+ viewModel.transactionTipDismissed()
+ }
+ )
+
+ // Show transaction items.
+ TransactionItems(
+ goalData.getOrderedTransactions(),
+ currencySymbol,
+ viewModel
+ )
+
} else {
Column(
modifier = Modifier.fillMaxWidth(),
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/TransactionItems.kt b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/TransactionItems.kt
index 50ff0c7d..5d0421bb 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/TransactionItems.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/info/composables/TransactionItems.kt
@@ -73,6 +73,7 @@ import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
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
@@ -87,6 +88,8 @@ import com.starry.greenstash.ui.screens.info.InfoViewModel
import com.starry.greenstash.ui.screens.settings.ThemeMode
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.utils.getActivity
+import com.starry.greenstash.utils.strongHapticFeedback
+import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -190,6 +193,7 @@ private fun TransactionSwipeContainer(
showEditSheet: MutableState,
showDeleteDialog: MutableState
) {
+ val view = LocalView.current
val coroutineScope = rememberCoroutineScope()
val swipeState = rememberSwipeToDismissBoxState(
confirmValueChange = { direction ->
@@ -197,14 +201,20 @@ private fun TransactionSwipeContainer(
SwipeToDismissBoxValue.EndToStart -> {
coroutineScope.launch {
delay(180) // allow the swipe to settle.
- withContext(Dispatchers.Main) { showEditSheet.value = true }
+ withContext(Dispatchers.Main) {
+ view.weakHapticFeedback()
+ showEditSheet.value = true
+ }
}
}
SwipeToDismissBoxValue.StartToEnd -> {
coroutineScope.launch {
delay(180) // allow the swipe to settle.
- withContext(Dispatchers.Main) { showDeleteDialog.value = true }
+ withContext(Dispatchers.Main) {
+ view.strongHapticFeedback()
+ showDeleteDialog.value = true
+ }
}
}
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 00ce47b1..31fc67d5 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
@@ -36,10 +36,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.animateContentSize
-import androidx.compose.animation.expandVertically
-import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@@ -66,7 +62,6 @@ 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.Image
-import androidx.compose.material.icons.filled.Lightbulb
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
@@ -104,6 +99,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
+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
@@ -139,6 +135,7 @@ import com.starry.greenstash.MainActivity
import com.starry.greenstash.R
import com.starry.greenstash.database.goal.GoalPriority
import com.starry.greenstash.ui.common.SelectableChipGroup
+import com.starry.greenstash.ui.common.TipCard
import com.starry.greenstash.ui.navigation.DrawerScreens
import com.starry.greenstash.ui.screens.input.InputViewModel
import com.starry.greenstash.ui.theme.greenstashFont
@@ -148,6 +145,7 @@ import com.starry.greenstash.utils.getActivity
import com.starry.greenstash.utils.hasNotificationPermission
import com.starry.greenstash.utils.toToast
import com.starry.greenstash.utils.validateAmount
+import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -158,7 +156,9 @@ import java.time.format.DateTimeFormatter
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun InputScreen(editGoalId: String?, navController: NavController) {
+ val view = LocalView.current
val context = LocalContext.current
+
val viewModel: InputViewModel = hiltViewModel()
val coroutineScope = rememberCoroutineScope()
val snackBarHostState = remember { SnackbarHostState() }
@@ -290,7 +290,10 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
fontFamily = greenstashFont
)
}, navigationIcon = {
- IconButton(onClick = { navController.navigateUp() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ navController.navigateUp()
+ }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
@@ -418,8 +421,8 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
}
}
- InputTipCard(
- icon = Icons.Filled.Lightbulb,
+ TipCard(
+ modifier = Modifier.fillMaxWidth(0.86f),
description = stringResource(id = R.string.input_remove_deadline_tip),
showTipCard = showRemoveDeadlineTip.value,
onDismissRequest = {
@@ -478,64 +481,6 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
}
}
-@Composable
-private fun InputTipCard(
- icon: ImageVector,
- description: String,
- showTipCard: Boolean,
- onDismissRequest: () -> Unit
-) {
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .animateContentSize(),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- AnimatedVisibility(
- visible = showTipCard,
- enter = expandVertically(),
- exit = shrinkVertically()
- ) {
- Card(
- modifier = Modifier
- .fillMaxWidth(0.86f)
- .padding(bottom = 10.dp),
- shape = RoundedCornerShape(16.dp),
- colors = CardDefaults.cardColors(
- containerColor = MaterialTheme.colorScheme.secondaryContainer
- )
- ) {
- Column(
- modifier = Modifier.padding(16.dp)
- ) {
- Row(
- verticalAlignment = Alignment.CenterVertically
- ) {
- Icon(
- imageVector = icon,
- contentDescription = null,
- modifier = Modifier.size(32.dp)
- )
- Spacer(modifier = Modifier.width(16.dp))
- Text(
- text = description,
- style = MaterialTheme.typography.titleSmall,
- fontFamily = greenstashFont,
- )
- }
- Spacer(modifier = Modifier.height(16.dp))
- Button(
- onClick = { onDismissRequest() },
- modifier = Modifier.align(Alignment.End)
- ) {
- Text(text = "OK")
- }
- }
- }
- }
- }
-}
-
@Composable
private fun GoalImagePicker(
goalImage: Any?,
@@ -544,6 +489,8 @@ private fun GoalImagePicker(
@SuppressLint("ModifierParameter") fabModifier: Modifier
) {
val context = LocalContext.current
+ val view = LocalView.current
+
Box(
modifier = Modifier
.fillMaxWidth()
@@ -581,6 +528,7 @@ private fun GoalImagePicker(
.align(Alignment.BottomEnd)
.padding(end = 24.dp),
onClick = {
+ view.weakHapticFeedback()
photoPicker.launch(
PickVisualMediaRequest(
ActivityResultContracts.PickVisualMedia.ImageOnly
@@ -628,8 +576,12 @@ private fun IconPickerCard(
// To be used for onboarding tap target.
modifier: Modifier = Modifier,
) {
+ val view = LocalView.current
Card(
- onClick = onClick,
+ onClick = {
+ view.weakHapticFeedback()
+ onClick()
+ },
modifier = Modifier.fillMaxWidth(0.86f),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
@@ -727,7 +679,7 @@ private fun GoalReminderMenu(
// To be used for onboarding tap target.
modifier: Modifier = Modifier
) {
- val haptic = LocalHapticFeedback.current
+ val view = LocalView.current
var hasNotificationPermission by remember { mutableStateOf(context.hasNotificationPermission()) }
val launcher = rememberLauncherForActivityResult(
@@ -782,7 +734,7 @@ private fun GoalReminderMenu(
Switch(
checked = viewModel.state.reminder,
onCheckedChange = { newValue ->
- haptic.performHapticFeedback(HapticFeedbackType.LongPress)
+ view.weakHapticFeedback()
viewModel.state = viewModel.state.copy(reminder = newValue)
// Ask for notification permission if android ver > 13.
if (newValue &&
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 d6c71282..26cfd5b6 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
@@ -54,12 +54,13 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.LargeTopAppBar
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -70,11 +71,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
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 com.starry.greenstash.MainActivity
@@ -84,6 +87,7 @@ import com.starry.greenstash.ui.screens.home.composables.GoalItemClassic
import com.starry.greenstash.ui.screens.home.composables.GoalItemCompact
import com.starry.greenstash.ui.theme.greenstashFont
import com.starry.greenstash.utils.getActivity
+import com.starry.greenstash.utils.weakHapticFeedback
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
@@ -91,12 +95,15 @@ import kotlinx.coroutines.delay
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GoalCardStyle(navController: NavController) {
+ val view = LocalView.current
val context = navController.context
+
val settingsVM = (context.getActivity() as MainActivity).settingsViewModel
val currentStyle = settingsVM.goalCardStyle.observeAsState().value!!
+ val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
Scaffold(modifier = Modifier.fillMaxSize(), topBar = {
- TopAppBar(
+ LargeTopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text(
@@ -106,12 +113,20 @@ fun GoalCardStyle(navController: NavController) {
fontFamily = greenstashFont,
)
}, navigationIcon = {
- IconButton(onClick = { navController.navigateUp() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ navController.navigateUp()
+ }) {
Icon(
- imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = null
)
}
- })
+ }, scrollBehavior = scrollBehavior, colors = TopAppBarDefaults.largeTopAppBarColors(
+ containerColor = MaterialTheme.colorScheme.surface,
+ scrolledContainerColor = MaterialTheme.colorScheme.surface,
+ )
+ )
}) { paddingValues ->
Column(
modifier = Modifier
@@ -127,9 +142,10 @@ fun GoalCardStyle(navController: NavController) {
) {
Text(
text = "Preview",
- style = MaterialTheme.typography.titleLarge,
- modifier = Modifier.padding(start = 16.dp, top = 16.dp),
- fontFamily = greenstashFont
+ modifier = Modifier.padding(start = 16.dp, top = 14.dp),
+ fontFamily = greenstashFont,
+ fontSize = 17.sp,
+ color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f)
)
AnimatedContent(
@@ -227,10 +243,10 @@ fun GoalCardStyle(navController: NavController) {
val showCompactTip = remember { mutableStateOf(false) }
LaunchedEffect(key1 = currentStyle) {
if (currentStyle == GoalCardStyle.Compact) {
- delay(500)
+ delay(600)
showCompactTip.value = true
} else {
- delay(600)
+ delay(700)
showCompactTip.value = false
}
}
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsItem.kt b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsItem.kt
index 97ff6a2d..1f692059 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsItem.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/settings/composables/SettingsItem.kt
@@ -43,10 +43,10 @@ import androidx.compose.runtime.MutableState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.hapticfeedback.HapticFeedbackType
-import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import com.starry.greenstash.ui.theme.greenstashFont
+import com.starry.greenstash.utils.weakHapticFeedback
@Composable
fun SettingsItem(title: String, description: String, icon: ImageVector, onClick: () -> Unit) {
@@ -96,7 +96,7 @@ fun SettingsItem(
switchState: MutableState,
onCheckChange: (Boolean) -> Unit,
) {
- val haptic = LocalHapticFeedback.current
+ val view = LocalView.current
Row(
modifier = Modifier
.fillMaxWidth()
@@ -133,7 +133,7 @@ fun SettingsItem(
Switch(
checked = switchState.value,
onCheckedChange = {
- haptic.performHapticFeedback(HapticFeedbackType.LongPress)
+ view.weakHapticFeedback()
onCheckChange(it)
},
thumbContent = if (switchState.value) {
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 9bdf8803..7d26a58f 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
@@ -66,6 +66,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.nestedscroll.nestedScroll
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.semantics.Role
@@ -88,12 +89,14 @@ import com.starry.greenstash.ui.theme.greenstashFont
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 java.util.concurrent.Executor
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(navController: NavController) {
+ val view = LocalView.current
val context = LocalContext.current
val viewModel = (context.getActivity() as MainActivity).settingsViewModel
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
@@ -109,7 +112,10 @@ fun SettingsScreen(navController: NavController) {
fontFamily = greenstashFont
)
}, navigationIcon = {
- IconButton(onClick = { navController.navigateUp() }) {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ navController.navigateUp()
+ }) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = null
diff --git a/app/src/main/java/com/starry/greenstash/utils/Extensions.kt b/app/src/main/java/com/starry/greenstash/utils/Extensions.kt
index 7311b9a3..172944d5 100644
--- a/app/src/main/java/com/starry/greenstash/utils/Extensions.kt
+++ b/app/src/main/java/com/starry/greenstash/utils/Extensions.kt
@@ -30,6 +30,8 @@ import android.content.Context
import android.content.ContextWrapper
import android.content.pm.PackageManager
import android.os.Build
+import android.view.HapticFeedbackConstants
+import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.lazy.LazyListState
@@ -43,12 +45,20 @@ import androidx.core.content.ContextCompat
import java.io.File
import java.io.PrintWriter
+/**
+ * Gets the activity from the context.
+ * @return the activity if the context is an instance of [AppCompatActivity], null otherwise.
+ */
fun Context.getActivity(): AppCompatActivity? = when (this) {
is AppCompatActivity -> this
is ContextWrapper -> baseContext.getActivity()
else -> null
}
+/**
+ * Checks if the app has the notification permission.
+ * @return true if the app has the notification permission, false otherwise.
+ */
fun Context.hasNotificationPermission() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(
@@ -58,6 +68,10 @@ fun Context.hasNotificationPermission() =
true
}
+/**
+ * Checks if the lazy list is scrolling up.
+ * @return true if the list is scrolling up, false otherwise.
+ */
@Composable
fun LazyListState.isScrollingUp(): Boolean {
var previousIndex by remember(this) { mutableIntStateOf(firstVisibleItemIndex) }
@@ -83,11 +97,18 @@ fun String.toToast(context: Context, length: Int = Toast.LENGTH_SHORT) {
Toast.makeText(context, this, length).show()
}
+/**
+ * Validates the amount.
+ * @return true if the amount is valid, false otherwise.
+ */
fun String.validateAmount() =
this.isNotEmpty() && this.isNotBlank()
&& !this.matches("[0.]+".toRegex())
&& !this.endsWith(".")
+/**
+ * Clears the text content of the file.
+ */
fun File.clearText() {
PrintWriter(this).also {
it.print("")
@@ -95,7 +116,24 @@ fun File.clearText() {
}
}
+/**
+ * Updates the text content of the file.
+ */
fun File.updateText(content: String) {
clearText()
appendText(content)
+}
+
+/**
+ * Performs a slight haptic feedback.
+ */
+fun View.weakHapticFeedback() {
+ this.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK)
+}
+
+/**
+ * Performs a strong haptic feedback.
+ */
+fun View.strongHapticFeedback() {
+ this.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
}
\ 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 e898ceea..ce83a1b4 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -100,7 +100,7 @@
¡Copiado correctamente!
Sin transacciones aún.
Actualizar Transacción
- Consejo: Desliza las transacciones hacia la izquierda o hacia la derecha para editarlas o eliminarlas.
+ Consejo: Desliza las transacciones hacia la izquierda o hacia la derecha para editarlas o eliminarlas.
¡Es momento de ahorrar!
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index b98c22a7..7c1462d1 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -100,7 +100,7 @@
Başarıyla Kopyalandı!
Henüz İşlem Yok.
İşlemi Güncelle
- İpucu: İşlemleri düzenlemek veya silmek için sola veya sağa kaydırın.
+ İpucu: İşlemleri düzenlemek veya silmek için sola veya sağa kaydırın.
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 34329c42..2b66a545 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -100,7 +100,7 @@
复制成功!
尚无收支。
更新交易
- 提示:左右滑动交易以编辑或删除它们。
+ 提示:左右滑动交易以编辑或删除它们。
省钱时间到!
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 80f4eeea..c2afc839 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -100,7 +100,7 @@
成功複製!
尚無交易。
更新交易
- 提示:向左或向右滑動交易以編輯或刪除它們。
+ 提示:向左或向右滑動交易以編輯或刪除它們。
該存錢的時候了!
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7e2261fa..9ef12fc1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -100,7 +100,7 @@
Copied Successfully!
No Transactions Yet.
Update Transaction
- Tip: Swipe transactions left or right to edit or delete them.
+ Tip! Swipe transactions left or right to edit or delete them.
It\'s time to save!
@@ -127,7 +127,7 @@
Great! Saving goal set.
Saving goal updated.
Do you want to remove deadline from this goal?
- Tip: You can remove the deadline from existing goals by long-pressing on the deadline field.
+ Tip! You can remove the deadline from existing goals by long-pressing on the deadline field.
Backup & Restore