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.DistanceRecord.DISTANCE_TOTAL; 22 import static android.health.connect.datatypes.ElevationGainedRecord.ELEVATION_GAINED_TOTAL; 23 import static android.health.connect.datatypes.ExerciseSessionRecord.EXERCISE_DURATION_TOTAL; 24 import static android.health.connect.datatypes.FloorsClimbedRecord.FLOORS_CLIMBED_TOTAL; 25 import static android.health.connect.datatypes.HeartRateRecord.BPM_AVG; 26 import static android.health.connect.datatypes.HeartRateRecord.BPM_MAX; 27 import static android.health.connect.datatypes.HeartRateRecord.BPM_MIN; 28 import static android.health.connect.datatypes.HeartRateRecord.HEART_MEASUREMENTS_COUNT; 29 import static android.health.connect.datatypes.HeightRecord.HEIGHT_AVG; 30 import static android.health.connect.datatypes.HeightRecord.HEIGHT_MAX; 31 import static android.health.connect.datatypes.HeightRecord.HEIGHT_MIN; 32 import static android.health.connect.datatypes.HydrationRecord.VOLUME_TOTAL; 33 import static android.health.connect.datatypes.NutritionRecord.BIOTIN_TOTAL; 34 import static android.health.connect.datatypes.NutritionRecord.CAFFEINE_TOTAL; 35 import static android.health.connect.datatypes.NutritionRecord.CALCIUM_TOTAL; 36 import static android.health.connect.datatypes.NutritionRecord.CHLORIDE_TOTAL; 37 import static android.health.connect.datatypes.NutritionRecord.CHOLESTEROL_TOTAL; 38 import static android.health.connect.datatypes.NutritionRecord.CHROMIUM_TOTAL; 39 import static android.health.connect.datatypes.NutritionRecord.COPPER_TOTAL; 40 import static android.health.connect.datatypes.NutritionRecord.DIETARY_FIBER_TOTAL; 41 import static android.health.connect.datatypes.NutritionRecord.ENERGY_FROM_FAT_TOTAL; 42 import static android.health.connect.datatypes.NutritionRecord.ENERGY_TOTAL; 43 import static android.health.connect.datatypes.NutritionRecord.FOLATE_TOTAL; 44 import static android.health.connect.datatypes.NutritionRecord.FOLIC_ACID_TOTAL; 45 import static android.health.connect.datatypes.NutritionRecord.IODINE_TOTAL; 46 import static android.health.connect.datatypes.NutritionRecord.IRON_TOTAL; 47 import static android.health.connect.datatypes.NutritionRecord.MAGNESIUM_TOTAL; 48 import static android.health.connect.datatypes.NutritionRecord.MANGANESE_TOTAL; 49 import static android.health.connect.datatypes.NutritionRecord.MOLYBDENUM_TOTAL; 50 import static android.health.connect.datatypes.NutritionRecord.MONOUNSATURATED_FAT_TOTAL; 51 import static android.health.connect.datatypes.NutritionRecord.NIACIN_TOTAL; 52 import static android.health.connect.datatypes.NutritionRecord.PANTOTHENIC_ACID_TOTAL; 53 import static android.health.connect.datatypes.NutritionRecord.PHOSPHORUS_TOTAL; 54 import static android.health.connect.datatypes.NutritionRecord.POLYUNSATURATED_FAT_TOTAL; 55 import static android.health.connect.datatypes.NutritionRecord.POTASSIUM_TOTAL; 56 import static android.health.connect.datatypes.NutritionRecord.PROTEIN_TOTAL; 57 import static android.health.connect.datatypes.NutritionRecord.RIBOFLAVIN_TOTAL; 58 import static android.health.connect.datatypes.NutritionRecord.SATURATED_FAT_TOTAL; 59 import static android.health.connect.datatypes.NutritionRecord.SELENIUM_TOTAL; 60 import static android.health.connect.datatypes.NutritionRecord.SODIUM_TOTAL; 61 import static android.health.connect.datatypes.NutritionRecord.SUGAR_TOTAL; 62 import static android.health.connect.datatypes.NutritionRecord.THIAMIN_TOTAL; 63 import static android.health.connect.datatypes.NutritionRecord.TOTAL_CARBOHYDRATE_TOTAL; 64 import static android.health.connect.datatypes.NutritionRecord.TOTAL_FAT_TOTAL; 65 import static android.health.connect.datatypes.NutritionRecord.UNSATURATED_FAT_TOTAL; 66 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_A_TOTAL; 67 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_B12_TOTAL; 68 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_B6_TOTAL; 69 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_C_TOTAL; 70 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_D_TOTAL; 71 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_E_TOTAL; 72 import static android.health.connect.datatypes.NutritionRecord.VITAMIN_K_TOTAL; 73 import static android.health.connect.datatypes.NutritionRecord.ZINC_TOTAL; 74 import static android.health.connect.datatypes.PowerRecord.POWER_AVG; 75 import static android.health.connect.datatypes.PowerRecord.POWER_MAX; 76 import static android.health.connect.datatypes.PowerRecord.POWER_MIN; 77 import static android.health.connect.datatypes.SleepSessionRecord.SLEEP_DURATION_TOTAL; 78 import static android.health.connect.datatypes.StepsRecord.STEPS_COUNT_TOTAL; 79 import static android.health.connect.datatypes.WeightRecord.WEIGHT_AVG; 80 import static android.health.connect.datatypes.WeightRecord.WEIGHT_MAX; 81 import static android.health.connect.datatypes.WeightRecord.WEIGHT_MIN; 82 import static android.health.connect.datatypes.WheelchairPushesRecord.WHEEL_CHAIR_PUSHES_COUNT_TOTAL; 83 84 import android.annotation.NonNull; 85 import android.health.connect.AggregateResult; 86 import android.health.connect.datatypes.AggregationType; 87 import android.health.connect.datatypes.RestingHeartRateRecord; 88 import android.health.connect.datatypes.TotalCaloriesBurnedRecord; 89 import android.health.connect.datatypes.units.Energy; 90 import android.health.connect.datatypes.units.Length; 91 import android.health.connect.datatypes.units.Mass; 92 import android.health.connect.datatypes.units.Power; 93 import android.health.connect.datatypes.units.Volume; 94 import android.os.Parcel; 95 96 import java.util.Arrays; 97 import java.util.HashMap; 98 import java.util.List; 99 import java.util.Map; 100 101 /** 102 * Creates and maintains a map of {@link AggregationType.AggregationTypeIdentifier} to {@link 103 * AggregationType} and its result creator {@link AggregationResultCreator} 104 * 105 * @hide 106 */ 107 public final class AggregationTypeIdMapper { 108 private static final int MAP_SIZE = 65; 109 private static volatile AggregationTypeIdMapper sAggregationTypeIdMapper; 110 private final Map<Integer, AggregationResultCreator> mIdToAggregateResult; 111 private final Map<Integer, AggregationType<?>> mIdDataAggregationTypeMap; 112 private final Map<AggregationType<?>, Integer> mDataAggregationTypeIdMap; 113 AggregationTypeIdMapper()114 private AggregationTypeIdMapper() { 115 mIdToAggregateResult = new HashMap<>(MAP_SIZE); 116 mIdDataAggregationTypeMap = new HashMap<>(MAP_SIZE); 117 mDataAggregationTypeIdMap = new HashMap<>(MAP_SIZE); 118 119 addLongIdsToAggregateResultMap( 120 Arrays.asList( 121 BPM_MAX, 122 BPM_MIN, 123 STEPS_COUNT_TOTAL, 124 BPM_AVG, 125 RestingHeartRateRecord.BPM_MAX, 126 RestingHeartRateRecord.BPM_MIN, 127 RestingHeartRateRecord.BPM_AVG, 128 WHEEL_CHAIR_PUSHES_COUNT_TOTAL, 129 HEART_MEASUREMENTS_COUNT, 130 SLEEP_DURATION_TOTAL, 131 EXERCISE_DURATION_TOTAL)); 132 addDoubleIdsToAggregateResultMap(Arrays.asList(FLOORS_CLIMBED_TOTAL)); 133 addPowerIdsToAggregateResultMap(Arrays.asList(POWER_MIN, POWER_MAX, POWER_AVG)); 134 addEnergyIdsToAggregateResultMap( 135 Arrays.asList( 136 ACTIVE_CALORIES_TOTAL, 137 BASAL_CALORIES_TOTAL, 138 ENERGY_TOTAL, 139 ENERGY_FROM_FAT_TOTAL, 140 TotalCaloriesBurnedRecord.ENERGY_TOTAL)); 141 addVolumeIdsToAggregateResultMap(Arrays.asList(VOLUME_TOTAL)); 142 addLengthIdsToAggregateResultMap( 143 Arrays.asList( 144 DISTANCE_TOTAL, 145 ELEVATION_GAINED_TOTAL, 146 HEIGHT_AVG, 147 HEIGHT_MAX, 148 HEIGHT_MIN)); 149 addMassIdsToAggregateResultMap( 150 Arrays.asList( 151 BIOTIN_TOTAL, 152 CAFFEINE_TOTAL, 153 CALCIUM_TOTAL, 154 CHLORIDE_TOTAL, 155 CHOLESTEROL_TOTAL, 156 CHROMIUM_TOTAL, 157 COPPER_TOTAL, 158 DIETARY_FIBER_TOTAL, 159 FOLATE_TOTAL, 160 FOLIC_ACID_TOTAL, 161 IODINE_TOTAL, 162 IRON_TOTAL, 163 MAGNESIUM_TOTAL, 164 MANGANESE_TOTAL, 165 MOLYBDENUM_TOTAL, 166 MONOUNSATURATED_FAT_TOTAL, 167 NIACIN_TOTAL, 168 PANTOTHENIC_ACID_TOTAL, 169 PHOSPHORUS_TOTAL, 170 POLYUNSATURATED_FAT_TOTAL, 171 POTASSIUM_TOTAL, 172 PROTEIN_TOTAL, 173 RIBOFLAVIN_TOTAL, 174 SATURATED_FAT_TOTAL, 175 SELENIUM_TOTAL, 176 SODIUM_TOTAL, 177 SUGAR_TOTAL, 178 THIAMIN_TOTAL, 179 TOTAL_CARBOHYDRATE_TOTAL, 180 TOTAL_FAT_TOTAL, 181 UNSATURATED_FAT_TOTAL, 182 VITAMIN_A_TOTAL, 183 VITAMIN_B12_TOTAL, 184 VITAMIN_B6_TOTAL, 185 VITAMIN_C_TOTAL, 186 VITAMIN_D_TOTAL, 187 VITAMIN_E_TOTAL, 188 VITAMIN_K_TOTAL, 189 ZINC_TOTAL, 190 WEIGHT_AVG, 191 WEIGHT_MAX, 192 WEIGHT_MIN)); 193 } 194 195 @NonNull getInstance()196 public static synchronized AggregationTypeIdMapper getInstance() { 197 if (sAggregationTypeIdMapper == null) { 198 sAggregationTypeIdMapper = new AggregationTypeIdMapper(); 199 } 200 201 return sAggregationTypeIdMapper; 202 } 203 204 @NonNull getAggregateResultFor( @ggregationType.AggregationTypeIdentifier.Id int id, @NonNull Parcel parcel)205 public AggregateResult<?> getAggregateResultFor( 206 @AggregationType.AggregationTypeIdentifier.Id int id, @NonNull Parcel parcel) { 207 return mIdToAggregateResult.get(id).getAggregateResult(parcel); 208 } 209 210 @NonNull getAggregationTypeFor( @ggregationType.AggregationTypeIdentifier.Id int id)211 public AggregationType<?> getAggregationTypeFor( 212 @AggregationType.AggregationTypeIdentifier.Id int id) { 213 return mIdDataAggregationTypeMap.get(id); 214 } 215 216 @NonNull 217 @AggregationType.AggregationTypeIdentifier.Id getIdFor(AggregationType<?> aggregationType)218 public int getIdFor(AggregationType<?> aggregationType) { 219 return mDataAggregationTypeIdMap.get(aggregationType); 220 } 221 222 @NonNull getLongResult(long result)223 private AggregateResult<Long> getLongResult(long result) { 224 return new AggregateResult<>(result); 225 } 226 227 @NonNull getDoubleResult(double result)228 private AggregateResult<Double> getDoubleResult(double result) { 229 return new AggregateResult<>(result); 230 } 231 232 @NonNull getEnergyResult(double result)233 private AggregateResult<Energy> getEnergyResult(double result) { 234 return new AggregateResult<>(Energy.fromCalories(result)); 235 } 236 237 @NonNull getPowerResult(double result)238 private AggregateResult<Power> getPowerResult(double result) { 239 return new AggregateResult<>(Power.fromWatts(result)); 240 } 241 242 @NonNull getLengthResult(double result)243 private AggregateResult<Length> getLengthResult(double result) { 244 return new AggregateResult<>(Length.fromMeters(result)); 245 } 246 247 @NonNull getVolumeResult(double result)248 private AggregateResult<Volume> getVolumeResult(double result) { 249 return new AggregateResult<>(Volume.fromLiters(result)); 250 } 251 252 @NonNull getMassResult(double result)253 private AggregateResult<Mass> getMassResult(double result) { 254 return new AggregateResult<>(Mass.fromGrams(result)); 255 } 256 addLongIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)257 private void addLongIdsToAggregateResultMap( 258 @NonNull List<AggregationType<?>> aggregationTypeList) { 259 for (AggregationType<?> aggregationType : aggregationTypeList) { 260 mIdToAggregateResult.put( 261 aggregationType.getAggregationTypeIdentifier(), 262 result -> getLongResult(result.readLong())); 263 populateIdDataAggregationType(aggregationType); 264 } 265 } 266 addDoubleIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)267 private void addDoubleIdsToAggregateResultMap( 268 @NonNull List<AggregationType<?>> aggregationTypeList) { 269 for (AggregationType<?> aggregationType : aggregationTypeList) { 270 mIdToAggregateResult.put( 271 aggregationType.getAggregationTypeIdentifier(), 272 result -> getDoubleResult(result.readDouble())); 273 populateIdDataAggregationType(aggregationType); 274 } 275 } 276 addEnergyIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)277 private void addEnergyIdsToAggregateResultMap( 278 @NonNull List<AggregationType<?>> aggregationTypeList) { 279 for (AggregationType<?> aggregationType : aggregationTypeList) { 280 mIdToAggregateResult.put( 281 aggregationType.getAggregationTypeIdentifier(), 282 result -> getEnergyResult(result.readDouble())); 283 populateIdDataAggregationType(aggregationType); 284 } 285 } 286 addPowerIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)287 private void addPowerIdsToAggregateResultMap( 288 @NonNull List<AggregationType<?>> aggregationTypeList) { 289 for (AggregationType<?> aggregationType : aggregationTypeList) { 290 mIdToAggregateResult.put( 291 aggregationType.getAggregationTypeIdentifier(), 292 result -> getPowerResult(result.readDouble())); 293 populateIdDataAggregationType(aggregationType); 294 } 295 } 296 addLengthIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)297 private void addLengthIdsToAggregateResultMap( 298 @NonNull List<AggregationType<?>> aggregationTypeList) { 299 for (AggregationType<?> aggregationType : aggregationTypeList) { 300 mIdToAggregateResult.put( 301 aggregationType.getAggregationTypeIdentifier(), 302 result -> getLengthResult(result.readDouble())); 303 populateIdDataAggregationType(aggregationType); 304 } 305 } 306 addVolumeIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)307 private void addVolumeIdsToAggregateResultMap( 308 @NonNull List<AggregationType<?>> aggregationTypeList) { 309 for (AggregationType<?> aggregationType : aggregationTypeList) { 310 mIdToAggregateResult.put( 311 aggregationType.getAggregationTypeIdentifier(), 312 result -> getVolumeResult(result.readDouble())); 313 populateIdDataAggregationType(aggregationType); 314 } 315 } 316 addMassIdsToAggregateResultMap( @onNull List<AggregationType<?>> aggregationTypeList)317 private void addMassIdsToAggregateResultMap( 318 @NonNull List<AggregationType<?>> aggregationTypeList) { 319 for (AggregationType<?> aggregationType : aggregationTypeList) { 320 mIdToAggregateResult.put( 321 aggregationType.getAggregationTypeIdentifier(), 322 result -> getMassResult(result.readDouble())); 323 populateIdDataAggregationType(aggregationType); 324 } 325 } 326 populateIdDataAggregationType(AggregationType<?> aggregationType)327 private void populateIdDataAggregationType(AggregationType<?> aggregationType) { 328 mIdDataAggregationTypeMap.put( 329 aggregationType.getAggregationTypeIdentifier(), aggregationType); 330 mDataAggregationTypeIdMap.put( 331 aggregationType, aggregationType.getAggregationTypeIdentifier()); 332 } 333 334 /** 335 * Implementation should get and covert result to appropriate type (such as long, double etc.) 336 * using {@code result} 337 */ 338 private interface AggregationResultCreator { getAggregateResult(Parcel result)339 AggregateResult<?> getAggregateResult(Parcel result); 340 } 341 } 342