1 /** 2 * Copyright (C) 2024 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.ExercisePerformanceGoal 20 import android.health.connect.datatypes.ExerciseSegmentType 21 import android.icu.text.MessageFormat 22 import android.util.Log 23 import com.android.healthconnect.controller.R 24 import com.android.healthconnect.controller.data.entries.FormattedEntry 25 import com.android.healthconnect.controller.data.entries.FormattedEntry.ExercisePerformanceGoalEntry 26 import com.android.healthconnect.controller.dataentries.formatters.SpeedFormatter.Companion.ACTIVITY_TYPES_WITH_PACE_VELOCITY 27 import com.android.healthconnect.controller.dataentries.formatters.SpeedFormatter.Companion.SWIMMING_ACTIVITY_TYPES 28 import com.android.healthconnect.controller.dataentries.units.UnitPreferences 29 import dagger.hilt.android.qualifiers.ApplicationContext 30 import javax.inject.Inject 31 32 /** Formatter for printing ExercisePerformanceGoal data. */ 33 class ExercisePerformanceGoalFormatter 34 @Inject 35 constructor( 36 @ApplicationContext private val context: Context, 37 private val speedFormatter: SpeedFormatter 38 ) { 39 private val ACTIVITY_TYPES_WITH_CADENCE_MOTION = 40 listOf( 41 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BIKING, 42 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BIKING_STATIONARY, 43 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_ROWING_MACHINE, 44 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_WHEELCHAIR) 45 formatGoalnull46 fun formatGoal( 47 goal: ExercisePerformanceGoal, 48 unitPreferences: UnitPreferences, 49 exerciseSegmentType: Int 50 ): FormattedEntry { 51 return ExercisePerformanceGoalEntry( 52 goal = goal, 53 title = formatPerformanceGoal(goal, unitPreferences, exerciseSegmentType), 54 titleA11y = formatPerformanceGoalA11y(goal, unitPreferences, exerciseSegmentType)) 55 } 56 formatPerformanceGoalnull57 private fun formatPerformanceGoal( 58 performanceGoal: ExercisePerformanceGoal, 59 unitPreferences: UnitPreferences, 60 exerciseSegmentType: Int 61 ): String { 62 return when (performanceGoal) { 63 is ExercisePerformanceGoal.PowerGoal -> 64 context.getString( 65 R.string.performance_goals_range, 66 MessageFormat.format( 67 context.getString(R.string.watt_format), 68 mapOf("value" to performanceGoal.minPower.inWatts)), 69 MessageFormat.format( 70 context.getString(R.string.watt_format), 71 mapOf("value" to performanceGoal.maxPower.inWatts))) 72 is ExercisePerformanceGoal.AmrapGoal -> 73 context.getString(R.string.amrap_performance_goal) 74 is ExercisePerformanceGoal.CadenceGoal -> { 75 if (ACTIVITY_TYPES_WITH_CADENCE_MOTION.contains(exerciseSegmentType)) { 76 return context.getString( 77 R.string.performance_goals_range, 78 MessageFormat.format( 79 context.getString(R.string.cycling_rpm), 80 mapOf("count" to performanceGoal.minRpm)), 81 MessageFormat.format( 82 context.getString(R.string.cycling_rpm), 83 mapOf("count" to performanceGoal.maxRpm))) 84 } 85 return context.getString( 86 R.string.performance_goals_range, 87 MessageFormat.format( 88 context.getString(R.string.steps_per_minute), 89 mapOf("value" to performanceGoal.minRpm)), 90 MessageFormat.format( 91 context.getString(R.string.steps_per_minute), 92 mapOf("value" to performanceGoal.maxRpm))) 93 } 94 is ExercisePerformanceGoal.SpeedGoal -> 95 if (ACTIVITY_TYPES_WITH_PACE_VELOCITY.contains(exerciseSegmentType) || 96 SWIMMING_ACTIVITY_TYPES.contains(exerciseSegmentType)) 97 context.getString( 98 R.string.performance_goals_range, 99 speedFormatter.formatSpeedValue( 100 performanceGoal.maxSpeed, unitPreferences, exerciseSegmentType), 101 speedFormatter.formatSpeedValue( 102 performanceGoal.minSpeed, unitPreferences, exerciseSegmentType)) 103 else 104 context.getString( 105 R.string.performance_goals_range, 106 speedFormatter.formatSpeedValue( 107 performanceGoal.minSpeed, unitPreferences, exerciseSegmentType), 108 speedFormatter.formatSpeedValue( 109 performanceGoal.maxSpeed, unitPreferences, exerciseSegmentType)) 110 is ExercisePerformanceGoal.HeartRateGoal -> 111 context.getString( 112 R.string.performance_goals_range, 113 MessageFormat.format( 114 context.getString(R.string.heart_rate_value), 115 mapOf("count" to performanceGoal.minBpm)), 116 MessageFormat.format( 117 context.getString(R.string.heart_rate_value), 118 mapOf("count" to performanceGoal.maxBpm))) 119 is ExercisePerformanceGoal.WeightGoal -> 120 MassFormatter.formatValue( 121 context, performanceGoal.mass, unitPreferences.getWeightUnit()) 122 is ExercisePerformanceGoal.RateOfPerceivedExertionGoal -> 123 context.getString(R.string.rate_of_perceived_exertion_goal, performanceGoal.rpe) 124 else -> { 125 Log.e("Error", "Unknown performance goal $performanceGoal") 126 return "" 127 } 128 } 129 } 130 formatPerformanceGoalA11ynull131 private fun formatPerformanceGoalA11y( 132 performanceGoal: ExercisePerformanceGoal, 133 unitPreferences: UnitPreferences, 134 exerciseSegmentType: Int 135 ): String { 136 return when (performanceGoal) { 137 is ExercisePerformanceGoal.PowerGoal -> 138 context.getString( 139 R.string.performance_goals_range, 140 MessageFormat.format( 141 context.getString(R.string.watt_format_long), 142 mapOf("value" to performanceGoal.minPower.inWatts)), 143 MessageFormat.format( 144 context.getString(R.string.watt_format_long), 145 mapOf("value" to performanceGoal.maxPower.inWatts))) 146 is ExercisePerformanceGoal.AmrapGoal -> 147 context.getString(R.string.amrap_performance_goal) 148 is ExercisePerformanceGoal.CadenceGoal -> { 149 if (ACTIVITY_TYPES_WITH_CADENCE_MOTION.contains(exerciseSegmentType)) { 150 return context.getString( 151 R.string.performance_goals_range, 152 MessageFormat.format( 153 context.getString(R.string.cycling_rpm_long), 154 mapOf("count" to performanceGoal.minRpm)), 155 MessageFormat.format( 156 context.getString(R.string.cycling_rpm_long), 157 mapOf("count" to performanceGoal.maxRpm))) 158 } 159 return context.getString( 160 R.string.performance_goals_range, 161 MessageFormat.format( 162 context.getString(R.string.steps_per_minute_long), 163 mapOf("value" to performanceGoal.minRpm)), 164 MessageFormat.format( 165 context.getString(R.string.steps_per_minute_long), 166 mapOf("value" to performanceGoal.maxRpm))) 167 } 168 is ExercisePerformanceGoal.SpeedGoal -> 169 if (ACTIVITY_TYPES_WITH_PACE_VELOCITY.contains(exerciseSegmentType) || 170 SWIMMING_ACTIVITY_TYPES.contains(exerciseSegmentType)) 171 context.getString( 172 R.string.performance_goals_range, 173 speedFormatter.formatA11ySpeedValue( 174 performanceGoal.maxSpeed, unitPreferences, exerciseSegmentType), 175 speedFormatter.formatA11ySpeedValue( 176 performanceGoal.minSpeed, unitPreferences, exerciseSegmentType)) 177 else 178 context.getString( 179 R.string.performance_goals_range, 180 speedFormatter.formatA11ySpeedValue( 181 performanceGoal.minSpeed, unitPreferences, exerciseSegmentType), 182 speedFormatter.formatA11ySpeedValue( 183 performanceGoal.maxSpeed, unitPreferences, exerciseSegmentType)) 184 is ExercisePerformanceGoal.HeartRateGoal -> 185 context.getString( 186 R.string.performance_goals_range, 187 MessageFormat.format( 188 context.getString(R.string.heart_rate_long_value), 189 mapOf("count" to performanceGoal.minBpm)), 190 MessageFormat.format( 191 context.getString(R.string.heart_rate_long_value), 192 mapOf("count" to performanceGoal.maxBpm))) 193 is ExercisePerformanceGoal.WeightGoal -> 194 MassFormatter.formatA11yValue( 195 context, performanceGoal.mass, unitPreferences.getWeightUnit()) 196 is ExercisePerformanceGoal.RateOfPerceivedExertionGoal -> 197 context.getString(R.string.rate_of_perceived_exertion_goal, performanceGoal.rpe) 198 else -> { 199 Log.e("Error", "Unknown performance goal $performanceGoal") 200 return "" 201 } 202 } 203 } 204 } 205