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.SexualActivityRecordInternal; 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 an occurrence of sexual activity. Each record is a single occurrence. ProtectionUsed 33 * field is optional. 34 */ 35 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_SEXUAL_ACTIVITY) 36 public final class SexualActivityRecord extends InstantRecord { 37 38 private final int mProtectionUsed; 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 protectionUsed ProtectionUsed of this activity 45 * @param skipValidation Boolean flag to skip validation of record values. 46 */ SexualActivityRecord( @onNull Metadata metadata, @NonNull Instant time, @NonNull ZoneOffset zoneOffset, @SexualActivityProtectionUsed.SexualActivityProtectionUsedTypes int protectionUsed, boolean skipValidation)47 private SexualActivityRecord( 48 @NonNull Metadata metadata, 49 @NonNull Instant time, 50 @NonNull ZoneOffset zoneOffset, 51 @SexualActivityProtectionUsed.SexualActivityProtectionUsedTypes int protectionUsed, 52 boolean skipValidation) { 53 super(metadata, time, zoneOffset, skipValidation); 54 Objects.requireNonNull(metadata); 55 Objects.requireNonNull(time); 56 Objects.requireNonNull(zoneOffset); 57 validateIntDefValue( 58 protectionUsed, 59 SexualActivityProtectionUsed.VALID_TYPES, 60 SexualActivityProtectionUsed.class.getSimpleName()); 61 mProtectionUsed = protectionUsed; 62 } 63 64 /** 65 * @return protectionUsed 66 */ 67 @SexualActivityProtectionUsed.SexualActivityProtectionUsedTypes getProtectionUsed()68 public int getProtectionUsed() { 69 return mProtectionUsed; 70 } 71 72 /** Identifier for sexual activity protection used */ 73 public static final class SexualActivityProtectionUsed { 74 public static final int PROTECTION_USED_UNKNOWN = 0; 75 public static final int PROTECTION_USED_PROTECTED = 1; 76 public static final int PROTECTION_USED_UNPROTECTED = 2; 77 78 /** 79 * Valid set of values for this IntDef. Update this set when add new type or deprecate 80 * existing type. 81 * 82 * @hide 83 */ 84 public static final Set<Integer> VALID_TYPES = 85 Set.of( 86 PROTECTION_USED_UNKNOWN, 87 PROTECTION_USED_PROTECTED, 88 PROTECTION_USED_UNPROTECTED); 89 SexualActivityProtectionUsed()90 SexualActivityProtectionUsed() {} 91 92 /** @hide */ 93 @IntDef({PROTECTION_USED_UNKNOWN, PROTECTION_USED_PROTECTED, PROTECTION_USED_UNPROTECTED}) 94 @Retention(RetentionPolicy.SOURCE) 95 public @interface SexualActivityProtectionUsedTypes {} 96 } 97 98 /** 99 * Indicates whether some other object is "equal to" this one. 100 * 101 * @param o the reference object with which to compare. 102 * @return {@code true} if this object is the same as the obj 103 */ 104 @Override equals(Object o)105 public boolean equals(Object o) { 106 if (this == o) return true; 107 if (!super.equals(o)) return false; 108 SexualActivityRecord that = (SexualActivityRecord) o; 109 return getProtectionUsed() == that.getProtectionUsed(); 110 } 111 112 /** Returns a hash code value for the object. */ 113 @Override hashCode()114 public int hashCode() { 115 return Objects.hash(super.hashCode(), getProtectionUsed()); 116 } 117 118 /** Builder class for {@link SexualActivityRecord} */ 119 public static final class Builder { 120 private final Metadata mMetadata; 121 private final Instant mTime; 122 private ZoneOffset mZoneOffset; 123 private final int mProtectionUsed; 124 125 /** 126 * @param metadata Metadata to be associated with the record. See {@link Metadata}. 127 * @param time Start time of this activity 128 * @param protectionUsed Whether protection was used during sexual activity. Optional field, 129 * null if unknown. Allowed values: Protection. 130 */ Builder( @onNull Metadata metadata, @NonNull Instant time, @SexualActivityProtectionUsed.SexualActivityProtectionUsedTypes int protectionUsed)131 public Builder( 132 @NonNull Metadata metadata, 133 @NonNull Instant time, 134 @SexualActivityProtectionUsed.SexualActivityProtectionUsedTypes 135 int protectionUsed) { 136 Objects.requireNonNull(metadata); 137 Objects.requireNonNull(time); 138 mMetadata = metadata; 139 mTime = time; 140 mProtectionUsed = protectionUsed; 141 mZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(time); 142 } 143 144 /** Sets the zone offset of the user when the activity happened */ 145 @NonNull setZoneOffset(@onNull ZoneOffset zoneOffset)146 public Builder setZoneOffset(@NonNull ZoneOffset zoneOffset) { 147 Objects.requireNonNull(zoneOffset); 148 mZoneOffset = zoneOffset; 149 return this; 150 } 151 152 /** Sets the zone offset of this record to system default. */ 153 @NonNull clearZoneOffset()154 public Builder clearZoneOffset() { 155 mZoneOffset = RecordUtils.getDefaultZoneOffset(); 156 return this; 157 } 158 159 /** 160 * @return Object of {@link SexualActivityRecord} without validating the values. 161 * @hide 162 */ 163 @NonNull buildWithoutValidation()164 public SexualActivityRecord buildWithoutValidation() { 165 return new SexualActivityRecord(mMetadata, mTime, mZoneOffset, mProtectionUsed, true); 166 } 167 168 /** 169 * @return Object of {@link SexualActivityRecord} 170 */ 171 @NonNull build()172 public SexualActivityRecord build() { 173 return new SexualActivityRecord(mMetadata, mTime, mZoneOffset, mProtectionUsed, false); 174 } 175 } 176 177 /** @hide */ 178 @Override toRecordInternal()179 public SexualActivityRecordInternal toRecordInternal() { 180 SexualActivityRecordInternal recordInternal = 181 (SexualActivityRecordInternal) 182 new SexualActivityRecordInternal() 183 .setUuid(getMetadata().getId()) 184 .setPackageName(getMetadata().getDataOrigin().getPackageName()) 185 .setLastModifiedTime( 186 getMetadata().getLastModifiedTime().toEpochMilli()) 187 .setClientRecordId(getMetadata().getClientRecordId()) 188 .setClientRecordVersion(getMetadata().getClientRecordVersion()) 189 .setManufacturer(getMetadata().getDevice().getManufacturer()) 190 .setModel(getMetadata().getDevice().getModel()) 191 .setDeviceType(getMetadata().getDevice().getType()) 192 .setRecordingMethod(getMetadata().getRecordingMethod()); 193 recordInternal.setTime(getTime().toEpochMilli()); 194 recordInternal.setZoneOffset(getZoneOffset().getTotalSeconds()); 195 recordInternal.setProtectionUsed(mProtectionUsed); 196 return recordInternal; 197 } 198 } 199