<lambda>null1 package com.android.healthconnect.controller.permissiontypes.api
2 
3 import android.health.connect.HealthConnectManager
4 import android.health.connect.HealthDataCategory
5 import android.health.connect.RecordTypeInfoResponse
6 import android.health.connect.datatypes.DataOrigin
7 import android.health.connect.datatypes.Record
8 import androidx.core.os.asOutcomeReceiver
9 import com.android.healthconnect.controller.service.IoDispatcher
10 import com.android.healthconnect.controller.shared.HealthDataCategoryInt
11 import com.android.healthconnect.controller.shared.app.AppInfoReader
12 import com.android.healthconnect.controller.shared.app.AppMetadata
13 import javax.inject.Inject
14 import javax.inject.Singleton
15 import kotlinx.coroutines.CoroutineDispatcher
16 import kotlinx.coroutines.suspendCancellableCoroutine
17 import kotlinx.coroutines.withContext
18 
19 @Singleton
20 class LoadContributingAppsUseCase
21 @Inject
22 constructor(
23     private val appInfoReader: AppInfoReader,
24     private val healthConnectManager: HealthConnectManager,
25     @IoDispatcher private val dispatcher: CoroutineDispatcher
26 ) {
27 
28     /** Returns list of contributing apps for selected [HealthDataCategory] */
29     suspend operator fun invoke(category: @HealthDataCategoryInt Int): List<AppMetadata> =
30         withContext(dispatcher) {
31             val appsList: MutableSet<AppMetadata> = mutableSetOf()
32             val contributingAppsList = getListOfContributingApps(category)
33             contributingAppsList.forEach { contributingApp ->
34                 if (appsList.none { it.packageName == contributingApp }) {
35                     appsList.add(appInfoReader.getAppMetadata(contributingApp))
36                 }
37             }
38             alphabeticallySortedMetadataList(appsList)
39         }
40 
41     /** Returns list of contributing apps for selected [HealthDataCategory] */
42     private suspend fun getListOfContributingApps(
43         category: @HealthDataCategoryInt Int
44     ): List<String> =
45         withContext(dispatcher) {
46             try {
47                 val recordTypeInfoMap: Map<Class<out Record>, RecordTypeInfoResponse> =
48                     suspendCancellableCoroutine { continuation ->
49                         healthConnectManager.queryAllRecordTypesInfo(
50                             Runnable::run, continuation.asOutcomeReceiver())
51                     }
52                 filterContributingApps(category, recordTypeInfoMap).map { it.packageName }
53             } catch (e: Exception) {
54                 emptyList()
55             }
56         }
57 
58     /**
59      * Filters list of contributing apps for selected [HealthDataCategory] from all record type
60      * information
61      */
62     private fun filterContributingApps(
63         category: @HealthDataCategoryInt Int,
64         recordTypeInfoMap: Map<Class<out Record>, RecordTypeInfoResponse>
65     ): List<DataOrigin> =
66         recordTypeInfoMap.values
67             .filter { it.dataCategory == category && it.contributingPackages.isNotEmpty() }
68             .map { it.contributingPackages }
69             .flatten()
70 
71     /** Sorts list of App Metadata alphabetically */
72     private fun alphabeticallySortedMetadataList(
73         packageNames: Set<AppMetadata>
74     ): List<AppMetadata> {
75         return packageNames
76             .stream()
77             .sorted(Comparator.comparing { appMetaData -> appMetaData.appName })
78             .toList()
79     }
80 }
81