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.testapps.toolbox.utils
17 
18 import android.content.Context
19 import android.content.Intent
20 import android.health.connect.HealthConnectManager
21 import android.health.connect.InsertRecordsResponse
22 import android.health.connect.ReadRecordsRequest
23 import android.health.connect.ReadRecordsRequestUsingFilters
24 import android.health.connect.ReadRecordsResponse
25 import android.health.connect.TimeRangeFilter
26 import android.health.connect.datatypes.DataOrigin
27 import android.health.connect.datatypes.Device
28 import android.health.connect.datatypes.Metadata
29 import android.health.connect.datatypes.Record
30 import android.os.Build.MANUFACTURER
31 import android.os.Build.MODEL
32 import android.util.Log
33 import androidx.appcompat.app.AlertDialog
34 import androidx.core.os.asOutcomeReceiver
35 import com.android.healthconnect.testapps.toolbox.R
36 import java.io.Serializable
37 import java.lang.reflect.Modifier
38 import kotlin.reflect.KClass
39 import kotlinx.coroutines.suspendCancellableCoroutine
40 
41 class GeneralUtils {
42 
43     companion object {
44         fun getMetaData(context: Context, recordUuid: String): Metadata {
45             val device: Device =
46                 Device.Builder().setManufacturer(MANUFACTURER).setModel(MODEL).setType(1).build()
47             val dataOrigin = DataOrigin.Builder().setPackageName(context.packageName).build()
48             return Metadata.Builder()
49                 .setDevice(device)
50                 .setDataOrigin(dataOrigin)
51                 .setId(recordUuid)
52                 .build()
53         }
54 
55         fun getMetaData(context: Context): Metadata {
56             val device: Device =
57                 Device.Builder().setManufacturer(MANUFACTURER).setModel(MODEL).setType(1).build()
58             val dataOrigin = DataOrigin.Builder().setPackageName(context.packageName).build()
59             return Metadata.Builder().setDevice(device).setDataOrigin(dataOrigin).build()
60         }
61 
62         suspend fun <T : Record> insertRecords(
63             records: List<T>,
64             manager: HealthConnectManager,
65         ): List<Record> {
66 
67             val insertedRecords =
68                 try {
69                     suspendCancellableCoroutine<InsertRecordsResponse> { continuation ->
70                             manager.insertRecords(
71                                 records,
72                                 Runnable::run,
73                                 continuation.asOutcomeReceiver(),
74                             )
75                         }
76                         .records
77                 } catch (ex: Exception) {
78                     throw ex
79                 }
80             return insertedRecords
81         }
82 
83         suspend fun <T : Record> updateRecords(records: List<T>, manager: HealthConnectManager) {
84             try {
85                 suspendCancellableCoroutine<Void> { continuation ->
86                     manager.updateRecords(records, Runnable::run, continuation.asOutcomeReceiver())
87                 }
88             } catch (ex: Exception) {
89                 throw ex
90             }
91         }
92 
93         fun <T : Any> getStaticFieldNamesAndValues(obj: KClass<T>): EnumFieldsWithValues {
94             return obj.java.declaredFields
95                 .filter { field ->
96                     Modifier.isStatic(field.modifiers) && field.type == Int::class.java
97                 }
98                 .associate { it.name to it.get(obj)!! }
99                 .let { EnumFieldsWithValues(it) }
100         }
101 
102         suspend fun readRecords(
103             recordType: Class<out Record>,
104             timeFilterRange: TimeRangeFilter,
105             numberOfRecordsPerBatch: Long,
106             manager: HealthConnectManager,
107         ): List<Record> {
108             val filter =
109                 ReadRecordsRequestUsingFilters.Builder(recordType)
110                     .setTimeRangeFilter(timeFilterRange)
111                     .setPageSize(numberOfRecordsPerBatch.toInt())
112                     .build()
113             val records =
114                 suspendCancellableCoroutine<ReadRecordsResponse<*>> { continuation ->
115                         manager.readRecords(filter, Runnable::run, continuation.asOutcomeReceiver())
116                     }
117                     .records
118             Log.d("READ_RECORDS", "Read ${records.size} records")
119             return records
120         }
121 
122         suspend fun <T : Record> readRecords(
123             manager: HealthConnectManager,
124             request: ReadRecordsRequest<T>,
125         ): List<T> {
126             val records =
127                 suspendCancellableCoroutine<ReadRecordsResponse<T>> { continuation ->
128                         manager.readRecords(
129                             request,
130                             Runnable::run,
131                             continuation.asOutcomeReceiver(),
132                         )
133                     }
134                     .records
135             Log.d("READ_RECORDS", "Read ${records.size} records")
136             return records
137         }
138 
139         inline fun <reified T> Context.requireSystemService(): T =
140             requireNotNull(getSystemService(T::class.java))
141 
142         fun Intent.requireStringExtra(name: String): String = requireNotNull(getStringExtra(name))
143 
144         fun Intent.requireByteArrayExtra(name: String): ByteArray =
145             requireNotNull(getByteArrayExtra(name))
146 
147         inline fun <reified T : Serializable> Intent.requireSerializable(name: String): T =
148             requireNotNull(getSerializableExtra(name, T::class.java))
149 
150         fun Context.showMessageDialog(text: String) {
151             AlertDialog.Builder(this)
152                 .setTitle(R.string.app_label)
153                 .setNegativeButton(android.R.string.cancel, null)
154                 .setMessage(text)
155                 .show()
156         }
157     }
158 }
159