1 /* 2 * Copyright (C) 2024 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 17 package android.health.connect.datatypes; 18 19 import static android.health.connect.datatypes.validation.ValidationUtils.validateIntDefValue; 20 21 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD; 22 23 import static java.util.Objects.hash; 24 import static java.util.Objects.requireNonNull; 25 26 import android.annotation.FlaggedApi; 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.Set; 35 36 /** 37 * Captures the user's medical data. This is the class used for all medical resource types, and the 38 * type is specified via {@link MedicalResourceType}. 39 */ 40 @FlaggedApi(FLAG_PERSONAL_HEALTH_RECORD) 41 public final class MedicalResource implements Parcelable { 42 /** Unknown medical resource type. */ 43 public static final int MEDICAL_RESOURCE_TYPE_UNKNOWN = 0; 44 45 /** Medical resource type to capture the immunizations data. */ 46 public static final int MEDICAL_RESOURCE_TYPE_IMMUNIZATION = 1; 47 48 /** @hide */ 49 @IntDef({MEDICAL_RESOURCE_TYPE_UNKNOWN, MEDICAL_RESOURCE_TYPE_IMMUNIZATION}) 50 @Retention(RetentionPolicy.SOURCE) 51 public @interface MedicalResourceType {} 52 53 @NonNull private final String mId; 54 @MedicalResourceType private final int mType; 55 @NonNull private final String mDataSourceId; 56 @NonNull private final String mData; 57 58 /** 59 * @param id The unique identifier of this data, assigned by the Android Health Platform at 60 * insertion time. 61 * @param type The medical resource type assigned by the Android Health Platform at insertion 62 * time. 63 * @param dataSourceId Where the data comes from. 64 * @param data The FHIR resource data in JSON representation. 65 */ MedicalResource( @onNull String id, @MedicalResourceType int type, @NonNull String dataSourceId, @NonNull String data)66 private MedicalResource( 67 @NonNull String id, 68 @MedicalResourceType int type, 69 @NonNull String dataSourceId, 70 @NonNull String data) { 71 requireNonNull(id); 72 requireNonNull(dataSourceId); 73 requireNonNull(data); 74 validateMedicalResourceType(type); 75 76 mId = id; 77 mType = type; 78 mDataSourceId = dataSourceId; 79 mData = data; 80 } 81 82 /** 83 * Constructs this object with the data present in {@code parcel}. It should be in the same 84 * order as {@link MedicalResource#writeToParcel}. 85 */ MedicalResource(@onNull Parcel in)86 private MedicalResource(@NonNull Parcel in) { 87 requireNonNull(in); 88 mId = requireNonNull(in.readString()); 89 mType = in.readInt(); 90 mDataSourceId = requireNonNull(in.readString()); 91 mData = requireNonNull(in.readString()); 92 } 93 94 @NonNull 95 public static final Creator<MedicalResource> CREATOR = 96 new Creator<>() { 97 /** 98 * Reading from the {@link Parcel} should have the same order as {@link 99 * MedicalResource#writeToParcel}. 100 */ 101 @Override 102 public MedicalResource createFromParcel(Parcel in) { 103 return new MedicalResource(in); 104 } 105 106 @Override 107 public MedicalResource[] newArray(int size) { 108 return new MedicalResource[size]; 109 } 110 }; 111 112 /** Returns the unique identifier of this data. */ 113 @NonNull getId()114 public String getId() { 115 return mId; 116 } 117 118 /** Returns the medical resource type. */ 119 @MedicalResourceType getType()120 public int getType() { 121 return mType; 122 } 123 124 /** Returns The data source ID where the data comes from. */ 125 @NonNull getDataSourceId()126 public String getDataSourceId() { 127 return mDataSourceId; 128 } 129 130 /** Returns the FHIR resource data in JSON representation. */ 131 @NonNull getData()132 public String getData() { 133 return mData; 134 } 135 136 @Override describeContents()137 public int describeContents() { 138 return 0; 139 } 140 141 /** Populates a {@link Parcel} with the self information. */ 142 @Override writeToParcel(@onNull Parcel dest, int flags)143 public void writeToParcel(@NonNull Parcel dest, int flags) { 144 requireNonNull(dest); 145 dest.writeString(getId()); 146 dest.writeInt(getType()); 147 dest.writeString(getDataSourceId()); 148 dest.writeString(getData()); 149 } 150 151 /** 152 * Valid set of values for this IntDef. Update this set when add new type or deprecate existing 153 * type. 154 * 155 * @hide 156 */ 157 public static final Set<Integer> VALID_TYPES = 158 Set.of(MEDICAL_RESOURCE_TYPE_UNKNOWN, MEDICAL_RESOURCE_TYPE_IMMUNIZATION); 159 160 /** 161 * Validates the provided {@code medicalResourceType} is in the {@link 162 * MedicalResource#VALID_TYPES} set. 163 * 164 * <p>Throws {@link IllegalArgumentException} if not. 165 * 166 * @hide 167 */ validateMedicalResourceType(@edicalResourceType int medicalResourceType)168 public static void validateMedicalResourceType(@MedicalResourceType int medicalResourceType) { 169 validateIntDefValue( 170 medicalResourceType, VALID_TYPES, MedicalResourceType.class.getSimpleName()); 171 } 172 173 /** Indicates whether some other object is "equal to" this one. */ 174 @Override equals(Object o)175 public boolean equals(Object o) { 176 if (this == o) return true; 177 if (!(o instanceof MedicalResource that)) return false; 178 return getId().equals(that.getId()) 179 && getType() == that.getType() 180 && getDataSourceId().equals(that.getDataSourceId()) 181 && getData().equals(that.getData()); 182 } 183 184 /** Returns a hash code value for the object. */ 185 @Override hashCode()186 public int hashCode() { 187 return hash(getId(), getType(), getDataSourceId(), getData()); 188 } 189 190 /** Returns a string representation of this {@link MedicalResource}. */ 191 @Override toString()192 public String toString() { 193 StringBuilder sb = new StringBuilder(); 194 sb.append(this.getClass().getSimpleName()).append("{"); 195 sb.append("id=").append(getId()); 196 sb.append(",type=").append(getType()); 197 sb.append(",dataSourceId=").append(getDataSourceId()); 198 sb.append(",data=").append(getData()); 199 sb.append("}"); 200 return sb.toString(); 201 } 202 203 /** Builder class for {@link MedicalResource} */ 204 public static final class Builder { 205 @NonNull private String mId; 206 @MedicalResourceType private int mType; 207 @NonNull private String mDataSourceId; 208 @NonNull private String mData; 209 Builder( @onNull String id, @MedicalResourceType int type, @NonNull String dataSourceId, @NonNull String data)210 public Builder( 211 @NonNull String id, 212 @MedicalResourceType int type, 213 @NonNull String dataSourceId, 214 @NonNull String data) { 215 requireNonNull(id); 216 requireNonNull(dataSourceId); 217 requireNonNull(data); 218 validateMedicalResourceType(type); 219 220 mId = id; 221 mType = type; 222 mDataSourceId = dataSourceId; 223 mData = data; 224 } 225 Builder(@onNull Builder original)226 public Builder(@NonNull Builder original) { 227 requireNonNull(original); 228 mId = original.mId; 229 mType = original.mType; 230 mDataSourceId = original.mDataSourceId; 231 mData = original.mData; 232 } 233 Builder(@onNull MedicalResource original)234 public Builder(@NonNull MedicalResource original) { 235 requireNonNull(original); 236 mId = original.getId(); 237 mType = original.getType(); 238 mDataSourceId = original.getDataSourceId(); 239 mData = original.getData(); 240 } 241 242 /** 243 * Sets the unique identifier of this data, assigned by the Android Health Platform at 244 * insertion time. 245 */ 246 @NonNull setId(@onNull String id)247 public Builder setId(@NonNull String id) { 248 requireNonNull(id); 249 mId = id; 250 return this; 251 } 252 253 /** 254 * Sets the medical resource type, assigned by the Android Health Platform at insertion 255 * time. 256 */ 257 @NonNull setType(@edicalResourceType int type)258 public Builder setType(@MedicalResourceType int type) { 259 validateMedicalResourceType(type); 260 mType = type; 261 return this; 262 } 263 264 /** Sets the data source ID where the data comes from. */ 265 @NonNull setDataSourceId(@onNull String dataSourceId)266 public Builder setDataSourceId(@NonNull String dataSourceId) { 267 requireNonNull(dataSourceId); 268 mDataSourceId = dataSourceId; 269 return this; 270 } 271 272 /** Sets the FHIR resource data in JSON representation. */ 273 @NonNull setData(@onNull String data)274 public Builder setData(@NonNull String data) { 275 requireNonNull(data); 276 mData = data; 277 return this; 278 } 279 280 /** Returns a new instance of {@link MedicalResource} with the specified parameters. */ 281 @NonNull build()282 public MedicalResource build() { 283 return new MedicalResource(mId, mType, mDataSourceId, mData); 284 } 285 } 286 } 287