update add ing profile menu
This commit is contained in:
parent
bc63ed83bb
commit
3db5b052a7
@ -14,7 +14,7 @@ import com.example.stepdrink.ui.navigation.NavGraph
|
|||||||
import com.example.stepdrink.ui.theme.StepDrinkTheme
|
import com.example.stepdrink.ui.theme.StepDrinkTheme
|
||||||
import com.example.stepdrink.viewmodel.StepViewModel
|
import com.example.stepdrink.viewmodel.StepViewModel
|
||||||
import com.example.stepdrink.viewmodel.WaterViewModel
|
import com.example.stepdrink.viewmodel.WaterViewModel
|
||||||
|
import com.example.stepdrink.viewmodel.ProfileViewModel
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -28,11 +28,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val stepViewModel: StepViewModel = viewModel()
|
val stepViewModel: StepViewModel = viewModel()
|
||||||
val waterViewModel: WaterViewModel = viewModel()
|
val waterViewModel: WaterViewModel = viewModel()
|
||||||
|
val profileViewModel: ProfileViewModel = viewModel()
|
||||||
|
|
||||||
NavGraph(
|
NavGraph(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
stepViewModel = stepViewModel,
|
stepViewModel = stepViewModel,
|
||||||
waterViewModel = waterViewModel
|
waterViewModel = waterViewModel,
|
||||||
|
profileViewModel = profileViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
package com.example.stepdrink.data.local
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
|
|
||||||
|
class PreferencesManager(private val context: Context) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val USER_NAME = stringPreferencesKey("user_name")
|
||||||
|
val STEP_GOAL = intPreferencesKey("step_goal")
|
||||||
|
val WATER_GOAL = intPreferencesKey("water_goal")
|
||||||
|
}
|
||||||
|
|
||||||
|
val userName: Flow<String> = context.dataStore.data.map { preferences ->
|
||||||
|
preferences[USER_NAME] ?: "Pengguna"
|
||||||
|
}
|
||||||
|
|
||||||
|
val stepGoal: Flow<Int> = context.dataStore.data.map { preferences ->
|
||||||
|
preferences[STEP_GOAL] ?: 10000
|
||||||
|
}
|
||||||
|
|
||||||
|
val waterGoal: Flow<Int> = context.dataStore.data.map { preferences ->
|
||||||
|
preferences[WATER_GOAL] ?: 2000
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveUserName(name: String) {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[USER_NAME] = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveStepGoal(goal: Int) {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[STEP_GOAL] = goal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveWaterGoal(goal: Int) {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[WATER_GOAL] = goal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,14 +7,17 @@ import androidx.navigation.compose.composable
|
|||||||
import com.example.stepdrink.ui.screen.home.HomeScreen
|
import com.example.stepdrink.ui.screen.home.HomeScreen
|
||||||
import com.example.stepdrink.ui.screen.steps.StepsScreen
|
import com.example.stepdrink.ui.screen.steps.StepsScreen
|
||||||
import com.example.stepdrink.ui.screen.water.WaterScreen
|
import com.example.stepdrink.ui.screen.water.WaterScreen
|
||||||
|
import com.example.stepdrink.ui.screen.profile.ProfileScreen
|
||||||
import com.example.stepdrink.viewmodel.StepViewModel
|
import com.example.stepdrink.viewmodel.StepViewModel
|
||||||
import com.example.stepdrink.viewmodel.WaterViewModel
|
import com.example.stepdrink.viewmodel.WaterViewModel
|
||||||
|
import com.example.stepdrink.viewmodel.ProfileViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavGraph(
|
fun NavGraph(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
stepViewModel: StepViewModel,
|
stepViewModel: StepViewModel,
|
||||||
waterViewModel: WaterViewModel
|
waterViewModel: WaterViewModel,
|
||||||
|
profileViewModel: ProfileViewModel
|
||||||
) {
|
) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
@ -24,7 +27,8 @@ fun NavGraph(
|
|||||||
HomeScreen(
|
HomeScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
stepViewModel = stepViewModel,
|
stepViewModel = stepViewModel,
|
||||||
waterViewModel = waterViewModel
|
waterViewModel = waterViewModel,
|
||||||
|
profileViewModel = profileViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,5 +45,11 @@ fun NavGraph(
|
|||||||
viewModel = waterViewModel
|
viewModel = waterViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
composable(Screen.Profile.route) {
|
||||||
|
ProfileScreen(
|
||||||
|
navController = navController,
|
||||||
|
viewModel = profileViewModel )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,4 +4,5 @@ sealed class Screen(val route: String) {
|
|||||||
object Home : Screen("home")
|
object Home : Screen("home")
|
||||||
object Steps : Screen("steps")
|
object Steps : Screen("steps")
|
||||||
object Water : Screen("water")
|
object Water : Screen("water")
|
||||||
|
object Profile : Screen("profile")
|
||||||
}
|
}
|
||||||
@ -3,6 +3,7 @@ package com.example.stepdrink.ui.screen.home
|
|||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.DirectionsWalk
|
import androidx.compose.material.icons.filled.DirectionsWalk
|
||||||
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material.icons.filled.WaterDrop
|
import androidx.compose.material.icons.filled.WaterDrop
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
@ -14,18 +15,22 @@ import com.example.stepdrink.ui.components.StatCard
|
|||||||
import com.example.stepdrink.ui.navigation.Screen
|
import com.example.stepdrink.ui.navigation.Screen
|
||||||
import com.example.stepdrink.viewmodel.StepViewModel
|
import com.example.stepdrink.viewmodel.StepViewModel
|
||||||
import com.example.stepdrink.viewmodel.WaterViewModel
|
import com.example.stepdrink.viewmodel.WaterViewModel
|
||||||
|
import com.example.stepdrink.viewmodel.ProfileViewModel
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
stepViewModel: StepViewModel,
|
stepViewModel: StepViewModel,
|
||||||
waterViewModel: WaterViewModel
|
waterViewModel: WaterViewModel,
|
||||||
|
profileViewModel: ProfileViewModel
|
||||||
) {
|
) {
|
||||||
val todaySteps by stepViewModel.todaySteps.collectAsState()
|
val todaySteps by stepViewModel.todaySteps.collectAsState()
|
||||||
val stepGoal by stepViewModel.dailyGoal.collectAsState()
|
val stepGoal by stepViewModel.dailyGoal.collectAsState()
|
||||||
val todayWater by waterViewModel.todayTotalWater.collectAsState()
|
val todayWater by waterViewModel.todayTotalWater.collectAsState()
|
||||||
val waterGoal by waterViewModel.dailyGoal.collectAsState()
|
val waterGoal by waterViewModel.dailyGoal.collectAsState()
|
||||||
|
val userName by profileViewModel.userName.collectAsState()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@ -35,6 +40,11 @@ fun HomeScreen(
|
|||||||
text = "Step & Drink",
|
text = "Step & Drink",
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
IconButton(onClick = { navController.navigate(Screen.Profile.route) }) {
|
||||||
|
Icon(Icons.Default.Person, "Profil")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
@ -50,6 +60,11 @@ fun HomeScreen(
|
|||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Halo, $userName! 👋",
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Aktivitas Hari Ini",
|
text = "Aktivitas Hari Ini",
|
||||||
style = MaterialTheme.typography.headlineSmall,
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
|||||||
317
app/src/main/java/ui/screen/profilescreen/ProfileScreen.kt
Normal file
317
app/src/main/java/ui/screen/profilescreen/ProfileScreen.kt
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
package com.example.stepdrink.ui.screen.profile
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.material3.*
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.example.stepdrink.viewmodel.ProfileViewModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ProfileScreen(
|
||||||
|
navController: NavController,
|
||||||
|
viewModel: ProfileViewModel
|
||||||
|
) {
|
||||||
|
val userName by viewModel.userName.collectAsState()
|
||||||
|
val stepGoal by viewModel.stepGoal.collectAsState()
|
||||||
|
val waterGoal by viewModel.waterGoal.collectAsState()
|
||||||
|
|
||||||
|
var showNameDialog by remember { mutableStateOf(false) }
|
||||||
|
var showStepGoalDialog by remember { mutableStateOf(false) }
|
||||||
|
var showWaterGoalDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text("Profil") },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = { navController.popBackStack() }) {
|
||||||
|
Icon(Icons.Default.ArrowBack, "Kembali")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(paddingValues)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
// Profile Header
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(120.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(MaterialTheme.colorScheme.primaryContainer),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Person,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(60.dp),
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = userName,
|
||||||
|
style = MaterialTheme.typography.headlineSmall,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
// Settings Section
|
||||||
|
Text(
|
||||||
|
text = "Pengaturan",
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
// Name Setting
|
||||||
|
SettingItem(
|
||||||
|
icon = Icons.Default.Person,
|
||||||
|
title = "Nama",
|
||||||
|
value = userName,
|
||||||
|
onClick = { showNameDialog = true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Step Goal Setting
|
||||||
|
SettingItem(
|
||||||
|
icon = Icons.Default.DirectionsWalk,
|
||||||
|
title = "Target Langkah Harian",
|
||||||
|
value = "$stepGoal langkah",
|
||||||
|
onClick = { showStepGoalDialog = true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Water Goal Setting
|
||||||
|
SettingItem(
|
||||||
|
icon = Icons.Default.WaterDrop,
|
||||||
|
title = "Target Air Minum Harian",
|
||||||
|
value = "${waterGoal}ml",
|
||||||
|
onClick = { showWaterGoalDialog = true }
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// Info Card
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Info,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.secondary
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Tentang Aplikasi",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "Step & Drink v1.0\nAplikasi untuk tracking langkah harian dan kebutuhan air minum.",
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialogs
|
||||||
|
if (showNameDialog) {
|
||||||
|
EditTextDialog(
|
||||||
|
title = "Edit Nama",
|
||||||
|
label = "Nama",
|
||||||
|
currentValue = userName,
|
||||||
|
onDismiss = { showNameDialog = false },
|
||||||
|
onConfirm = { newName ->
|
||||||
|
viewModel.updateUserName(newName)
|
||||||
|
showNameDialog = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showStepGoalDialog) {
|
||||||
|
EditNumberDialog(
|
||||||
|
title = "Edit Target Langkah",
|
||||||
|
label = "Target (langkah)",
|
||||||
|
currentValue = stepGoal,
|
||||||
|
onDismiss = { showStepGoalDialog = false },
|
||||||
|
onConfirm = { newGoal ->
|
||||||
|
viewModel.updateStepGoal(newGoal)
|
||||||
|
showStepGoalDialog = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showWaterGoalDialog) {
|
||||||
|
EditNumberDialog(
|
||||||
|
title = "Edit Target Air Minum",
|
||||||
|
label = "Target (ml)",
|
||||||
|
currentValue = waterGoal,
|
||||||
|
onDismiss = { showWaterGoalDialog = false },
|
||||||
|
onConfirm = { newGoal ->
|
||||||
|
viewModel.updateWaterGoal(newGoal)
|
||||||
|
showWaterGoalDialog = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingItem(
|
||||||
|
icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||||
|
title: String,
|
||||||
|
value: String,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = value,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Edit,
|
||||||
|
contentDescription = "Edit",
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EditTextDialog(
|
||||||
|
title: String,
|
||||||
|
label: String,
|
||||||
|
currentValue: String,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onConfirm: (String) -> Unit
|
||||||
|
) {
|
||||||
|
var text by remember { mutableStateOf(currentValue) }
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = { Text(title) },
|
||||||
|
text = {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = text,
|
||||||
|
onValueChange = { text = it },
|
||||||
|
label = { Text(label) },
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
if (text.isNotBlank()) {
|
||||||
|
onConfirm(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text("Simpan")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text("Batal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EditNumberDialog(
|
||||||
|
title: String,
|
||||||
|
label: String,
|
||||||
|
currentValue: Int,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onConfirm: (Int) -> Unit
|
||||||
|
) {
|
||||||
|
var number by remember { mutableStateOf(currentValue.toString()) }
|
||||||
|
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
title = { Text(title) },
|
||||||
|
text = {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = number,
|
||||||
|
onValueChange = { number = it.filter { char -> char.isDigit() } },
|
||||||
|
label = { Text(label) },
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
val value = number.toIntOrNull()
|
||||||
|
if (value != null && value > 0) {
|
||||||
|
onConfirm(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text("Simpan")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text("Batal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
42
app/src/main/java/viewmodel/ProfileViewModel.kt
Normal file
42
app/src/main/java/viewmodel/ProfileViewModel.kt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package com.example.stepdrink.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.stepdrink.data.local.PreferencesManager
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class ProfileViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
|
private val preferencesManager = PreferencesManager(application)
|
||||||
|
|
||||||
|
val userName: StateFlow<String> = preferencesManager.userName
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "Pengguna")
|
||||||
|
|
||||||
|
val stepGoal: StateFlow<Int> = preferencesManager.stepGoal
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 10000)
|
||||||
|
|
||||||
|
val waterGoal: StateFlow<Int> = preferencesManager.waterGoal
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 2000)
|
||||||
|
|
||||||
|
fun updateUserName(name: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
preferencesManager.saveUserName(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateStepGoal(goal: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
preferencesManager.saveStepGoal(goal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateWaterGoal(goal: Int) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
preferencesManager.saveWaterGoal(goal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,7 @@ import com.example.stepdrink.data.local.database.AppDatabase
|
|||||||
import com.example.stepdrink.data.local.entity.StepRecord
|
import com.example.stepdrink.data.local.entity.StepRecord
|
||||||
import com.example.stepdrink.data.repository.StepRepository
|
import com.example.stepdrink.data.repository.StepRepository
|
||||||
import com.example.stepdrink.sensor.StepCounterManager
|
import com.example.stepdrink.sensor.StepCounterManager
|
||||||
|
import com.example.stepdrink.data.local.PreferencesManager
|
||||||
import com.example.stepdrink.util.DateUtils
|
import com.example.stepdrink.util.DateUtils
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -18,8 +19,10 @@ class StepViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
)
|
)
|
||||||
val stepCounterManager: StepCounterManager = StepCounterManager(application)
|
val stepCounterManager: StepCounterManager = StepCounterManager(application)
|
||||||
|
|
||||||
private val _dailyGoal = MutableStateFlow(10000)
|
private val preferencesManager = PreferencesManager(application)
|
||||||
val dailyGoal: StateFlow<Int> = _dailyGoal.asStateFlow()
|
|
||||||
|
val dailyGoal: StateFlow<Int> = preferencesManager.stepGoal
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 10000)
|
||||||
|
|
||||||
val todaySteps: StateFlow<StepRecord?> = repository.getStepsByDate(DateUtils.getCurrentDate())
|
val todaySteps: StateFlow<StepRecord?> = repository.getStepsByDate(DateUtils.getCurrentDate())
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
|
||||||
@ -44,15 +47,6 @@ class StepViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDailyGoal(goal: Int) {
|
|
||||||
_dailyGoal.value = goal
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getProgressPercentage(): Float {
|
|
||||||
val today = todaySteps.value?.steps ?: 0
|
|
||||||
return (today.toFloat() / _dailyGoal.value.toFloat()).coerceIn(0f, 1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
stepCounterManager.stopTracking()
|
stepCounterManager.stopTracking()
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import android.app.Application
|
|||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.example.stepdrink.data.local.database.AppDatabase
|
import com.example.stepdrink.data.local.database.AppDatabase
|
||||||
|
import com.example.stepdrink.data.local.PreferencesManager
|
||||||
import com.example.stepdrink.data.local.entity.WaterRecord
|
import com.example.stepdrink.data.local.entity.WaterRecord
|
||||||
import com.example.stepdrink.data.repository.WaterRepository
|
import com.example.stepdrink.data.repository.WaterRepository
|
||||||
import com.example.stepdrink.util.DateUtils
|
import com.example.stepdrink.util.DateUtils
|
||||||
@ -16,8 +17,10 @@ class WaterViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
AppDatabase.getDatabase(application).waterDao()
|
AppDatabase.getDatabase(application).waterDao()
|
||||||
)
|
)
|
||||||
|
|
||||||
private val _dailyGoal = MutableStateFlow(2000) // 2000ml = 2 liter
|
private val preferencesManager = PreferencesManager(application)
|
||||||
val dailyGoal: StateFlow<Int> = _dailyGoal.asStateFlow()
|
|
||||||
|
val dailyGoal: StateFlow<Int> = preferencesManager.waterGoal
|
||||||
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 2000)
|
||||||
|
|
||||||
val todayWaterRecords: StateFlow<List<WaterRecord>> =
|
val todayWaterRecords: StateFlow<List<WaterRecord>> =
|
||||||
repository.getWaterRecordsByDate(DateUtils.getCurrentDate())
|
repository.getWaterRecordsByDate(DateUtils.getCurrentDate())
|
||||||
@ -43,12 +46,4 @@ class WaterViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
repository.deleteWaterRecord(record)
|
repository.deleteWaterRecord(record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDailyGoal(goal: Int) {
|
|
||||||
_dailyGoal.value = goal
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getProgressPercentage(): Float {
|
|
||||||
return (todayTotalWater.value.toFloat() / _dailyGoal.value.toFloat()).coerceIn(0f, 1f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user