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_BASAL_METABOLIC_RATE; 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.units.Power; 24 import android.health.connect.datatypes.validation.ValidationUtils; 25 import android.health.connect.internal.datatypes.BasalMetabolicRateRecordInternal; 26 27 import java.time.Instant; 28 import java.time.ZoneOffset; 29 import java.util.Objects; 30 31 /** 32 * Captures the BMR of a user. Each record represents the energy a user would burn if at rest all 33 * day, based on their height and weight. 34 */ 35 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_BASAL_METABOLIC_RATE) 36 public final class BasalMetabolicRateRecord extends InstantRecord { 37 private final Power mBasalMetabolicRate; 38 39 /** 40 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 41 * @param time Start time of this activity 42 * @param zoneOffset Zone offset of the user when the activity started 43 * @param basalMetabolicRate BasalMetabolicRate of this activity 44 * @param skipValidation Boolean flag to skip validation of record values. 45 */ BasalMetabolicRateRecord( @onNull Metadata metadata, @NonNull Instant time, @NonNull ZoneOffset zoneOffset, @NonNull Power basalMetabolicRate, boolean skipValidation)46 private BasalMetabolicRateRecord( 47 @NonNull Metadata metadata, 48 @NonNull Instant time, 49 @NonNull ZoneOffset zoneOffset, 50 @NonNull Power basalMetabolicRate, 51 boolean skipValidation) { 52 super(metadata, time, zoneOffset, skipValidation); 53 Objects.requireNonNull(metadata); 54 Objects.requireNonNull(time); 55 Objects.requireNonNull(zoneOffset); 56 Objects.requireNonNull(basalMetabolicRate); 57 if (!skipValidation) { 58 ValidationUtils.requireInRange( 59 basalMetabolicRate.getInWatts(), 0.0, 484.259, "basalMetabolicRate"); 60 } 61 mBasalMetabolicRate = basalMetabolicRate; 62 } 63 64 /** 65 * @return basalMetabolicRate 66 */ 67 @NonNull getBasalMetabolicRate()68 public Power getBasalMetabolicRate() { 69 return mBasalMetabolicRate; 70 } 71 72 /** 73 * Indicates whether some other object is "equal to" this one. 74 * 75 * @param o the reference object with which to compare. 76 * @return {@code true} if this object is the same as the obj 77 */ 78 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 79 @Override equals(Object o)80 public boolean equals(Object o) { 81 if (this == o) return true; 82 if (!super.equals(o)) return false; 83 BasalMetabolicRateRecord that = (BasalMetabolicRateRecord) o; 84 return getBasalMetabolicRate().equals(that.getBasalMetabolicRate()); 85 } 86 87 /** Returns a hash code value for the object. */ 88 @Override hashCode()89 public int hashCode() { 90 return Objects.hash(super.hashCode(), getBasalMetabolicRate()); 91 } 92 93 /** Builder class for {@link BasalMetabolicRateRecord} */ 94 public static final class Builder { 95 private final Metadata mMetadata; 96 private final Instant mTime; 97 private ZoneOffset mZoneOffset; 98 private final Power mBasalMetabolicRate; 99 100 /** 101 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 102 * @param time Start time of this activity 103 * @param basalMetabolicRate Basal metabolic rate, in {@link Power} unit. Required field. 104 * Valid range: 0-10000 kcal/day. 105 */ Builder( @onNull Metadata metadata, @NonNull Instant time, @NonNull Power basalMetabolicRate)106 public Builder( 107 @NonNull Metadata metadata, 108 @NonNull Instant time, 109 @NonNull Power basalMetabolicRate) { 110 Objects.requireNonNull(metadata); 111 Objects.requireNonNull(time); 112 Objects.requireNonNull(basalMetabolicRate); 113 mMetadata = metadata; 114 mTime = time; 115 mBasalMetabolicRate = basalMetabolicRate; 116 mZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(time); 117 } 118 119 /** Sets the zone offset of the user when the activity happened */ 120 @NonNull setZoneOffset(@onNull ZoneOffset zoneOffset)121 public Builder setZoneOffset(@NonNull ZoneOffset zoneOffset) { 122 Objects.requireNonNull(zoneOffset); 123 mZoneOffset = zoneOffset; 124 return this; 125 } 126 127 /** Sets the zone offset of this record to system default. */ 128 @NonNull clearZoneOffset()129 public Builder clearZoneOffset() { 130 mZoneOffset = RecordUtils.getDefaultZoneOffset(); 131 return this; 132 } 133 134 /** 135 * @return Object of {@link BasalMetabolicRateRecord} without validating the values. 136 * @hide 137 */ 138 @NonNull buildWithoutValidation()139 public BasalMetabolicRateRecord buildWithoutValidation() { 140 return new BasalMetabolicRateRecord( 141 mMetadata, mTime, mZoneOffset, mBasalMetabolicRate, true); 142 } 143 144 /** 145 * @return Object of {@link BasalMetabolicRateRecord} 146 */ 147 @NonNull build()148 public BasalMetabolicRateRecord build() { 149 return new BasalMetabolicRateRecord( 150 mMetadata, mTime, mZoneOffset, mBasalMetabolicRate, false); 151 } 152 } 153 154 /** 155 * Metric identifier get total basal calories burnt using aggregate APIs in {@link 156 * HealthConnectManager} 157 */ 158 @NonNull 159 public static final AggregationType<Energy> BASAL_CALORIES_TOTAL = 160 new AggregationType<>( 161 AggregationType.AggregationTypeIdentifier.BMR_RECORD_BASAL_CALORIES_TOTAL, 162 AggregationType.SUM, 163 RECORD_TYPE_BASAL_METABOLIC_RATE, 164 Energy.class); 165 166 /** @hide */ 167 @Override toRecordInternal()168 public BasalMetabolicRateRecordInternal toRecordInternal() { 169 BasalMetabolicRateRecordInternal recordInternal = 170 (BasalMetabolicRateRecordInternal) 171 new BasalMetabolicRateRecordInternal() 172 .setUuid(getMetadata().getId()) 173 .setPackageName(getMetadata().getDataOrigin().getPackageName()) 174 .setLastModifiedTime( 175 getMetadata().getLastModifiedTime().toEpochMilli()) 176 .setClientRecordId(getMetadata().getClientRecordId()) 177 .setClientRecordVersion(getMetadata().getClientRecordVersion()) 178 .setManufacturer(getMetadata().getDevice().getManufacturer()) 179 .setModel(getMetadata().getDevice().getModel()) 180 .setDeviceType(getMetadata().getDevice().getType()) 181 .setRecordingMethod(getMetadata().getRecordingMethod()); 182 recordInternal.setTime(getTime().toEpochMilli()); 183 recordInternal.setZoneOffset(getZoneOffset().getTotalSeconds()); 184 recordInternal.setBasalMetabolicRate(mBasalMetabolicRate.getInWatts()); 185 return recordInternal; 186 } 187 } 188