1 /* 2 * 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 package android.health.connect.internal.datatypes.utils; 18 19 import static android.health.connect.datatypes.ActiveCaloriesBurnedRecord.ACTIVE_CALORIES_TOTAL; 20 import static android.health.connect.datatypes.BasalMetabolicRateRecord.BASAL_CALORIES_TOTAL; 21 import static android.health.connect.datatypes.BloodPressureRecord.DIASTOLIC_AVG; 22 import static android.health.connect.datatypes.BloodPressureRecord.DIASTOLIC_MAX; 23 import static android.health.connect.datatypes.BloodPressureRecord.DIASTOLIC_MIN; 24 import static android.health.connect.datatypes.BloodPressureRecord.SYSTOLIC_AVG; 25 import static android.health.connect.datatypes.BloodPressureRecord.SYSTOLIC_MAX; 26 import static android.health.connect.datatypes.BloodPressureRecord.SYSTOLIC_MIN; 27 import static android.health.connect.datatypes.CyclingPedalingCadenceRecord.RPM_AVG; 28 import static android.health.connect.datatypes.CyclingPedalingCadenceRecord.RPM_MAX; 29 import static android.health.connect.datatypes.CyclingPedalingCadenceRecord.RPM_MIN; 30 import static android.health.connect.datatypes.DistanceRecord.DISTANCE_TOTAL; 31 import static android.health.connect.datatypes.ElevationGainedRecord.ELEVATION_GAINED_TOTAL; 32 import static android.health.connect.datatypes.ExerciseSessionRecord.EXERCISE_DURATION_TOTAL; 33 import static android.health.connect.datatypes.FloorsClimbedRecord.FLOORS_CLIMBED_TOTAL; 34 import static android.health.connect.datatypes.HeartRateRecord.BPM_AVG; 35 import static android.health.connect.datatypes.HeartRateRecord.BPM_MAX; 36 import static android.health.connect.datatypes.HeartRateRecord.BPM_MIN; 37 import static android.health.connect.datatypes.HeartRateRecord.HEART_MEASUREMENTS_COUNT; 38 import static android.health.connect.datatypes.HeightRecord.HEIGHT_AVG; 39 import static android.health.connect.datatypes.HeightRecord.HEIGHT_MAX; 40 import static android.health.connect.datatypes.HeightRecord.HEIGHT_MIN; 41 import static android.health.connect.datatypes.HydrationRecord.VOLUME_TOTAL; 42 import static android.health.connect.datatypes.NutritionRecord.BIOTIN_TOTAL; 43 import static android.health.connect.datatypes.NutritionRecord.CAFFEINE_TOTAL; 44 import static android.health.connect.datatypes.NutritionRecord.CALCIUM_TOTAL; 45 import static android.health.connect.datatypes.NutritionRecord.CHLORIDE_TOTAL; 46 import static android.health.connect.datatypes.NutritionRecord.CHOLESTEROL_TOTAL; 47 import static android.health.connect.datatypes.NutritionRecord.CHROMIUM_TOTAL; 48 import static android.health.connect.datatypes.NutritionRecord.COPPER_TOTAL; 49 import static android.health.connect.datatypes.NutritionRecord.DIETARY_FIBER_TOTAL; 50 import static android.health.connect.datatypes.NutritionRecord.ENERGY_FROM_FAT_TOTAL; 51 import static android.health.connect.datatypes.NutritionRecord.ENERGY_TOTAL; 52 import static android.health.connect.datatypes.NutritionRecord.FOLATE_TOTAL; 53 import static android.health.connect.datatypes.NutritionRecord.FOLIC_ACID_TOTAL; 54 import static android.health.connect.datatypes.NutritionRecord.IODINE_TOTAL; 55 import static android.health.connect.datatypes.NutritionRecord.IRON_TOTAL; 56 import static android.health.connect.datatypes.NutritionRecord.MAGNESIUM_TOTAL; 57 import static android.health.connect.datatypes.NutritionRecord.MANGANESE_TOTAL; 58 import static android.health.connect.datatypes.NutritionRecord.MOLYBDENUM_TOTAL; 59 import static android.health.connect.datatypes.NutritionRecord.MONOUNSATURATED_FAT_TOTAL; 60 import static android.health.connect.datatypes.NutritionRecord.NIACIN_TOTAL; 61 import static android.health.connect.datatypes.NutritionRecord.PANTOTHENIC_ACID_TOTAL; 62 import static android.health.connect.datatypes.NutritionRecord.PHOSPHORUS_TOTAL; 63 import static android.health.connect.datatypes.NutritionRecord.POLYUNSATURATED_FAT_TOTAL; 64 import static android.health.connect.datatypes.NutritionRecord.POTASSIUM_TOTAL; 65 import static android.health.connect.datatypes.NutritionRecord.PROTEIN_TOTAL; 66 import static android.health.connect.datatypes.NutritionRecord.RIBOFLAVIN_TOTAL; 67 import static android.health.connect.datatypes.NutritionRecord.SATURATED_FAT_TOTAL; 68 import static android.health.connect.datatypes.NutritionRecord.SELENIUM_TOTAL; 69 import static android.health.connect.datatypes.NutritionRecord.SODIUM_TOTAL; 70 import static android.health.connect.datatypes.NutritionRecord.SUGAR_TOTAL; 71 import static android.health.connect.datatypes.NutritionRecord.THIAMIN_TOTAL; 72 import static android.health.connect.datatypes.NutritionRecord.TOTAL_CARBOHYDRATE_TOTAL; 73 import static android.health.connect.datatypes.NutritionRecord.TOTAL_FAT_TOTAL; 74 import static android.health.connect.datatypes.NutritionRecord.TRANS_FAT_TOTAL; 75 import static android.health.connect.datatypes.NutritionRecord.UNSATURATED_FAT_TOTAL; 76 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_A_TOTAL; 77 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_B12_TOTAL; 78 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_B6_TOTAL; 79 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_C_TOTAL; 80 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_D_TOTAL; 81 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_E_TOTAL; 82 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_K_TOTAL; 83 import static android.health.connect.datatypes.NutritionRecord.ZINC_TOTAL; 84 import static android.health.connect.datatypes.PowerRecord.POWER_AVG; 85 import static android.health.connect.datatypes.PowerRecord.POWER_MAX; 86 import static android.health.connect.datatypes.PowerRecord.POWER_MIN; 87 import static android.health.connect.datatypes.SkinTemperatureRecord.SKIN_TEMPERATURE_DELTA_AVG; 88 import static android.health.connect.datatypes.SkinTemperatureRecord.SKIN_TEMPERATURE_DELTA_MAX; 89 import static android.health.connect.datatypes.SkinTemperatureRecord.SKIN_TEMPERATURE_DELTA_MIN; 90 import static android.health.connect.datatypes.SleepSessionRecord.SLEEP_DURATION_TOTAL; 91 import static android.health.connect.datatypes.SpeedRecord.SPEED_AVG; 92 import static android.health.connect.datatypes.SpeedRecord.SPEED_MAX; 93 import static android.health.connect.datatypes.SpeedRecord.SPEED_MIN; 94 import static android.health.connect.datatypes.StepsCadenceRecord.STEPS_CADENCE_RATE_AVG; 95 import static android.health.connect.datatypes.StepsCadenceRecord.STEPS_CADENCE_RATE_MAX; 96 import static android.health.connect.datatypes.StepsCadenceRecord.STEPS_CADENCE_RATE_MIN; 97 import static android.health.connect.datatypes.StepsRecord.STEPS_COUNT_TOTAL; 98 import static android.health.connect.datatypes.WeightRecord.WEIGHT_AVG; 99 import static android.health.connect.datatypes.WeightRecord.WEIGHT_MAX; 100 import static android.health.connect.datatypes.WeightRecord.WEIGHT_MIN; 101 import static android.health.connect.datatypes.WheelchairPushesRecord.WHEEL_CHAIR_PUSHES_COUNT_TOTAL; 102 103 import android.annotation.NonNull; 104 import android.health.connect.AggregateResult; 105 import android.health.connect.datatypes.AggregationType; 106 import android.health.connect.datatypes.RestingHeartRateRecord; 107 import android.health.connect.datatypes.TotalCaloriesBurnedRecord; 108 import android.health.connect.datatypes.units.Energy; 109 import android.health.connect.datatypes.units.Length; 110 import android.health.connect.datatypes.units.Mass; 111 import android.health.connect.datatypes.units.Power; 112 import android.health.connect.datatypes.units.Pressure; 113 import android.health.connect.datatypes.units.TemperatureDelta; 114 import android.health.connect.datatypes.units.Velocity; 115 import android.health.connect.datatypes.units.Volume; 116 import android.os.Parcel; 117 118 import java.util.Arrays; 119 import java.util.HashMap; 120 import java.util.List; 121 import java.util.Map; 122 123 /** 124 * Creates and maintains a map of {@link AggregationType.AggregationTypeIdentifier} to {@link 125 * AggregationType} and its result creator {@link AggregationResultCreator} 126 * 127 * @hide 128 */ 129 public final class AggregationTypeIdMapper { 130 @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression 131 private static volatile AggregationTypeIdMapper sAggregationTypeIdMapper; 132 133 private final Map<Integer, AggregationResultCreator> mIdToAggregateResult; 134 private final Map<Integer, AggregationType<?>> mIdDataAggregationTypeMap; 135 private final Map<AggregationType<?>, Integer> mDataAggregationTypeIdMap; 136 AggregationTypeIdMapper()137 private AggregationTypeIdMapper() { 138 mIdToAggregateResult = new HashMap<>(); 139 mIdDataAggregationTypeMap = new HashMap<>(); 140 mDataAggregationTypeIdMap = new HashMap<>(); 141 142 addLongIdsToAggregateResultMap( 143 Arrays.asList( 144 BPM_MAX, 145 BPM_MIN, 146 STEPS_COUNT_TOTAL, 147 BPM_AVG, 148 RestingHeartRateRecord.BPM_MAX, 149 RestingHeartRateRecord.BPM_MIN, 150 RestingHeartRateRecord.BPM_AVG, 151 WHEEL_CHAIR_PUSHES_COUNT_TOTAL, 152 HEART_MEASUREMENTS_COUNT, 153 SLEEP_DURATION_TOTAL, 154 EXERCISE_DURATION_TOTAL)); 155 addDoubleIdsToAggregateResultMap( 156 Arrays.asList( 157 FLOORS_CLIMBED_TOTAL, 158 RPM_AVG, 159 RPM_MAX, 160 RPM_MIN, 161 STEPS_CADENCE_RATE_MAX, 162 STEPS_CADENCE_RATE_AVG, 163 STEPS_CADENCE_RATE_MIN)); 164 addPowerIdsToAggregateResultMap(Arrays.asList(POWER_MIN, POWER_MAX, POWER_AVG)); 165 addTemperatureDeltaIdsToAggregateResultMap( 166 Arrays.asList( 167 SKIN_TEMPERATURE_DELTA_AVG, 168 SKIN_TEMPERATURE_DELTA_MIN, 169 SKIN_TEMPERATURE_DELTA_MAX)); 170 addEnergyIdsToAggregateResultMap( 171 Arrays.asList( 172 ACTIVE_CALORIES_TOTAL, 173 BASAL_CALORIES_TOTAL, 174 ENERGY_TOTAL, 175 ENERGY_FROM_FAT_TOTAL, 176 TotalCaloriesBurnedRecord.ENERGY_TOTAL)); 177 addVolumeIdsToAggregateResultMap(Arrays.asList(VOLUME_TOTAL)); 178 addLengthIdsToAggregateResultMap( 179 Arrays.asList( 180 DISTANCE_TOTAL, 181 ELEVATION_GAINED_TOTAL, 182 HEIGHT_AVG, 183 HEIGHT_MAX, 184 HEIGHT_MIN)); 185 addMassIdsToAggregateResultMap( 186 Arrays.asList( 187 BIOTIN_TOTAL, 188 CAFFEINE_TOTAL, 189 CALCIUM_TOTAL, 190 CHLORIDE_TOTAL, 191 CHOLESTEROL_TOTAL, 192 CHROMIUM_TOTAL, 193 COPPER_TOTAL, 194 DIETARY_FIBER_TOTAL, 195 FOLATE_TOTAL, 196 FOLIC_ACID_TOTAL, 197 IODINE_TOTAL, 198 IRON_TOTAL, 199 MAGNESIUM_TOTAL, 200 MANGANESE_TOTAL, 201 MOLYBDENUM_TOTAL, 202 MONOUNSATURATED_FAT_TOTAL, 203 NIACIN_TOTAL, 204 PANTOTHENIC_ACID_TOTAL, 205 PHOSPHORUS_TOTAL, 206 POLYUNSATURATED_FAT_TOTAL, 207 POTASSIUM_TOTAL, 208 PROTEIN_TOTAL, 209 RIBOFLAVIN_TOTAL, 210 SATURATED_FAT_TOTAL, 211 SELENIUM_TOTAL, 212 SODIUM_TOTAL, 213 SUGAR_TOTAL, 214 THIAMIN_TOTAL, 215 TOTAL_CARBOHYDRATE_TOTAL, 216 TOTAL_FAT_TOTAL, 217 UNSATURATED_FAT_TOTAL, 218 VITAMIN_A_TOTAL, 219 VITAMIN_B12_TOTAL, 220 VITAMIN_B6_TOTAL, 221 VITAMIN_C_TOTAL, 222 VITAMIN_D_TOTAL, 223 VITAMIN_E_TOTAL, 224 VITAMIN_K_TOTAL, 225 ZINC_TOTAL, 226 WEIGHT_AVG, 227 WEIGHT_MAX, 228 WEIGHT_MIN, 229 TRANS_FAT_TOTAL)); 230 addVelocityIdsToAggregateResultMap(Arrays.asList(SPEED_MAX, SPEED_AVG, SPEED_MIN)); 231 addPressureIdsToAggregateResultMap( 232 Arrays.asList( 233 DIASTOLIC_AVG, 234 DIASTOLIC_MAX, 235 DIASTOLIC_MIN, 236 SYSTOLIC_AVG, 237 SYSTOLIC_MAX, 238 SYSTOLIC_MIN)); 239 } 240 241 @NonNull getInstance()242 public static synchronized AggregationTypeIdMapper getInstance() { 243 if (sAggregationTypeIdMapper == null) { 244 sAggregationTypeIdMapper = new AggregationTypeIdMapper(); 245 } 246 247 return sAggregationTypeIdMapper; 248 } 249 250 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 251 @NonNull getAggregateResultFor( @ggregationType.AggregationTypeIdentifier.Id int id, @NonNull Parcel parcel)252 public AggregateResult<?> getAggregateResultFor( 253 @AggregationType.AggregationTypeIdentifier.Id int id, @NonNull Parcel parcel) { 254 return mIdToAggregateResult.get(id).getAggregateResult(parcel); 255 } 256 257 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 258 @NonNull getAggregationTypeFor( @ggregationType.AggregationTypeIdentifier.Id int id)259 public AggregationType<?> getAggregationTypeFor( 260 @AggregationType.AggregationTypeIdentifier.Id int id) { 261 return mIdDataAggregationTypeMap.get(id); 262 } 263 264 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 265 @NonNull 266 @AggregationType.AggregationTypeIdentifier.Id getIdFor(AggregationType<?> aggregationType)267 public int getIdFor(AggregationType<?> aggregationType) { 268 return mDataAggregationTypeIdMap.get(aggregationType); 269 } 270 271 @NonNull getLongResult(long result)272 private AggregateResult<Long> getLongResult(long result) { 273 return new AggregateResult<>(result); 274 } 275 276 @NonNull getDoubleResult(double result)277 private AggregateResult<Double> getDoubleResult(double result) { 278 return new AggregateResult<>(result); 279 } 280 281 @NonNull getEnergyResult(double result)282 private AggregateResult<Energy> getEnergyResult(double result) { 283 return new AggregateResult<>(Energy.fromCalories(result)); 284 } 285 286 @NonNull getPowerResult(double result)287 private AggregateResult<Power> getPowerResult(double result) { 288 return new AggregateResult<>(Power.fromWatts(result)); 289 } 290 291 @NonNull getTemperatureDeltaResult(double result)292 private AggregateResult<TemperatureDelta> getTemperatureDeltaResult(double result) { 293 return new AggregateResult<>(TemperatureDelta.fromCelsius(result)); 294 } 295 296 @NonNull getPressureResult(double result)297 private AggregateResult<Pressure> getPressureResult(double result) { 298 return new AggregateResult<>(Pressure.fromMillimetersOfMercury(result)); 299 } 300 301 @NonNull getLengthResult(double result)302 private AggregateResult<Length> getLengthResult(double result) { 303 return new AggregateResult<>(Length.fromMeters(result)); 304 } 305 306 @NonNull getVolumeResult(double result)307 private AggregateResult<Volume> getVolumeResult(double result) { 308 return new AggregateResult<>(Volume.fromLiters(result)); 309 } 310 311 @NonNull getMassResult(double result)312 private AggregateResult<Mass> getMassResult(double result) { 313 return new AggregateResult<>(Mass.fromGrams(result)); 314 } 315 316 @NonNull getVelocityResult(double result)317 private AggregateResult<Velocity> getVelocityResult(double result) { 318 return new AggregateResult<>(Velocity.fromMetersPerSecond(result)); 319 } 320 addLongIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)321 private void addLongIdsToAggregateResultMap( 322 @NonNull List<AggregationType<?>> aggregationTypeList) { 323 for (AggregationType<?> aggregationType : aggregationTypeList) { 324 mIdToAggregateResult.put( 325 aggregationType.getAggregationTypeIdentifier(), 326 result -> getLongResult(result.readLong())); 327 populateIdDataAggregationType(aggregationType); 328 } 329 } 330 addDoubleIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)331 private void addDoubleIdsToAggregateResultMap( 332 @NonNull List<AggregationType<?>> aggregationTypeList) { 333 for (AggregationType<?> aggregationType : aggregationTypeList) { 334 mIdToAggregateResult.put( 335 aggregationType.getAggregationTypeIdentifier(), 336 result -> getDoubleResult(result.readDouble())); 337 populateIdDataAggregationType(aggregationType); 338 } 339 } 340 addEnergyIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)341 private void addEnergyIdsToAggregateResultMap( 342 @NonNull List<AggregationType<?>> aggregationTypeList) { 343 for (AggregationType<?> aggregationType : aggregationTypeList) { 344 mIdToAggregateResult.put( 345 aggregationType.getAggregationTypeIdentifier(), 346 result -> getEnergyResult(result.readDouble())); 347 populateIdDataAggregationType(aggregationType); 348 } 349 } 350 addPowerIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)351 private void addPowerIdsToAggregateResultMap( 352 @NonNull List<AggregationType<?>> aggregationTypeList) { 353 for (AggregationType<?> aggregationType : aggregationTypeList) { 354 mIdToAggregateResult.put( 355 aggregationType.getAggregationTypeIdentifier(), 356 result -> getPowerResult(result.readDouble())); 357 populateIdDataAggregationType(aggregationType); 358 } 359 } 360 addTemperatureDeltaIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)361 private void addTemperatureDeltaIdsToAggregateResultMap( 362 @NonNull List<AggregationType<?>> aggregationTypeList) { 363 for (AggregationType<?> aggregationType : aggregationTypeList) { 364 mIdToAggregateResult.put( 365 aggregationType.getAggregationTypeIdentifier(), 366 result -> getTemperatureDeltaResult(result.readDouble())); 367 populateIdDataAggregationType(aggregationType); 368 } 369 } 370 addPressureIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)371 private void addPressureIdsToAggregateResultMap( 372 @NonNull List<AggregationType<?>> aggregationTypeList) { 373 for (AggregationType<?> aggregationType : aggregationTypeList) { 374 mIdToAggregateResult.put( 375 aggregationType.getAggregationTypeIdentifier(), 376 result -> getPressureResult(result.readDouble())); 377 populateIdDataAggregationType(aggregationType); 378 } 379 } 380 addLengthIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)381 private void addLengthIdsToAggregateResultMap( 382 @NonNull List<AggregationType<?>> aggregationTypeList) { 383 for (AggregationType<?> aggregationType : aggregationTypeList) { 384 mIdToAggregateResult.put( 385 aggregationType.getAggregationTypeIdentifier(), 386 result -> getLengthResult(result.readDouble())); 387 populateIdDataAggregationType(aggregationType); 388 } 389 } 390 addVolumeIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)391 private void addVolumeIdsToAggregateResultMap( 392 @NonNull List<AggregationType<?>> aggregationTypeList) { 393 for (AggregationType<?> aggregationType : aggregationTypeList) { 394 mIdToAggregateResult.put( 395 aggregationType.getAggregationTypeIdentifier(), 396 result -> getVolumeResult(result.readDouble())); 397 populateIdDataAggregationType(aggregationType); 398 } 399 } 400 addMassIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)401 private void addMassIdsToAggregateResultMap( 402 @NonNull List<AggregationType<?>> aggregationTypeList) { 403 for (AggregationType<?> aggregationType : aggregationTypeList) { 404 mIdToAggregateResult.put( 405 aggregationType.getAggregationTypeIdentifier(), 406 result -> getMassResult(result.readDouble())); 407 populateIdDataAggregationType(aggregationType); 408 } 409 } 410 addVelocityIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)411 private void addVelocityIdsToAggregateResultMap( 412 @NonNull List<AggregationType<?>> aggregationTypeList) { 413 for (AggregationType<?> aggregationType : aggregationTypeList) { 414 mIdToAggregateResult.put( 415 aggregationType.getAggregationTypeIdentifier(), 416 result -> getVelocityResult(result.readDouble())); 417 populateIdDataAggregationType(aggregationType); 418 } 419 } 420 populateIdDataAggregationType(AggregationType<?> aggregationType)421 private void populateIdDataAggregationType(AggregationType<?> aggregationType) { 422 mIdDataAggregationTypeMap.put( 423 aggregationType.getAggregationTypeIdentifier(), aggregationType); 424 mDataAggregationTypeIdMap.put( 425 aggregationType, aggregationType.getAggregationTypeIdentifier()); 426 } 427 428 /** 429 * Implementation should get and covert result to appropriate type (such as long, double etc.) 430 * using {@code result} 431 */ 432 private interface AggregationResultCreator { getAggregateResult(Parcel result)433 AggregateResult<?> getAggregateResult(Parcel result); 434 } 435 } 436