Skip to content

Commit

Permalink
✨ implement PreferencesDatastore for storing tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
kmkim2689 committed Jul 29, 2024
1 parent f41de37 commit 9b18d8e
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 0 deletions.
3 changes: 3 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,7 @@ dependencies {

// Indicator animation open source
implementation("com.tbuonomo:dotsindicator:5.0")

// Preferences Datastore
implementation("androidx.datastore:datastore-preferences:1.1.1")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.pengcook.android.data.datasource.auth

import kotlinx.coroutines.flow.Flow
import net.pengcook.android.data.model.auth.Authorization
import net.pengcook.android.data.model.auth.Platform

interface AuthorizationLocalDataSource {
val authorizationData: Flow<Authorization>

suspend fun updatePlatformToken(platformToken: String?)

suspend fun updateAccessToken(accessToken: String?)

suspend fun updateRefreshToken(refreshToken: String?)

suspend fun updateFcmToken(fcmToken: String?)

suspend fun updateCurrentPlatform(platform: Platform)

suspend fun clearAll()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package net.pengcook.android.data.datasource.auth

import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.core.IOException
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import net.pengcook.android.data.model.auth.Authorization
import net.pengcook.android.data.model.auth.Platform

class DefaultAuthorizationLocalDataSource(
private val dataStore: DataStore<Preferences>,
) : AuthorizationLocalDataSource {
override val authorizationData: Flow<Authorization> =
dataStore
.data
.catchException()
.map(::mapUserPreferences)

override suspend fun updatePlatformToken(platformToken: String?) {
dataStore.modifyValue(KEY_PLATFORM_TOKEN, platformToken)
}

override suspend fun updateAccessToken(accessToken: String?) {
dataStore.modifyValue(KEY_ACCESS_TOKEN, accessToken)
}

override suspend fun updateRefreshToken(refreshToken: String?) {
dataStore.modifyValue(KEY_REFRESH_TOKEN, refreshToken)
}

override suspend fun updateFcmToken(fcmToken: String?) {
dataStore.modifyValue(KEY_FCM_TOKEN, fcmToken)
}

override suspend fun updateCurrentPlatform(platform: Platform) {
dataStore.modifyValue(KEY_CURRENT_PLATFORM, platform.platformName)
}

override suspend fun clearAll() {
dataStore.edit { preferences ->
preferences.clear()
}
}

private suspend fun DataStore<Preferences>.modifyValue(
key: Preferences.Key<String>,
value: String?,
) {
edit { preferences ->
if (value == null) {
preferences.remove(key)
return@edit
}
preferences[key] = value
}
}

private fun Flow<Preferences>.catchException(): Flow<Preferences> =
catch { exception ->
if (exception is IOException) {
Log.e(TAG, EXCEPTION_ERROR_READING_EXCEPTION, exception)
emit(emptyPreferences())
} else {
throw exception
}
}

private fun mapUserPreferences(preferences: Preferences): Authorization {
val platformToken = preferences[KEY_PLATFORM_TOKEN]
val accessToken = preferences[KEY_ACCESS_TOKEN]
val refreshToken = preferences[KEY_REFRESH_TOKEN]
val fcmToken = preferences[KEY_FCM_TOKEN]
val currentPlatform = preferences[KEY_CURRENT_PLATFORM] ?: Platform.NONE.platformName

return Authorization(
platformToken = platformToken,
accessToken = accessToken,
refreshToken = refreshToken,
fcmToken = fcmToken,
currentPlatform = currentPlatform,
)
}

companion object {
private const val TAG = "DefaultAuthorizationLocalDataSource"
private const val EXCEPTION_ERROR_READING_EXCEPTION = "Error reading preferences."
private val KEY_ACCESS_TOKEN = stringPreferencesKey("access_token")
private val KEY_REFRESH_TOKEN = stringPreferencesKey("refresh_token")
private val KEY_PLATFORM_TOKEN = stringPreferencesKey("platform_token")
private val KEY_FCM_TOKEN = stringPreferencesKey("fcm_token")
private val KEY_CURRENT_PLATFORM = stringPreferencesKey("current_platform")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.pengcook.android.data.local.preferences

import android.content.Context
import androidx.datastore.preferences.preferencesDataStore

private const val AUTHORIZATION_PREFERENCES = "authorization_preferences"

private val Context.dataStore by preferencesDataStore(
name = AUTHORIZATION_PREFERENCES,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.pengcook.android.data.model.auth

data class Authorization(
val platformToken: String?,
val accessToken: String?,
val refreshToken: String?,
val fcmToken: String?,
val currentPlatform: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package net.pengcook.android.data.model.auth

enum class Platform(val platformName: String?) {
GOOGLE("google"),
NONE(null),
;

companion object {
fun find(platformName: String): Platform? {
return Platform.entries.find { it.platformName == platformName }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.pengcook.android.data.repository.auth

import kotlinx.coroutines.flow.Flow
import net.pengcook.android.data.model.auth.Authorization
import net.pengcook.android.data.model.auth.Platform

interface AuthorizationRepository {
val authorizationData: Flow<Authorization>

suspend fun updatePlatformToken(platformToken: String?)

suspend fun updateAccessToken(accessToken: String?)

suspend fun updateRefreshToken(refreshToken: String?)

suspend fun updateFcmToken(fcmToken: String?)

suspend fun updateCurrentPlatform(platform: Platform)

suspend fun clearAll()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.pengcook.android.data.repository.auth

import kotlinx.coroutines.flow.Flow
import net.pengcook.android.data.datasource.auth.AuthorizationLocalDataSource
import net.pengcook.android.data.model.auth.Authorization
import net.pengcook.android.data.model.auth.Platform

class DefaultAuthorizationRepository(
private val authorizationLocalDataSource: AuthorizationLocalDataSource,
) : AuthorizationRepository {
override val authorizationData: Flow<Authorization> =
authorizationLocalDataSource.authorizationData

override suspend fun updatePlatformToken(platformToken: String?) {
authorizationLocalDataSource.updatePlatformToken(platformToken)
}

override suspend fun updateAccessToken(accessToken: String?) {
authorizationLocalDataSource.updateAccessToken(accessToken)
}

override suspend fun updateRefreshToken(refreshToken: String?) {
authorizationLocalDataSource.updateRefreshToken(refreshToken)
}

override suspend fun updateFcmToken(fcmToken: String?) {
authorizationLocalDataSource.updateFcmToken(fcmToken)
}

override suspend fun updateCurrentPlatform(platform: Platform) {
authorizationLocalDataSource.updateCurrentPlatform(platform)
}

override suspend fun clearAll() {
authorizationLocalDataSource.clearAll()
}
}

0 comments on commit 9b18d8e

Please sign in to comment.