17
17
package com.codelab.android.datastore.data
18
18
19
19
import android.content.Context
20
- import androidx.core.content.edit
21
- import kotlinx.coroutines.flow.MutableStateFlow
22
- import kotlinx.coroutines.flow.StateFlow
20
+ import android.util.Log
21
+ import androidx.datastore.DataStore
22
+ import androidx.datastore.preferences.PreferenceDataStoreFactory
23
+ import androidx.datastore.preferences.Preferences
24
+ import androidx.datastore.preferences.SharedPreferencesMigration
25
+ import kotlinx.coroutines.flow.Flow
26
+ import kotlinx.coroutines.flow.catch
27
+ import kotlinx.coroutines.flow.map
28
+ import java.io.File
29
+ import java.io.IOException
23
30
24
31
private const val USER_PREFERENCES_NAME = " user_preferences"
32
+ private const val USER_PREFERENCES_STORE_FILE_NAME = " user.preferences_pb"
25
33
private const val SORT_ORDER_KEY = " sort_order"
34
+ private const val SHOW_COMPLETED_KEY = " show_completed"
26
35
27
36
enum class SortOrder {
28
37
NONE ,
@@ -31,70 +40,120 @@ enum class SortOrder {
31
40
BY_DEADLINE_AND_PRIORITY
32
41
}
33
42
43
+ data class UserPreferences (
44
+ val showCompleted : Boolean ,
45
+ val sortOrder : SortOrder
46
+ )
47
+
48
+ /* *
49
+ * Extension function on Preferences to easily get the sort order
50
+ */
51
+ private fun Preferences.getSortOrder (): SortOrder {
52
+ val order = getString(SORT_ORDER_KEY , SortOrder .NONE .name)
53
+ return SortOrder .valueOf(order)
54
+ }
55
+
56
+ /* *
57
+ * Extension function on Preferences to easily set the sort order
58
+ */
59
+ private fun Preferences.withSortOrder (newSortOrder : SortOrder ) =
60
+ this .toBuilder().setString(SORT_ORDER_KEY , newSortOrder.name).build()
61
+
34
62
/* *
35
63
* Class that handles saving and retrieving user preferences
36
64
*/
37
65
class UserPreferencesRepository private constructor(context : Context ) {
38
66
39
- private val sharedPreferences =
40
- context.applicationContext.getSharedPreferences(USER_PREFERENCES_NAME , Context .MODE_PRIVATE )
67
+ private val TAG : String = " UserPreferencesRepo"
41
68
42
- // Keep the sort order as a stream of changes
43
- private val _sortOrderFlow = MutableStateFlow (sortOrder)
44
- val sortOrderFlow: StateFlow <SortOrder > = _sortOrderFlow
69
+ private val dataStore: DataStore <Preferences > by lazy {
70
+ PreferenceDataStoreFactory ().create(
71
+ produceFile = {
72
+ File (
73
+ context.applicationContext.filesDir,
74
+ USER_PREFERENCES_STORE_FILE_NAME
75
+ )
76
+ },
77
+ // Since we're migrating from SharedPreferences, add a migration based on the
78
+ // SharedPreferences name
79
+ migrationProducers = listOf (SharedPreferencesMigration (context, USER_PREFERENCES_NAME ))
80
+ )
81
+ }
45
82
46
83
/* *
47
- * Get the sort order. By default, sort order is None .
84
+ * Get the user preferences flow .
48
85
*/
49
- private val sortOrder: SortOrder
50
- get() {
51
- val order = sharedPreferences.getString(SORT_ORDER_KEY , SortOrder .NONE .name)
52
- return SortOrder .valueOf(order ? : SortOrder .NONE .name)
86
+ val userPreferencesFlow: Flow <UserPreferences > = dataStore.data
87
+ .catch { exception ->
88
+ // dataStore.data throws an IOException when an error is encountered when reading data
89
+ if (exception is IOException ) {
90
+ Log .e(TAG , " Error reading preferences." , exception)
91
+ emit(Preferences .empty())
92
+ } else {
93
+ throw exception
94
+ }
95
+ }.map { preferences ->
96
+ // Get the sort order from preferences and convert it to a [SortOrder] object
97
+ val sortOrder = preferences.getSortOrder()
98
+ val showCompleted = preferences.getBoolean(SHOW_COMPLETED_KEY , false )
99
+ UserPreferences (showCompleted, sortOrder)
53
100
}
54
101
55
- fun enableSortByDeadline (enable : Boolean ) {
56
- val currentOrder = sortOrderFlow.value
57
- val newSortOrder =
58
- if (enable) {
59
- if (currentOrder == SortOrder .BY_PRIORITY ) {
60
- SortOrder .BY_DEADLINE_AND_PRIORITY
61
- } else {
62
- SortOrder .BY_DEADLINE
63
- }
64
- } else {
65
- if (currentOrder == SortOrder .BY_DEADLINE_AND_PRIORITY ) {
66
- SortOrder .BY_PRIORITY
102
+ /* *
103
+ * Enable / disable sort by deadline.
104
+ */
105
+ suspend fun enableSortByDeadline (enable : Boolean ) {
106
+ // updateData handles data transactionally, ensuring that if the sort is updated at the same
107
+ // time from another thread, we won't have conflicts
108
+ dataStore.updateData { currentPreferences ->
109
+ val currentOrder = currentPreferences.getSortOrder()
110
+ val newSortOrder =
111
+ if (enable) {
112
+ if (currentOrder == SortOrder .BY_PRIORITY ) {
113
+ SortOrder .BY_DEADLINE_AND_PRIORITY
114
+ } else {
115
+ SortOrder .BY_DEADLINE
116
+ }
67
117
} else {
68
- SortOrder .NONE
118
+ if (currentOrder == SortOrder .BY_DEADLINE_AND_PRIORITY ) {
119
+ SortOrder .BY_PRIORITY
120
+ } else {
121
+ SortOrder .NONE
122
+ }
69
123
}
70
- }
71
- updateSortOrder(newSortOrder)
72
- _sortOrderFlow .value = newSortOrder
124
+ currentPreferences.withSortOrder(newSortOrder)
125
+ }
73
126
}
74
127
75
- fun enableSortByPriority (enable : Boolean ) {
76
- val currentOrder = sortOrderFlow.value
77
- val newSortOrder =
78
- if (enable) {
79
- if (currentOrder == SortOrder .BY_DEADLINE ) {
80
- SortOrder .BY_DEADLINE_AND_PRIORITY
81
- } else {
82
- SortOrder .BY_PRIORITY
83
- }
84
- } else {
85
- if (currentOrder == SortOrder .BY_DEADLINE_AND_PRIORITY ) {
86
- SortOrder .BY_DEADLINE
128
+ /* *
129
+ * Enable / disable sort by priority.
130
+ */
131
+ suspend fun enableSortByPriority (enable : Boolean ) {
132
+ // updateData handles data transactionally, ensuring that if the sort is updated at the same
133
+ // time from another thread, we won't have conflicts
134
+ dataStore.updateData { currentPreferences ->
135
+ val currentOrder = currentPreferences.getSortOrder()
136
+ val newSortOrder =
137
+ if (enable) {
138
+ if (currentOrder == SortOrder .BY_DEADLINE ) {
139
+ SortOrder .BY_DEADLINE_AND_PRIORITY
140
+ } else {
141
+ SortOrder .BY_PRIORITY
142
+ }
87
143
} else {
88
- SortOrder .NONE
144
+ if (currentOrder == SortOrder .BY_DEADLINE_AND_PRIORITY ) {
145
+ SortOrder .BY_DEADLINE
146
+ } else {
147
+ SortOrder .NONE
148
+ }
89
149
}
90
- }
91
- updateSortOrder(newSortOrder)
92
- _sortOrderFlow .value = newSortOrder
150
+ currentPreferences.withSortOrder(newSortOrder)
151
+ }
93
152
}
94
153
95
- private fun updateSortOrder ( sortOrder : SortOrder ) {
96
- sharedPreferences.edit {
97
- putString( SORT_ORDER_KEY , sortOrder.name )
154
+ suspend fun updateShowCompleted ( showCompleted : Boolean ) {
155
+ dataStore.updateData { currentPreferences ->
156
+ currentPreferences.toBuilder().setBoolean( SHOW_COMPLETED_KEY , showCompleted).build( )
98
157
}
99
158
}
100
159
0 commit comments