• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_BLOOD_PRESSURE;
19 import static android.health.connect.datatypes.validation.ValidationUtils.validateIntDefValue;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.health.connect.HealthConnectManager;
24 import android.health.connect.datatypes.units.Pressure;
25 import android.health.connect.datatypes.validation.ValidationUtils;
26 import android.health.connect.internal.datatypes.BloodPressureRecordInternal;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.time.Instant;
31 import java.time.ZoneOffset;
32 import java.util.Objects;
33 import java.util.Set;
34 
35 /**
36  * Captures the blood pressure of a user. Each record represents a single instantaneous blood
37  * pressure reading.
38  */
39 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_BLOOD_PRESSURE)
40 public final class BloodPressureRecord extends InstantRecord {
41     private static final double SYSTOLIC_MIN_VALUE = 20.0;
42     private static final double SYSTOLIC_MAX_VALUE = 300.0;
43     private static final double DIASTOLIC_MIN_VALUE = 10.0;
44     private static final double DIASTOLIC_MAX_VALUE = 300.0;
45 
46     /**
47      * Metric identifier to get average diastolic pressure using aggregate APIs in {@link
48      * HealthConnectManager}
49      */
50     @NonNull
51     public static final AggregationType<Pressure> DIASTOLIC_AVG =
52             new AggregationType<>(
53                     AggregationType.AggregationTypeIdentifier.BLOOD_PRESSURE_RECORD_DIASTOLIC_AVG,
54                     AggregationType.AVG,
55                     RECORD_TYPE_BLOOD_PRESSURE,
56                     Pressure.class);
57 
58     /**
59      * Metric identifier to get maximum diastolic pressure using aggregate APIs in {@link
60      * HealthConnectManager}
61      */
62     @NonNull
63     public static final AggregationType<Pressure> DIASTOLIC_MAX =
64             new AggregationType<>(
65                     AggregationType.AggregationTypeIdentifier.BLOOD_PRESSURE_RECORD_DIASTOLIC_MAX,
66                     AggregationType.MAX,
67                     RECORD_TYPE_BLOOD_PRESSURE,
68                     Pressure.class);
69 
70     /**
71      * Metric identifier to get minimum diastolic pressure using aggregate APIs in {@link
72      * HealthConnectManager}
73      */
74     @NonNull
75     public static final AggregationType<Pressure> DIASTOLIC_MIN =
76             new AggregationType<>(
77                     AggregationType.AggregationTypeIdentifier.BLOOD_PRESSURE_RECORD_DIASTOLIC_MIN,
78                     AggregationType.MIN,
79                     RECORD_TYPE_BLOOD_PRESSURE,
80                     Pressure.class);
81 
82     /**
83      * Metric identifier to get average systolic pressure using aggregate APIs in {@link
84      * HealthConnectManager}
85      */
86     @NonNull
87     public static final AggregationType<Pressure> SYSTOLIC_AVG =
88             new AggregationType<>(
89                     AggregationType.AggregationTypeIdentifier.BLOOD_PRESSURE_RECORD_SYSTOLIC_AVG,
90                     AggregationType.AVG,
91                     RECORD_TYPE_BLOOD_PRESSURE,
92                     Pressure.class);
93 
94     /**
95      * Metric identifier to get maximum systolic pressure using aggregate APIs in {@link
96      * HealthConnectManager}
97      */
98     @NonNull
99     public static final AggregationType<Pressure> SYSTOLIC_MAX =
100             new AggregationType<>(
101                     AggregationType.AggregationTypeIdentifier.BLOOD_PRESSURE_RECORD_SYSTOLIC_MAX,
102                     AggregationType.MAX,
103                     RECORD_TYPE_BLOOD_PRESSURE,
104                     Pressure.class);
105 
106     /**
107      * Metric identifier to get minimum systolic pressure using aggregate APIs in {@link
108      * HealthConnectManager}
109      */
110     @NonNull
111     public static final AggregationType<Pressure> SYSTOLIC_MIN =
112             new AggregationType<>(
113                     AggregationType.AggregationTypeIdentifier.BLOOD_PRESSURE_RECORD_SYSTOLIC_MIN,
114                     AggregationType.MIN,
115                     RECORD_TYPE_BLOOD_PRESSURE,
116                     Pressure.class);
117 
118     private final int mMeasurementLocation;
119     private final Pressure mSystolic;
120     private final Pressure mDiastolic;
121     private final int mBodyPosition;
122 
123     /**
124      * @param metadata Metadata to be associated with the record. See {@link Metadata}.
125      * @param time Start time of this activity
126      * @param zoneOffset Zone offset of the user when the activity started
127      * @param measurementLocation MeasurementLocation of this activity
128      * @param systolic Systolic of this activity
129      * @param diastolic Diastolic of this activity
130      * @param bodyPosition BodyPosition of this activity
131      * @param skipValidation Boolean flag to skip validation of record values.
132      */
BloodPressureRecord( @onNull Metadata metadata, @NonNull Instant time, @NonNull ZoneOffset zoneOffset, @BloodPressureMeasurementLocation.BloodPressureMeasurementLocations int measurementLocation, @NonNull Pressure systolic, @NonNull Pressure diastolic, @BodyPosition.BodyPositionType int bodyPosition, boolean skipValidation)133     private BloodPressureRecord(
134             @NonNull Metadata metadata,
135             @NonNull Instant time,
136             @NonNull ZoneOffset zoneOffset,
137             @BloodPressureMeasurementLocation.BloodPressureMeasurementLocations
138                     int measurementLocation,
139             @NonNull Pressure systolic,
140             @NonNull Pressure diastolic,
141             @BodyPosition.BodyPositionType int bodyPosition,
142             boolean skipValidation) {
143         super(metadata, time, zoneOffset, skipValidation);
144         Objects.requireNonNull(metadata);
145         Objects.requireNonNull(time);
146         Objects.requireNonNull(zoneOffset);
147         Objects.requireNonNull(systolic);
148         Objects.requireNonNull(diastolic);
149         validateIntDefValue(
150                 measurementLocation,
151                 BloodPressureMeasurementLocation.VALID_TYPES,
152                 BloodPressureMeasurementLocation.class.getSimpleName());
153         if (!skipValidation) {
154             ValidationUtils.requireInRange(
155                     systolic.getInMillimetersOfMercury(),
156                     SYSTOLIC_MIN_VALUE,
157                     SYSTOLIC_MAX_VALUE,
158                     "systolic");
159             ValidationUtils.requireInRange(
160                     diastolic.getInMillimetersOfMercury(),
161                     DIASTOLIC_MIN_VALUE,
162                     DIASTOLIC_MAX_VALUE,
163                     "diastolic");
164         }
165         validateIntDefValue(
166                 bodyPosition, BodyPosition.VALID_TYPES, BodyPosition.class.getSimpleName());
167         mMeasurementLocation = measurementLocation;
168         mSystolic = systolic;
169         mDiastolic = diastolic;
170         mBodyPosition = bodyPosition;
171     }
172 
173     /**
174      * @return measurementLocation
175      */
176     @BloodPressureMeasurementLocation.BloodPressureMeasurementLocations
getMeasurementLocation()177     public int getMeasurementLocation() {
178         return mMeasurementLocation;
179     }
180 
181     /**
182      * @return systolic
183      */
184     @NonNull
getSystolic()185     public Pressure getSystolic() {
186         return mSystolic;
187     }
188 
189     /**
190      * @return diastolic
191      */
192     @NonNull
getDiastolic()193     public Pressure getDiastolic() {
194         return mDiastolic;
195     }
196 
197     /**
198      * @return bodyPosition
199      */
200     @BodyPosition.BodyPositionType
getBodyPosition()201     public int getBodyPosition() {
202         return mBodyPosition;
203     }
204 
205     /** Identifier for Blood Pressure Measurement Location */
206     public static final class BloodPressureMeasurementLocation {
207 
208         public static final int BLOOD_PRESSURE_MEASUREMENT_LOCATION_UNKNOWN = 0;
209         /** Blood pressure measurement location constant for the left wrist. */
210         public static final int BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_WRIST = 1;
211         /** Blood pressure measurement location constant for the right wrist. */
212         public static final int BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_WRIST = 2;
213         /** Blood pressure measurement location constant for the left upper arm. */
214         public static final int BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_UPPER_ARM = 3;
215         /** Blood pressure measurement location constant for the right upper arm. */
216         public static final int BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_UPPER_ARM = 4;
217 
218         /**
219          * Valid set of values for this IntDef. Update this set when add new type or deprecate
220          * existing type.
221          *
222          * @hide
223          */
224         public static final Set<Integer> VALID_TYPES =
225                 Set.of(
226                         BLOOD_PRESSURE_MEASUREMENT_LOCATION_UNKNOWN,
227                         BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_WRIST,
228                         BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_WRIST,
229                         BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_UPPER_ARM,
230                         BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_UPPER_ARM);
231 
BloodPressureMeasurementLocation()232         private BloodPressureMeasurementLocation() {}
233 
234         /** @hide */
235         @IntDef({
236             BLOOD_PRESSURE_MEASUREMENT_LOCATION_UNKNOWN,
237             BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_WRIST,
238             BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_WRIST,
239             BLOOD_PRESSURE_MEASUREMENT_LOCATION_LEFT_UPPER_ARM,
240             BLOOD_PRESSURE_MEASUREMENT_LOCATION_RIGHT_UPPER_ARM
241         })
242         @Retention(RetentionPolicy.SOURCE)
243         public @interface BloodPressureMeasurementLocations {}
244     }
245 
246     /** Identifier for body position */
247     public static final class BodyPosition {
248 
249         /** Body position unknown / not identified. */
250         public static final int BODY_POSITION_UNKNOWN = 0;
251         /** Body position constant representing standing up. */
252         public static final int BODY_POSITION_STANDING_UP = 1;
253         /** Body position constant representing sitting down. */
254         public static final int BODY_POSITION_SITTING_DOWN = 2;
255         /** Body position constant representing lying down. */
256         public static final int BODY_POSITION_LYING_DOWN = 3;
257         /** Body position constant representing semi-recumbent (partially reclining) pose. */
258         public static final int BODY_POSITION_RECLINING = 4;
259 
260         /**
261          * Valid set of values for this IntDef. Update this set when add new type or deprecate
262          * existing type.
263          *
264          * @hide
265          */
266         public static final Set<Integer> VALID_TYPES =
267                 Set.of(
268                         BODY_POSITION_UNKNOWN,
269                         BODY_POSITION_STANDING_UP,
270                         BODY_POSITION_SITTING_DOWN,
271                         BODY_POSITION_LYING_DOWN,
272                         BODY_POSITION_RECLINING);
273 
BodyPosition()274         private BodyPosition() {}
275 
276         /** @hide */
277         @IntDef({
278             BODY_POSITION_UNKNOWN,
279             BODY_POSITION_STANDING_UP,
280             BODY_POSITION_SITTING_DOWN,
281             BODY_POSITION_LYING_DOWN,
282             BODY_POSITION_RECLINING
283         })
284         @Retention(RetentionPolicy.SOURCE)
285         public @interface BodyPositionType {}
286     }
287 
288     /**
289      * Indicates whether some other object is "equal to" this one.
290      *
291      * @param o the reference object with which to compare.
292      * @return {@code true} if this object is the same as the obj
293      */
294     @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression
295     @Override
equals(Object o)296     public boolean equals(Object o) {
297         if (this == o) return true;
298         if (!super.equals(o)) return false;
299         BloodPressureRecord that = (BloodPressureRecord) o;
300         return getMeasurementLocation() == that.getMeasurementLocation()
301                 && getBodyPosition() == that.getBodyPosition()
302                 && getSystolic().equals(that.getSystolic())
303                 && getDiastolic().equals(that.getDiastolic());
304     }
305 
306     /** Returns a hash code value for the object. */
307     @Override
hashCode()308     public int hashCode() {
309         return Objects.hash(
310                 super.hashCode(),
311                 getMeasurementLocation(),
312                 getSystolic(),
313                 getDiastolic(),
314                 getBodyPosition());
315     }
316 
317     /** Builder class for {@link BloodPressureRecord} */
318     public static final class Builder {
319         private final Metadata mMetadata;
320         private final Instant mTime;
321         private ZoneOffset mZoneOffset;
322         private final int mMeasurementLocation;
323         private final Pressure mSystolic;
324         private final Pressure mDiastolic;
325         private final int mBodyPosition;
326 
327         /**
328          * @param metadata Metadata to be associated with the record. See {@link Metadata}.
329          * @param time Start time of this activity
330          * @param measurementLocation The arm and part of the arm where the measurement was taken.
331          *     Optional field. Allowed values: {@link BodyTemperatureMeasurementLocation}.
332          * @param systolic Systolic blood pressure measurement, in {@link Pressure} unit. Required
333          *     field. Valid range: 20-200 mmHg.
334          * @param diastolic Diastolic blood pressure measurement, in {@link Pressure} unit. Required
335          *     field. Valid range: 10-180 mmHg.
336          * @param bodyPosition The user's body position when the measurement was taken. Optional
337          *     field. Allowed values: {@link BodyPosition}.
338          */
Builder( @onNull Metadata metadata, @NonNull Instant time, @BloodPressureMeasurementLocation.BloodPressureMeasurementLocations int measurementLocation, @NonNull Pressure systolic, @NonNull Pressure diastolic, @BodyPosition.BodyPositionType int bodyPosition)339         public Builder(
340                 @NonNull Metadata metadata,
341                 @NonNull Instant time,
342                 @BloodPressureMeasurementLocation.BloodPressureMeasurementLocations
343                         int measurementLocation,
344                 @NonNull Pressure systolic,
345                 @NonNull Pressure diastolic,
346                 @BodyPosition.BodyPositionType int bodyPosition) {
347             Objects.requireNonNull(metadata);
348             Objects.requireNonNull(time);
349             Objects.requireNonNull(systolic);
350             Objects.requireNonNull(diastolic);
351             validateIntDefValue(
352                     measurementLocation,
353                     BloodPressureMeasurementLocation.VALID_TYPES,
354                     BloodPressureMeasurementLocation.class.getSimpleName());
355             mMetadata = metadata;
356             mTime = time;
357             mMeasurementLocation = measurementLocation;
358             mSystolic = systolic;
359             mDiastolic = diastolic;
360             mBodyPosition = bodyPosition;
361             mZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(time);
362         }
363 
364         /** Sets the zone offset of the user when the activity happened */
365         @NonNull
setZoneOffset(@onNull ZoneOffset zoneOffset)366         public Builder setZoneOffset(@NonNull ZoneOffset zoneOffset) {
367             Objects.requireNonNull(zoneOffset);
368             mZoneOffset = zoneOffset;
369             return this;
370         }
371 
372         /** Sets the zone offset of this record to system default. */
373         @NonNull
clearZoneOffset()374         public Builder clearZoneOffset() {
375             mZoneOffset = RecordUtils.getDefaultZoneOffset();
376             return this;
377         }
378 
379         /**
380          * @return Object of {@link BloodPressureRecord} without validating the values.
381          * @hide
382          */
383         @NonNull
buildWithoutValidation()384         public BloodPressureRecord buildWithoutValidation() {
385             return new BloodPressureRecord(
386                     mMetadata,
387                     mTime,
388                     mZoneOffset,
389                     mMeasurementLocation,
390                     mSystolic,
391                     mDiastolic,
392                     mBodyPosition,
393                     true);
394         }
395 
396         /**
397          * @return Object of {@link BloodPressureRecord}
398          */
399         @NonNull
build()400         public BloodPressureRecord build() {
401             return new BloodPressureRecord(
402                     mMetadata,
403                     mTime,
404                     mZoneOffset,
405                     mMeasurementLocation,
406                     mSystolic,
407                     mDiastolic,
408                     mBodyPosition,
409                     false);
410         }
411     }
412 
413     /** @hide */
414     @Override
toRecordInternal()415     public BloodPressureRecordInternal toRecordInternal() {
416         BloodPressureRecordInternal recordInternal =
417                 (BloodPressureRecordInternal)
418                         new BloodPressureRecordInternal()
419                                 .setUuid(getMetadata().getId())
420                                 .setPackageName(getMetadata().getDataOrigin().getPackageName())
421                                 .setLastModifiedTime(
422                                         getMetadata().getLastModifiedTime().toEpochMilli())
423                                 .setClientRecordId(getMetadata().getClientRecordId())
424                                 .setClientRecordVersion(getMetadata().getClientRecordVersion())
425                                 .setManufacturer(getMetadata().getDevice().getManufacturer())
426                                 .setModel(getMetadata().getDevice().getModel())
427                                 .setDeviceType(getMetadata().getDevice().getType())
428                                 .setRecordingMethod(getMetadata().getRecordingMethod());
429         recordInternal.setTime(getTime().toEpochMilli());
430         recordInternal.setZoneOffset(getZoneOffset().getTotalSeconds());
431         recordInternal.setMeasurementLocation(mMeasurementLocation);
432         recordInternal.setSystolic(mSystolic.getInMillimetersOfMercury());
433         recordInternal.setDiastolic(mDiastolic.getInMillimetersOfMercury());
434         recordInternal.setBodyPosition(mBodyPosition);
435         return recordInternal;
436     }
437 }
438