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.internal.datatypes;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.health.connect.datatypes.HeartRateRecord;
22 import android.health.connect.datatypes.Identifier;
23 import android.health.connect.datatypes.RecordTypeIdentifier;
24 import android.os.Parcel;
25 
26 import java.time.Instant;
27 import java.util.ArrayList;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.Set;
32 
33 /**
34  * @see HeartRateRecord
35  * @hide
36  */
37 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_HEART_RATE)
38 public class HeartRateRecordInternal
39         extends SeriesRecordInternal<HeartRateRecord, HeartRateRecord.HeartRateSample> {
40     public static final class HeartRateSample implements Sample {
41         private final int mBeatsPerMinute;
42         private final long mEpochMillis;
43 
HeartRateSample(int beatsPerMinute, long epochMillis)44         public HeartRateSample(int beatsPerMinute, long epochMillis) {
45             mBeatsPerMinute = beatsPerMinute;
46             mEpochMillis = epochMillis;
47         }
48 
getBeatsPerMinute()49         public int getBeatsPerMinute() {
50             return mBeatsPerMinute;
51         }
52 
getEpochMillis()53         public long getEpochMillis() {
54             return mEpochMillis;
55         }
56 
57         @Override
equals(@ullable Object object)58         public boolean equals(@Nullable Object object) {
59             if (super.equals(object) && object instanceof HeartRateRecordInternal.HeartRateSample) {
60                 HeartRateRecordInternal.HeartRateSample other =
61                         (HeartRateRecordInternal.HeartRateSample) object;
62                 return getEpochMillis() == other.getEpochMillis();
63             }
64             return false;
65         }
66 
67         @Override
hashCode()68         public int hashCode() {
69             return Objects.hash(getEpochMillis());
70         }
71     }
72 
73     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
74     private Set<HeartRateSample> mHeartRateHeartRateSamples;
75 
76     @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression
77     @Override
78     @Nullable
getSamples()79     public Set<HeartRateSample> getSamples() {
80         return mHeartRateHeartRateSamples;
81     }
82 
83     @Override
setSamples(Set<? extends Sample> samples)84     public HeartRateRecordInternal setSamples(Set<? extends Sample> samples) {
85         this.mHeartRateHeartRateSamples = (Set<HeartRateSample>) samples;
86         return this;
87     }
88 
89     @Override
90     @NonNull
toExternalRecord()91     public HeartRateRecord toExternalRecord() {
92         return new HeartRateRecord.Builder(
93                         buildMetaData(), getStartTime(), getEndTime(), getExternalSamples())
94                 .setStartZoneOffset(getStartZoneOffset())
95                 .setEndZoneOffset(getEndZoneOffset())
96                 .buildWithoutValidation();
97     }
98 
99     @Override
populateIntervalRecordFrom(@onNull Parcel parcel)100     void populateIntervalRecordFrom(@NonNull Parcel parcel) {
101         int size = parcel.readInt();
102         mHeartRateHeartRateSamples = new HashSet<>(size);
103         for (int i = 0; i < size; i++) {
104             mHeartRateHeartRateSamples.add(
105                     new HeartRateSample(parcel.readInt(), parcel.readLong()));
106         }
107     }
108 
109     @Override
populateIntervalRecordTo(@onNull Parcel parcel)110     void populateIntervalRecordTo(@NonNull Parcel parcel) {
111         parcel.writeInt(mHeartRateHeartRateSamples.size());
112         for (HeartRateSample heartRateSample : mHeartRateHeartRateSamples) {
113             parcel.writeInt(heartRateSample.getBeatsPerMinute());
114             parcel.writeLong(heartRateSample.getEpochMillis());
115         }
116     }
117 
getExternalSamples()118     private List<HeartRateRecord.HeartRateSample> getExternalSamples() {
119         List<HeartRateRecord.HeartRateSample> heartRateRecords =
120                 new ArrayList<>(mHeartRateHeartRateSamples.size());
121 
122         for (HeartRateSample heartRateSample : mHeartRateHeartRateSamples) {
123             heartRateRecords.add(
124                     new HeartRateRecord.HeartRateSample(
125                             heartRateSample.getBeatsPerMinute(),
126                             Instant.ofEpochMilli(heartRateSample.getEpochMillis()),
127                             true));
128         }
129 
130         return heartRateRecords;
131     }
132 }
133