Skip to content

Commit cfb5ac6

Browse files
Adds saved state to TasksVM
2 parents bf1d733 + be5a9d6 commit cfb5ac6

File tree

6 files changed

+61
-34
lines changed

6 files changed

+61
-34
lines changed

app/src/main/java/com/example/android/architecture/blueprints/todoapp/ViewModelFactory.kt

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,14 @@
1515
*/
1616
package com.example.android.architecture.blueprints.todoapp
1717

18+
import android.app.Application
19+
import android.os.Bundle
20+
import androidx.lifecycle.AbstractSavedStateViewModelFactory
21+
import androidx.lifecycle.SavedStateHandle
22+
import androidx.lifecycle.SavedStateViewModelFactory
1823
import androidx.lifecycle.ViewModel
1924
import androidx.lifecycle.ViewModelProvider
25+
import androidx.savedstate.SavedStateRegistryOwner
2026
import com.example.android.architecture.blueprints.todoapp.addedittask.AddEditTaskViewModel
2127
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository
2228
import com.example.android.architecture.blueprints.todoapp.statistics.StatisticsViewModel
@@ -28,22 +34,27 @@ import com.example.android.architecture.blueprints.todoapp.tasks.TasksViewModel
2834
*/
2935
@Suppress("UNCHECKED_CAST")
3036
class ViewModelFactory constructor(
31-
private val tasksRepository: TasksRepository
32-
) : ViewModelProvider.NewInstanceFactory() {
37+
private val tasksRepository: TasksRepository,
38+
owner: SavedStateRegistryOwner,
39+
defaultArgs: Bundle? = null
40+
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
3341

34-
override fun <T : ViewModel> create(modelClass: Class<T>) =
35-
with(modelClass) {
36-
when {
37-
isAssignableFrom(StatisticsViewModel::class.java) ->
38-
StatisticsViewModel(tasksRepository)
39-
isAssignableFrom(TaskDetailViewModel::class.java) ->
40-
TaskDetailViewModel(tasksRepository)
41-
isAssignableFrom(AddEditTaskViewModel::class.java) ->
42-
AddEditTaskViewModel(tasksRepository)
43-
isAssignableFrom(TasksViewModel::class.java) ->
44-
TasksViewModel(tasksRepository)
45-
else ->
46-
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
47-
}
48-
} as T
42+
override fun <T : ViewModel> create(
43+
key: String,
44+
modelClass: Class<T>,
45+
handle: SavedStateHandle
46+
) = with(modelClass) {
47+
when {
48+
isAssignableFrom(StatisticsViewModel::class.java) ->
49+
StatisticsViewModel(tasksRepository)
50+
isAssignableFrom(TaskDetailViewModel::class.java) ->
51+
TaskDetailViewModel(tasksRepository)
52+
isAssignableFrom(AddEditTaskViewModel::class.java) ->
53+
AddEditTaskViewModel(tasksRepository)
54+
isAssignableFrom(TasksViewModel::class.java) ->
55+
TasksViewModel(tasksRepository, handle)
56+
else ->
57+
throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}")
58+
}
59+
} as T
4960
}

app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModel.kt

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ import androidx.annotation.DrawableRes
1919
import androidx.annotation.StringRes
2020
import androidx.lifecycle.LiveData
2121
import androidx.lifecycle.MutableLiveData
22+
import androidx.lifecycle.SavedStateHandle
2223
import androidx.lifecycle.Transformations
2324
import androidx.lifecycle.ViewModel
25+
import androidx.lifecycle.distinctUntilChanged
2426
import androidx.lifecycle.switchMap
2527
import androidx.lifecycle.viewModelScope
2628
import com.example.android.architecture.blueprints.todoapp.Event
@@ -30,13 +32,17 @@ import com.example.android.architecture.blueprints.todoapp.data.Result.Success
3032
import com.example.android.architecture.blueprints.todoapp.data.Task
3133
import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource
3234
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository
35+
import com.example.android.architecture.blueprints.todoapp.tasks.TasksFilterType.ACTIVE_TASKS
36+
import com.example.android.architecture.blueprints.todoapp.tasks.TasksFilterType.ALL_TASKS
37+
import com.example.android.architecture.blueprints.todoapp.tasks.TasksFilterType.COMPLETED_TASKS
3338
import kotlinx.coroutines.launch
3439

3540
/**
3641
* ViewModel for the task list screen.
3742
*/
3843
class TasksViewModel(
39-
private val tasksRepository: TasksRepository
44+
private val tasksRepository: TasksRepository,
45+
private val savedStateHandle: SavedStateHandle
4046
) : ViewModel() {
4147

4248
private val _forceUpdate = MutableLiveData<Boolean>(false)
@@ -49,7 +55,7 @@ class TasksViewModel(
4955
_dataLoading.value = false
5056
}
5157
}
52-
tasksRepository.observeTasks().switchMap { filterTasks(it) }
58+
tasksRepository.observeTasks().distinctUntilChanged().switchMap { filterTasks(it) }
5359
}
5460

