1 /** <lambda>null2 * Copyright (C) 2023 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 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package com.android.healthconnect.controller.entrydetails 15 16 import android.health.connect.HealthConnectManager 17 import android.health.connect.ReadRecordsRequestUsingIds 18 import android.health.connect.ReadRecordsResponse 19 import android.health.connect.datatypes.Record 20 import androidx.core.os.asOutcomeReceiver 21 import com.android.healthconnect.controller.data.entries.FormattedEntry 22 import com.android.healthconnect.controller.dataentries.formatters.shared.HealthDataEntryDetailsFormatter 23 import com.android.healthconnect.controller.dataentries.formatters.shared.HealthDataEntryFormatter 24 import com.android.healthconnect.controller.permissions.data.HealthPermissionType 25 import com.android.healthconnect.controller.service.IoDispatcher 26 import com.android.healthconnect.controller.shared.HealthPermissionToDatatypeMapper 27 import com.android.healthconnect.controller.shared.usecase.BaseUseCase 28 import javax.inject.Inject 29 import kotlinx.coroutines.CoroutineDispatcher 30 import kotlinx.coroutines.suspendCancellableCoroutine 31 32 class LoadEntryDetailsUseCase 33 @Inject 34 constructor( 35 private val healthConnectManager: HealthConnectManager, 36 private val entryFormatter: HealthDataEntryFormatter, 37 private val entryDetailsFormatter: HealthDataEntryDetailsFormatter, 38 @IoDispatcher private val dispatcher: CoroutineDispatcher 39 ) : BaseUseCase<LoadDataEntryInput, List<FormattedEntry>>(dispatcher) { 40 41 override suspend fun execute(input: LoadDataEntryInput): List<FormattedEntry> { 42 val dataTypes = HealthPermissionToDatatypeMapper.getDataTypes(input.permissionType) 43 val results = dataTypes.map { record -> readDataType(record, input.entryId) }.flatten() 44 return if (results.isEmpty()) { 45 throw IllegalStateException("Record not found!") 46 } else if (results.size > 1) { 47 throw IllegalStateException("Multiple records found with the same id: ${input.entryId}") 48 } else { 49 formatEntry(results[0], input.showDataOrigin) 50 } 51 } 52 53 private suspend fun readDataType(record: Class<out Record>, id: String): List<Record> { 54 val filter = ReadRecordsRequestUsingIds.Builder(record).addId(id).build() 55 val response = 56 suspendCancellableCoroutine<ReadRecordsResponse<*>> { continuation -> 57 healthConnectManager.readRecords( 58 filter, Runnable::run, continuation.asOutcomeReceiver()) 59 } 60 return response.records.orEmpty() 61 } 62 63 private suspend fun formatEntry(record: Record, showDataOrigin: Boolean): List<FormattedEntry> { 64 return buildList { 65 // header 66 add(entryFormatter.format(record, showDataOrigin)) 67 // details 68 addAll(entryDetailsFormatter.formatDetails(record)) 69 } 70 } 71 } 72 73 data class LoadDataEntryInput( 74 val permissionType: HealthPermissionType, 75 val entryId: String, 76 val showDataOrigin: Boolean 77 ) 78