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 17 package android.health.connect.changelog; 18 19 import android.annotation.NonNull; 20 import android.health.connect.HealthConnectManager; 21 import android.health.connect.aidl.RecordsParcel; 22 import android.health.connect.datatypes.Record; 23 import android.health.connect.internal.datatypes.RecordInternal; 24 import android.health.connect.internal.datatypes.utils.InternalExternalRecordConverter; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 28 import java.time.Instant; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Objects; 32 33 /** 34 * Response class for {@link HealthConnectManager#getChangeLogs} This is the response to clients 35 * fetching changes 36 */ 37 public final class ChangeLogsResponse implements Parcelable { 38 private final List<Record> mUpsertedRecords; 39 private final List<DeletedLog> mDeletedLogs; 40 private final String mNextChangesToken; 41 private final boolean mHasMorePages; 42 43 /** 44 * Response for {@link HealthConnectManager#getChangeLogs} 45 * 46 * @hide 47 */ ChangeLogsResponse( @onNull RecordsParcel upsertedRecords, @NonNull List<DeletedLog> deletedLogs, @NonNull String nextChangesToken, boolean hasMorePages)48 public ChangeLogsResponse( 49 @NonNull RecordsParcel upsertedRecords, 50 @NonNull List<DeletedLog> deletedLogs, 51 @NonNull String nextChangesToken, 52 boolean hasMorePages) { 53 Objects.requireNonNull(upsertedRecords); 54 Objects.requireNonNull(deletedLogs); 55 Objects.requireNonNull(nextChangesToken); 56 57 mUpsertedRecords = 58 InternalExternalRecordConverter.getInstance() 59 .getExternalRecords(upsertedRecords.getRecords()); 60 mDeletedLogs = deletedLogs; 61 mNextChangesToken = nextChangesToken; 62 mHasMorePages = hasMorePages; 63 } 64 ChangeLogsResponse(Parcel in)65 private ChangeLogsResponse(Parcel in) { 66 mUpsertedRecords = 67 InternalExternalRecordConverter.getInstance() 68 .getExternalRecords( 69 in.readParcelable( 70 RecordsParcel.class.getClassLoader(), 71 RecordsParcel.class) 72 .getRecords()); 73 int size = in.readInt(); 74 List<DeletedLog> deletedLogs = new ArrayList<>(size); 75 for (int i = 0; i < size; i++) { 76 String id = in.readString(); 77 long time = in.readLong(); 78 deletedLogs.add(new DeletedLog(id, time)); 79 } 80 mDeletedLogs = deletedLogs; 81 mNextChangesToken = in.readString(); 82 mHasMorePages = in.readBoolean(); 83 } 84 85 @NonNull 86 public static final Creator<ChangeLogsResponse> CREATOR = 87 new Creator<ChangeLogsResponse>() { 88 @Override 89 public ChangeLogsResponse createFromParcel(Parcel in) { 90 return new ChangeLogsResponse(in); 91 } 92 93 @Override 94 public ChangeLogsResponse[] newArray(int size) { 95 return new ChangeLogsResponse[size]; 96 } 97 }; 98 99 /** 100 * Returns records that have been updated or inserted post the time when the given token was 101 * generated. 102 * 103 * <p>Clients can use the last modified time of the record to check when the record was 104 * modified. 105 */ 106 @NonNull getUpsertedRecords()107 public List<Record> getUpsertedRecords() { 108 return mUpsertedRecords; 109 } 110 111 /** 112 * Returns delete logs for records that have been deleted post the time when the token was 113 * requested from {@link HealthConnectManager#getChangeLogToken} 114 * 115 * <p>This contains record id of deleted record and the timestamp when the record was deleted. 116 */ 117 @NonNull getDeletedLogs()118 public List<DeletedLog> getDeletedLogs() { 119 return mDeletedLogs; 120 } 121 122 /** Returns token for future reads using {@link HealthConnectManager#getChangeLogs} */ 123 @NonNull getNextChangesToken()124 public String getNextChangesToken() { 125 return mNextChangesToken; 126 } 127 128 /** Returns whether there are more pages available for read */ hasMorePages()129 public boolean hasMorePages() { 130 return mHasMorePages; 131 } 132 133 @Override describeContents()134 public int describeContents() { 135 return 0; 136 } 137 138 @Override writeToParcel(@onNull Parcel dest, int flags)139 public void writeToParcel(@NonNull Parcel dest, int flags) { 140 List<RecordInternal<?>> recordInternal = new ArrayList<>(); 141 for (Record record : mUpsertedRecords) { 142 recordInternal.add(record.toRecordInternal()); 143 } 144 dest.writeParcelable(new RecordsParcel(recordInternal), 0); 145 dest.writeInt(mDeletedLogs.size()); 146 for (DeletedLog deletedLog : mDeletedLogs) { 147 dest.writeString(deletedLog.getDeletedRecordId()); 148 dest.writeLong(deletedLog.getDeletedTime().toEpochMilli()); 149 } 150 dest.writeString(mNextChangesToken); 151 dest.writeBoolean(mHasMorePages); 152 } 153 154 /** A class to represent a delete log in ChangeLogsResponse */ 155 public static final class DeletedLog { 156 private final String mDeletedRecordId; 157 private final Instant mDeletedTime; 158 DeletedLog(@onNull String deletedRecordId, long deletedTime)159 public DeletedLog(@NonNull String deletedRecordId, long deletedTime) { 160 Objects.requireNonNull(deletedRecordId); 161 mDeletedRecordId = deletedRecordId; 162 mDeletedTime = Instant.ofEpochMilli(deletedTime); 163 } 164 165 /** Returns record id of the record deleted */ 166 @NonNull getDeletedRecordId()167 public String getDeletedRecordId() { 168 return mDeletedRecordId; 169 } 170 171 /** Returns timestamp when the record was deleted */ 172 @NonNull getDeletedTime()173 public Instant getDeletedTime() { 174 return mDeletedTime; 175 } 176 } 177 } 178