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.CervicalMucusRecordInternal; 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 the description of cervical mucus. Each record represents a self-assessed description of 33 * cervical mucus for a user. All fields are optional and can be used to describe the look and feel 34 * of cervical mucus. 35 */ 36 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_CERVICAL_MUCUS) 37 public final class CervicalMucusRecord extends InstantRecord { 38 39 private final int mSensation; 40 private final int mAppearance; 41 42 /** 43 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 44 * @param time Start time of this activity 45 * @param zoneOffset Zone offset of the user when the activity started 46 * @param sensation Sensation of this activity 47 * @param appearance Appearance of this activity 48 * @param skipValidation Boolean flag to skip validation of record values. 49 */ CervicalMucusRecord( @onNull Metadata metadata, @NonNull Instant time, @NonNull ZoneOffset zoneOffset, @CervicalMucusSensation.CervicalMucusSensations int sensation, @CervicalMucusAppearance.CervicalMucusAppearances int appearance, boolean skipValidation)50 private CervicalMucusRecord( 51 @NonNull Metadata metadata, 52 @NonNull Instant time, 53 @NonNull ZoneOffset zoneOffset, 54 @CervicalMucusSensation.CervicalMucusSensations int sensation, 55 @CervicalMucusAppearance.CervicalMucusAppearances int appearance, 56 boolean skipValidation) { 57 super(metadata, time, zoneOffset, skipValidation); 58 Objects.requireNonNull(metadata); 59 Objects.requireNonNull(time); 60 Objects.requireNonNull(zoneOffset); 61 validateIntDefValue( 62 sensation, 63 CervicalMucusSensation.VALID_TYPES, 64 CervicalMucusSensation.class.getSimpleName()); 65 validateIntDefValue( 66 appearance, 67 CervicalMucusAppearance.VALID_TYPES, 68 CervicalMucusAppearance.class.getSimpleName()); 69 mSensation = sensation; 70 mAppearance = appearance; 71 } 72 73 /** 74 * @return sensation 75 */ 76 @CervicalMucusSensation.CervicalMucusSensations getSensation()77 public int getSensation() { 78 return mSensation; 79 } 80 81 /** 82 * @return appearance 83 */ 84 @CervicalMucusAppearance.CervicalMucusAppearances getAppearance()85 public int getAppearance() { 86 return mAppearance; 87 } 88 89 /** Identifier for Cervical Mucus Appearance */ 90 public static final class CervicalMucusAppearance { 91 /** A constant describing cervical mucus which appearance is unknown. */ 92 public static final int APPEARANCE_UNKNOWN = 0; 93 94 /** A constant describing a dry cervical mucus. */ 95 public static final int APPEARANCE_DRY = 1; 96 97 /** A constant describing a sticky cervical mucus. */ 98 public static final int APPEARANCE_STICKY = 2; 99 100 /** A constant describing creamy like looking cervical mucus. */ 101 public static final int APPEARANCE_CREAMY = 3; 102 103 /** A constant describing watery like looking cervical mucus. */ 104 public static final int APPEARANCE_WATERY = 4; 105 106 /** A constant describing clear or egg white like looking cervical mucus. */ 107 public static final int APPEARANCE_EGG_WHITE = 5; 108 109 /** A constant describing an unusual (worth attention) kind of cervical mucus. */ 110 public static final int APPEARANCE_UNUSUAL = 6; 111 112 /** 113 * Valid set of values for this IntDef. Update this set when add new type or deprecate 114 * existing type. 115 * 116 * @hide 117 */ 118 public static final Set<Integer> VALID_TYPES = 119 Set.of( 120 APPEARANCE_UNKNOWN, 121 APPEARANCE_DRY, 122 APPEARANCE_STICKY, 123 APPEARANCE_CREAMY, 124 APPEARANCE_WATERY, 125 APPEARANCE_EGG_WHITE, 126 APPEARANCE_UNUSUAL); 127 CervicalMucusAppearance()128 CervicalMucusAppearance() {} 129 130 /** @hide */ 131 @IntDef({ 132 APPEARANCE_UNKNOWN, 133 APPEARANCE_DRY, 134 APPEARANCE_STICKY, 135 APPEARANCE_CREAMY, 136 APPEARANCE_WATERY, 137 APPEARANCE_EGG_WHITE, 138 APPEARANCE_UNUSUAL 139 }) 140 @Retention(RetentionPolicy.SOURCE) 141 public @interface CervicalMucusAppearances {} 142 } 143 144 /** Identifier for Cervical Mucus Sensation */ 145 public static final class CervicalMucusSensation { 146 public static final int SENSATION_UNKNOWN = 0; 147 public static final int SENSATION_LIGHT = 1; 148 public static final int SENSATION_MEDIUM = 2; 149 public static final int SENSATION_HEAVY = 3; 150 151 /** 152 * Valid set of values for this IntDef. Update this set when add new type or deprecate 153 * existing type. 154 * 155 * @hide 156 */ 157 public static final Set<Integer> VALID_TYPES = 158 Set.of(SENSATION_UNKNOWN, SENSATION_LIGHT, SENSATION_MEDIUM, SENSATION_HEAVY); 159 CervicalMucusSensation()160 CervicalMucusSensation() {} 161 162 /** @hide */ 163 @IntDef({SENSATION_UNKNOWN, SENSATION_LIGHT, SENSATION_MEDIUM, SENSATION_HEAVY}) 164 @Retention(RetentionPolicy.SOURCE) 165 public @interface CervicalMucusSensations {} 166 } 167 168 /** 169 * Indicates whether some other object is "equal to" this one. 170 * 171 * @param o the reference object with which to compare. 172 * @return {@code true} if this object is the same as the obj 173 */ 174 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 175 @Override equals(Object o)176 public boolean equals(Object o) { 177 if (this == o) return true; 178 if (!super.equals(o)) return false; 179 CervicalMucusRecord that = (CervicalMucusRecord) o; 180 return getSensation() == that.getSensation() && getAppearance() == that.getAppearance(); 181 } 182 183 /** Returns a hash code value for the object. */ 184 @Override hashCode()185 public int hashCode() { 186 return Objects.hash(super.hashCode(), getSensation(), getAppearance()); 187 } 188 189 /** Builder class for {@link CervicalMucusRecord} */ 190 public static final class Builder { 191 private final Metadata mMetadata; 192 private final Instant mTime; 193 private ZoneOffset mZoneOffset; 194 private final int mSensation; 195 private final int mAppearance; 196 197 /** 198 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 199 * @param time Start time of this activity 200 * @param sensation The feel of the user's cervical mucus. Optional field. Allowed values: 201 * {@link CervicalMucusSensation}. 202 * @param appearance The consistency of the user's cervical mucus. Optional field. Allowed 203 * values: {@link CervicalMucusAppearance}. 204 */ Builder( @onNull Metadata metadata, @NonNull Instant time, @CervicalMucusSensation.CervicalMucusSensations int sensation, @CervicalMucusAppearance.CervicalMucusAppearances int appearance)205 public Builder( 206 @NonNull Metadata metadata, 207 @NonNull Instant time, 208 @CervicalMucusSensation.CervicalMucusSensations int sensation, 209 @CervicalMucusAppearance.CervicalMucusAppearances int appearance) { 210 Objects.requireNonNull(metadata); 211 Objects.requireNonNull(time); 212 mMetadata = metadata; 213 mTime = time; 214 mSensation = sensation; 215 mAppearance = appearance; 216 mZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(time); 217 } 218 219 /** Sets the zone offset of the user when the activity happened */ 220 @NonNull setZoneOffset(@onNull ZoneOffset zoneOffset)221 public Builder setZoneOffset(@NonNull ZoneOffset zoneOffset) { 222 Objects.requireNonNull(zoneOffset); 223 mZoneOffset = zoneOffset; 224 return this; 225 } 226 227 /** Sets the zone offset of this record to system default. */ 228 @NonNull clearZoneOffset()229 public Builder clearZoneOffset() { 230 mZoneOffset = RecordUtils.getDefaultZoneOffset(); 231 return this; 232 } 233 234 /** 235 * @return Object of {@link CervicalMucusRecord} without validating the values. 236 * @hide 237 */ 238 @NonNull buildWithoutValidation()239 public CervicalMucusRecord buildWithoutValidation() { 240 return new CervicalMucusRecord( 241 mMetadata, mTime, mZoneOffset, mSensation, mAppearance, true); 242 } 243 244 /** 245 * @return Object of {@link CervicalMucusRecord} 246 */ 247 @NonNull build()248 public CervicalMucusRecord build() { 249 return new CervicalMucusRecord( 250 mMetadata, mTime, mZoneOffset, mSensation, mAppearance, false); 251 } 252 } 253 254 /** @hide */ 255 @Override toRecordInternal()256 public CervicalMucusRecordInternal toRecordInternal() { 257 CervicalMucusRecordInternal recordInternal = 258 (CervicalMucusRecordInternal) 259 new CervicalMucusRecordInternal() 260 .setUuid(getMetadata().getId()) 261 .setPackageName(getMetadata().getDataOrigin().getPackageName()) 262 .setLastModifiedTime( 263 getMetadata().getLastModifiedTime().toEpochMilli()) 264 .setClientRecordId(getMetadata().getClientRecordId()) 265 .setClientRecordVersion(getMetadata().getClientRecordVersion()) 266 .setManufacturer(getMetadata().getDevice().getManufacturer()) 267 .setModel(getMetadata().getDevice().getModel()) 268 .setDeviceType(getMetadata().getDevice().getType()) 269 .setRecordingMethod(getMetadata().getRecordingMethod()); 270 recordInternal.setTime(getTime().toEpochMilli()); 271 recordInternal.setZoneOffset(getZoneOffset().getTotalSeconds()); 272 recordInternal.setSensation(mSensation); 273 recordInternal.setAppearance(mAppearance); 274 return recordInternal; 275 } 276 } 277