Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ plugins {
}

android {
compileSdkVersion 32

compileSdkVersion 34
namespace 'com.codelab.android.datastore'
defaultConfig {
applicationId "com.codelab.android.datastore"
minSdkVersion 16
targetSdkVersion 32
minSdkVersion 21
targetSdkVersion 34
versionCode 1
versionName "1.0"

Expand All @@ -39,11 +39,11 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}

android.buildFeatures.viewBinding = true
Expand All @@ -54,6 +54,7 @@ dependencies {
implementation "androidx.appcompat:appcompat:$supportLibVersion"
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
implementation "com.google.android.material:material:$materialVersion"
implementation "androidx.datastore:datastore-preferences:1.1.7"

// kotlin
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@
~ limitations under the License.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codelab.android.datastore">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,23 @@
package com.codelab.android.datastore.data

import android.content.Context
import androidx.core.content.edit
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
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.emptyPreferences
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.io.IOException

private const val USER_PREFERENCES_NAME = "user_preferences"
private const val SORT_ORDER_KEY = "sort_order"

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
name = USER_PREFERENCES_NAME
)

enum class SortOrder {
NONE,
Expand All @@ -31,31 +42,52 @@ enum class SortOrder {
BY_DEADLINE_AND_PRIORITY
}

data class UserPreferences(
val showCompleted: Boolean,
val sortOrder: SortOrder
)

/**
* Class that handles saving and retrieving user preferences
*/
class UserPreferencesRepository private constructor(context: Context) {

private val sharedPreferences =
context.applicationContext.getSharedPreferences(USER_PREFERENCES_NAME, Context.MODE_PRIVATE)
class UserPreferencesRepository private constructor(
private val dataStore: DataStore<Preferences>
) {

// Keep the sort order as a stream of changes
private val _sortOrderFlow = MutableStateFlow(sortOrder)
val sortOrderFlow: StateFlow<SortOrder> = _sortOrderFlow
private object PreferencesKeys {
val SHOW_COMPLETED = booleanPreferencesKey("show_completed")
val SORT_ORDER = stringPreferencesKey("sort_order")
}

/**
* Get the sort order. By default, sort order is None.
* Get the user preferences flow.
*/
private val sortOrder: SortOrder
get() {
val order = sharedPreferences.getString(SORT_ORDER_KEY, SortOrder.NONE.name)
return SortOrder.valueOf(order ?: SortOrder.NONE.name)
val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
val showCompleted = preferences[PreferencesKeys.SHOW_COMPLETED] ?: false
val sortOrder = SortOrder.valueOf(
preferences[PreferencesKeys.SORT_ORDER] ?: SortOrder.NONE.name
)
UserPreferences(showCompleted, sortOrder)
}

fun enableSortByDeadline(enable: Boolean) {
val currentOrder = sortOrderFlow.value
val newSortOrder =
if (enable) {
/**
* Enable / disable sort by deadline.
*/
suspend fun enableSortByDeadline(enable: Boolean) {
dataStore.edit { preferences ->
val currentOrder = SortOrder.valueOf(
preferences[PreferencesKeys.SORT_ORDER] ?: SortOrder.NONE.name
)

val newOrder = if (enable) {
if (currentOrder == SortOrder.BY_PRIORITY) {
SortOrder.BY_DEADLINE_AND_PRIORITY
} else {
Expand All @@ -68,14 +100,20 @@ class UserPreferencesRepository private constructor(context: Context) {
SortOrder.NONE
}
}
updateSortOrder(newSortOrder)
_sortOrderFlow.value = newSortOrder
preferences[PreferencesKeys.SORT_ORDER] = newOrder.name
}
}

fun enableSortByPriority(enable: Boolean) {
val currentOrder = sortOrderFlow.value
val newSortOrder =
if (enable) {
/**
* Enable / disable sort by priority.
*/
suspend fun enableSortByPriority(enable: Boolean) {
dataStore.edit { preferences ->
val currentOrder = SortOrder.valueOf(
preferences[PreferencesKeys.SORT_ORDER] ?: SortOrder.NONE.name
)

val newOrder = if (enable) {
if (currentOrder == SortOrder.BY_DEADLINE) {
SortOrder.BY_DEADLINE_AND_PRIORITY
} else {
Expand All @@ -88,13 +126,13 @@ class UserPreferencesRepository private constructor(context: Context) {
SortOrder.NONE
}
}
updateSortOrder(newSortOrder)
_sortOrderFlow.value = newSortOrder
preferences[PreferencesKeys.SORT_ORDER] = newOrder.name
}
}

private fun updateSortOrder(sortOrder: SortOrder) {
sharedPreferences.edit {
putString(SORT_ORDER_KEY, sortOrder.name)
suspend fun updateShowCompleted(showCompleted: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.SHOW_COMPLETED] = showCompleted
}
}

Expand All @@ -104,11 +142,7 @@ class UserPreferencesRepository private constructor(context: Context) {

fun getInstance(context: Context): UserPreferencesRepository {
return INSTANCE ?: synchronized(this) {
INSTANCE?.let {
return it
}

val instance = UserPreferencesRepository(context)
val instance = UserPreferencesRepository(context.dataStore)
INSTANCE = instance
instance
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ package com.codelab.android.datastore.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.codelab.android.datastore.data.SortOrder
import com.codelab.android.datastore.data.Task
import com.codelab.android.datastore.data.TasksRepository
import com.codelab.android.datastore.data.UserPreferences
import com.codelab.android.datastore.data.UserPreferencesRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch

data class TasksUiModel(
val tasks: List<Task>,
Expand All @@ -37,23 +39,23 @@ class TasksViewModel(
private val userPreferencesRepository: UserPreferencesRepository
) : ViewModel() {

// Keep the show completed filter as a stream of changes
private val showCompletedFlow = MutableStateFlow(false)
// Get the user preferences flow
private val userPreferencesFlow = userPreferencesRepository.userPreferencesFlow

// Keep the sort order as a stream of changes
private val sortOrderFlow = userPreferencesRepository.sortOrderFlow

// Every time the sort order, the show completed filter or the list of tasks emit,
// Every time the user preferences or the list of tasks emit,
// we should recreate the list of tasks
private val tasksUiModelFlow = combine(
repository.tasks,
showCompletedFlow,
sortOrderFlow
) { tasks: List<Task>, showCompleted: Boolean, sortOrder: SortOrder ->
userPreferencesFlow
) { tasks: List<Task>, userPreferences: UserPreferences ->
return@combine TasksUiModel(
tasks = filterSortTasks(tasks, showCompleted, sortOrder),
showCompleted = showCompleted,
sortOrder = sortOrder
tasks = filterSortTasks(
tasks,
userPreferences.showCompleted,
userPreferences.sortOrder
),
showCompleted = userPreferences.showCompleted,
sortOrder = userPreferences.sortOrder
)
}
val tasksUiModel = tasksUiModelFlow.asLiveData()
Expand Down Expand Up @@ -81,15 +83,21 @@ class TasksViewModel(
}

fun showCompletedTasks(show: Boolean) {
showCompletedFlow.value = show
viewModelScope.launch {
userPreferencesRepository.updateShowCompleted(show)
}
}

fun enableSortByDeadline(enable: Boolean) {
userPreferencesRepository.enableSortByDeadline(enable)
viewModelScope.launch {
userPreferencesRepository.enableSortByDeadline(enable)
}
}

fun enableSortByPriority(enable: Boolean) {
userPreferencesRepository.enableSortByPriority(enable)
viewModelScope.launch {
userPreferencesRepository.enableSortByPriority(enable)
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = "1.6.10"
ext.kotlin_version = "1.9.24"
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.1'
classpath 'com.android.tools.build:gradle:8.5.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

}
}

Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 4 additions & 3 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#Thu Feb 25 11:54:48 GMT 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading