Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import br.com.sailboat.todozy.feature.settings.impl.domain.usecase.SetAlarmVibra
import br.com.sailboat.todozy.feature.settings.impl.domain.usecase.SetAlarmVibrateSettingUseCaseImpl
import br.com.sailboat.todozy.feature.settings.impl.presentation.navigator.SettingsNavigatorImpl
import br.com.sailboat.todozy.feature.settings.impl.presentation.viewmodel.SettingsViewModel
import br.com.sailboat.todozy.utility.android.sqlite.DatabaseJsonBackupService
import br.com.sailboat.todozy.utility.kotlin.LogService
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.module.Module
import org.koin.dsl.module
Expand All @@ -29,6 +31,8 @@ private val presentation =
setAlarmSoundSettingUseCase = get(),
getAlarmVibrateSettingUseCase = get(),
setAlarmVibrateSettingUseCase = get(),
databaseJsonBackupService = get<DatabaseJsonBackupService>(),
logService = get<LogService>(),
)
}
factory<SettingsNavigator> { SettingsNavigatorImpl() }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package br.com.sailboat.todozy.feature.settings.impl.presentation

import android.app.Activity
import android.media.RingtoneManager
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import br.com.sailboat.todozy.feature.navigation.android.AboutNavigator
import br.com.sailboat.todozy.feature.settings.impl.databinding.FrgSettingsBinding
Expand All @@ -22,6 +27,18 @@ internal class SettingsFragment : Fragment() {

private lateinit var binding: FrgSettingsBinding

private val exportDatabaseLauncher =
registerForActivityResult(ActivityResultContracts.CreateDocument("application/json")) { uri ->
uri?.run {
viewModel.dispatchViewIntent(SettingsViewIntent.OnSelectExportDatabaseUri(uri))
}
}

private val importDatabaseLauncher =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
uri?.run { showConfirmImportDatabaseDialog(uri) }
}

