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