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