這是一個新的頁面,所以和之前一樣,從 use case 開始建立
@Singleton
class UserPreferencesRepository @Inject constructor(
@ApplicationContext private val context: Context
) {
private object Keys {
val isSoundEnabled = booleanPreferencesKey("is_sound_enabled")
val isVibrationEnabled = booleanPreferencesKey("is_vibration_enabled")
}
val isSoundEnabled: Flow<Boolean> = context.dataStore.data
.map { preferences ->
preferences[Keys.isSoundEnabled] ?: true // Default to true
}
val isVibrationEnabled: Flow<Boolean> = context.dataStore.data
.map { preferences ->
preferences[Keys.isVibrationEnabled] ?: true // Default to true
}
suspend fun setSoundEnabled(isEnabled: Boolean) {
context.dataStore.edit { preferences ->
preferences[Keys.isSoundEnabled] = isEnabled
}
}
suspend fun setVibrationEnabled(isEnabled: Boolean) {
context.dataStore.edit { preferences ->
preferences[Keys.isVibrationEnabled] = isEnabled
}
}
}
data class SettingsData(
val isSoundEnabled: Boolean,
val isVibrationEnabled: Boolean
)
class GetSettingsUseCase @Inject constructor(
private val repository: UserPreferencesRepository
) {
operator fun invoke(): Flow<SettingsData> {
return combine(
repository.isSoundEnabled,
repository.isVibrationEnabled
) { sound, vibration ->
SettingsData(
isSoundEnabled = sound,
isVibrationEnabled = vibration
)
}
}
}
class UpdateSoundSettingUseCase @Inject constructor(
private val repository: UserPreferencesRepository
) {
suspend operator fun invoke(isEnabled: Boolean) {
repository.setSoundEnabled(isEnabled)
}
}
class UpdateVibrationSettingUseCase @Inject constructor(
private val repository: UserPreferencesRepository
) {
suspend operator fun invoke(isEnabled: Boolean) {
repository.setVibrationEnabled(isEnabled)
}
}
data class SettingsUiState(
val settings: SettingsData = SettingsData(isSoundEnabled = true, isVibrationEnabled = true),
val isLoading: Boolean = true
)
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val getSettingsUseCase: GetSettingsUseCase,
private val updateSoundSettingUseCase: UpdateSoundSettingUseCase,
private val updateVibrationSettingUseCase: UpdateVibrationSettingUseCase
) : ViewModel() {
val uiState: StateFlow<SettingsUiState> = getSettingsUseCase()
.map { SettingsUiState(settings = it, isLoading = false) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = SettingsUiState(isLoading = true)
)
fun onSoundToggled(isEnabled: Boolean) {
viewModelScope.launch {
updateSoundSettingUseCase(isEnabled)
}
}
fun onVibrationToggled(isEnabled: Boolean) {
viewModelScope.launch {
updateVibrationSettingUseCase(isEnabled)
}
}
}
@Composable
fun SettingsScreen(
modifier: Modifier = Modifier,
viewModel: SettingsViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()
SettingsScreenContent(
modifier = modifier,
uiState = uiState,
onSoundToggled = viewModel::onSoundToggled,
onVibrationToggled = viewModel::onVibrationToggled
)
}
@Composable
private fun SettingsScreenContent(
modifier: Modifier = Modifier,
uiState: SettingsUiState,
onSoundToggled: (Boolean) -> Unit,
onVibrationToggled: (Boolean) -> Unit
) {
Box(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
if (uiState.isLoading) {
CircularProgressIndicator()
} else {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "設定",
style = MaterialTheme.typography.headlineLarge,
color = MaterialTheme.colorScheme.primary
)
SettingSwitchItem(
title = "按鈕音效",
isChecked = uiState.settings.isSoundEnabled,
onCheckedChange = onSoundToggled
)
SettingSwitchItem(
title = "按鈕震動",
isChecked = uiState.settings.isVibrationEnabled,
onCheckedChange = onVibrationToggled
)
}
}
}
}
@Composable
private fun SettingSwitchItem(
title: String,
isChecked: Boolean,
onCheckedChange: (Boolean) -> Unit
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(text = title, style = MaterialTheme.typography.bodyLarge)
Switch(checked = isChecked, onCheckedChange = onCheckedChange)
}
}
@Preview(showBackground = true)
@Composable
private fun SettingsScreenPreview() {
AndroidTemplateTheme {
SettingsScreenContent(
uiState = SettingsUiState(isLoading = false),
onSoundToggled = {},
onVibrationToggled = {}
)
}
}