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 dagger.hilt.android.HiltAndroidApp
|
||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||
import gq.kirmanak.mealient.data.migration.MigrationDetector
|
||||
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
|
||||
|
||||
@HiltAndroidApp
|
||||
@@ -16,9 +21,15 @@ class App : Application() {
|
||||
@Inject
|
||||
lateinit var buildConfiguration: BuildConfiguration
|
||||
|
||||
@Inject
|
||||
lateinit var migrationDetector: MigrationDetector
|
||||
|
||||
private val appCoroutineScope = CoroutineScope(Dispatchers.Main + Job())
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
logger.v { "onCreate() called" }
|
||||
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 lastExecutedMigrationVersionKey: Preferences.Key<Int>
|
||||
|
||||
suspend fun <T> getValue(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.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
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.Singleton
|
||||
|
||||
@@ -22,6 +28,9 @@ class PreferencesStorageImpl @Inject constructor(
|
||||
|
||||
override val isDisclaimerAcceptedKey = booleanPreferencesKey("isDisclaimedAccepted")
|
||||
|
||||
override val lastExecutedMigrationVersionKey: Preferences.Key<Int> =
|
||||
intPreferencesKey("lastExecutedMigrationVersion")
|
||||
|
||||
override suspend fun <T> getValue(key: Preferences.Key<T>): T? {
|
||||
val value = dataStore.data.first()[key]
|
||||
logger.v { "getValue() returned: $value for $key" }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package gq.kirmanak.mealient.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import gq.kirmanak.mealient.architecture.configuration.BuildConfiguration
|
||||
import gq.kirmanak.mealient.data.configuration.BuildConfigurationImpl
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface ArchitectureModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindBuildConfiguration(buildConfigurationImpl: BuildConfigurationImpl): BuildConfiguration
|
||||
}
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user