1 /**
<lambda>null2  * 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.categories
17 
18 import android.health.connect.HealthConnectManager
19 import android.health.connect.RecordTypeInfoResponse
20 import android.health.connect.datatypes.Record
21 import androidx.core.os.asOutcomeReceiver
22 import com.android.healthconnect.controller.service.IoDispatcher
23 import com.android.healthconnect.controller.shared.HEALTH_DATA_CATEGORIES
24 import com.android.healthconnect.controller.shared.HealthDataCategoryInt
25 import com.android.healthconnect.controller.shared.usecase.UseCaseResults
26 import javax.inject.Inject
27 import javax.inject.Singleton
28 import kotlinx.coroutines.CoroutineDispatcher
29 import kotlinx.coroutines.suspendCancellableCoroutine
30 import kotlinx.coroutines.withContext
31 
32 @Singleton
33 class LoadHealthCategoriesUseCase
34 @Inject
35 constructor(
36     private val healthConnectManager: HealthConnectManager,
37     @IoDispatcher private val dispatcher: CoroutineDispatcher
38 ) {
39 
40     /** Returns list of health categories to be shown in Health Connect UI. */
41     suspend operator fun invoke(): UseCaseResults<List<HealthCategoryUiState>> =
42         withContext(dispatcher) {
43             try {
44                 val recordTypeInfoMap: Map<Class<out Record>, RecordTypeInfoResponse> =
45                     suspendCancellableCoroutine { continuation ->
46                         healthConnectManager.queryAllRecordTypesInfo(
47                             Runnable::run, continuation.asOutcomeReceiver())
48                     }
49                 val categories =
50                     HEALTH_DATA_CATEGORIES.map {
51                         HealthCategoryUiState(it, hasData(it, recordTypeInfoMap))
52                     }
53                 UseCaseResults.Success(categories)
54             } catch (e: Exception) {
55                 UseCaseResults.Failed(e)
56             }
57         }
58 
59     private fun hasData(
60         category: @HealthDataCategoryInt Int,
61         recordTypeInfoMap: Map<Class<out Record>, RecordTypeInfoResponse>
62     ): Boolean {
63         return recordTypeInfoMap.values.firstOrNull {
64             it.dataCategory == category && it.contributingPackages.isNotEmpty()
65         } != null
66     }
67 }
68 
69 /**
70  * Represents Health Category group to be shown in health connect screens.
71  *
72  * @param category Category id
73  * @param hasData represent this category with related data in health connect.
74  */
75 data class HealthCategoryUiState(val category: @HealthDataCategoryInt Int, val hasData: Boolean)
76