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 package android.health.connect.datatypes; 17 18 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_ACTIVE_CALORIES_BURNED; 19 20 import android.annotation.NonNull; 21 import android.health.connect.HealthConnectManager; 22 import android.health.connect.datatypes.units.Energy; 23 import android.health.connect.datatypes.validation.ValidationUtils; 24 import android.health.connect.internal.datatypes.ActiveCaloriesBurnedRecordInternal; 25 26 import java.time.Instant; 27 import java.time.ZoneOffset; 28 import java.util.Objects; 29 30 /** 31 * Captures the estimated active energy burned by the user (in kilocalories), excluding basal 32 * metabolic rate (BMR). 33 */ 34 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_ACTIVE_CALORIES_BURNED) 35 public final class ActiveCaloriesBurnedRecord extends IntervalRecord { 36 /** Builder class for {@link ActiveCaloriesBurnedRecord} */ 37 public static final class Builder { 38 private final Metadata mMetadata; 39 private final Instant mStartTime; 40 private final Instant mEndTime; 41 private ZoneOffset mStartZoneOffset; 42 private ZoneOffset mEndZoneOffset; 43 private final Energy mEnergy; 44 45 /** 46 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 47 * @param startTime Start time of this activity 48 * @param endTime End time of this activity 49 * @param energy Energy in {@link Energy} unit. Required field. Valid range: 0-1000000 kcal. 50 */ Builder( @onNull Metadata metadata, @NonNull Instant startTime, @NonNull Instant endTime, @NonNull Energy energy)51 public Builder( 52 @NonNull Metadata metadata, 53 @NonNull Instant startTime, 54 @NonNull Instant endTime, 55 @NonNull Energy energy) { 56 Objects.requireNonNull(metadata); 57 Objects.requireNonNull(startTime); 58 Objects.requireNonNull(endTime); 59 Objects.requireNonNull(energy); 60 mMetadata = metadata; 61 mStartTime = startTime; 62 mEndTime = endTime; 63 mEnergy = energy; 64 mStartZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(startTime); 65 mEndZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(endTime); 66 } 67 68 /** Sets the zone offset of the user when the activity started */ 69 @NonNull setStartZoneOffset(@onNull ZoneOffset startZoneOffset)70 public Builder setStartZoneOffset(@NonNull ZoneOffset startZoneOffset) { 71 Objects.requireNonNull(startZoneOffset); 72 73 mStartZoneOffset = startZoneOffset; 74 return this; 75 } 76 77 /** Sets the zone offset of the user when the activity ended */ 78 @NonNull setEndZoneOffset(@onNull ZoneOffset endZoneOffset)79 public Builder setEndZoneOffset(@NonNull ZoneOffset endZoneOffset) { 80 Objects.requireNonNull(endZoneOffset); 81 82 mEndZoneOffset = endZoneOffset; 83 return this; 84 } 85 86 /** Sets the start zone offset of this record to system default. */ 87 @NonNull clearStartZoneOffset()88 public Builder clearStartZoneOffset() { 89 mStartZoneOffset = RecordUtils.getDefaultZoneOffset(); 90 return this; 91 } 92 93 /** Sets the start zone offset of this record to system default. */ 94 @NonNull clearEndZoneOffset()95 public Builder clearEndZoneOffset() { 96 mEndZoneOffset = RecordUtils.getDefaultZoneOffset(); 97 return this; 98 } 99 100 /** 101 * @return Object of {@link ActiveCaloriesBurnedRecord} without validating the values. 102 * @hide 103 */ 104 @NonNull buildWithoutValidation()105 public ActiveCaloriesBurnedRecord buildWithoutValidation() { 106 return new ActiveCaloriesBurnedRecord( 107 mMetadata, 108 mStartTime, 109 mStartZoneOffset, 110 mEndTime, 111 mEndZoneOffset, 112 mEnergy, 113 true); 114 } 115 116 /** 117 * @return Object of {@link ActiveCaloriesBurnedRecord} 118 */ 119 @NonNull build()120 public ActiveCaloriesBurnedRecord build() { 121 return new ActiveCaloriesBurnedRecord( 122 mMetadata, 123 mStartTime, 124 mStartZoneOffset, 125 mEndTime, 126 mEndZoneOffset, 127 mEnergy, 128 false); 129 } 130 } 131 132 /** 133 * Metric identifier to get total active calories burnt using aggregate APIs in {@link 134 * HealthConnectManager} 135 */ 136 @NonNull 137 public static final AggregationType<Energy> ACTIVE_CALORIES_TOTAL = 138 new AggregationType<>( 139 AggregationType.AggregationTypeIdentifier 140 .ACTIVE_CALORIES_BURNED_RECORD_ACTIVE_CALORIES_TOTAL, 141 AggregationType.SUM, 142 RECORD_TYPE_ACTIVE_CALORIES_BURNED, 143 Energy.class); 144 145 private final Energy mEnergy; 146 147 /** 148 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 149 * @param startTime Start time of this activity 150 * @param startZoneOffset Zone offset of the user when the activity started 151 * @param endTime End time of this activity 152 * @param endZoneOffset Zone offset of the user when the activity finished 153 * @param energy Energy of this activity 154 * @param skipValidation Boolean flag to skip validation of record values. 155 */ ActiveCaloriesBurnedRecord( @onNull Metadata metadata, @NonNull Instant startTime, @NonNull ZoneOffset startZoneOffset, @NonNull Instant endTime, @NonNull ZoneOffset endZoneOffset, @NonNull Energy energy, boolean skipValidation)156 private ActiveCaloriesBurnedRecord( 157 @NonNull Metadata metadata, 158 @NonNull Instant startTime, 159 @NonNull ZoneOffset startZoneOffset, 160 @NonNull Instant endTime, 161 @NonNull ZoneOffset endZoneOffset, 162 @NonNull Energy energy, 163 boolean skipValidation) { 164 super( 165 metadata, 166 startTime, 167 startZoneOffset, 168 endTime, 169 endZoneOffset, 170 skipValidation, 171 /* enforceFutureTimeRestrictions= */ true); 172 Objects.requireNonNull(energy); 173 if (!skipValidation) { 174 ValidationUtils.requireInRange(energy.getInCalories(), 0.0, 1000000000.0, "energy"); 175 } 176 mEnergy = energy; 177 } 178 179 /** 180 * @return energy in {@link Energy} unit. 181 */ 182 @NonNull getEnergy()183 public Energy getEnergy() { 184 return mEnergy; 185 } 186 187 /** 188 * Indicates whether some other object is "equal to" this one. 189 * 190 * @param o the reference object with which to compare. 191 * @return {@code true} if this object is the same as the obj 192 */ 193 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 194 @Override equals(Object o)195 public boolean equals(Object o) { 196 if (this == o) return true; 197 if (!super.equals(o)) return false; 198 ActiveCaloriesBurnedRecord that = (ActiveCaloriesBurnedRecord) o; 199 return getEnergy().equals(that.getEnergy()); 200 } 201 202 /** 203 * @return a hash code value for this object. 204 */ 205 @Override hashCode()206 public int hashCode() { 207 return Objects.hash(super.hashCode(), getEnergy()); 208 } 209 210 /** @hide */ 211 @Override toRecordInternal()212 public ActiveCaloriesBurnedRecordInternal toRecordInternal() { 213 ActiveCaloriesBurnedRecordInternal recordInternal = 214 (ActiveCaloriesBurnedRecordInternal) 215 new ActiveCaloriesBurnedRecordInternal() 216 .setUuid(getMetadata().getId()) 217 .setPackageName(getMetadata().getDataOrigin().getPackageName()) 218 .setLastModifiedTime( 219 getMetadata().getLastModifiedTime().toEpochMilli()) 220 .setClientRecordId(getMetadata().getClientRecordId()) 221 .setClientRecordVersion(getMetadata().getClientRecordVersion()) 222 .setManufacturer(getMetadata().getDevice().getManufacturer()) 223 .setModel(getMetadata().getDevice().getModel()) 224 .setDeviceType(getMetadata().getDevice().getType()) 225 .setRecordingMethod(getMetadata().getRecordingMethod()); 226 recordInternal.setStartTime(getStartTime().toEpochMilli()); 227 recordInternal.setEndTime(getEndTime().toEpochMilli()); 228 recordInternal.setStartZoneOffset(getStartZoneOffset().getTotalSeconds()); 229 recordInternal.setEndZoneOffset(getEndZoneOffset().getTotalSeconds()); 230 recordInternal.setEnergy(mEnergy.getInCalories()); 231 return recordInternal; 232 } 233 } 234