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 class DeletionViewModel 36 @Inject 37 constructor( 38 private val deleteAllDataUseCase: DeleteAllDataUseCase, 39 private val deleteCategoryUseCase: DeleteCategoryUseCase, 40 private val deletePermissionTypeUseCase: DeletePermissionTypeUseCase, 41 private val deleteEntryUseCase: DeleteEntryUseCase, 42 private val deleteAppDataUseCase: DeleteAppDataUseCase 43 ) : ViewModel() { 44 45 companion object { 46 private const val TAG = "DeletionViewModel" 47 } 48 49 private val _deletionParameters = MutableLiveData(DeletionParameters()) 50 private var _removePermissions = false 51 52 val deletionParameters: LiveData<DeletionParameters> 53 get() = _deletionParameters 54 55 val showTimeRangeDialogFragment: Boolean 56 get() = currentDeletionParameters().showTimeRangePickerDialog 57 currentDeletionParametersnull58 private fun currentDeletionParameters() = _deletionParameters.value!! 59 60 fun setRemovePermissions(boolean: Boolean) { 61 _removePermissions = boolean 62 } 63 setDeletionTypenull64 fun setDeletionType(deletionType: DeletionType) { 65 val showTimeRangePickerDialog = 66 when (deletionType) { 67 is DeletionType.DeleteDataEntry -> false 68 else -> true 69 } 70 _deletionParameters.value = 71 currentDeletionParameters() 72 .copy( 73 showTimeRangePickerDialog = showTimeRangePickerDialog, 74 deletionType = deletionType) 75 } 76 setChosenRangenull77 fun setChosenRange(chosenRange: ChosenRange) { 78 _deletionParameters.value = _deletionParameters.value?.copy(chosenRange = chosenRange) 79 } 80 setEndTimenull81 fun setEndTime(endTime: Instant) { 82 _deletionParameters.value = 83 _deletionParameters.value?.copy(endTimeMs = endTime.toEpochMilli()) 84 } 85 setStartTimenull86 fun setStartTime(startTime: Instant) { 87 _deletionParameters.value = 88 _deletionParameters.value?.copy(startTimeMs = startTime.toEpochMilli()) 89 } 90 91 private var _categoriesReloadNeeded = MutableLiveData(false) 92 private val _appPermissionReloadNeeded = MutableLiveData(false) 93 94 // Whether the categories screen needs to be reloaded after category data deletion. 95 val categoriesReloadNeeded: LiveData<Boolean> 96 get() = _categoriesReloadNeeded 97 98 val appPermissionReloadNeeded: LiveData<Boolean> 99 get() = _appPermissionReloadNeeded 100 setDeletionStatenull101 private fun setDeletionState(newState: DeletionState) { 102 _deletionParameters.value = currentDeletionParameters().copy(deletionState = newState) 103 } 104 deletenull105 fun delete() { 106 viewModelScope.launch { 107 setDeletionState(DeletionState.STATE_DELETION_STARTED) 108 109 try { 110 111 setDeletionState(DeletionState.STATE_PROGRESS_INDICATOR_STARTED) 112 113 val timeRangeFilter = 114 TimeInstantRangeFilter.Builder() 115 .setStartTime(currentDeletionParameters().getStartTimeInstant()) 116 .setEndTime(currentDeletionParameters().getEndTimeInstant()) 117 .build() 118 119 when (val deletionType = currentDeletionParameters().deletionType) { 120 is DeletionType.DeleteDataEntry -> { 121 _deletionParameters.value?.let { deleteEntryUseCase.invoke(deletionType) } 122 } 123 is DeletionType.DeletionTypeAllData -> { 124 _deletionParameters.value?.let { 125 deleteAllDataUseCase.invoke(timeRangeFilter) 126 _categoriesReloadNeeded.postValue(true) 127 } 128 } 129 is DeletionType.DeletionTypeCategoryData -> { 130 deletionParameters.value?.let { 131 deleteCategoryUseCase.invoke(deletionType, timeRangeFilter) 132 _categoriesReloadNeeded.postValue(true) 133 } 134 } 135 is DeletionType.DeletionTypeHealthPermissionTypeData -> { 136 deletionParameters.value?.let { 137 deletePermissionTypeUseCase.invoke(deletionType, timeRangeFilter) 138 } 139 } 140 is DeletionType.DeletionTypeAppData -> { 141 deletionParameters.value?.let { 142 deleteAppDataUseCase.invoke( 143 deletionType, timeRangeFilter, _removePermissions) 144 if (_removePermissions) { 145 _appPermissionReloadNeeded.value = true 146 } 147 } 148 } 149 else -> {} 150 } 151 setDeletionState(DeletionState.STATE_DELETION_SUCCESSFUL) 152 } catch (error: Exception) { 153 Log.e(TAG, "Failed to delete data ${currentDeletionParameters()}", error) 154 155 setDeletionState(DeletionState.STATE_DELETION_FAILED) 156 } finally { 157 setDeletionState(DeletionState.STATE_PROGRESS_INDICATOR_CAN_END) 158 } 159 } 160 } 161 } 162