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 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 100 @Override equals(Object o)101 public boolean equals(Object o) { 102 if (this == o) return true; 103 if (!super.equals(o)) return false; 104 MenstruationFlowRecord that = (MenstruationFlowRecord) o; 105 return getFlow() == that.getFlow(); 106 } 107 108 /** Returns a hash code value for the object. */ 109 @Override hashCode()110 public int hashCode() { 111 return Objects.hash(super.hashCode(), getFlow()); 112 } 113 114 /** Builder class for {@link MenstruationFlowRecord} */ 115 public static final class Builder { 116 private final Metadata mMetadata; 117 private final Instant mTime; 118 private ZoneOffset mZoneOffset; 119 private final int mFlow; 120 121 /** 122 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 123 * @param time Start time of this activity 124 * @param flow How heavy the user's menstrual flow was. Optional field. Allowed values: 125 * {@link MenstruationFlowType}. 126 */ Builder( @onNull Metadata metadata, @NonNull Instant time, @MenstruationFlowType.MenstruationFlowTypes int flow)127 public Builder( 128 @NonNull Metadata metadata, 129 @NonNull Instant time, 130 @MenstruationFlowType.MenstruationFlowTypes int flow) { 131 Objects.requireNonNull(metadata); 132 Objects.requireNonNull(time); 133 mMetadata = metadata; 134 mTime = time; 135 mFlow = flow; 136 mZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(time); 137 } 138 139 /** Sets the zone offset of the user when the activity happened */ 140 @NonNull setZoneOffset(@onNull ZoneOffset zoneOffset)141 public Builder setZoneOffset(@NonNull ZoneOffset zoneOffset) { 142 Objects.requireNonNull(zoneOffset); 143 mZoneOffset = zoneOffset; 144 return this; 145 } 146 147 /** Sets the zone offset of this record to system default. */ 148 @NonNull clearZoneOffset()149 public Builder clearZoneOffset() { 150 mZoneOffset = RecordUtils.getDefaultZoneOffset(); 151 return this; 152 } 153 154 /** 155 * @return Object of {@link MenstruationFlowRecord} without validating the values. 156 * @hide 157 */ 158 @NonNull buildWithoutValidation()159 public MenstruationFlowRecord buildWithoutValidation() { 160 return new MenstruationFlowRecord(mMetadata, mTime, mZoneOffset, mFlow, true); 161 } 162 163 /** 164 * @return Object of {@link MenstruationFlowRecord} 165 */ 166 @NonNull build()167 public MenstruationFlowRecord build() { 168 return new MenstruationFlowRecord(mMetadata, mTime, mZoneOffset, mFlow, false); 169 } 170 } 171 172 /** @hide */ 173 @Override toRecordInternal()174 public MenstruationFlowRecordInternal toRecordInternal() { 175 MenstruationFlowRecordInternal recordInternal = 176 (MenstruationFlowRecordInternal) 177 new MenstruationFlowRecordInternal() 178 .setUuid(getMetadata().getId()) 179 .setPackageName(getMetadata().getDataOrigin().getPackageName()) 180 .setLastModifiedTime( 181 getMetadata().getLastModifiedTime().toEpochMilli()) 182 .setClientRecordId(getMetadata().getClientRecordId()) 183 .setClientRecordVersion(getMetadata().getClientRecordVersion()) 184 .setManufacturer(getMetadata().getDevice().getManufacturer()) 185 .setModel(getMetadata().getDevice().getModel()) 186 .setDeviceType(getMetadata().getDevice().getType()) 187 .setRecordingMethod(getMetadata().getRecordingMethod()); 188 recordInternal.setTime(getTime().toEpochMilli()); 189 recordInternal.setZoneOffset(getZoneOffset().getTotalSeconds()); 190 recordInternal.setFlow(mFlow); 191 return recordInternal; 192 } 193 } 194