• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  *
17  */
18 
19 package com.android.healthconnect.controller.dataentries.formatters
20 
21 import android.content.Context
22 import android.health.connect.datatypes.ExerciseLap
23 import android.health.connect.datatypes.ExerciseSegment
24 import android.health.connect.datatypes.ExerciseSessionRecord
25 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_BADMINTON
26 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_BASEBALL
27 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_BASKETBALL
28 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_BIKING
29 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_BIKING_STATIONARY
30 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_BOOT_CAMP
31 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_BOXING
32 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_CALISTHENICS
33 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_CRICKET
34 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_DANCING
35 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_ELLIPTICAL
36 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_EXERCISE_CLASS
37 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_FENCING
38 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_FOOTBALL_AMERICAN
39 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_FOOTBALL_AUSTRALIAN
40 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_FRISBEE_DISC
41 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_GOLF
42 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_GUIDED_BREATHING
43 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_GYMNASTICS
44 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_HANDBALL
45 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING
46 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_HIKING
47 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_ICE_HOCKEY
48 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_ICE_SKATING
49 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_MARTIAL_ARTS
50 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_OTHER_WORKOUT
51 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_PADDLING
52 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_PARAGLIDING
53 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_PILATES
54 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_RACQUETBALL
55 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_ROCK_CLIMBING
56 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_ROLLER_HOCKEY
57 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_ROWING
58 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_ROWING_MACHINE
59 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_RUGBY
60 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_RUNNING
61 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_RUNNING_TREADMILL
62 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SAILING
63 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SCUBA_DIVING
64 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SKATING
65 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SKIING
66 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SNOWBOARDING
67 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SNOWSHOEING
68 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SOCCER
69 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SOFTBALL
70 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SQUASH
71 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_STAIR_CLIMBING
72 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_STAIR_CLIMBING_MACHINE
73 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_STRENGTH_TRAINING
74 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_STRETCHING
75 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SURFING
76 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SWIMMING_OPEN_WATER
77 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_SWIMMING_POOL
78 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_TABLE_TENNIS
79 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_TENNIS
80 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_VOLLEYBALL
81 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_WALKING
82 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_WATER_POLO
83 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_WEIGHTLIFTING
84 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_WHEELCHAIR
85 import android.health.connect.datatypes.ExerciseSessionType.EXERCISE_SESSION_TYPE_YOGA
86 import android.icu.text.MessageFormat.format
87 import com.android.healthconnect.controller.R
88 import com.android.healthconnect.controller.data.entries.FormattedEntry
89 import com.android.healthconnect.controller.data.entries.FormattedEntry.ExerciseSessionEntry
90 import com.android.healthconnect.controller.data.entries.FormattedEntry.FormattedSessionDetail
91 import com.android.healthconnect.controller.data.entries.FormattedEntry.SessionHeader
92 import com.android.healthconnect.controller.dataentries.formatters.DurationFormatter.formatDurationLong
93 import com.android.healthconnect.controller.dataentries.formatters.DurationFormatter.formatDurationShort
94 import com.android.healthconnect.controller.dataentries.formatters.shared.BaseFormatter
95 import com.android.healthconnect.controller.dataentries.formatters.shared.LengthFormatter
96 import com.android.healthconnect.controller.dataentries.formatters.shared.RecordDetailsFormatter
97 import com.android.healthconnect.controller.dataentries.units.UnitPreferences
98 import com.android.healthconnect.controller.utils.LocalDateTimeFormatter
99 import dagger.hilt.android.qualifiers.ApplicationContext
100 import java.time.Duration
101 import javax.inject.Inject
102 
103 /** Formatter for printing ExerciseSessionRecord data. */
104 class ExerciseSessionFormatter
105 @Inject
106 constructor(
107     @ApplicationContext private val context: Context,
108     private val exerciseSegmentTypeFormatter: ExerciseSegmentTypeFormatter,
109 ) : BaseFormatter<ExerciseSessionRecord>(context), RecordDetailsFormatter<ExerciseSessionRecord> {
110 
111     private val timeFormatter = LocalDateTimeFormatter(context)
112 
113     override suspend fun formatRecord(
114         record: ExerciseSessionRecord,
115         header: String,
116         headerA11y: String,
117         unitPreferences: UnitPreferences,
118     ): FormattedEntry {
119         return ExerciseSessionEntry(
120             uuid = record.metadata.id,
121             header = header,
122             headerA11y = headerA11y,
123             title = formatValue(record),
124             titleA11y = formatA11yValue(record),
125             dataType = record::class,
126             notes = getNotes(record),
127             route = record.route,
128         )
129     }
130 
131     fun formatValue(record: ExerciseSessionRecord): String {
132         return formatSession(record) { duration -> formatDurationShort(context, duration) }
133     }
134 
135     fun formatA11yValue(record: ExerciseSessionRecord): String {
136         return formatSession(record) { duration -> formatDurationLong(context, duration) }
137     }
138 
139     fun getNotes(record: ExerciseSessionRecord): String? {
140         return record.notes?.toString()
141     }
142 
143     override suspend fun formatRecordDetails(record: ExerciseSessionRecord): List<FormattedEntry> {
144         val sortedSegments =
145             record.segments.sortedBy { it.startTime }.map { formatSegment(record.metadata.id, it) }
146         val sortedLaps =
147             record.laps.sortedBy { it.startTime }.map { formatLaps(record.metadata.id, it) }
148         return buildList {
149             if (sortedSegments.isNotEmpty()) {
150                 add(SessionHeader(context.getString(R.string.exercise_segments_header)))
151                 addAll(sortedSegments)
152             }
153             if (sortedLaps.isNotEmpty()) {
154                 add(SessionHeader(context.getString(R.string.exercise_laps_header)))
155                 addAll(sortedLaps)
156             }
157         }
158     }
159 
160     private fun formatSession(
161         record: ExerciseSessionRecord,
162         formatDuration: (duration: Duration) -> String,
163     ): String {
164         val type = getExerciseType(context, record.exerciseType)
165         return if (!record.title.isNullOrBlank()) {
166             context.getString(R.string.session_title, record.title, type)
167         } else {
168             val duration = Duration.between(record.startTime, record.endTime)
169             context.getString(R.string.session_title, formatDuration(duration), type)
170         }
171     }
172 
173     private fun formatSegment(id: String, segment: ExerciseSegment): FormattedSessionDetail {
174         return FormattedSessionDetail(
175             uuid = id,
176             header = timeFormatter.formatTimeRange(segment.startTime, segment.endTime),
177             headerA11y = timeFormatter.formatTimeRangeA11y(segment.startTime, segment.endTime),
178             title = formatSegmentTitle(segment.repetitionsCount, segment.segmentType),
179             titleA11y = formatSegmentTitleA11y(segment.repetitionsCount, segment.segmentType),
180         )
181     }
182 
183     private fun formatLaps(id: String, lap: ExerciseLap): FormattedSessionDetail {
184         return FormattedSessionDetail(
185             uuid = id,
186             header = timeFormatter.formatTimeRange(lap.startTime, lap.endTime),
187             headerA11y = timeFormatter.formatTimeRangeA11y(lap.startTime, lap.endTime),
188             title = LengthFormatter.formatValue(context, lap.length, unitPreferences),
189             titleA11y = LengthFormatter.formatA11yValue(context, lap.length, unitPreferences),
190         )
191     }
192 
193     private fun formatSegmentTitle(repetitionsCount: Int, type: Int): String {
194         val segmentType = exerciseSegmentTypeFormatter.getSegmentType(type)
195         val repetitions =
196             format(context.getString(R.string.repetitions), mapOf("count" to repetitionsCount))
197         return context.getString(R.string.repetitions_format, segmentType, repetitions)
198     }
199 
200     private fun formatSegmentTitleA11y(repetitionsCount: Int, type: Int): String {
201         val segmentType = exerciseSegmentTypeFormatter.getSegmentType(type)
202         val repetitions =
203             format(context.getString(R.string.repetitions_long), mapOf("count" to repetitionsCount))
204         return context.getString(R.string.repetitions_format, segmentType, repetitions)
205     }
206 
207     companion object {
208         fun getExerciseType(context: Context, type: Int): String {
209             return when (type) {
210                 EXERCISE_SESSION_TYPE_BADMINTON -> context.getString(R.string.badminton)
211                 EXERCISE_SESSION_TYPE_BASEBALL -> context.getString(R.string.baseball)
212                 EXERCISE_SESSION_TYPE_BASKETBALL -> context.getString(R.string.basketball)
213                 EXERCISE_SESSION_TYPE_BIKING -> context.getString(R.string.biking)
214                 EXERCISE_SESSION_TYPE_BIKING_STATIONARY ->
215                     context.getString(R.string.biking_stationary)
216 
217                 EXERCISE_SESSION_TYPE_BOOT_CAMP -> context.getString(R.string.boot_camp)
218                 EXERCISE_SESSION_TYPE_BOXING -> context.getString(R.string.boxing)
219                 EXERCISE_SESSION_TYPE_CALISTHENICS -> context.getString(R.string.calisthenics)
220                 EXERCISE_SESSION_TYPE_CRICKET -> context.getString(R.string.cricket)
221                 EXERCISE_SESSION_TYPE_DANCING -> context.getString(R.string.dancing)
222                 EXERCISE_SESSION_TYPE_ELLIPTICAL -> context.getString(R.string.elliptical)
223                 EXERCISE_SESSION_TYPE_EXERCISE_CLASS -> context.getString(R.string.exercise_class)
224                 EXERCISE_SESSION_TYPE_FENCING -> context.getString(R.string.fencing)
225                 EXERCISE_SESSION_TYPE_FOOTBALL_AMERICAN ->
226                     context.getString(R.string.football_american)
227 
228                 EXERCISE_SESSION_TYPE_FOOTBALL_AUSTRALIAN ->
229                     context.getString(R.string.activity_type_australian_football)
230 
231                 EXERCISE_SESSION_TYPE_FRISBEE_DISC -> context.getString(R.string.frisbee_disc)
232                 EXERCISE_SESSION_TYPE_GOLF -> context.getString(R.string.golf)
233                 EXERCISE_SESSION_TYPE_GUIDED_BREATHING ->
234                     context.getString(R.string.guided_breathing)
235 
236                 EXERCISE_SESSION_TYPE_GYMNASTICS -> context.getString(R.string.gymnastics)
237                 EXERCISE_SESSION_TYPE_HANDBALL -> context.getString(R.string.handball)
238                 EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING ->
239                     context.getString(R.string.high_intensity_interval_training)
240 
241                 EXERCISE_SESSION_TYPE_HIKING -> context.getString(R.string.hiking)
242                 EXERCISE_SESSION_TYPE_ICE_HOCKEY -> context.getString(R.string.ice_hockey)
243                 EXERCISE_SESSION_TYPE_ICE_SKATING -> context.getString(R.string.ice_skating)
244                 EXERCISE_SESSION_TYPE_MARTIAL_ARTS -> context.getString(R.string.martial_arts)
245                 EXERCISE_SESSION_TYPE_OTHER_WORKOUT -> context.getString(R.string.workout)
246                 EXERCISE_SESSION_TYPE_PADDLING -> context.getString(R.string.paddling)
247                 EXERCISE_SESSION_TYPE_PILATES -> context.getString(R.string.pilates)
248                 EXERCISE_SESSION_TYPE_RACQUETBALL -> context.getString(R.string.racquetball)
249                 EXERCISE_SESSION_TYPE_ROCK_CLIMBING -> context.getString(R.string.rock_climbing)
250                 EXERCISE_SESSION_TYPE_ROLLER_HOCKEY -> context.getString(R.string.roller_hockey)
251                 EXERCISE_SESSION_TYPE_ROWING -> context.getString(R.string.rowing)
252                 EXERCISE_SESSION_TYPE_ROWING_MACHINE -> context.getString(R.string.rowing_machine)
253                 EXERCISE_SESSION_TYPE_RUGBY -> context.getString(R.string.rugby)
254                 EXERCISE_SESSION_TYPE_RUNNING -> context.getString(R.string.running)
255                 EXERCISE_SESSION_TYPE_RUNNING_TREADMILL ->
256                     context.getString(R.string.running_treadmill)
257 
258                 EXERCISE_SESSION_TYPE_SAILING -> context.getString(R.string.sailing)
259                 EXERCISE_SESSION_TYPE_SCUBA_DIVING -> context.getString(R.string.scuba_diving)
260                 EXERCISE_SESSION_TYPE_SKATING -> context.getString(R.string.skating)
261                 EXERCISE_SESSION_TYPE_SKIING -> context.getString(R.string.skiing)
262                 EXERCISE_SESSION_TYPE_SNOWBOARDING -> context.getString(R.string.snowboarding)
263                 EXERCISE_SESSION_TYPE_SNOWSHOEING -> context.getString(R.string.snowshoeing)
264                 EXERCISE_SESSION_TYPE_SOCCER -> context.getString(R.string.soccer)
265                 EXERCISE_SESSION_TYPE_SOFTBALL -> context.getString(R.string.softball)
266                 EXERCISE_SESSION_TYPE_SQUASH -> context.getString(R.string.squash)
267                 EXERCISE_SESSION_TYPE_STAIR_CLIMBING -> context.getString(R.string.stair_climbing)
268                 EXERCISE_SESSION_TYPE_STAIR_CLIMBING_MACHINE ->
269                     context.getString(R.string.stair_climbing_machine)
270 
271                 EXERCISE_SESSION_TYPE_STRENGTH_TRAINING ->
272                     context.getString(R.string.strength_training)
273 
274                 EXERCISE_SESSION_TYPE_STRETCHING -> context.getString(R.string.stretching)
275                 EXERCISE_SESSION_TYPE_SURFING -> context.getString(R.string.surfing)
276                 EXERCISE_SESSION_TYPE_SWIMMING_OPEN_WATER ->
277                     context.getString(R.string.swimming_open_water)
278 
279                 EXERCISE_SESSION_TYPE_SWIMMING_POOL -> context.getString(R.string.swimming_pool)
280                 EXERCISE_SESSION_TYPE_TABLE_TENNIS -> context.getString(R.string.table_tennis)
281                 EXERCISE_SESSION_TYPE_TENNIS -> context.getString(R.string.tennis)
282                 EXERCISE_SESSION_TYPE_VOLLEYBALL -> context.getString(R.string.volleyball)
283                 EXERCISE_SESSION_TYPE_WALKING -> context.getString(R.string.walking)
284                 EXERCISE_SESSION_TYPE_WATER_POLO -> context.getString(R.string.water_polo)
285                 EXERCISE_SESSION_TYPE_WEIGHTLIFTING -> context.getString(R.string.weightlifting)
286                 EXERCISE_SESSION_TYPE_WHEELCHAIR -> context.getString(R.string.wheelchair)
287                 EXERCISE_SESSION_TYPE_YOGA -> context.getString(R.string.yoga)
288                 EXERCISE_SESSION_TYPE_PARAGLIDING -> context.getString(R.string.paragliding)
289                 else -> context.getString(R.string.unknown_type)
290             }
291         }
292     }
293 }
294