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.OvulationTestRecordInternal; 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 /** Each record represents the result of an ovulation test. */ 32 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_OVULATION_TEST) 33 public final class OvulationTestRecord extends InstantRecord { 34 35 private final int mResult; 36 37 /** 38 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 39 * @param time Start time of this activity 40 * @param zoneOffset Zone offset of the user when the activity started 41 * @param result Result of this activity 42 * @param skipValidation Boolean flag to skip validation of record values. 43 */ OvulationTestRecord( @onNull Metadata metadata, @NonNull Instant time, @NonNull ZoneOffset zoneOffset, @OvulationTestResult.OvulationTestResults int result, boolean skipValidation)44 private OvulationTestRecord( 45 @NonNull Metadata metadata, 46 @NonNull Instant time, 47 @NonNull ZoneOffset zoneOffset, 48 @OvulationTestResult.OvulationTestResults int result, 49 boolean skipValidation) { 50 super(metadata, time, zoneOffset, skipValidation); 51 Objects.requireNonNull(metadata); 52 Objects.requireNonNull(time); 53 Objects.requireNonNull(zoneOffset); 54 validateIntDefValue( 55 result, OvulationTestResult.VALID_TYPES, OvulationTestResult.class.getSimpleName()); 56 mResult = result; 57 } 58 59 /** 60 * @return test result 61 */ 62 @OvulationTestResult.OvulationTestResults getResult()63 public int getResult() { 64 return mResult; 65 } 66 67 /** Identifier for Ovulation Test Result */ 68 public static final class OvulationTestResult { 69 /** 70 * Inconclusive result. Refers to ovulation test results that are indeterminate (e.g. may be 71 * testing malfunction, user error, etc.). ". Any unknown value will also be returned as 72 * [RESULT_INCONCLUSIVE]. 73 */ 74 public static final int RESULT_INCONCLUSIVE = 0; 75 76 /** 77 * Positive fertility (may also be referred as "peak" fertility). Refers to the peak of the 78 * luteinizing hormone (LH) surge and ovulation is expected to occur in 10-36 hours. 79 */ 80 public static final int RESULT_POSITIVE = 1; 81 82 /** 83 * High fertility. Refers to a rise in estrogen or luteinizing hormone that may signal the 84 * fertile window (time in the menstrual cycle when conception is likely to occur). 85 */ 86 public static final int RESULT_HIGH = 2; 87 88 /** 89 * Negative fertility (may also be referred as "low" fertility). Refers to the time in the 90 * cycle where fertility/conception is expected to be low. 91 */ 92 public static final int RESULT_NEGATIVE = 3; 93 94 /** 95 * Valid set of values for this IntDef. Update this set when add new type or deprecate 96 * existing type. 97 * 98 * @hide 99 */ 100 public static final Set<Integer> VALID_TYPES = 101 Set.of(RESULT_INCONCLUSIVE, RESULT_POSITIVE, RESULT_HIGH, RESULT_NEGATIVE); 102 OvulationTestResult()103 OvulationTestResult() {} 104 105 /** @hide */ 106 @IntDef({RESULT_INCONCLUSIVE, RESULT_POSITIVE, RESULT_HIGH, RESULT_NEGATIVE}) 107 @Retention(RetentionPolicy.SOURCE) 108 public @interface OvulationTestResults {} 109 } 110 111 /** 112 * Indicates whether some other object is "equal to" this one. 113 * 114 * @param o the reference object with which to compare. 115 * @return {@code true} if this object is the same as the obj 116 */ 117 @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression 118 @Override equals(Object o)119 public boolean equals(Object o) { 120 if (this == o) return true; 121 if (!super.equals(o)) return false; 122 OvulationTestRecord that = (OvulationTestRecord) o; 123 return getResult() == that.getResult(); 124 } 125 126 /** Returns a hash code value for the object. */ 127 @Override hashCode()128 public int hashCode() { 129 return Objects.hash(super.hashCode(), getResult()); 130 } 131 132 /** Builder class for {@link OvulationTestRecord} */ 133 public static final class Builder { 134 private final Metadata mMetadata; 135 private final Instant mTime; 136 private ZoneOffset mZoneOffset; 137 private final int mResult; 138 139 /** 140 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 141 * @param time Start time of this activity 142 * @param result The result of a user's ovulation test, which shows if they're ovulating or 143 * not. Required field. Allowed values: {@link OvulationTestResult}. 144 */ Builder( @onNull Metadata metadata, @NonNull Instant time, @OvulationTestResult.OvulationTestResults int result)145 public Builder( 146 @NonNull Metadata metadata, 147 @NonNull Instant time, 148 @OvulationTestResult.OvulationTestResults int result) { 149 Objects.requireNonNull(metadata); 150 Objects.requireNonNull(time); 151 mMetadata = metadata; 152 mTime = time; 153 mResult = result; 154 mZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(time); 155 } 156 157 /** Sets the zone offset of the user when the activity happened */ 158 @NonNull setZoneOffset(@onNull ZoneOffset zoneOffset)159 public Builder setZoneOffset(@NonNull ZoneOffset zoneOffset) { 160 Objects.requireNonNull(zoneOffset); 161 mZoneOffset = zoneOffset; 162 return this; 163 } 164 165 /** Sets the zone offset of this record to system default. */ 166 @NonNull clearZoneOffset()167 public Builder clearZoneOffset() { 168 mZoneOffset = RecordUtils.getDefaultZoneOffset(); 169 return this; 170 } 171 172 /** 173 * @return Object of {@link OvulationTestRecord} without validating the values. 174 * @hide 175 */ 176 @NonNull buildWithoutValidation()177 public OvulationTestRecord buildWithoutValidation() { 178 return new OvulationTestRecord(mMetadata, mTime, mZoneOffset, mResult, true); 179 } 180 181 /** 182 * @return Object of {@link OvulationTestRecord} 183 */ 184 @NonNull build()185 public OvulationTestRecord build() { 186 return new OvulationTestRecord(mMetadata, mTime, mZoneOffset, mResult, false); 187 } 188 } 189 190 /** @hide */ 191 @Override toRecordInternal()192 public OvulationTestRecordInternal toRecordInternal() { 193 OvulationTestRecordInternal recordInternal = 194 (OvulationTestRecordInternal) 195 new OvulationTestRecordInternal() 196 .setUuid(getMetadata().getId()) 197 .setPackageName(getMetadata().getDataOrigin().getPackageName()) 198 .setLastModifiedTime( 199 getMetadata().getLastModifiedTime().toEpochMilli()) 200 .setClientRecordId(getMetadata().getClientRecordId()) 201 .setClientRecordVersion(getMetadata().getClientRecordVersion()) 202 .setManufacturer(getMetadata().getDevice().getManufacturer()) 203 .setModel(getMetadata().getDevice().getModel()) 204 .setDeviceType(getMetadata().getDevice().getType()) 205 .setRecordingMethod(getMetadata().getRecordingMethod()); 206 recordInternal.setTime(getTime().toEpochMilli()); 207 recordInternal.setZoneOffset(getZoneOffset().getTotalSeconds()); 208 recordInternal.setResult(mResult); 209 return recordInternal; 210 } 211 } 212