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.permissiontypes 17 18 import android.util.Log 19 import androidx.lifecycle.LiveData 20 import androidx.lifecycle.MutableLiveData 21 import androidx.lifecycle.ViewModel 22 import androidx.lifecycle.viewModelScope 23 import com.android.healthconnect.controller.permissions.data.HealthPermissionType 24 import com.android.healthconnect.controller.permissiontypes.api.FilterPermissionTypesUseCase 25 import com.android.healthconnect.controller.permissiontypes.api.LoadContributingAppsUseCase 26 import com.android.healthconnect.controller.permissiontypes.api.LoadPermissionTypesUseCase 27 import com.android.healthconnect.controller.permissiontypes.api.LoadPriorityListUseCase 28 import com.android.healthconnect.controller.permissiontypes.api.UpdatePriorityListUseCase 29 import com.android.healthconnect.controller.shared.HealthDataCategoryInt 30 import com.android.healthconnect.controller.shared.app.AppInfoReader 31 import com.android.healthconnect.controller.shared.app.AppMetadata 32 import com.android.healthconnect.controller.shared.usecase.UseCaseResults 33 import dagger.hilt.android.lifecycle.HiltViewModel 34 import javax.inject.Inject 35 import kotlinx.coroutines.launch 36 37 @HiltViewModel 38 class HealthPermissionTypesViewModel 39 @Inject 40 constructor( 41 private val loadPermissionTypesUseCase: LoadPermissionTypesUseCase, 42 private val loadPriorityListUseCase: LoadPriorityListUseCase, 43 private val updatePriorityListUseCase: UpdatePriorityListUseCase, 44 private val appInfoReader: AppInfoReader, 45 private val loadContributingAppsUseCase: LoadContributingAppsUseCase, 46 private val filterPermissionTypesUseCase: FilterPermissionTypesUseCase 47 ) : ViewModel() { 48 49 companion object { 50 private const val TAG = "PriorityListView" 51 } 52 53 private val _permissionTypesData = MutableLiveData<PermissionTypesState>() 54 private val _priorityList = MutableLiveData<PriorityListState>() 55 private val _appsWithData = MutableLiveData<AppsWithDataFragmentState>() 56 private val _selectedAppFilter = MutableLiveData("All apps") 57 private val _editedPriorityList = MutableLiveData<List<AppMetadata>>() 58 private val _categoryLabel = MutableLiveData<String>() 59 60 /** Provides a list of [HealthPermissionType]s displayed in [HealthPermissionTypesFragment]. */ 61 val permissionTypesData: LiveData<PermissionTypesState> 62 get() = _permissionTypesData 63 64 /** 65 * Provides a list of [AppMetadata]s used in [HealthPermissionTypesFragment] and in 66 * [com.android.healthconnect.controller.permissiontypes.prioritylist.PriorityListDialogFragment]. 67 */ 68 val priorityList: LiveData<PriorityListState> 69 get() = _priorityList 70 71 72 /** Provides a list of apps with data in [HealthPermissionTypesFragment]. */ 73 val appsWithData: LiveData<AppsWithDataFragmentState> 74 get() = _appsWithData 75 76 /** Stores currently selected app filter. */ 77 val selectedAppFilter: LiveData<String> 78 get() = _selectedAppFilter 79 80 /** 81 * Provides a reordered version of [priorityList] that is shown on the priority dialog but is 82 * not saved yet by the user. 83 * 84 * If it is empty then the original [priorityList] should be shown on the dialog. 85 */ 86 val editedPriorityList: LiveData<List<AppMetadata>> 87 get() = _editedPriorityList 88 89 val categoryLabel: LiveData<String> 90 get() = _categoryLabel 91 setAppFilternull92 fun setAppFilter(selectedAppFilter: String) { 93 _selectedAppFilter.postValue(selectedAppFilter) 94 } 95 setEditedPriorityListnull96 fun setEditedPriorityList(newList: List<AppMetadata>) { 97 _editedPriorityList.postValue(newList) 98 } 99 setCategoryLabelnull100 fun setCategoryLabel(label: String) { 101 _categoryLabel.postValue(label) 102 } 103 loadDatanull104 fun loadData(category: @HealthDataCategoryInt Int) { 105 _permissionTypesData.postValue(PermissionTypesState.Loading) 106 _priorityList.postValue(PriorityListState.Loading) 107 108 viewModelScope.launch { 109 val permissionTypes = loadPermissionTypesUseCase.invoke(category) 110 _permissionTypesData.postValue(PermissionTypesState.WithData(permissionTypes)) 111 112 when (val result = loadPriorityListUseCase.invoke(category)) { 113 is UseCaseResults.Success -> { 114 _priorityList.postValue( 115 if (result.data.isEmpty()) { 116 PriorityListState.WithData(listOf()) 117 } else { 118 PriorityListState.WithData(result.data) 119 }) 120 } 121 is UseCaseResults.Failed -> { 122 Log.e(TAG, "Load error ", result.exception) 123 _priorityList.postValue(PriorityListState.LoadingFailed) 124 } 125 } 126 } 127 } 128 loadAppsWithDatanull129 fun loadAppsWithData(category: @HealthDataCategoryInt Int) { 130 _appsWithData.postValue(AppsWithDataFragmentState.Loading) 131 viewModelScope.launch { 132 val appsWithHealthPermissions = loadContributingAppsUseCase.invoke(category) 133 _appsWithData.postValue(AppsWithDataFragmentState.WithData(appsWithHealthPermissions)) 134 } 135 } 136 filterPermissionTypesnull137 fun filterPermissionTypes( 138 category: @HealthDataCategoryInt Int, 139 selectedAppPackageName: String 140 ) { 141 _permissionTypesData.postValue(PermissionTypesState.Loading) 142 viewModelScope.launch { 143 val permissionTypes = 144 filterPermissionTypesUseCase.invoke(category, selectedAppPackageName) 145 if (permissionTypes.isNotEmpty()) { 146 _permissionTypesData.postValue(PermissionTypesState.WithData(permissionTypes)) 147 } else { 148 val allPermissionTypes = loadPermissionTypesUseCase.invoke(category) 149 _permissionTypesData.postValue(PermissionTypesState.WithData(allPermissionTypes)) 150 } 151 } 152 } 153 updatePriorityListnull154 fun updatePriorityList(category: @HealthDataCategoryInt Int, newPriorityList: List<String>) { 155 _priorityList.postValue(PriorityListState.Loading) 156 157 viewModelScope.launch { 158 updatePriorityListUseCase.invoke(newPriorityList, category) 159 val appMetadataList: List<AppMetadata> = 160 newPriorityList.map { appInfoReader.getAppMetadata(it) } 161 _priorityList.postValue(PriorityListState.WithData(appMetadataList)) 162 } 163 } 164 165 sealed class PermissionTypesState { 166 object Loading : PermissionTypesState() 167 data class WithData(val permissionTypes: List<HealthPermissionType>) : 168 PermissionTypesState() 169 } 170 171 sealed class PriorityListState { 172 object Loading : PriorityListState() 173 object LoadingFailed : PriorityListState() 174 data class WithData(val priorityList: List<AppMetadata>) : PriorityListState() 175 } 176 177 sealed class AppsWithDataFragmentState { 178 object Loading : AppsWithDataFragmentState() 179 data class WithData(val appsWithData: List<AppMetadata>) : AppsWithDataFragmentState() 180 } 181 } 182