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