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.dataentries.formatters
17 
18 import android.content.Context
19 import android.health.connect.datatypes.HeartRateRecord
20 import android.icu.text.MessageFormat
21 import androidx.annotation.StringRes
22 import com.android.healthconnect.controller.R
23 import com.android.healthconnect.controller.data.entries.FormattedEntry
24 import com.android.healthconnect.controller.dataentries.formatters.shared.EntryFormatter
25 import com.android.healthconnect.controller.dataentries.formatters.shared.RecordDetailsFormatter
26 import com.android.healthconnect.controller.dataentries.units.UnitPreferences
27 import com.android.healthconnect.controller.utils.LocalDateTimeFormatter
28 import dagger.hilt.android.qualifiers.ApplicationContext
29 import javax.inject.Inject
30 import javax.inject.Singleton
31 
32 /** Formatter for printing HeartRate data. */
33 @Singleton
34 class HeartRateFormatter @Inject constructor(@ApplicationContext private val context: Context) :
35     EntryFormatter<HeartRateRecord>(context), RecordDetailsFormatter<HeartRateRecord> {
36 
37     private val timeFormatter = LocalDateTimeFormatter(context)
38 
39     override suspend fun formatRecord(
40         record: HeartRateRecord,
41         header: String,
42         headerA11y: String,
43         unitPreferences: UnitPreferences
44     ): FormattedEntry {
45         return FormattedEntry.SeriesDataEntry(
46             uuid = record.metadata.id,
47             header = header,
48             headerA11y = headerA11y,
49             title = formatValue(record, unitPreferences),
50             titleA11y = formatA11yValue(record, unitPreferences),
51             dataType = getDataType(record))
52     }
53 
54     override suspend fun formatValue(
55         record: HeartRateRecord,
56         unitPreferences: UnitPreferences
57     ): String {
58         return if (record.samples.size == 1) {
59             formatSampleValue(R.string.heart_rate_value, record.samples.first().beatsPerMinute)
60         } else {
61             formatRange(R.string.heart_rate_series_range, record) { heartRate: Long ->
62                 formatSampleValue(R.string.heart_rate_value, heartRate)
63             }
64         }
65     }
66 
67     override suspend fun formatA11yValue(
68         record: HeartRateRecord,
69         unitPreferences: UnitPreferences
70     ): String {
71         return if (record.samples.size == 1) {
72             formatSampleValue(R.string.heart_rate_long_value, record.samples.first().beatsPerMinute)
73         } else {
74             return formatRange(
75                 R.string.heart_rate_series_range_long,
76                 record,
77             ) { heartRate: Long ->
78                 formatSampleValue(R.string.heart_rate_long_value, heartRate)
79             }
80         }
81     }
82 
83     override suspend fun formatRecordDetails(record: HeartRateRecord): List<FormattedEntry> {
84         val samples =
85             record.samples.sortedBy { it.time }.map { formatSample(record.metadata.id, it) }
86         return buildList {
87             if (samples.isNotEmpty()) {
88                 addAll(samples)
89             }
90         }
91     }
92 
93     private fun formatSample(
94         id: String,
95         sample: HeartRateRecord.HeartRateSample
96     ): FormattedEntry.FormattedSessionDetail {
97         return FormattedEntry.FormattedSessionDetail(
98             uuid = id,
99             header = timeFormatter.formatTime(sample.time),
100             headerA11y = timeFormatter.formatTime(sample.time),
101             title = formatSampleValue(R.string.heart_rate_value, sample.beatsPerMinute),
102             titleA11y = formatSampleValue(R.string.heart_rate_long_value, sample.beatsPerMinute),
103         )
104     }
105 
106     private fun formatSampleValue(@StringRes res: Int, heartRate: Long): String {
107         return MessageFormat.format(context.getString(res), mapOf("count" to heartRate))
108     }
109 
110     private fun formatRange(
111         @StringRes res: Int,
112         record: HeartRateRecord,
113         getSample: (heartRate: Long) -> String
114     ): String {
115         val min = record.samples.minOf { it.beatsPerMinute }
116         val max = record.samples.maxOf { it.beatsPerMinute }
117         return context.getString(res, getSample(min), getSample(max))
118     }
119 }
120