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.dataentries 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.data.entries.FormattedEntry 24 import com.android.healthconnect.controller.data.entries.FormattedEntry.FormattedAggregation 25 import com.android.healthconnect.controller.permissions.data.HealthPermissionType 26 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.DISTANCE 27 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.STEPS 28 import com.android.healthconnect.controller.permissions.data.HealthPermissionType.TOTAL_CALORIES_BURNED 29 import com.android.healthconnect.controller.shared.usecase.UseCaseResults 30 import dagger.hilt.android.lifecycle.HiltViewModel 31 import java.time.Instant 32 import javax.inject.Inject 33 import kotlinx.coroutines.launch 34 35 /** View model for {@link DataEntriesFragment} . */ 36 @HiltViewModel 37 class DataEntriesFragmentViewModel 38 @Inject 39 constructor( 40 private val loadDataEntriesUseCase: LoadDataEntriesUseCase, 41 private val loadMenstruationDataUseCase: LoadMenstruationDataUseCase, 42 private val loadDataAggregationsUseCase: LoadDataAggregationsUseCase 43 ) : ViewModel() { 44 45 companion object { 46 private const val TAG = "DataEntriesFragmentView" 47 private val AGGREGATE_HEADER_DATA_TYPES = listOf(STEPS, DISTANCE, TOTAL_CALORIES_BURNED) 48 } 49 50 private val _dataEntries = MutableLiveData<DataEntriesFragmentState>() 51 val dataEntries: LiveData<DataEntriesFragmentState> 52 get() = _dataEntries 53 54 val currentSelectedDate = MutableLiveData<Instant>() 55 loadDatanull56 fun loadData(permissionType: HealthPermissionType, selectedDate: Instant) { 57 _dataEntries.postValue(DataEntriesFragmentState.Loading) 58 currentSelectedDate.postValue(selectedDate) 59 viewModelScope.launch { 60 val list = ArrayList<FormattedEntry>() 61 val entriesResults = 62 when (permissionType) { 63 // Special-casing Menstruation as it spans multiple days 64 HealthPermissionType.MENSTRUATION -> { 65 loadMenstruationDataUseCase.invoke(selectedDate) 66 } 67 else -> { 68 loadEntries(permissionType, selectedDate) 69 } 70 } 71 when (entriesResults) { 72 is UseCaseResults.Success -> { 73 list.addAll(entriesResults.data) 74 if (list.isEmpty()) { 75 _dataEntries.postValue(DataEntriesFragmentState.Empty) 76 } else { 77 addAggregation(permissionType, selectedDate, list) 78 _dataEntries.postValue(DataEntriesFragmentState.WithData(list)) 79 } 80 } 81 is UseCaseResults.Failed -> { 82 Log.e(TAG, "Loading error ", entriesResults.exception) 83 _dataEntries.postValue(DataEntriesFragmentState.LoadingFailed) 84 } 85 } 86 } 87 } 88 loadAggregationnull89 private suspend fun loadAggregation( 90 permissionType: HealthPermissionType, 91 selectedDate: Instant 92 ): UseCaseResults<FormattedAggregation> { 93 val input = LoadAggregationInput(permissionType, selectedDate) 94 return loadDataAggregationsUseCase.invoke(input) 95 } 96 loadEntriesnull97 private suspend fun loadEntries( 98 permissionType: HealthPermissionType, 99 selectedDate: Instant 100 ): UseCaseResults<List<FormattedEntry>> { 101 val input = LoadDataEntriesInput(permissionType, selectedDate) 102 return loadDataEntriesUseCase.invoke(input) 103 } 104 addAggregationnull105 private suspend fun addAggregation( 106 permissionType: HealthPermissionType, 107 selectedDate: Instant, 108 list: ArrayList<FormattedEntry> 109 ) { 110 if (permissionType in AGGREGATE_HEADER_DATA_TYPES) { 111 when (val aggregationResult = loadAggregation(permissionType, selectedDate)) { 112 is UseCaseResults.Success -> { 113 list.add(0, aggregationResult.data) 114 } 115 is UseCaseResults.Failed -> { 116 Log.e(TAG, "Failed to load aggregation!", aggregationResult.exception) 117 } 118 } 119 } 120 } 121 122 sealed class DataEntriesFragmentState { 123 object Loading : DataEntriesFragmentState() 124 125 object Empty : DataEntriesFragmentState() 126 127 object LoadingFailed : DataEntriesFragmentState() 128 129 data class WithData(val entries: List<FormattedEntry>) : DataEntriesFragmentState() 130 } 131 } 132