Create API token when updating from 24
This commit is contained in:
@@ -4,7 +4,12 @@ import android.app.Application
|
|||||||
import com.google.android.material.color.DynamicColors
|
import com.google.android.material.color.DynamicColors
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
import dagger.hilt.android.HiltAndroidApp
|
||||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||||
|
import gq.kirmanak.mealient.data.migration.MigrationDetector
|
||||||
import gq.kirmanak.mealient.logging.Logger
|
import gq.kirmanak.mealient.logging.Logger
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltAndroidApp
|
@HiltAndroidApp
|
||||||
@@ -16,9 +21,15 @@ class App : Application() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var buildConfiguration: BuildConfiguration
|
lateinit var buildConfiguration: BuildConfiguration
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var migrationDetector: MigrationDetector
|
||||||
|
|
||||||
|
private val appCoroutineScope = CoroutineScope(Dispatchers.Main + Job())
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
logger.v { "onCreate() called" }
|
logger.v { "onCreate() called" }
|
||||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||||
|
appCoroutineScope.launch { migrationDetector.executeMigrations() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package gq.kirmanak.mealient.data.configuration
|
||||||
|
|
||||||
|
import gq.kirmanak.mealient.BuildConfig
|
||||||
|
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class BuildConfigurationImpl @Inject constructor() : BuildConfiguration {
|
||||||
|
|
||||||
|
@get:JvmName("_isDebug")
|
||||||
|
private val isDebug by lazy { BuildConfig.DEBUG }
|
||||||
|
|
||||||
|
private val versionCode by lazy { BuildConfig.VERSION_CODE }
|
||||||
|
|
||||||
|
override fun isDebug(): Boolean = isDebug
|
||||||
|
|
||||||
|
override fun versionCode(): Int = versionCode
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package gq.kirmanak.mealient.data.migration
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import gq.kirmanak.mealient.data.auth.AuthRepo
|
||||||
|
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||||
|
import gq.kirmanak.mealient.datastore.DataStoreModule.Companion.ENCRYPTED
|
||||||
|
import gq.kirmanak.mealient.logging.Logger
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Named
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class From24AuthMigrationExecutor @Inject constructor(
|
||||||
|
@Named(ENCRYPTED) private val sharedPreferences: SharedPreferences,
|
||||||
|
private val authRepo: AuthRepo,
|
||||||
|
private val logger: Logger,
|
||||||
|
) : MigrationExecutor {
|
||||||
|
|
||||||
|
override val migratingFrom: Int = 24
|
||||||
|
|
||||||
|
override suspend fun executeMigration() {
|
||||||
|
logger.v { "executeMigration() was called" }
|
||||||
|
val email = sharedPreferences.getString(EMAIL_KEY, null)
|
||||||
|
val password = sharedPreferences.getString(PASSWORD_KEY, null)
|
||||||
|
if (email != null && password != null) {
|
||||||
|
runCatchingExceptCancel { authRepo.authenticate(email, password) }
|
||||||
|
.onFailure { logger.e(it) { "API token creation failed" } }
|
||||||
|
.onSuccess { logger.i { "Created API token during migration" } }
|
||||||
|
}
|
||||||
|
sharedPreferences.edit {
|
||||||
|
remove(EMAIL_KEY)
|
||||||
|
remove(PASSWORD_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val EMAIL_KEY = "email"
|
||||||
|
private const val PASSWORD_KEY = "password"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package gq.kirmanak.mealient.data.migration
|
||||||
|
|
||||||
|
interface MigrationDetector {
|
||||||
|
|
||||||
|
suspend fun executeMigrations()
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package gq.kirmanak.mealient.data.migration
|
||||||
|
|
||||||
|
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||||
|
import gq.kirmanak.mealient.data.storage.PreferencesStorage
|
||||||
|
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||||
|
import gq.kirmanak.mealient.logging.Logger
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class MigrationDetectorImpl @Inject constructor(
|
||||||
|
private val preferencesStorage: PreferencesStorage,
|
||||||
|
private val migrationExecutors: Set<@JvmSuppressWildcards MigrationExecutor>,
|
||||||
|
private val buildConfiguration: BuildConfiguration,
|
||||||
|
private val logger: Logger,
|
||||||
|
) : MigrationDetector {
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun executeMigrations() {
|
||||||
|
val key = preferencesStorage.lastExecutedMigrationVersionKey
|
||||||
|
|
||||||
|
val lastVersion = preferencesStorage.getValue(key) ?: VERSION_BEFORE_MIGRATION_IMPLEMENTED
|
||||||
|
val currentVersion = buildConfiguration.versionCode()
|
||||||
|
logger.i { "Last migration version is $lastVersion, current is $currentVersion" }
|
||||||
|
|
||||||
|
if (lastVersion != currentVersion) {
|
||||||
|
migrationExecutors
|
||||||
|
.filter { it.migratingFrom <= lastVersion }
|
||||||
|
.forEach {
|
||||||
|
runCatchingExceptCancel { it.executeMigration() }
|
||||||
|
.onFailure { logger.e(it) { "Migration executor failed: $it" } }
|
||||||
|
.onSuccess { logger.i { "Migration executor succeeded: $it" } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preferencesStorage.storeValues(Pair(key, currentVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val VERSION_BEFORE_MIGRATION_IMPLEMENTED = 24
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package gq.kirmanak.mealient.data.migration
|
||||||
|
|
||||||
|
interface MigrationExecutor {
|
||||||
|
|
||||||
|
val migratingFrom: Int
|
||||||
|
|
||||||
|
suspend fun executeMigration()
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ interface PreferencesStorage {
|
|||||||
|
|
||||||
val isDisclaimerAcceptedKey: Preferences.Key<Boolean>
|
val isDisclaimerAcceptedKey: Preferences.Key<Boolean>
|
||||||
|
|
||||||
|
val lastExecutedMigrationVersionKey: Preferences.Key<Int>
|
||||||
|
|
||||||
suspend fun <T> getValue(key: Preferences.Key<T>): T?
|
suspend fun <T> getValue(key: Preferences.Key<T>): T?
|
||||||
|
|
||||||
suspend fun <T> requireValue(key: Preferences.Key<T>): T
|
suspend fun <T> requireValue(key: Preferences.Key<T>): T
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ import androidx.datastore.core.DataStore
|
|||||||
import androidx.datastore.preferences.core.Preferences
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import gq.kirmanak.mealient.logging.Logger
|
import gq.kirmanak.mealient.logging.Logger
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -22,6 +28,9 @@ class PreferencesStorageImpl @Inject constructor(
|
|||||||
|
|
||||||
override val isDisclaimerAcceptedKey = booleanPreferencesKey("isDisclaimedAccepted")
|
override val isDisclaimerAcceptedKey = booleanPreferencesKey("isDisclaimedAccepted")
|
||||||
|
|
||||||
|
override val lastExecutedMigrationVersionKey: Preferences.Key<Int> =
|
||||||
|
intPreferencesKey("lastExecutedMigrationVersion")
|
||||||
|
|
||||||
override suspend fun <T> getValue(key: Preferences.Key<T>): T? {
|
override suspend fun <T> getValue(key: Preferences.Key<T>): T? {
|
||||||
val value = dataStore.data.first()[key]
|
val value = dataStore.data.first()[key]
|
||||||
logger.v { "getValue() returned: $value for $key" }
|
logger.v { "getValue() returned: $value for $key" }
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package gq.kirmanak.mealient.architecture
|
package gq.kirmanak.mealient.di
|
||||||
|
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfigurationImpl
|
import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
26
app/src/main/java/gq/kirmanak/mealient/di/MigrationModule.kt
Normal file
26
app/src/main/java/gq/kirmanak/mealient/di/MigrationModule.kt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package gq.kirmanak.mealient.di
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import dagger.multibindings.IntoSet
|
||||||
|
import gq.kirmanak.mealient.data.migration.From24AuthMigrationExecutor
|
||||||
|
import gq.kirmanak.mealient.data.migration.MigrationDetector
|
||||||
|
import gq.kirmanak.mealient.data.migration.MigrationDetectorImpl
|
||||||
|
import gq.kirmanak.mealient.data.migration.MigrationExecutor
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
interface MigrationModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
@IntoSet
|
||||||
|
fun bindFrom24AuthMigrationExecutor(from24AuthMigrationExecutor: From24AuthMigrationExecutor): MigrationExecutor
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
fun bindMigrationDetector(migrationDetectorImpl: MigrationDetectorImpl): MigrationDetector
|
||||||
|
}
|
||||||
@@ -3,4 +3,6 @@ package gq.kirmanak.mealient.architecture.configuration
|
|||||||
interface BuildConfiguration {
|
interface BuildConfiguration {
|
||||||
|
|
||||||
fun isDebug(): Boolean
|
fun isDebug(): Boolean
|
||||||
|
|
||||||
|
fun versionCode(): Int
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package gq.kirmanak.mealient.architecture.configuration
|
|
||||||
|
|
||||||
import gq.kirmanak.mealient.architecture.BuildConfig
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class BuildConfigurationImpl @Inject constructor() : BuildConfiguration {
|
|
||||||
|
|
||||||
@get:JvmName("_isDebug")
|
|
||||||
private val isDebug by lazy { BuildConfig.DEBUG }
|
|
||||||
|
|
||||||
override fun isDebug(): Boolean = isDebug
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user