diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 00000000..b268ef36
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/starry/greenstash/ui/common/SelectableChipGroup.kt b/app/src/main/java/com/starry/greenstash/ui/common/SelectableChipGroup.kt
deleted file mode 100644
index 4dcca914..00000000
--- a/app/src/main/java/com/starry/greenstash/ui/common/SelectableChipGroup.kt
+++ /dev/null
@@ -1,134 +0,0 @@
-/**
- * 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.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
-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.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Check
-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.draw.clip
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
-import com.starry.greenstash.ui.theme.greenstashFont
-
-@Composable
-fun SelectableChipGroup(
- modifier: Modifier,
- choices: List,
- selected: String,
- onSelected: (String) -> Unit
-) {
- Box(modifier = modifier, contentAlignment = Alignment.Center) {
- Row {
- choices.forEach { it ->
- Chip(
- title = it,
- selected = selected,
- onSelected = { onSelected(it) }
- )
- }
- }
- }
-
-}
-
-@Composable
-fun Chip(
- title: String,
- selected: String,
- onSelected: (String) -> Unit
-) {
- val isSelected = selected == title
- Box(
- modifier = Modifier
- .padding(end = 10.dp)
- .height(35.dp)
- .clip(RoundedCornerShape(12.dp))
- .background(MaterialTheme.colorScheme.primary)
- .clickable(
- onClick = {
- onSelected(title)
- }
- )
- ) {
- Row(
- modifier = Modifier.padding(start = 10.dp, end = 10.dp, top = 5.dp),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.spacedBy(4.dp)
- ) {
-
- AnimatedVisibility(visible = isSelected) {
- Icon(
- imageVector = Icons.Filled.Check,
- contentDescription = "check",
- tint = MaterialTheme.colorScheme.onPrimary,
- modifier = Modifier.size(18.dp)
- )
- }
- Text(
- text = title,
- color = MaterialTheme.colorScheme.onPrimary,
- fontSize = 16.sp,
- fontFamily = greenstashFont
- )
-
- }
- }
-}
-
-@Preview(showBackground = true)
-@Composable
-fun SelectableChipGroupPV() {
- SelectableChipGroup(
- modifier = Modifier
- .fillMaxWidth()
- .padding(14.dp),
- choices = listOf("High", "Normal", "Low"),
- selected = "Normal",
- onSelected = { }
- )
-}
-
-
-
-
-
diff --git a/app/src/main/java/com/starry/greenstash/ui/screens/input/InputViewModel.kt b/app/src/main/java/com/starry/greenstash/ui/screens/input/InputViewModel.kt
index 471cbbae..8baaa51d 100644
--- a/app/src/main/java/com/starry/greenstash/ui/screens/input/InputViewModel.kt
+++ b/app/src/main/java/com/starry/greenstash/ui/screens/input/InputViewModel.kt
@@ -176,10 +176,36 @@ class InputViewModel @Inject constructor(
}
}
+ // State update functions ==============
+
+ fun updatePriority(priority: String) {
+ state = state.copy(priority = priority)
+ }
+
+ fun updateReminder(reminder: Boolean) {
+ state = state.copy(reminder = reminder)
+ }
+
+ fun updateTitle(title: String) {
+ state = state.copy(goalTitleText = title)
+ }
+
+ fun updateTargetAmount(amount: String) {
+ state = state.copy(targetAmount = amount)
+ }
+
+ fun updateDeadline(deadline: String) {
+ state = state.copy(deadline = deadline)
+ }
+
fun removeDeadLine() {
state = state.copy(deadline = "")
}
+ fun updateAdditionalNotes(notes: String) {
+ state = state.copy(additionalNotes = notes)
+ }
+
fun getDateStyleValue() = preferenceUtil.getString(
PreferenceUtil.DATE_FORMAT_STR, DateStyle.DateMonthYear.pattern
)
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 31fc67d5..1594b255 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
@@ -27,7 +27,6 @@ package com.starry.greenstash.ui.screens.input.composables
import android.Manifest
import android.annotation.SuppressLint
-import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
@@ -75,6 +74,9 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SegmentedButton
+import androidx.compose.material3.SegmentedButtonDefaults
+import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
@@ -94,6 +96,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.layout.ContentScale
@@ -134,7 +137,6 @@ import com.starry.greenstash.BuildConfig
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
@@ -177,8 +179,7 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
if (editGoalId != null) {
LaunchedEffect(key1 = true, block = {
- viewModel.setEditGoalData(
- goalId = editGoalId.toLong(),
+ viewModel.setEditGoalData(goalId = editGoalId.toLong(),
onEditDataSet = { goalImageBm, goalIconId ->
goalImageBm?.let { goalImage = it }
goalIconId?.let { id ->
@@ -224,16 +225,14 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
// Icon Picker Dialog.
val showIconPickerDialog = remember { mutableStateOf(false) }
- IconPickerDialog(
- viewModel = viewModel,
+ IconPickerDialog(viewModel = viewModel,
showDialog = showIconPickerDialog,
onIconSelected = { icon ->
icon?.let {
goalIcon = it.image ?: Icons.Filled.Image
viewModel.updateSelectedIcon(it)
}
- }
- )
+ })
// Remove Deadline Dialog.
if (showRemoveDeadlineDialog.value) {
@@ -274,35 +273,30 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
onComplete = { viewModel.onboardingTapTargetsShown() },
modifier = Modifier.fillMaxSize()
) {
- Scaffold(
- modifier = Modifier
- .fillMaxSize()
- .imePadding(),
+ Scaffold(modifier = Modifier
+ .fillMaxSize()
+ .imePadding(),
snackbarHost = { SnackbarHost(snackBarHostState) },
topBar = {
- TopAppBar(
- modifier = Modifier.fillMaxWidth(),
- title = {
- Text(
- text = topBarText,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- fontFamily = greenstashFont
+ TopAppBar(modifier = Modifier.fillMaxWidth(), title = {
+ Text(
+ text = topBarText,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ fontFamily = greenstashFont
+ )
+ }, navigationIcon = {
+ IconButton(onClick = {
+ view.weakHapticFeedback()
+ navController.navigateUp()
+ }) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = null
)
- }, navigationIcon = {
- IconButton(onClick = {
- view.weakHapticFeedback()
- navController.navigateUp()
- }) {
- Icon(
- imageVector = Icons.AutoMirrored.Filled.ArrowBack,
- contentDescription = null
- )
- }
}
- )
- }
- ) { paddingValues ->
+ })
+ }) { paddingValues ->
if (showGoalAddedAnim.value) {
GoalAddedOREditedAnimation(editGoalId = editGoalId)
} else {
@@ -379,13 +373,20 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
)
Spacer(modifier = Modifier.height(10.dp))
- GoalPriorityMenu(viewModel = viewModel)
+ GoalPriorityMenu(
+ selectedPriority = viewModel.state.priority,
+ obPriorityChanged = { newValue ->
+ viewModel.updatePriority(newValue)
+ }
+ )
Spacer(modifier = Modifier.height(10.dp))
GoalReminderMenu(
- viewModel = viewModel,
- context = context,
+ reminderState = viewModel.state.reminder,
+ onReminderChanged = { newValue ->
+ viewModel.updateReminder(newValue)
+ },
snackBarHostState = snackBarHostState,
coroutineScope = coroutineScope,
modifier = Modifier.tapTarget(
@@ -421,19 +422,24 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
}
}
- TipCard(
- modifier = Modifier.fillMaxWidth(0.86f),
+ TipCard(modifier = Modifier.fillMaxWidth(0.86f),
description = stringResource(id = R.string.input_remove_deadline_tip),
showTipCard = showRemoveDeadlineTip.value,
onDismissRequest = {
showRemoveDeadlineTip.value = false
viewModel.removeDeadlineTipShown()
- }
- )
+ })
InputTextFields(
- viewModel = viewModel,
+ goalTitle = viewModel.state.goalTitleText,
+ targetAmount = viewModel.state.targetAmount,
+ deadline = viewModel.state.deadline,
+ additionalNotes = viewModel.state.additionalNotes,
+ onTitleChange = { newText -> viewModel.updateTitle(newText) },
+ onAmountChange = { newText -> viewModel.updateTargetAmount(newText) },
+ onDeadlineChange = { newText -> viewModel.updateDeadline(newText) },
+ onNotesChange = { newText -> viewModel.updateAdditionalNotes(newText) },
calenderState = calenderState,
showRemoveDeadlineDialog = showRemoveDeadlineDialog
)
@@ -483,8 +489,7 @@ fun InputScreen(editGoalId: String?, navController: NavController) {
@Composable
private fun GoalImagePicker(
- goalImage: Any?,
- photoPicker: ActivityResultLauncher,
+ goalImage: Any?, photoPicker: ActivityResultLauncher,
// To be used for onboarding tap target.
@SuppressLint("ModifierParameter") fabModifier: Modifier
) {
@@ -514,8 +519,8 @@ private fun GoalImagePicker(
.clip(RoundedCornerShape(16.dp))
) {
AsyncImage(
- model = ImageRequest.Builder(context).data(goalImage)
- .crossfade(enable = true).build(),
+ model = ImageRequest.Builder(context).data(goalImage).crossfade(enable = true)
+ .build(),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
@@ -564,7 +569,7 @@ private fun InputQuoteText() {
.padding(top = 20.dp, bottom = 20.dp, start = 30.dp, end = 30.dp),
text = stringResource(id = R.string.input_page_quote),
textAlign = TextAlign.Center,
- fontSize = 13.sp,
+ fontSize = 13.5f.sp,
fontFamily = greenstashFont
)
}
@@ -581,29 +586,25 @@ private fun IconPickerCard(
onClick = {
view.weakHapticFeedback()
onClick()
- },
- modifier = Modifier.fillMaxWidth(0.86f),
- colors = CardDefaults.cardColors(
+ }, modifier = Modifier.fillMaxWidth(0.86f), colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
- ),
- shape = RoundedCornerShape(14.dp)
+ ), shape = RoundedCornerShape(14.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
- .padding(16.dp)
+ .padding(
+ top = 12.dp, bottom = 12.dp, start = 22.dp, end = 20.dp
+ )
) {
Box(
modifier = modifier
.size(48.dp)
.background(
- color = MaterialTheme.colorScheme.surface,
- shape = CircleShape
+ color = MaterialTheme.colorScheme.surface, shape = CircleShape
)
- .padding(8.dp),
- contentAlignment = Alignment.Center
+ .padding(8.dp), contentAlignment = Alignment.Center
) {
Icon(
imageVector = goalIcon,
@@ -611,13 +612,13 @@ private fun IconPickerCard(
tint = MaterialTheme.colorScheme.onSurface
)
}
-
- Spacer(modifier = Modifier.width(14.dp))
+ Spacer(modifier = Modifier.width(16.dp))
Text(
text = stringResource(id = R.string.input_pick_icon),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer,
- fontSize = 17.sp, maxLines = 2,
+ fontSize = 16.sp,
+ maxLines = 2,
fontFamily = greenstashFont,
overflow = TextOverflow.Ellipsis
@@ -627,43 +628,106 @@ private fun IconPickerCard(
}
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
-private fun GoalPriorityMenu(viewModel: InputViewModel) {
+private fun GoalPriorityMenu(selectedPriority: String, obPriorityChanged: (String) -> Unit) {
Card(
- modifier = Modifier.fillMaxWidth(0.86f),
- colors = CardDefaults.cardColors(
+ modifier = Modifier.fillMaxWidth(0.86f), colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
), shape = RoundedCornerShape(14.dp)
) {
- Box(
+
+ Column(
modifier = Modifier.fillMaxWidth(),
- contentAlignment = Alignment.Center
) {
- Column {
- Text(
- modifier = Modifier
- .fillMaxWidth()
- .padding(top = 6.dp),
- text = stringResource(id = R.string.input_goal_priority),
- textAlign = TextAlign.Center,
- fontFamily = greenstashFont,
- color = MaterialTheme.colorScheme.onPrimaryContainer
- )
- SelectableChipGroup(
+ Text(
+ modifier = Modifier.padding(
+ top = 8.dp, bottom = 4.dp, start = 24.dp, end = 24.dp
+ ),
+ text = stringResource(id = R.string.input_goal_priority),
+ fontFamily = greenstashFont,
+ fontSize = 16.sp,
+ color = MaterialTheme.colorScheme.onPrimaryContainer
+ )
+
+ Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
+ SingleChoiceSegmentedButtonRow(
modifier = Modifier
.fillMaxWidth()
- .padding(14.dp),
- choices = listOf(
- GoalPriority.High.name,
- GoalPriority.Normal.name,
- GoalPriority.Low.name
- ),
- selected = viewModel.state.priority,
- onSelected = { newValue ->
- viewModel.state =
- viewModel.state.copy(priority = newValue)
- }
- )
+ .padding(start = 20.dp, end = 20.dp, bottom = 8.dp)
+ ) {
+ SegmentedButton(
+ selected = selectedPriority == GoalPriority.High.name,
+ onClick = { obPriorityChanged(GoalPriority.High.name) },
+ shape = RoundedCornerShape(topStart = 14.dp, bottomStart = 14.dp),
+ label = {
+ Text(
+ text = GoalPriority.High.name, fontFamily = greenstashFont
+ )
+ },
+ icon = {
+ if (selectedPriority == GoalPriority.High.name) {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(16.dp)
+ )
+ }
+ },
+ colors = SegmentedButtonDefaults.colors(
+ activeContentColor = MaterialTheme.colorScheme.onPrimary,
+ activeContainerColor = MaterialTheme.colorScheme.primary,
+ )
+ )
+
+ SegmentedButton(
+ selected = selectedPriority == GoalPriority.Normal.name,
+ onClick = { obPriorityChanged(GoalPriority.Normal.name) },
+ shape = RectangleShape,
+ label = {
+ Text(
+ text = GoalPriority.Normal.name, fontFamily = greenstashFont
+ )
+ },
+ icon = {
+ if (selectedPriority == GoalPriority.Normal.name) {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(16.dp)
+ )
+ }
+ },
+ colors = SegmentedButtonDefaults.colors(
+ activeContentColor = MaterialTheme.colorScheme.onPrimary,
+ activeContainerColor = MaterialTheme.colorScheme.primary,
+ )
+ )
+
+ SegmentedButton(
+ selected = selectedPriority == GoalPriority.Low.name,
+ onClick = { obPriorityChanged(GoalPriority.Low.name) },
+ shape = RoundedCornerShape(topEnd = 14.dp, bottomEnd = 14.dp),
+ label = {
+ Text(
+ text = GoalPriority.Low.name, fontFamily = greenstashFont
+ )
+ },
+ icon = {
+ if (selectedPriority == GoalPriority.Low.name) {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(16.dp)
+ )
+ }
+ },
+ colors = SegmentedButtonDefaults.colors(
+ activeContentColor = MaterialTheme.colorScheme.onPrimary,
+ activeContainerColor = MaterialTheme.colorScheme.primary,
+ )
+ )
+ }
}
}
}
@@ -672,91 +736,80 @@ private fun GoalPriorityMenu(viewModel: InputViewModel) {
@Composable
private fun GoalReminderMenu(
- context: Context,
- viewModel: InputViewModel,
+ reminderState: Boolean,
+ onReminderChanged: (Boolean) -> Unit,
snackBarHostState: SnackbarHostState,
coroutineScope: CoroutineScope,
// To be used for onboarding tap target.
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
) {
val view = LocalView.current
+ val context = LocalContext.current
var hasNotificationPermission by remember { mutableStateOf(context.hasNotificationPermission()) }
- val launcher = rememberLauncherForActivityResult(
- contract = ActivityResultContracts.RequestPermission(),
- onResult = { isGranted ->
- hasNotificationPermission = isGranted
- if (!isGranted) {
- viewModel.state = viewModel.state.copy(reminder = false)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
- && ActivityCompat.shouldShowRequestPermissionRationale(
- context.getActivity() as MainActivity,
- Manifest.permission.POST_NOTIFICATIONS
- )
- ) {
- coroutineScope.launch {
- val snackBarResult =
- snackBarHostState.showSnackbar(
+ val launcher =
+ rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission(),
+ onResult = { isGranted ->
+ hasNotificationPermission = isGranted
+ if (!isGranted) {
+ onReminderChanged(false)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ActivityCompat.shouldShowRequestPermissionRationale(
+ context.getActivity() as MainActivity,
+ Manifest.permission.POST_NOTIFICATIONS
+ )
+ ) {
+ coroutineScope.launch {
+ val snackBarResult = snackBarHostState.showSnackbar(
message = context.getString(R.string.notification_permission_error),
actionLabel = "Open Settings"
)
- if (snackBarResult == SnackbarResult.ActionPerformed) {
- val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- val uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
- intent.data = uri
- startActivity(context, intent, null)
+ if (snackBarResult == SnackbarResult.ActionPerformed) {
+ val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ val uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
+ intent.data = uri
+ startActivity(context, intent, null)
+ }
}
}
}
- }
- }
- )
+ })
Card(
- modifier = Modifier.fillMaxWidth(0.86f),
- colors = CardDefaults.cardColors(
+ modifier = Modifier.fillMaxWidth(0.86f), colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer
), shape = RoundedCornerShape(14.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
- .padding(4.dp),
+ .padding(horizontal = 24.dp, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text = stringResource(id = R.string.input_goal_reminder),
- fontSize = 18.sp,
+ fontSize = 16.sp,
fontFamily = greenstashFont
)
- Spacer(modifier = Modifier.width(14.dp))
- Switch(
- checked = viewModel.state.reminder,
- onCheckedChange = { newValue ->
- view.weakHapticFeedback()
- viewModel.state = viewModel.state.copy(reminder = newValue)
- // Ask for notification permission if android ver > 13.
- if (newValue &&
- !hasNotificationPermission &&
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
- ) {
- launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
- }
- },
- thumbContent = if (viewModel.state.reminder) {
- {
- Icon(
- imageVector = Icons.Filled.Check,
- contentDescription = null,
- modifier = Modifier.size(SwitchDefaults.IconSize),
- )
- }
- } else {
- null
- },
- modifier = modifier
- )
+ Spacer(modifier = Modifier.weight(1f))
+ Switch(checked = reminderState, onCheckedChange = { newValue ->
+ view.weakHapticFeedback()
+ onReminderChanged(newValue)
+ // Ask for notification permission if android ver > 13.
+ if (newValue && !hasNotificationPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ }
+ }, thumbContent = if (reminderState) {
+ {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = null,
+ modifier = Modifier.size(SwitchDefaults.IconSize),
+ )
+ }
+ } else {
+ null
+ }, modifier = modifier)
}
}
}
@@ -764,7 +817,14 @@ private fun GoalReminderMenu(
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun InputTextFields(
- viewModel: InputViewModel,
+ goalTitle: String,
+ targetAmount: String,
+ deadline: String,
+ additionalNotes: String,
+ onTitleChange: (String) -> Unit,
+ onAmountChange: (String) -> Unit,
+ onDeadlineChange: (String) -> Unit,
+ onNotesChange: (String) -> Unit,
calenderState: UseCaseState,
showRemoveDeadlineDialog: MutableState
) {
@@ -773,15 +833,12 @@ private fun InputTextFields(
val containerColorAlpha = 0.25f
OutlinedTextField(
- value = viewModel.state.goalTitleText,
- onValueChange = { newText ->
- viewModel.state = viewModel.state.copy(goalTitleText = newText)
- },
+ value = goalTitle,
+ onValueChange = { newText -> onTitleChange(newText) },
modifier = Modifier.fillMaxWidth(0.86f),
label = {
Text(
- text = stringResource(id = R.string.input_text_title),
- fontFamily = greenstashFont
+ text = stringResource(id = R.string.input_text_title), fontFamily = greenstashFont
)
},
leadingIcon = {
@@ -804,20 +861,12 @@ private fun InputTextFields(
Spacer(modifier = Modifier.height(textFieldSpacing))
OutlinedTextField(
- value = viewModel.state.targetAmount,
- onValueChange = { newText ->
- viewModel.state =
- viewModel.state.copy(
- targetAmount = Utils.getValidatedNumber(
- newText
- )
- )
- },
+ value = targetAmount,
+ onValueChange = { newText -> onAmountChange(Utils.getValidatedNumber(newText)) },
modifier = Modifier.fillMaxWidth(0.86f),
label = {
Text(
- text = stringResource(id = R.string.input_text_amount),
- fontFamily = greenstashFont
+ text = stringResource(id = R.string.input_text_amount), fontFamily = greenstashFont
)
},
leadingIcon = {
@@ -842,29 +891,22 @@ private fun InputTextFields(
val interactionSource = remember { MutableInteractionSource() }
OutlinedTextField(
- value = viewModel.state.deadline,
- onValueChange = { newText ->
- viewModel.state = viewModel.state.copy(deadline = newText)
- },
+ value = deadline,
+ onValueChange = { newText -> onDeadlineChange(newText) },
modifier = Modifier
.fillMaxWidth(0.86f)
- .combinedClickable(
- onClick = { calenderState.show() },
- onLongClick = {
- haptic.performHapticFeedback(
- HapticFeedbackType.LongPress
- )
- if (viewModel.state.deadline.isNotEmpty()) {
- showRemoveDeadlineDialog.value = true
- }
- },
- interactionSource = interactionSource,
- indication = null
+ .combinedClickable(onClick = { calenderState.show() }, onLongClick = {
+ haptic.performHapticFeedback(
+ HapticFeedbackType.LongPress
+ )
+ if (deadline.isNotEmpty()) {
+ showRemoveDeadlineDialog.value = true
+ }
+ }, interactionSource = interactionSource, indication = null
),
label = {
Text(
- text = stringResource(id = R.string.input_deadline),
- fontFamily = greenstashFont
+ text = stringResource(id = R.string.input_deadline), fontFamily = greenstashFont
)
},
leadingIcon = {
@@ -891,10 +933,8 @@ private fun InputTextFields(
Spacer(modifier = Modifier.height(textFieldSpacing))
OutlinedTextField(
- value = viewModel.state.additionalNotes,
- onValueChange = { newText ->
- viewModel.state = viewModel.state.copy(additionalNotes = newText)
- },
+ value = additionalNotes,
+ onValueChange = { newText -> onNotesChange(newText) },
modifier = Modifier.fillMaxWidth(0.86f),
label = {
Text(
@@ -922,17 +962,13 @@ private fun InputTextFields(
@Composable
private fun GoalAddedOREditedAnimation(editGoalId: String?) {
Column(
- modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally
+ modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally
) {
val compositionResult: LottieCompositionResult = rememberLottieComposition(
spec = LottieCompositionSpec.RawRes(R.raw.goal_saved_lottie)
)
val progressAnimation by animateLottieCompositionAsState(
- compositionResult.value,
- isPlaying = true,
- iterations = 1,
- speed = 1.4f
+ compositionResult.value, isPlaying = true, iterations = 1, speed = 1.4f
)
Spacer(modifier = Modifier.weight(1f))
diff --git a/build.gradle b/build.gradle
index fe9bca8b..d9d0d210 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
buildscript {
ext {
kotlin_version = '1.9.23'
- gradle_version = '8.3.2'
+ gradle_version = '8.4.0'
hilt_version = '2.50'
room_version = '2.6.1'
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 13f5f913..f62dd583 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Mon Jan 23 11:16:57 IST 2023
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME