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 package android.health.connect; 17 18 import static android.health.connect.Constants.DEFAULT_PAGE_SIZE; 19 import static android.health.connect.Constants.MAXIMUM_PAGE_SIZE; 20 import static android.health.connect.Constants.MINIMUM_PAGE_SIZE; 21 import static android.health.connect.datatypes.MedicalDataSource.validateMedicalDataSourceIds; 22 import static android.health.connect.datatypes.MedicalResource.MedicalResourceType; 23 import static android.health.connect.datatypes.MedicalResource.validateMedicalResourceType; 24 import static android.health.connect.datatypes.validation.ValidationUtils.requireInRange; 25 26 import static com.android.healthfitness.flags.Flags.FLAG_PERSONAL_HEALTH_RECORD; 27 28 import static java.util.Objects.hash; 29 import static java.util.Objects.requireNonNull; 30 31 import android.annotation.FlaggedApi; 32 import android.annotation.IntRange; 33 import android.annotation.NonNull; 34 import android.health.connect.aidl.ReadMedicalResourcesRequestParcel; 35 import android.health.connect.datatypes.MedicalDataSource; 36 import android.health.connect.datatypes.MedicalResource; 37 import android.util.ArraySet; 38 39 import java.util.HashSet; 40 import java.util.Set; 41 42 /** 43 * An initial read request with specified filters for {@link 44 * HealthConnectManager#readMedicalResources}. 45 * 46 * <p>On receiving the response, if {@link ReadMedicalResourcesResponse#getNextPageToken()} is not 47 * {@code null}, then use the next token with {@link ReadMedicalResourcesPageRequest} to read the 48 * next page. 49 * 50 * <p>Example usage: 51 * 52 * <pre>{@code 53 * ReadMedicalResourcesInitialRequest initialRequest 54 * = new ReadMedicalResourcesInitialRequest.Builder(...).build(); 55 * ReadMedicalResourcesResponse response = makeRequest(initialRequest); 56 * String pageToken = response.getNextPageToken(); 57 * 58 * while (pageToken != null) { 59 * ReadMedicalResourcesPageRequest pageRequest = new ReadMedicalResourcesPageRequest(pageToken); 60 * response = makeRequest(pageRequest); 61 * pageToken = response.getNextPageToken(); 62 * } 63 * }</pre> 64 */ 65 @FlaggedApi(FLAG_PERSONAL_HEALTH_RECORD) 66 public final class ReadMedicalResourcesInitialRequest extends ReadMedicalResourcesRequest { 67 @MedicalResourceType private final int mMedicalResourceType; 68 @NonNull private final Set<String> mDataSourceIds; 69 70 /** 71 * Creates a new instance of {@link ReadMedicalResourcesInitialRequest}. Please see {@link 72 * ReadMedicalResourcesInitialRequest.Builder} for more detailed parameters information. 73 */ ReadMedicalResourcesInitialRequest( @edicalResourceType int medicalResourceType, @NonNull Set<String> dataSourceIds, @IntRange(from = MINIMUM_PAGE_SIZE, to = MAXIMUM_PAGE_SIZE) int pageSize)74 private ReadMedicalResourcesInitialRequest( 75 @MedicalResourceType int medicalResourceType, 76 @NonNull Set<String> dataSourceIds, 77 @IntRange(from = MINIMUM_PAGE_SIZE, to = MAXIMUM_PAGE_SIZE) int pageSize) { 78 super(pageSize); 79 validateMedicalResourceType(medicalResourceType); 80 requireNonNull(dataSourceIds); 81 validateMedicalDataSourceIds(dataSourceIds); 82 83 mMedicalResourceType = medicalResourceType; 84 mDataSourceIds = dataSourceIds; 85 } 86 87 /** Returns the medical resource type. */ 88 @MedicalResourceType getMedicalResourceType()89 public int getMedicalResourceType() { 90 return mMedicalResourceType; 91 } 92 93 /** 94 * Returns the set of IDs of the {@link MedicalDataSource} filter to read from, or an empty set 95 * for no filter. 96 */ 97 @NonNull getDataSourceIds()98 public Set<String> getDataSourceIds() { 99 return new ArraySet<>(mDataSourceIds); 100 } 101 102 @Override equals(Object o)103 public boolean equals(Object o) { 104 if (this == o) return true; 105 if (!(o instanceof ReadMedicalResourcesInitialRequest that)) return false; 106 107 return getMedicalResourceType() == that.getMedicalResourceType() 108 && getDataSourceIds().equals(that.getDataSourceIds()) 109 && getPageSize() == that.getPageSize(); 110 } 111 112 @Override hashCode()113 public int hashCode() { 114 return hash(getMedicalResourceType(), getDataSourceIds(), getPageSize()); 115 } 116 117 @Override toString()118 public String toString() { 119 return this.getClass().getSimpleName() 120 + "{" 121 + "medicalResourceType=" 122 + getMedicalResourceType() 123 + ",dataSourceIds=" 124 + getDataSourceIds() 125 + ",pageSize=" 126 + getPageSize() 127 + "}"; 128 } 129 130 /** 131 * Returns an instance of {@link ReadMedicalResourcesRequestParcel} to carry the request. 132 * 133 * @hide 134 */ toParcel()135 public ReadMedicalResourcesRequestParcel toParcel() { 136 return new ReadMedicalResourcesRequestParcel(this); 137 } 138 139 /** Builder class for {@link ReadMedicalResourcesInitialRequest}. */ 140 public static final class Builder { 141 @MedicalResourceType private int mMedicalResourceType; 142 @NonNull private Set<String> mDataSourceIds = new HashSet<>(); 143 private int mPageSize = DEFAULT_PAGE_SIZE; 144 145 /** 146 * Constructs a new {@link ReadMedicalResourcesInitialRequest.Builder} instance. 147 * 148 * @param medicalResourceType The medical resource type. 149 * @throws IllegalArgumentException if the provided {@code medicalResourceType} is not a 150 * supported type. 151 */ Builder(@edicalResourceType int medicalResourceType)152 public Builder(@MedicalResourceType int medicalResourceType) { 153 validateMedicalResourceType(medicalResourceType); 154 mMedicalResourceType = medicalResourceType; 155 } 156 157 /** Constructs a clone of the other {@link ReadMedicalResourcesInitialRequest.Builder}. */ Builder(@onNull Builder other)158 public Builder(@NonNull Builder other) { 159 mMedicalResourceType = other.mMedicalResourceType; 160 mDataSourceIds.addAll(other.mDataSourceIds); 161 mPageSize = other.mPageSize; 162 } 163 164 /** Constructs a clone of the other {@link ReadMedicalResourcesInitialRequest} instance. */ Builder(@onNull ReadMedicalResourcesInitialRequest other)165 public Builder(@NonNull ReadMedicalResourcesInitialRequest other) { 166 mMedicalResourceType = other.getMedicalResourceType(); 167 mDataSourceIds.addAll(other.getDataSourceIds()); 168 mPageSize = other.getPageSize(); 169 } 170 171 /** 172 * Sets the medical resource type. 173 * 174 * @throws IllegalArgumentException if the provided {@code medicalResourceType} is not a 175 * supported type. 176 */ 177 @NonNull setMedicalResourceType(@edicalResourceType int medicalResourceType)178 public Builder setMedicalResourceType(@MedicalResourceType int medicalResourceType) { 179 validateMedicalResourceType(medicalResourceType); 180 mMedicalResourceType = medicalResourceType; 181 return this; 182 } 183 184 /** 185 * Adds the data source ID based on which the read operation is to be performed. This should 186 * be an ID of the existing {@link MedicalDataSource}. 187 * 188 * <p>If no {@link MedicalDataSource} ID is added, then {@link MedicalResource}s from all 189 * {@link MedicalDataSource}s will be read. 190 * 191 * @throws IllegalArgumentException if the provided {@code dataSourceId} is not a valid ID. 192 */ 193 @NonNull addDataSourceId(@onNull String dataSourceId)194 public Builder addDataSourceId(@NonNull String dataSourceId) { 195 requireNonNull(dataSourceId); 196 validateMedicalDataSourceIds(Set.of(dataSourceId)); 197 mDataSourceIds.add(dataSourceId); 198 return this; 199 } 200 201 /** 202 * Adds all data source IDs based on which the read operation is to be performed. This 203 * should all be IDs of existing {@link MedicalDataSource}s. 204 * 205 * <p>If no {@link MedicalDataSource} IDs are added, then {@link MedicalResource}s from all 206 * {@link MedicalDataSource}s will be read. 207 * 208 * @throws IllegalArgumentException if the provided {@code dataSourceIds} is null, or any ID 209 * in it is not valid. 210 */ 211 @NonNull addDataSourceIds(@onNull Set<String> dataSourceIds)212 public Builder addDataSourceIds(@NonNull Set<String> dataSourceIds) { 213 requireNonNull(dataSourceIds); 214 validateMedicalDataSourceIds(dataSourceIds); 215 mDataSourceIds.addAll(dataSourceIds); 216 return this; 217 } 218 219 /** Clears all the {@link MedicalDataSource} filters for this builder. */ 220 @NonNull clearDataSourceIds()221 public Builder clearDataSourceIds() { 222 mDataSourceIds.clear(); 223 return this; 224 } 225 226 /** 227 * Sets the maximum number of {@code MedicalResource}s to be returned by the read operation. 228 * 229 * <p>If not set, default to 1000. 230 * 231 * @throws IllegalArgumentException if the provided {@code pageSize} is less than 1 or more 232 * than 5000. 233 */ 234 @NonNull setPageSize( @ntRangefrom = MINIMUM_PAGE_SIZE, to = MAXIMUM_PAGE_SIZE) int pageSize)235 public Builder setPageSize( 236 @IntRange(from = MINIMUM_PAGE_SIZE, to = MAXIMUM_PAGE_SIZE) int pageSize) { 237 requireInRange(pageSize, MINIMUM_PAGE_SIZE, MAXIMUM_PAGE_SIZE, "pageSize"); 238 mPageSize = pageSize; 239 return this; 240 } 241 242 /** 243 * Returns a new instance of {@link ReadMedicalResourcesInitialRequest} with the specified 244 * parameters. 245 */ 246 @NonNull build()247 public ReadMedicalResourcesInitialRequest build() { 248 return new ReadMedicalResourcesInitialRequest( 249 mMedicalResourceType, mDataSourceIds, mPageSize); 250 } 251 } 252 } 253