5561
val items: LiveData<List<Task>> = _items
@@ -72,8 +78,6 @@ class TasksViewModel(
7278
private val _snackbarText = MutableLiveData<Event<Int>>()
7379
val snackbarText: LiveData<Event<Int>> = _snackbarText
7480

75-
private var currentFiltering = TasksFilterType.ALL_TASKS
76-
7781
// Not used at the moment
7882
private val isDataLoadingError = MutableLiveData<Boolean>()
7983

@@ -92,7 +96,7 @@ class TasksViewModel(
9296

9397
init {
9498
// Set initial state
95-
setFiltering(TasksFilterType.ALL_TASKS)
99+
setFiltering(getSavedFilterType())
96100
loadTasks(true)
97101
}
98102

@@ -104,23 +108,23 @@ class TasksViewModel(
104108
* [TasksFilterType.ACTIVE_TASKS]
105109
*/
106110
fun setFiltering(requestType: TasksFilterType) {
107-
currentFiltering = requestType
111+
savedStateHandle.set(TASKS_FILTER_SAVED_STATE_KEY, requestType)
108112

109113
// Depending on the filter type, set the filtering label, icon drawables, etc.
110114
when (requestType) {
111-
TasksFilterType.ALL_TASKS -> {
115+
ALL_TASKS -> {
112116
setFilter(
113117
R.string.label_all, R.string.no_tasks_all,
114118
R.drawable.logo_no_fill, true
115119
)
116120
}
117-
TasksFilterType.ACTIVE_TASKS -> {
121+
ACTIVE_TASKS -> {
118122
setFilter(
119123
R.string.label_active, R.string.no_tasks_active,
120124
R.drawable.ic_check_circle_96dp, false
121125
)
122126
}
123-
TasksFilterType.COMPLETED_TASKS -> {
127+
COMPLETED_TASKS -> {
124128
setFilter(
125129
R.string.label_completed, R.string.no_tasks_completed,
126130
R.drawable.ic_verified_user_96dp, false
@@ -195,7 +199,7 @@ class TasksViewModel(
195199
if (tasksResult is Success) {
196200
isDataLoadingError.value = false
197201
viewModelScope.launch {
198-
result.value = filterItems(tasksResult.data, currentFiltering)
202+
result.value = filterItems(tasksResult.data, getSavedFilterType())
199203
}
200204
} else {
201205
result.value = emptyList()
@@ -218,11 +222,11 @@ class TasksViewModel(
218222
// We filter the tasks based on the requestType
219223
for (task in tasks) {
220224
when (filteringType) {
221-
TasksFilterType.ALL_TASKS -> tasksToShow.add(task)
222-
TasksFilterType.ACTIVE_TASKS -> if (task.isActive) {
225+
ALL_TASKS -> tasksToShow.add(task)
226+
ACTIVE_TASKS -> if (task.isActive) {
223227
tasksToShow.add(task)
224228
}
225-
TasksFilterType.COMPLETED_TASKS -> if (task.isCompleted) {
229+
COMPLETED_TASKS -> if (task.isCompleted) {
226230
tasksToShow.add(task)
227231
}
228232
}
@@ -233,4 +237,11 @@ class TasksViewModel(
233237
fun refresh() {
234238
_forceUpdate.value = true
235239
}
240+
241+
private fun getSavedFilterType() : TasksFilterType {
242+
return savedStateHandle.get(TASKS_FILTER_SAVED_STATE_KEY) ?: ALL_TASKS
243+
}
236244
}
245+
246+
// Used to save the current filtering in SavedStateHandle.
247+
const val TASKS_FILTER_SAVED_STATE_KEY = "TASKS_FILTER_SAVED_STATE_KEY"

app/src/main/java/com/example/android/architecture/blueprints/todoapp/util/FragmentExt.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ import com.example.android.architecture.blueprints.todoapp.ViewModelFactory
2525

2626
fun Fragment.getViewModelFactory(): ViewModelFactory {
2727
val repository = (requireContext().applicationContext as TodoApplication).taskRepository
28-
return ViewModelFactory(repository)
28+
return ViewModelFactory(repository, this)
2929
}

app/src/prod/java/com/example/android/architecture/blueprints/todoapp/ServiceLocator.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ object ServiceLocator {
4646
}
4747

4848
private fun createTasksRepository(context: Context): TasksRepository {
49-
val newRepo = DefaultTasksRepository(TasksRemoteDataSource, createTaskLocalDataSource(context))
49+
val newRepo =
50+
DefaultTasksRepository(TasksRemoteDataSource, createTaskLocalDataSource(context))
5051
tasksRepository = newRepo
5152
return newRepo
5253
}
@@ -59,7 +60,7 @@ object ServiceLocator {
5960
private fun createDataBase(context: Context): ToDoDatabase {
6061
val result = Room.databaseBuilder(
6162
context.applicationContext,
62-
ToDoDatabase::class.java, "Tasks.db"
63+
ToDoDatabase::class.java, DB_NAME
6364
).build()
6465
database = result
6566
return result
@@ -81,3 +82,5 @@ object ServiceLocator {
8182
}
8283
}
8384
}
85+
86+
private const val DB_NAME = "Tasks.db"

app/src/test/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksViewModelTest.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.example.android.architecture.blueprints.todoapp.tasks
1818

1919
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
20+
import androidx.lifecycle.SavedStateHandle
2021
import com.example.android.architecture.blueprints.todoapp.MainCoroutineRule
2122
import com.example.android.architecture.blueprints.todoapp.R
2223
import com.example.android.architecture.blueprints.todoapp.assertLiveDataEventTriggered
@@ -62,7 +63,7 @@ class TasksViewModelTest {
6263
val task3 = Task("Title3", "Description3", true)
6364
tasksRepository.addTasks(task1, task2, task3)
6465

65-
tasksViewModel = TasksViewModel(tasksRepository)
66+
tasksViewModel = TasksViewModel(tasksRepository, SavedStateHandle())
6667
}
6768

6869
@Test

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ android.enableJetifier=true
2020
android.useAndroidX=true
2121
android.enableUnitTestBinaryResources=true
2222
android.enableR8=true
23+
kapt.incremental.apt=true

0 commit comments

Comments
 (0)