1 /**
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * ```
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  * ```
10  *
11  * Unless required by applicable law or agreed to in writing, software distributed under the License
12  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.android.healthconnect.controller.deletion
17 
18 import android.health.connect.TimeInstantRangeFilter
19 import android.util.Log
20 import androidx.lifecycle.LiveData
21 import androidx.lifecycle.MutableLiveData
22 import androidx.lifecycle.ViewModel
23 import androidx.lifecycle.viewModelScope
24 import com.android.healthconnect.controller.deletion.api.DeleteAllDataUseCase
25 import com.android.healthconnect.controller.deletion.api.DeleteAppDataUseCase
26 import com.android.healthconnect.controller.deletion.api.DeleteCategoryUseCase
27 import com.android.healthconnect.controller.deletion.api.DeleteEntryUseCase
28 import com.android.healthconnect.controller.deletion.api.DeletePermissionTypeUseCase
29 import dagger.hilt.android.lifecycle.HiltViewModel
30 import java.time.Instant
31 import javax.inject.Inject
32 import kotlinx.coroutines.launch
33 
34 @HiltViewModel
35 @Deprecated("This won't be used once the NEW_INFORMATION_ARCHITECTURE feature is enabled.")
36 class DeletionViewModel
37 @Inject
38 constructor(
39     private val deleteAllDataUseCase: DeleteAllDataUseCase,
40     private val deleteCategoryUseCase: DeleteCategoryUseCase,
41     private val deletePermissionTypeUseCase: DeletePermissionTypeUseCase,
42     private val deleteEntryUseCase: DeleteEntryUseCase,
43     private val deleteAppDataUseCase: DeleteAppDataUseCase
44 ) : ViewModel() {
45 
46     companion object {
47         private const val TAG = "DeletionViewModel"
48     }
49 
50     private val _deletionParameters = MutableLiveData(DeletionParameters())
51     private var _removePermissions = false
52     var isInactiveApp = false
53 
54     val deletionParameters: LiveData<DeletionParameters>
55         get() = _deletionParameters
56 
57     val showTimeRangeDialogFragment: Boolean
58         get() = currentDeletionParameters().showTimeRangePickerDialog
59 
currentDeletionParametersnull60     private fun currentDeletionParameters() = _deletionParameters.value!!
61 
62     fun setRemovePermissions(boolean: Boolean) {
63         _removePermissions = boolean
64     }
65 
setDeletionTypenull66     fun setDeletionType(deletionType: DeletionType) {
67         val showTimeRangePickerDialog =
68             when (deletionType) {
69                 is DeletionType.DeleteDataEntry -> false
70                 else -> true
71             }
72         _deletionParameters.value =
73             currentDeletionParameters()
74                 .copy(
75                     showTimeRangePickerDialog = showTimeRangePickerDialog,
76                     deletionType = deletionType)
77     }
78 
setChosenRangenull79     fun setChosenRange(chosenRange: ChosenRange) {
80         _deletionParameters.value = _deletionParameters.value?.copy(chosenRange = chosenRange)
81     }
82 
setEndTimenull83     fun setEndTime(endTime: Instant) {
84         _deletionParameters.value =
85             _deletionParameters.value?.copy(endTimeMs = endTime.toEpochMilli())
86     }
87 
setStartTimenull88     fun setStartTime(startTime: Instant) {
89         _deletionParameters.value =
90             _deletionParameters.value?.copy(startTimeMs = startTime.toEpochMilli())
91     }
92 
93     private var _categoriesReloadNeeded = MutableLiveData(false)
94     private val _appPermissionReloadNeeded = MutableLiveData(false)
95 
96     // Whether the categories screen needs to be reloaded after category data deletion.
97     val categoriesReloadNeeded: LiveData<Boolean>
98         get() = _categoriesReloadNeeded
99 
100     val appPermissionReloadNeeded: LiveData<Boolean>
101         get() = _appPermissionReloadNeeded
102 
setDeletionStatenull103     private fun setDeletionState(newState: DeletionState) {
104         _deletionParameters.value = currentDeletionParameters().copy(deletionState = newState)
105     }
106 
deletenull107     fun delete() {
108         viewModelScope.launch {
109             setDeletionState(DeletionState.STATE_DELETION_STARTED)
110 
111             try {
112 
113                 setDeletionState(DeletionState.STATE_PROGRESS_INDICATOR_STARTED)
114 
115                 val timeRangeFilter =
116                     TimeInstantRangeFilter.Builder()
117                         .setStartTime(currentDeletionParameters().getStartTimeInstant())
118                         .setEndTime(currentDeletionParameters().getEndTimeInstant())
119                         .build()
120 
121                 when (val deletionType = currentDeletionParameters().deletionType) {
122                     is DeletionType.DeleteDataEntry -> {
123                         _deletionParameters.value?.let { deleteEntryUseCase.invoke(deletionType) }
124                     }
125                     is DeletionType.DeletionTypeAllData -> {
126                         _deletionParameters.value?.let {
127                             deleteAllDataUseCase.invoke(timeRangeFilter)
128                             _categoriesReloadNeeded.postValue(true)
129                         }
130                     }
131                     is DeletionType.DeletionTypeCategoryData -> {
132                         deletionParameters.value?.let {
133                             deleteCategoryUseCase.invoke(deletionType, timeRangeFilter)
134                             _categoriesReloadNeeded.postValue(true)
135                         }
136                     }
137                     is DeletionType.DeletionTypeHealthPermissionTypeData -> {
138                         deletionParameters.value?.let {
139                             deletePermissionTypeUseCase.invoke(deletionType, timeRangeFilter)
140                         }
141                     }
142                     is DeletionType.DeletionTypeAppData -> {
143                         deletionParameters.value?.let {
144                             deleteAppDataUseCase.invoke(
145                                 deletionType, timeRangeFilter, _removePermissions)
146                             if (_removePermissions) {
147                                 _appPermissionReloadNeeded.value = true
148                             }
149                         }
150                     }
151                     else -> {}
152                 }
153                 setDeletionState(DeletionState.STATE_DELETION_SUCCESSFUL)
154             } catch (error: Exception) {
155                 Log.e(TAG, "Failed to delete data ${currentDeletionParameters()}", error)
156 
157                 setDeletionState(DeletionState.STATE_DELETION_FAILED)
158             } finally {
159                 setDeletionState(DeletionState.STATE_PROGRESS_INDICATOR_CAN_END)
160             }
161         }
162     }
163 }
164