Skip to content

Commit ac5cecc

Browse files
authored
migrate to datastore (#14)
1 parent 11fe28d commit ac5cecc

30 files changed

+310
-261
lines changed

app/build.gradle.kts

+3
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ dependencies {
139139
implementation(libs.coil.compose)
140140
implementation(libs.lottie.compose)
141141

142+
// Datastore
143+
implementation(libs.datastore.preferences)
144+
142145
// Unit Test
143146
testImplementation(libs.junit)
144147
testImplementation(libs.mockk)

app/src/main/kotlin/com/whereismymotivation/WimmApplication.kt

+10-4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import com.whereismymotivation.init.FirebaseInit
99
import com.whereismymotivation.init.MetricInit
1010
import com.whereismymotivation.init.WorkInit
1111
import dagger.hilt.android.HiltAndroidApp
12+
import kotlinx.coroutines.DelicateCoroutinesApi
13+
import kotlinx.coroutines.GlobalScope
14+
import kotlinx.coroutines.launch
1215
import javax.inject.Inject
1316

1417
@HiltAndroidApp
@@ -42,12 +45,15 @@ class WimmApplication : Application(), Configuration.Provider {
4245
.setWorkerFactory(workerFactory)
4346
.build()
4447

48+
@OptIn(DelicateCoroutinesApi::class)
4549
override fun onCreate() {
4650
super.onCreate()
4751
tracker.trackAppOpen()
48-
metricInit.init()
49-
workInit.init()
50-
firebaseInit.init()
51-
coilInit.init()
52+
GlobalScope.launch {
53+
metricInit.init()
54+
workInit.init()
55+
firebaseInit.init()
56+
coilInit.init()
57+
}
5258
}
5359
}
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,52 @@
11
package com.whereismymotivation.data.local.prefs
22

3-
import android.content.SharedPreferences
3+
import androidx.datastore.core.DataStore
4+
import androidx.datastore.preferences.core.Preferences
5+
import androidx.datastore.preferences.core.booleanPreferencesKey
6+
import androidx.datastore.preferences.core.edit
7+
import androidx.datastore.preferences.core.intPreferencesKey
8+
import androidx.datastore.preferences.core.longPreferencesKey
9+
import kotlinx.coroutines.flow.first
10+
import kotlinx.coroutines.flow.map
411
import javax.inject.Inject
512
import javax.inject.Singleton
613

714
@Singleton
8-
class AppMetricPreferences @Inject constructor(private val prefs: SharedPreferences) {
15+
class AppMetricPreferences @Inject constructor(private val dataStore: DataStore<Preferences>) {
916

1017
companion object {
11-
12-
private const val CURRENT_APP_VERSION = "PREF_KEY_CURRENT_APP_VERSION"
13-
14-
private const val DAILY_RECORD_ALARM_TIME_HOUR = "PREF_KEY_DAILY_RECORD_ALARM_TIME_HOUR"
15-
private const val DAILY_RECORD_ALARM_TIME_MIN = "PREF_KEY_DAILY_RECORD_ALARM_TIME_MIN"
16-
private const val DAILY_RECORD_ALARM_TIME_SEC = "PREF_KEY_DAILY_RECORD_ALARM_TIME_SEC"
17-
18-
private const val DAILY_MOOD_RECORDER_NOTIFICATION =
19-
"PREF_KEY_DAILY_MOOD_RECORDER_NOTIFICATION"
20-
18+
private val CURRENT_APP_VERSION = longPreferencesKey("CURRENT_APP_VERSION")
19+
private val DAILY_RECORD_ALARM_TIME_HOUR = intPreferencesKey("DAILY_RECORD_ALARM_TIME_HOUR")
20+
private val DAILY_RECORD_ALARM_TIME_MIN = intPreferencesKey("DAILY_RECORD_ALARM_TIME_MIN")
21+
private val DAILY_RECORD_ALARM_TIME_SEC = intPreferencesKey("DAILY_RECORD_ALARM_TIME_SEC")
22+
private val DAILY_MOOD_RECORDER_NOTIFICATION =
23+
booleanPreferencesKey("DAILY_MOOD_RECORDER_NOTIFICATION")
2124
}
2225

23-
fun setCurrentAppVersion(appVersion: Long) =
24-
prefs.edit().putLong(CURRENT_APP_VERSION, appVersion).apply()
26+
suspend fun setCurrentAppVersion(appVersion: Long) {
27+
dataStore.edit { it[CURRENT_APP_VERSION] = appVersion }
28+
}
2529

26-
fun getCurrentAppVersion(): Long =
27-
prefs.getLong(CURRENT_APP_VERSION, 0)
30+
suspend fun getCurrentAppVersion() =
31+
dataStore.data.map { it[CURRENT_APP_VERSION] ?: 0 }.first()
2832

29-
fun setDailyRecordAlarmTime(hour: Int, min: Int, sec: Int) {
30-
prefs.edit().putInt(DAILY_RECORD_ALARM_TIME_HOUR, hour).apply()
31-
prefs.edit().putInt(DAILY_RECORD_ALARM_TIME_MIN, min).apply()
32-
prefs.edit().putInt(DAILY_RECORD_ALARM_TIME_SEC, sec).apply()
33+
suspend fun setDailyRecordAlarmTime(hour: Int, min: Int, sec: Int) {
34+
dataStore.edit { it[DAILY_RECORD_ALARM_TIME_HOUR] = hour }
35+
dataStore.edit { it[DAILY_RECORD_ALARM_TIME_MIN] = min }
36+
dataStore.edit { it[DAILY_RECORD_ALARM_TIME_SEC] = sec }
3337
}
3438

35-
fun getDailyRecordAlarmTime(): Triple<Int, Int, Int> {
36-
val hour = prefs.getInt(DAILY_RECORD_ALARM_TIME_HOUR, 20) // 8PM
37-
val min = prefs.getInt(DAILY_RECORD_ALARM_TIME_MIN, 0)
38-
val sec = prefs.getInt(DAILY_RECORD_ALARM_TIME_SEC, 0)
39+
suspend fun getDailyRecordAlarmTime(): Triple<Int, Int, Int> {
40+
val hour = dataStore.data.map { it[DAILY_RECORD_ALARM_TIME_HOUR] ?: 22 }.first() // 8PM
41+
val min = dataStore.data.map { it[DAILY_RECORD_ALARM_TIME_MIN] ?: 34 }.first()
42+
val sec = dataStore.data.map { it[DAILY_RECORD_ALARM_TIME_SEC] ?: 0 }.first()
3943
return Triple(hour, min, sec)
4044
}
4145

42-
fun setDailyMoodRecorderNotificationEnable(enable: Boolean) =
43-
prefs.edit().putBoolean(DAILY_MOOD_RECORDER_NOTIFICATION, enable).apply()
44-
45-
fun getDailyMoodRecorderNotificationEnable() =
46-
prefs.getBoolean(DAILY_MOOD_RECORDER_NOTIFICATION, true)
46+
suspend fun setDailyMoodRecorderNotificationEnable(enable: Boolean) {
47+
dataStore.edit { it[DAILY_MOOD_RECORDER_NOTIFICATION] = enable }
48+
}
4749

50+
suspend fun getDailyMoodRecorderNotificationEnable() =
51+
dataStore.data.map { it[DAILY_MOOD_RECORDER_NOTIFICATION] ?: true }.first()
4852
}
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
package com.whereismymotivation.data.local.prefs
22

3-
import android.content.SharedPreferences
3+
import androidx.datastore.core.DataStore
4+
import androidx.datastore.preferences.core.Preferences
5+
import androidx.datastore.preferences.core.edit
6+
import androidx.datastore.preferences.core.intPreferencesKey
7+
import androidx.datastore.preferences.core.longPreferencesKey
8+
import kotlinx.coroutines.flow.first
9+
import kotlinx.coroutines.flow.map
410
import javax.inject.Inject
511

6-
class ContentPreferences @Inject constructor(private val prefs: SharedPreferences) {
12+
class ContentPreferences @Inject constructor(private val dataStore: DataStore<Preferences>) {
713

814
companion object {
9-
private const val FEED_NEXT_PAGE_NUMBER = "FEED_NEXT_PAGE_NUMBER"
10-
private const val FEED_LAST_SEEN = "FEED_LAST_SEEN"
15+
private val FEED_NEXT_PAGE_NUMBER = intPreferencesKey("FEED_NEXT_PAGE_NUMBER")
16+
private val FEED_LAST_SEEN = longPreferencesKey("FEED_LAST_SEEN")
1117
}
1218

13-
fun getFeedNextPageNumber(): Int =
14-
prefs.getInt(FEED_NEXT_PAGE_NUMBER, 1)
19+
suspend fun getFeedNextPageNumber() =
20+
dataStore.data.map { it[FEED_NEXT_PAGE_NUMBER] ?: 1 }.first()
1521

16-
fun setFeedNextPageNumber(pageNumber: Int) =
17-
prefs.edit().putInt(FEED_NEXT_PAGE_NUMBER, pageNumber).apply()
22+
suspend fun setFeedNextPageNumber(pageNumber: Int) {
23+
dataStore.edit { it[FEED_NEXT_PAGE_NUMBER] = pageNumber }
24+
}
1825

19-
fun getFeedLastSeen(): Long =
20-
prefs.getLong(FEED_LAST_SEEN, System.currentTimeMillis())
26+
suspend fun getFeedLastSeen() =
27+
dataStore.data.map { it[FEED_LAST_SEEN] ?: System.currentTimeMillis() }.first()
2128

22-
fun setFeedLastSeen(time: Long) =
23-
prefs.edit().putLong(FEED_LAST_SEEN, time).apply()
29+
suspend fun setFeedLastSeen(time: Long) {
30+
dataStore.edit { it[FEED_LAST_SEEN] = time }
31+
}
2432
}
Original file line numberDiff line numberDiff line change
@@ -1,114 +1,136 @@
11
package com.whereismymotivation.data.local.prefs
22

3-
import android.content.SharedPreferences
3+
import androidx.datastore.core.DataStore
4+
import androidx.datastore.preferences.core.Preferences
5+
import androidx.datastore.preferences.core.booleanPreferencesKey
6+
import androidx.datastore.preferences.core.edit
7+
import androidx.datastore.preferences.core.stringPreferencesKey
8+
import kotlinx.coroutines.flow.first
9+
import kotlinx.coroutines.flow.map
410
import javax.inject.Inject
511
import javax.inject.Singleton
612

713
@Singleton
8-
class UserPreferences @Inject constructor(private val prefs: SharedPreferences) {
14+
class UserPreferences @Inject constructor(private val dataStore: DataStore<Preferences>) {
915

1016
companion object {
11-
private const val ON_BOARDING_COMPLETED = "PREF_KEY_USER_ON_BOARDING_COMPLETED"
12-
private const val USER_ID = "PREF_KEY_USER_ID"
13-
private const val USER_NAME = "PREF_KEY_USER_NAME"
14-
private const val USER_EMAIL = "PREF_KEY_USER_EMAIL"
15-
private const val USER_PROFILE_PIC_URL = "USER_PROFILE_PIC_URL"
16-
private const val ACCESS_TOKEN = "PREF_KEY_ACCESS_TOKEN"
17-
private const val REFRESH_TOKEN = "PREF_KEY_REFRESH_TOKEN"
18-
private const val DEVICE_ID = "PREF_KEY_DEVICE_ID"
19-
private const val USER_ROLES = "PREF_KEY_USER_ROLES"
20-
private const val FIREBASE_TOKEN = "FIREBASE_TOKEN"
21-
private const val FIREBASE_TOKEN_SENT = "FIREBASE_TOKEN_SENT"
17+
private val ON_BOARDING_COMPLETED = booleanPreferencesKey("ON_BOARDING_COMPLETED")
18+
private val USER_ID = stringPreferencesKey("USER_ID")
19+
private val USER_NAME = stringPreferencesKey("USER_NAME")
20+
private val USER_EMAIL = stringPreferencesKey("USER_EMAIL")
21+
private val USER_PROFILE_PIC_URL = stringPreferencesKey("USER_PROFILE_PIC_URL")
22+
private val ACCESS_TOKEN = stringPreferencesKey("ACCESS_TOKEN")
23+
private val REFRESH_TOKEN = stringPreferencesKey("REFRESH_TOKEN")
24+
private val DEVICE_ID = stringPreferencesKey("DEVICE_ID")
25+
private val USER_ROLES = stringPreferencesKey("USER_ROLES")
26+
private val FIREBASE_TOKEN = stringPreferencesKey("FIREBASE_TOKEN")
27+
private val FIREBASE_TOKEN_SENT = booleanPreferencesKey("FIREBASE_TOKEN_SENT")
2228
}
2329

24-
fun getUserId(): String? =
25-
prefs.getString(USER_ID, null)
30+
suspend fun getUserId() = dataStore.data.map { it[USER_ID] }.first()
2631

27-
fun setUserId(userId: String) =
28-
prefs.edit().putString(USER_ID, userId).apply()
32+
suspend fun setUserId(userId: String) {
33+
dataStore.edit { it[USER_ID] = userId }
34+
}
2935

30-
fun removeUserId() =
31-
prefs.edit().remove(USER_ID).apply()
36+
suspend fun removeUserId() {
37+
dataStore.edit { it.remove(USER_ID) }
38+
}
3239

33-
fun getUserName(): String? =
34-
prefs.getString(USER_NAME, null)
40+
suspend fun getUserName() = dataStore.data.map { it[USER_NAME] }.first()
3541

36-
fun setUserName(userName: String) =
37-
prefs.edit().putString(USER_NAME, userName).apply()
42+
suspend fun setUserName(userName: String) {
43+
dataStore.edit { it[USER_NAME] = userName }
44+
}
3845

39-
fun removeUserName() =
40-
prefs.edit().remove(USER_NAME).apply()
46+
suspend fun removeUserName() {
47+
dataStore.edit { it.remove(USER_NAME) }
48+
}
4149

42-
fun getUserEmail(): String? =
43-
prefs.getString(USER_EMAIL, null)
50+
suspend fun getUserEmail() = dataStore.data.map { it[USER_EMAIL] }.first()
4451

45-
fun setUserEmail(email: String) =
46-
prefs.edit().putString(USER_EMAIL, email).apply()
52+
suspend fun setUserEmail(email: String) {
53+
dataStore.edit { it[USER_EMAIL] = email }
54+
}
4755

48-
fun removeUserEmail() =
49-
prefs.edit().remove(USER_EMAIL).apply()
56+
suspend fun removeUserEmail() {
57+
dataStore.edit { it.remove(USER_EMAIL) }
58+
}
5059

51-
fun getUserProfilePicUrlUrl(): String? =
52-
prefs.getString(USER_PROFILE_PIC_URL, null)
60+
suspend fun getUserProfilePicUrlUrl() = dataStore.data.map { it[USER_PROFILE_PIC_URL] }.first()
5361

54-
fun setUserProfileProfilePicUrl(url: String?) =
55-
prefs.edit().putString(USER_PROFILE_PIC_URL, url).apply()
62+
suspend fun setUserProfileProfilePicUrl(url: String?) {
63+
url?.let {
64+
dataStore.edit { it[USER_PROFILE_PIC_URL] = url }
65+
} ?: removeUserProfilePicUrl()
66+
}
5667

57-
fun removeUserProfilePicUrl() =
58-
prefs.edit().remove(USER_PROFILE_PIC_URL).apply()
68+
suspend fun removeUserProfilePicUrl() {
69+
dataStore.edit { it.remove(USER_PROFILE_PIC_URL) }
70+
}
5971

60-
fun getAccessToken(): String? =
61-
prefs.getString(ACCESS_TOKEN, null)
72+
suspend fun getAccessToken() = dataStore.data.map { it[ACCESS_TOKEN] }.first()
6273

63-
fun setAccessToken(token: String) =
64-
prefs.edit().putString(ACCESS_TOKEN, token).apply()
74+
suspend fun setAccessToken(token: String) {
75+
dataStore.edit { it[ACCESS_TOKEN] = token }
76+
}
6577

66-
fun removeAccessToken() =
67-
prefs.edit().remove(ACCESS_TOKEN).apply()
78+
suspend fun removeAccessToken() {
79+
dataStore.edit { it.remove(ACCESS_TOKEN) }
80+
}
6881

69-
fun getRefreshToken(): String? =
70-
prefs.getString(REFRESH_TOKEN, null)
82+
suspend fun getRefreshToken() = dataStore.data.map { it[REFRESH_TOKEN] }.first()
7183

72-
fun setRefreshToken(token: String) =
73-
prefs.edit().putString(REFRESH_TOKEN, token).apply()
84+
suspend fun setRefreshToken(token: String) {
85+
dataStore.edit { it[REFRESH_TOKEN] = token }
86+
}
7487

75-
fun removeRefreshToken() =
76-
prefs.edit().remove(REFRESH_TOKEN).apply()
88+
suspend fun removeRefreshToken() {
89+
dataStore.edit { it.remove(REFRESH_TOKEN) }
90+
}
7791

78-
fun getOnBoardingComplete(): Boolean =
79-
prefs.getBoolean(ON_BOARDING_COMPLETED, false)
92+
suspend fun getOnBoardingComplete() =
93+
dataStore.data.map { it[ON_BOARDING_COMPLETED] }.first() ?: false
8094

81-
fun setOnBoardingComplete(complete: Boolean) =
82-
prefs.edit().putBoolean(ON_BOARDING_COMPLETED, complete).apply()
95+
suspend fun setOnBoardingComplete(complete: Boolean) {
96+
dataStore.edit { it[ON_BOARDING_COMPLETED] = complete }
97+
}
8398

84-
fun removeOnBoardingComplete() =
85-
prefs.edit().remove(ON_BOARDING_COMPLETED).apply()
99+
suspend fun removeOnBoardingComplete() {
100+
dataStore.edit { it.remove(ON_BOARDING_COMPLETED) }
101+
}
86102

87-
fun getDeviceId(): String? =
88-
prefs.getString(DEVICE_ID, null)
103+
suspend fun getDeviceId() = dataStore.data.map { it[DEVICE_ID] }.first()
89104

90-
fun setDeviceId(deviceId: String) =
91-
prefs.edit().putString(DEVICE_ID, deviceId).apply()
105+
suspend fun setDeviceId(deviceId: String) {
106+
dataStore.edit { it[DEVICE_ID] = deviceId }
107+
}
92108

93-
fun setFirebaseToken(token: String) =
94-
prefs.edit().putString(FIREBASE_TOKEN, token).apply()
109+
suspend fun setFirebaseToken(token: String) {
110+
dataStore.edit { it[FIREBASE_TOKEN] = token }
111+
}
95112

96-
fun getFirebaseToken(): String? =
97-
prefs.getString(FIREBASE_TOKEN, null)
113+
suspend fun getFirebaseToken() = dataStore.data.map { it[FIREBASE_TOKEN] }.first()
98114

99-
fun getFirebaseTokenSent(): Boolean =
100-
prefs.getBoolean(FIREBASE_TOKEN_SENT, false)
115+
suspend fun getFirebaseTokenSent() =
116+
dataStore.data.map { it[FIREBASE_TOKEN_SENT] }.first() ?: false
101117

102-
fun setFirebaseTokenSent() =
103-
prefs.edit().putBoolean(FIREBASE_TOKEN_SENT, true).apply()
118+
suspend fun setFirebaseTokenSent() {
119+
dataStore.edit { it[FIREBASE_TOKEN_SENT] = true }
120+
}
104121

105-
fun removeFirebaseTokenSent() =
106-
prefs.edit().remove(FIREBASE_TOKEN_SENT).apply()
122+
suspend fun removeFirebaseTokenSent() {
123+
dataStore.edit { it.remove(FIREBASE_TOKEN_SENT) }
124+
}
107125

108-
fun getUserRoles(): String? = prefs.getString(USER_ROLES, null)
126+
suspend fun getUserRoles() = dataStore.data.map { it[USER_ROLES] }.first()
109127

110-
fun setUserRoles(roles: String) = prefs.edit().putString(USER_ROLES, roles).apply()
128+
suspend fun setUserRoles(roles: String) {
129+
dataStore.edit { it[USER_ROLES] = roles }
130+
}
111131

112-
fun removeUserRoles() = prefs.edit().remove(USER_ROLES).apply()
132+
suspend fun removeUserRoles() {
133+
dataStore.edit { it.remove(USER_ROLES) }
134+
}
113135

114136
}

app/src/main/kotlin/com/whereismymotivation/data/remote/RequestHeaders.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import com.whereismymotivation.di.qualifier.AccessTokenInfo
55
import com.whereismymotivation.di.qualifier.ApiKeyInfo
66
import com.whereismymotivation.di.qualifier.AppVersionCodeInfo
77
import com.whereismymotivation.di.qualifier.DeviceIdInfo
8-
import com.whereismymotivation.utils.common.ResultFetcher
8+
import com.whereismymotivation.utils.common.ResultFetcherBlocking
99
import javax.inject.Inject
1010

1111
class RequestHeaders @Inject constructor(
1212
@ApiKeyInfo val apiKey: String,
13-
@AccessTokenInfo val accessTokenFetcher: ResultFetcher<String>,
14-
@DeviceIdInfo val deviceIdFetcher: ResultFetcher<String>,
15-
@AppVersionCodeInfo val appVersionCodeFetcher: ResultFetcher<Long>,
13+
@AccessTokenInfo val accessTokenFetcher: ResultFetcherBlocking<String>,
14+
@DeviceIdInfo val deviceIdFetcher: ResultFetcherBlocking<String>,
15+
@AppVersionCodeInfo val appVersionCodeFetcher: ResultFetcherBlocking<Long>,
1616
) {
1717
object Key {
1818
const val API_AUTH_TYPE = "API_AUTH_TYPE"

0 commit comments

Comments
 (0)