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.validation.ValidationUtils.validateIntDefValue; 19 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.health.connect.internal.datatypes.MenstruationFlowRecordInternal; 23 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.time.Instant; 27 import java.time.ZoneOffset; 28 import java.util.Objects; 29 import java.util.Set; 30 31 /** 32 * Captures a description of how heavy a user's menstrual flow was (spotting, light, medium, or 33 * heavy). Each record represents a description of how heavy the user's menstrual bleeding was. 34 */ 35 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_MENSTRUATION_FLOW) 36 public final class MenstruationFlowRecord extends InstantRecord { 37 38 private final int mFlow; 39 40 /** 41 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 42 * @param time Start time of this activity 43 * @param zoneOffset Zone offset of the user when the activity started 44 * @param flow Flow of this activity 45 * @param skipValidation Boolean flag to skip validation of record values. 46 */ MenstruationFlowRecord( @onNull Metadata metadata, @NonNull Instant time, @NonNull ZoneOffset zoneOffset, @MenstruationFlowType.MenstruationFlowTypes int flow, boolean skipValidation)47 private MenstruationFlowRecord( 48 @NonNull Metadata metadata, 49 @NonNull Instant time, 50 @NonNull ZoneOffset zoneOffset, 51 @MenstruationFlowType.MenstruationFlowTypes int flow, 52 boolean skipValidation) { 53 super(metadata, time, zoneOffset, skipValidation); 54 Objects.requireNonNull(metadata); 55 Objects.requireNonNull(time); 56 Objects.requireNonNull(zoneOffset); 57 validateIntDefValue( 58 flow, MenstruationFlowType.VALID_TYPES, MenstruationFlowType.class.getSimpleName()); 59 mFlow = flow; 60 } 61 62 /** 63 * @return menstruation flow 64 */ getFlow()65 public int getFlow() { 66 return mFlow; 67 } 68 69 /** Identifier for Menstruation Flow */ 70 public static final class MenstruationFlowType { 71 public static final int FLOW_UNKNOWN = 0; 72 public static final int FLOW_LIGHT = 1; 73 public static final int FLOW_MEDIUM = 2; 74 public static final int FLOW_HEAVY = 3; 75 76 /** 77 * Valid set of values for this IntDef. Update this set when add new type or deprecate 78 * existing type. 79 * 80 * @hide 81 */ 82 public static final Set<Integer> VALID_TYPES = 83 Set.of(FLOW_UNKNOWN, FLOW_LIGHT, FLOW_MEDIUM, FLOW_HEAVY); 84 MenstruationFlowType()85 MenstruationFlowType() {} 86 87 /** @hide */ 88 @IntDef({FLOW_UNKNOWN, FLOW_LIGHT, FLOW_MEDIUM, FLOW_HEAVY}) 89 @Retention(RetentionPolicy.SOURCE) 90 public @interface MenstruationFlowTypes {} 91 } 92 93 /** 94 * Indicates whether some other object is "equal to" this one. 95 * 96 * @param o the reference object with which to compare. 97 * @return {@code true} if this object is the same as the obj 98 */ 99 @Override equals(Object o)100 public boolean equals(Object o) { 101 if (this == o) return true; 102 if (!super.equals(o)) return false; 103 MenstruationFlowRecord that = (MenstruationFlowRecord) o; 104 return getFlow() == that.getFlow(); 105 } 106 107 /** Returns a hash code value for the object. */ 108 @Override hashCode()109 public int hashCode() { 110 return Objects.hash(super.hashCode(), getFlow()); 111 } 112 113 /** Builder class for {@link MenstruationFlowRecord} */ 114 public static final class Builder { 115 private final Metadata mMetadata; 116 private final Instant mTime; 117 private ZoneOffset mZoneOffset; 118 private final int mFlow; 119 120 /** 121 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 122 * @param time Start time of this activity 123 * @param flow How heavy the user's menstrual flow was. Optional field. Allowed values: 124 * {@link MenstruationFlowType}. 125 */ Builder( @onNull Metadata metadata, @NonNull Instant time, @MenstruationFlowType.MenstruationFlowTypes int flow)126 public Builder( 127 @NonNull Metadata metadata, 128 @NonNull Instant time, 129 @MenstruationFlowType.MenstruationFlowTypes int flow) { 130 Objects.requireNonNull(metadata); 131 Objects.requireNonNull(time); 132 mMetadata = metadata; 133 mTime = time; 134 mFlow = flow; 135 mZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(time); 136 } 137 138 /** Sets the zone offset of the user when the activity happened */ 139 @NonNull setZoneOffset(@onNull ZoneOffset zoneOffset)140 public Builder setZoneOffset(@NonNull ZoneOffset zoneOffset) { 141 Objects.requireNonNull(zoneOffset); 142 mZoneOffset = zoneOffset; 143 return this; 144 } 145 146 /** Sets the zone offset of this record to system default. */ 147 @NonNull clearZoneOffset()148 public Builder clearZoneOffset() { 149 mZoneOffset = RecordUtils.getDefaultZoneOffset(); 150 return this; 151 } 152 153 /** 154 * @return Object of {@link MenstruationFlowRecord} without validating the values. 155 * @hide 156 */ 157 @NonNull buildWithoutValidation()158 public MenstruationFlowRecord buildWithoutValidation() { 159 return new MenstruationFlowRecord(mMetadata, mTime, mZoneOffset, mFlow, true); 160 } 161 162 /** 163 * @return Object of {@link MenstruationFlowRecord} 164 */ 165 @NonNull build()166 public MenstruationFlowRecord build() { 167 return new MenstruationFlowRecord(mMetadata, mTime, mZoneOffset, mFlow, false); 168 } 169 } 170 171 /** @hide */ 172 @Override toRecordInternal()173 public MenstruationFlowRecordInternal toRecordInternal() { 174 MenstruationFlowRecordInternal recordInternal = 175 (MenstruationFlowRecordInternal) 176 new MenstruationFlowRecordInternal() 177 .setUuid(getMetadata().getId()) 178 .setPackageName(getMetadata().getDataOrigin().getPackageName()) 179 .setLastModifiedTime( 180 getMetadata().getLastModifiedTime().toEpochMilli()) 181 .setClientRecordId(getMetadata().getClientRecordId()) 182 .setClientRecordVersion(getMetadata().getClientRecordVersion()) 183 .setManufacturer(getMetadata().getDevice().getManufacturer()) 184 .setModel(getMetadata().getDevice().getModel()) 185 .setDeviceType(getMetadata().getDevice().getType()) 186 .setRecordingMethod(getMetadata().getRecordingMethod()); 187 recordInternal.setTime(getTime().toEpochMilli()); 188 recordInternal.setZoneOffset(getZoneOffset().getTotalSeconds()); 189 recordInternal.setFlow(mFlow); 190 return recordInternal; 191 } 192 } 193