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