mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 08:45:27 +02:00
Use wrapper for table entities
This commit is contained in:
parent
f7fe300706
commit
2d78ab05bd
@ -0,0 +1,4 @@
|
|||||||
|
-keepclassmembers class ** {
|
||||||
|
@com.squareup.moshi.FromJson *;
|
||||||
|
@com.squareup.moshi.ToJson *;
|
||||||
|
}
|
@ -52,6 +52,7 @@ import com.sadellie.unitto.data.userprefs.getUnitConverterFormatTime
|
|||||||
import com.sadellie.unitto.data.userprefs.getUnitConverterSorting
|
import com.sadellie.unitto.data.userprefs.getUnitConverterSorting
|
||||||
import com.squareup.moshi.JsonAdapter
|
import com.squareup.moshi.JsonAdapter
|
||||||
import com.squareup.moshi.Moshi
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.squareup.moshi.adapter
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@ -61,6 +62,7 @@ import java.io.File
|
|||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
class BackupManager @Inject constructor(
|
class BackupManager @Inject constructor(
|
||||||
@ApplicationContext private val mContext: Context,
|
@ApplicationContext private val mContext: Context,
|
||||||
private val dataStore: DataStore<Preferences>,
|
private val dataStore: DataStore<Preferences>,
|
||||||
@ -69,8 +71,10 @@ class BackupManager @Inject constructor(
|
|||||||
// Not planned at the moment
|
// Not planned at the moment
|
||||||
// private val calculatorHistoryDao: CalculatorHistoryDao,
|
// private val calculatorHistoryDao: CalculatorHistoryDao,
|
||||||
) {
|
) {
|
||||||
private val moshi: Moshi = Moshi.Builder().build()
|
private val moshi: Moshi = Moshi.Builder()
|
||||||
private val jsonAdapter: JsonAdapter<UserData> = moshi.adapter(UserData::class.java)
|
.add(UserDataTableAdapter())
|
||||||
|
.build()
|
||||||
|
private val jsonAdapter: JsonAdapter<UserData> = moshi.adapter<UserData>()
|
||||||
private val auth = "com.sadellie.unitto.BackupManager"
|
private val auth = "com.sadellie.unitto.BackupManager"
|
||||||
|
|
||||||
suspend fun backup(): Uri = withContext(Dispatchers.IO) {
|
suspend fun backup(): Uri = withContext(Dispatchers.IO) {
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.data.backup
|
||||||
|
|
||||||
|
import com.sadellie.unitto.data.database.TimeZoneEntity
|
||||||
|
import com.sadellie.unitto.data.database.UnitsEntity
|
||||||
|
import com.squareup.moshi.FromJson
|
||||||
|
import com.squareup.moshi.ToJson
|
||||||
|
|
||||||
|
// Have to use this wrapper since entity classes are in database module
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
internal class UserDataTableAdapter {
|
||||||
|
@ToJson
|
||||||
|
fun toJson(unitsEntity: UnitsEntity): UserDataUnit =
|
||||||
|
UserDataUnit(
|
||||||
|
unitId = unitsEntity.unitId,
|
||||||
|
isFavorite = unitsEntity.isFavorite,
|
||||||
|
pairedUnitId = unitsEntity.pairedUnitId,
|
||||||
|
frequency = unitsEntity.frequency
|
||||||
|
)
|
||||||
|
|
||||||
|
@FromJson
|
||||||
|
fun fromJson(userDataUnit: UserDataUnit): UnitsEntity =
|
||||||
|
UnitsEntity(
|
||||||
|
unitId = userDataUnit.unitId,
|
||||||
|
isFavorite = userDataUnit.isFavorite,
|
||||||
|
pairedUnitId = userDataUnit.pairedUnitId,
|
||||||
|
frequency = userDataUnit.frequency
|
||||||
|
)
|
||||||
|
|
||||||
|
@ToJson
|
||||||
|
fun toJson(timeZoneEntity: TimeZoneEntity): UserDataTimezone =
|
||||||
|
UserDataTimezone(
|
||||||
|
id = timeZoneEntity.id,
|
||||||
|
position = timeZoneEntity.position,
|
||||||
|
label = timeZoneEntity.label
|
||||||
|
)
|
||||||
|
|
||||||
|
@FromJson
|
||||||
|
fun fromJson(userDataTimezone: UserDataTimezone): TimeZoneEntity =
|
||||||
|
TimeZoneEntity(
|
||||||
|
id = userDataTimezone.id,
|
||||||
|
position = userDataTimezone.position,
|
||||||
|
label = userDataTimezone.label
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.data.backup
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class UserDataTimezone(
|
||||||
|
val id: String,
|
||||||
|
val position: Int,
|
||||||
|
val label: String,
|
||||||
|
)
|
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Unitto is a unit converter for Android
|
||||||
|
* Copyright (c) 2023 Elshan Agaev
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sadellie.unitto.data.backup
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class UserDataUnit(
|
||||||
|
val unitId: String,
|
||||||
|
val isFavorite: Boolean,
|
||||||
|
val pairedUnitId: String?,
|
||||||
|
val frequency: Int,
|
||||||
|
)
|
@ -26,7 +26,6 @@ import androidx.activity.compose.BackHandler
|
|||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.Crossfade
|
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
@ -110,14 +109,11 @@ internal fun SettingsRoute(
|
|||||||
if (showErrorToast) Toast.makeText(mContext, errorLabel, Toast.LENGTH_SHORT).show()
|
if (showErrorToast) Toast.makeText(mContext, errorLabel, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
Crossfade(targetState = uiState) { state ->
|
when (uiState) {
|
||||||
when (state) {
|
|
||||||
SettingsUIState.Loading -> UnittoEmptyScreen()
|
SettingsUIState.Loading -> UnittoEmptyScreen()
|
||||||
|
|
||||||
SettingsUIState.BackupInProgress -> BackingUpScreen()
|
|
||||||
|
|
||||||
is SettingsUIState.Ready -> SettingsScreen(
|
is SettingsUIState.Ready -> SettingsScreen(
|
||||||
uiState = state,
|
uiState = uiState,
|
||||||
navigateUp = navigateUp,
|
navigateUp = navigateUp,
|
||||||
navControllerAction = navControllerAction,
|
navControllerAction = navControllerAction,
|
||||||
updateVibrations = viewModel::updateVibrations,
|
updateVibrations = viewModel::updateVibrations,
|
||||||
@ -127,23 +123,6 @@ internal fun SettingsRoute(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun BackingUpScreen() {
|
|
||||||
Scaffold { padding ->
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(padding)
|
|
||||||
.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.Center,
|
|
||||||
) {
|
|
||||||
CircularProgressIndicator()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BackHandler {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SettingsScreen(
|
private fun SettingsScreen(
|
||||||
@ -163,6 +142,8 @@ private fun SettingsScreen(
|
|||||||
if (pickedUri != null) restore(pickedUri)
|
if (pickedUri != null) restore(pickedUri)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BackHandler(uiState.backupInProgress) {}
|
||||||
|
|
||||||
UnittoScreenWithLargeTopBar(
|
UnittoScreenWithLargeTopBar(
|
||||||
title = stringResource(R.string.settings_title),
|
title = stringResource(R.string.settings_title),
|
||||||
navigationIcon = { NavigateUpButton(navigateUp) },
|
navigationIcon = { NavigateUpButton(navigateUp) },
|
||||||
@ -176,11 +157,11 @@ private fun SettingsScreen(
|
|||||||
onDismissRequest = { showMenu = false }
|
onDismissRequest = { showMenu = false }
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
onClick = backup,
|
onClick = { showMenu = false; backup() },
|
||||||
text = { Text("Backup") }
|
text = { Text("Backup") }
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
onClick = { launcher.launch(arrayOf(backupMimeType)) },
|
onClick = { showMenu = false; launcher.launch(arrayOf(backupMimeType)) },
|
||||||
text = { Text("Restore") }
|
text = { Text("Restore") }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -266,13 +247,26 @@ private fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AnimatedVisibility(visible = uiState.backupInProgress) {
|
||||||
|
Scaffold { padding ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(padding)
|
||||||
|
.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Context.share(uri: Uri) {
|
private fun Context.share(uri: Uri) {
|
||||||
val shareIntent = Intent().apply {
|
val shareIntent = Intent().apply {
|
||||||
action = Intent.ACTION_SEND
|
action = Intent.ACTION_SEND
|
||||||
putExtra(Intent.EXTRA_STREAM, uri)
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
type = backupMimeType // This is a fucking war crime, it should be text/json
|
type = backupMimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
startActivity(shareIntent)
|
startActivity(shareIntent)
|
||||||
@ -289,6 +283,7 @@ private fun PreviewSettingsScreen() {
|
|||||||
uiState = SettingsUIState.Ready(
|
uiState = SettingsUIState.Ready(
|
||||||
enableVibrations = false,
|
enableVibrations = false,
|
||||||
cacheSize = 2,
|
cacheSize = 2,
|
||||||
|
backupInProgress = false
|
||||||
),
|
),
|
||||||
navigateUp = {},
|
navigateUp = {},
|
||||||
navControllerAction = {},
|
navControllerAction = {},
|
||||||
@ -301,5 +296,16 @@ private fun PreviewSettingsScreen() {
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun PreviewBackingUpScreen() {
|
private fun PreviewBackingUpScreen() {
|
||||||
BackingUpScreen()
|
SettingsScreen(
|
||||||
|
uiState = SettingsUIState.Ready(
|
||||||
|
enableVibrations = false,
|
||||||
|
cacheSize = 2,
|
||||||
|
backupInProgress = true
|
||||||
|
),
|
||||||
|
navigateUp = {},
|
||||||
|
navControllerAction = {},
|
||||||
|
updateVibrations = {},
|
||||||
|
clearCache = {},
|
||||||
|
backup = {}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,9 @@ package com.sadellie.unitto.feature.settings
|
|||||||
internal sealed class SettingsUIState {
|
internal sealed class SettingsUIState {
|
||||||
data object Loading : SettingsUIState()
|
data object Loading : SettingsUIState()
|
||||||
|
|
||||||
data object BackupInProgress : SettingsUIState()
|
|
||||||
|
|
||||||
data class Ready(
|
data class Ready(
|
||||||
val enableVibrations: Boolean,
|
val enableVibrations: Boolean,
|
||||||
val cacheSize: Int,
|
val cacheSize: Int,
|
||||||
|
val backupInProgress: Boolean,
|
||||||
) : SettingsUIState()
|
) : SettingsUIState()
|
||||||
}
|
}
|
||||||
|
@ -49,19 +49,19 @@ internal class SettingsViewModel @Inject constructor(
|
|||||||
private val _showErrorToast = MutableSharedFlow<Boolean>()
|
private val _showErrorToast = MutableSharedFlow<Boolean>()
|
||||||
val showErrorToast = _showErrorToast.asSharedFlow()
|
val showErrorToast = _showErrorToast.asSharedFlow()
|
||||||
|
|
||||||
private val _operation = MutableStateFlow(false)
|
private val _backupInProgress = MutableStateFlow(false)
|
||||||
private var backupJob: Job? = null
|
private var backupJob: Job? = null
|
||||||
|
|
||||||
val uiState = combine(
|
val uiState = combine(
|
||||||
userPrefsRepository.generalPrefs,
|
userPrefsRepository.generalPrefs,
|
||||||
currencyRatesDao.size(),
|
currencyRatesDao.size(),
|
||||||
_operation,
|
_backupInProgress,
|
||||||
) { prefs, cacheSize, operation ->
|
) { prefs, cacheSize, backupInProgress ->
|
||||||
if (operation) return@combine SettingsUIState.BackupInProgress
|
|
||||||
|
|
||||||
SettingsUIState.Ready(
|
SettingsUIState.Ready(
|
||||||
enableVibrations = prefs.enableVibrations,
|
enableVibrations = prefs.enableVibrations,
|
||||||
cacheSize = cacheSize,
|
cacheSize = cacheSize,
|
||||||
|
backupInProgress = backupInProgress
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.stateIn(viewModelScope, SettingsUIState.Loading)
|
.stateIn(viewModelScope, SettingsUIState.Loading)
|
||||||
@ -69,7 +69,7 @@ internal class SettingsViewModel @Inject constructor(
|
|||||||
fun backup() {
|
fun backup() {
|
||||||
backupJob?.cancel()
|
backupJob?.cancel()
|
||||||
backupJob = viewModelScope.launch(Dispatchers.IO) {
|
backupJob = viewModelScope.launch(Dispatchers.IO) {
|
||||||
_operation.update { true }
|
_backupInProgress.update { true }
|
||||||
try {
|
try {
|
||||||
val backupFileUri = backupManager.backup()
|
val backupFileUri = backupManager.backup()
|
||||||
_backupFileUri.emit(backupFileUri) // Emit to trigger file share intent
|
_backupFileUri.emit(backupFileUri) // Emit to trigger file share intent
|
||||||
@ -78,14 +78,14 @@ internal class SettingsViewModel @Inject constructor(
|
|||||||
_showErrorToast.emit(true)
|
_showErrorToast.emit(true)
|
||||||
Log.e(TAG, "$e")
|
Log.e(TAG, "$e")
|
||||||
}
|
}
|
||||||
_operation.update { false }
|
_backupInProgress.update { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun restore(uri: Uri) {
|
fun restore(uri: Uri) {
|
||||||
backupJob?.cancel()
|
backupJob?.cancel()
|
||||||
backupJob = viewModelScope.launch(Dispatchers.IO) {
|
backupJob = viewModelScope.launch(Dispatchers.IO) {
|
||||||
_operation.update { true }
|
_backupInProgress.update { true }
|
||||||
try {
|
try {
|
||||||
backupManager.restore(uri)
|
backupManager.restore(uri)
|
||||||
_showErrorToast.emit(false)
|
_showErrorToast.emit(false)
|
||||||
@ -93,7 +93,7 @@ internal class SettingsViewModel @Inject constructor(
|
|||||||
_showErrorToast.emit(true)
|
_showErrorToast.emit(true)
|
||||||
Log.e(TAG, "$e")
|
Log.e(TAG, "$e")
|
||||||
}
|
}
|
||||||
_operation.update { false }
|
_backupInProgress.update { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user