private val pickRingtoneLauncher =
registerForActivityResult(PickRingtoneContract()) { uri ->
uri?.run {
Expand Down Expand Up @@ -53,6 +70,7 @@ internal class SettingsFragment : Fragment() {
initAbout()
initCheckBoxVibrate()
initToneVibrate()
initBackupViews()
}

private fun observeViewModel() {
Expand All @@ -75,6 +93,16 @@ internal class SettingsFragment : Fragment() {
when (action) {
is SettingsViewAction.NavigateToAlarmToneSelector -> navigateToAlarmToneSelector(action)
is SettingsViewAction.NavigateToAbout -> navigateToAbout()
is SettingsViewAction.CreateDatabaseBackupDocument -> exportDatabaseLauncher.launch(action.fileName)
is SettingsViewAction.OpenDatabaseBackupDocument -> importDatabaseLauncher.launch(
arrayOf(
"application/json",
"text/*",
),
)
is SettingsViewAction.ShowDatabaseExportSuccess -> showDatabaseExportSuccess()
is SettingsViewAction.ShowDatabaseImportSuccess -> showDatabaseImportSuccess()
is SettingsViewAction.ShowDatabaseBackupError -> showDatabaseBackupError()
}
}
}
Expand Down Expand Up @@ -112,4 +140,37 @@ internal class SettingsFragment : Fragment() {
private fun initToneVibrate() = with(binding) {
flSettingsVibrate.setSafeClickListener { cbSettingsVibrate.performClick() }
}

private fun initBackupViews() = with(binding) {
tvSettingsExportDatabase.setSafeClickListener {
viewModel.dispatchViewIntent(SettingsViewIntent.OnClickExportDatabase)
}
tvSettingsImportDatabase.setSafeClickListener {
viewModel.dispatchViewIntent(SettingsViewIntent.OnClickImportDatabase)
}
}

private fun showConfirmImportDatabaseDialog(uri: Uri) {
AlertDialog.Builder(requireContext())
.setTitle(UiR.string.are_you_sure)
.setMessage(getString(UiR.string.msg_confirm_database_import))
.setPositiveButton(UiR.string.import_action) { _, _ ->
viewModel.dispatchViewIntent(SettingsViewIntent.OnConfirmImportDatabase(uri))
}
.setNegativeButton(android.R.string.cancel, null)
.show()
}

private fun showDatabaseExportSuccess() {
Toast.makeText(activity, UiR.string.msg_database_exported, Toast.LENGTH_SHORT).show()
}

private fun showDatabaseImportSuccess() {
activity?.setResult(Activity.RESULT_OK)
Toast.makeText(activity, UiR.string.msg_database_imported, Toast.LENGTH_SHORT).show()
}

private fun showDatabaseBackupError() {
Toast.makeText(activity, UiR.string.msg_error, Toast.LENGTH_SHORT).show()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@ import android.net.Uri
internal sealed class SettingsViewAction {
object NavigateToAbout : SettingsViewAction()
data class NavigateToAlarmToneSelector(val uri: Uri?) : SettingsViewAction()
data class CreateDatabaseBackupDocument(val fileName: String) : SettingsViewAction()
object OpenDatabaseBackupDocument : SettingsViewAction()
object ShowDatabaseExportSuccess : SettingsViewAction()
object ShowDatabaseImportSuccess : SettingsViewAction()
object ShowDatabaseBackupError : SettingsViewAction()
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ internal sealed class SettingsViewIntent {
object OnStart : SettingsViewIntent()
object OnClickMenuAlarmTone : SettingsViewIntent()
object OnClickAbout : SettingsViewIntent()
object OnClickExportDatabase : SettingsViewIntent()
object OnClickImportDatabase : SettingsViewIntent()
data class OnClickVibrateAlarm(val vibrate: Boolean) : SettingsViewIntent()
data class OnSelectAlarmTone(val uri: Uri) : SettingsViewIntent()
data class OnSelectExportDatabaseUri(val uri: Uri) : SettingsViewIntent()
data class OnConfirmImportDatabase(val uri: Uri) : SettingsViewIntent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,35 @@ import br.com.sailboat.todozy.feature.settings.android.domain.usecase.GetAlarmSo
import br.com.sailboat.todozy.feature.settings.domain.usecase.GetAlarmVibrateSettingUseCase
import br.com.sailboat.todozy.feature.settings.impl.domain.usecase.SetAlarmSoundSettingUseCase
import br.com.sailboat.todozy.feature.settings.impl.domain.usecase.SetAlarmVibrateSettingUseCase
import br.com.sailboat.todozy.utility.android.sqlite.DatabaseJsonBackupService
import br.com.sailboat.todozy.utility.android.viewmodel.BaseViewModel
import br.com.sailboat.todozy.utility.kotlin.LogService
import kotlinx.coroutines.launch
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Locale

internal class SettingsViewModel(
override val viewState: SettingsViewState = SettingsViewState(),
private val getAlarmSoundSettingUseCase: GetAlarmSoundSettingUseCase,
private val setAlarmSoundSettingUseCase: SetAlarmSoundSettingUseCase,
private val getAlarmVibrateSettingUseCase: GetAlarmVibrateSettingUseCase,
private val setAlarmVibrateSettingUseCase: SetAlarmVibrateSettingUseCase,
private val databaseJsonBackupService: DatabaseJsonBackupService,
private val logService: LogService,
) : BaseViewModel<SettingsViewState, SettingsViewIntent>() {
override fun dispatchViewIntent(viewIntent: SettingsViewIntent) {
when (viewIntent) {
is SettingsViewIntent.OnStart -> onStart()
is SettingsViewIntent.OnClickMenuAlarmTone -> onClickMenuAlarmTone()
is SettingsViewIntent.OnClickAbout -> onClickAbout()
is SettingsViewIntent.OnClickExportDatabase -> onClickExportDatabase()
is SettingsViewIntent.OnClickImportDatabase -> onClickImportDatabase()
is SettingsViewIntent.OnClickVibrateAlarm -> onClickVibrateAlarm(viewIntent)
is SettingsViewIntent.OnSelectAlarmTone -> onSelectAlarmTone(viewIntent)
is SettingsViewIntent.OnSelectExportDatabaseUri -> onSelectExportDatabaseUri(viewIntent)
is SettingsViewIntent.OnConfirmImportDatabase -> onConfirmImportDatabase(viewIntent)
}
}

Expand All @@ -39,6 +51,21 @@ internal class SettingsViewModel(
viewState.viewAction.value = SettingsViewAction.NavigateToAbout
}

private fun onClickExportDatabase() {
val timestamp =
LocalDateTime
.now(ZoneId.systemDefault())
.format(BACKUP_FILE_NAME_FORMATTER)
viewState.viewAction.value =
SettingsViewAction.CreateDatabaseBackupDocument(
"todozy-backup_$timestamp.json",
)
}

private fun onClickImportDatabase() {
viewState.viewAction.value = SettingsViewAction.OpenDatabaseBackupDocument
}

private fun onClickVibrateAlarm(viewIntent: SettingsViewIntent.OnClickVibrateAlarm) {
viewModelScope.launch {
viewState.vibrate.value = viewIntent.vibrate
Expand All @@ -52,4 +79,36 @@ internal class SettingsViewModel(
setAlarmSoundSettingUseCase(viewIntent.uri)
}
}

private fun onSelectExportDatabaseUri(viewIntent: SettingsViewIntent.OnSelectExportDatabaseUri) {
viewModelScope.launch {
try {
databaseJsonBackupService.exportToJson(viewIntent.uri)
viewState.viewAction.value = SettingsViewAction.ShowDatabaseExportSuccess
} catch (e: Exception) {
logService.error(e)
viewState.viewAction.value = SettingsViewAction.ShowDatabaseBackupError
}
}
}

private fun onConfirmImportDatabase(viewIntent: SettingsViewIntent.OnConfirmImportDatabase) {
viewModelScope.launch {
try {
databaseJsonBackupService.importFromJson(viewIntent.uri)
viewState.viewAction.value = SettingsViewAction.ShowDatabaseImportSuccess
} catch (e: Exception) {
logService.error(e)
viewState.viewAction.value = SettingsViewAction.ShowDatabaseBackupError
}
}
}

private companion object {
private val BACKUP_FILE_NAME_FORMATTER =
DateTimeFormatter.ofPattern(
"yyyyMMdd_HHmmss",
Locale.US,
)
}
}
36 changes: 35 additions & 1 deletion feature/settings/impl/src/main/res/layout/frg_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,40 @@

<include layout="@layout/divider" />

<TextView
style="@style/TextViewLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/spacing_medium"
android:paddingTop="@dimen/spacing_medium"
android:text="@string/backup" />

<TextView
android:id="@+id/tvSettingsExportDatabase"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingStart="@dimen/spacing_medium"
android:text="@string/export_database_json"
android:textColor="@color/md_blue_grey_700"
android:textSize="@dimen/text_size_small" />

<include layout="@layout/divider" />

<TextView
android:id="@+id/tvSettingsImportDatabase"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingStart="@dimen/spacing_medium"
android:text="@string/import_database_json"
android:textColor="@color/md_blue_grey_700"
android:textSize="@dimen/text_size_small" />

<include layout="@layout/divider" />

<TextView
android:id="@+id/tvSettingsAbout"
android:layout_width="match_parent"
Expand All @@ -97,4 +131,4 @@

<include layout="@layout/divider" />

</LinearLayout>
</LinearLayout>
Loading