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.ExerciseSessionRecord;
22 import android.health.connect.datatypes.ExerciseSessionType;
23 import android.health.connect.datatypes.Identifier;
24 import android.health.connect.datatypes.PlannedExerciseSessionRecord;
25 import android.health.connect.datatypes.RecordTypeIdentifier;
26 import android.os.Parcel;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Objects;
31 import java.util.UUID;
32 
33 /**
34  * @see ExerciseSessionRecord
35  * @hide
36  */
37 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_EXERCISE_SESSION)
38 public final class ExerciseSessionRecordInternal
39         extends IntervalRecordInternal<ExerciseSessionRecord> {
40     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
41     private String mNotes;
42 
43     private int mExerciseType;
44 
45     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
46     private String mTitle;
47 
48     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
49     private ExerciseRouteInternal mExerciseRoute;
50 
51     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
52     private List<ExerciseLapInternal> mExerciseLaps;
53 
54     @SuppressWarnings("NullAway.Init") // TODO(b/317029272): fix this suppression
55     private List<ExerciseSegmentInternal> mExerciseSegments;
56 
57     private boolean mHasRoute;
58 
59     @Nullable private UUID mPlannedExerciseSessionId;
60 
61     @Nullable
getNotes()62     public String getNotes() {
63         return mNotes;
64     }
65 
66     /** returns this object with the specified notes */
67     @NonNull
setNotes(String notes)68     public ExerciseSessionRecordInternal setNotes(String notes) {
69         this.mNotes = notes;
70         return this;
71     }
72 
73     @ExerciseSessionType.ExerciseSessionTypes
getExerciseType()74     public int getExerciseType() {
75         return mExerciseType;
76     }
77 
78     /** returns this object with the specified exerciseType */
79     @NonNull
setExerciseType(int exerciseType)80     public ExerciseSessionRecordInternal setExerciseType(int exerciseType) {
81         this.mExerciseType = exerciseType;
82         return this;
83     }
84 
85     @Nullable
getTitle()86     public String getTitle() {
87         return mTitle;
88     }
89 
90     /** returns this object with the specified title */
91     @NonNull
setTitle(String title)92     public ExerciseSessionRecordInternal setTitle(String title) {
93         this.mTitle = title;
94         return this;
95     }
96 
97     /**
98      * @return route of this activity
99      */
100     @Nullable
getRoute()101     public ExerciseRouteInternal getRoute() {
102         return mExerciseRoute;
103     }
104 
105     /** returns this object with the specified route */
106     @NonNull
setRoute(ExerciseRouteInternal route)107     public ExerciseSessionRecordInternal setRoute(ExerciseRouteInternal route) {
108         this.mExerciseRoute = route;
109         if (route != null) {
110             this.mHasRoute = true;
111         }
112         return this;
113     }
114 
115     /** returns if this session has route */
hasRoute()116     public boolean hasRoute() {
117         return mHasRoute;
118     }
119 
120     /** returns this object with hasRoute set */
121     @NonNull
setHasRoute(boolean hasRoute)122     public ExerciseSessionRecordInternal setHasRoute(boolean hasRoute) {
123         if (mExerciseRoute != null && !hasRoute) {
124             throw new IllegalArgumentException("HasRoute must be true if the route is not null");
125         }
126         this.mHasRoute = hasRoute;
127         return this;
128     }
129 
130     /** returns laps of this session */
131     @Nullable
getLaps()132     public List<ExerciseLapInternal> getLaps() {
133         return mExerciseLaps;
134     }
135 
136     /** returns this object with exercise laps set */
setExerciseLaps( @onNull List<ExerciseLapInternal> exerciseLaps)137     public ExerciseSessionRecordInternal setExerciseLaps(
138             @NonNull List<ExerciseLapInternal> exerciseLaps) {
139         Objects.requireNonNull(exerciseLaps);
140         mExerciseLaps = new ArrayList<>(exerciseLaps);
141         return this;
142     }
143 
144     @Nullable
getSegments()145     public List<ExerciseSegmentInternal> getSegments() {
146         return mExerciseSegments;
147     }
148 
149     /** returns this object with exercise segments set */
150     @NonNull
setExerciseSegments( @onNull List<ExerciseSegmentInternal> exerciseSegments)151     public ExerciseSessionRecordInternal setExerciseSegments(
152             @NonNull List<ExerciseSegmentInternal> exerciseSegments) {
153         Objects.requireNonNull(exerciseSegments);
154         mExerciseSegments = new ArrayList<>(exerciseSegments);
155         return this;
156     }
157 
158     /** Sets the {@link PlannedExerciseSessionRecord} that this session was based upon. */
159     @NonNull
setPlannedExerciseSessionId(@ullable UUID id)160     public ExerciseSessionRecordInternal setPlannedExerciseSessionId(@Nullable UUID id) {
161         mPlannedExerciseSessionId = id;
162         return this;
163     }
164 
165     /**
166      * Returns the ID of the {@link PlannedExerciseSessionRecord} that this session was based upon.
167      * If not set, returns null.
168      */
169     @Nullable
getPlannedExerciseSessionId()170     public UUID getPlannedExerciseSessionId() {
171         return mPlannedExerciseSessionId;
172     }
173 
174     @NonNull
175     @Override
toExternalRecord()176     public ExerciseSessionRecord toExternalRecord() {
177         ExerciseSessionRecord.Builder builder =
178                 new ExerciseSessionRecord.Builder(
179                         buildMetaData(), getStartTime(), getEndTime(), getExerciseType());
180 
181         if (getStartZoneOffset() != null) {
182             builder.setStartZoneOffset(getStartZoneOffset());
183         }
184 
185         if (getEndZoneOffset() != null) {
186             builder.setEndZoneOffset(getEndZoneOffset());
187         }
188 
189         if (getNotes() != null) {
190             builder.setNotes(getNotes());
191         }
192 
193         if (getTitle() != null) {
194             builder.setTitle(getTitle());
195         }
196 
197         if (mExerciseRoute != null) {
198             builder.setRoute(mExerciseRoute.toExternalRoute());
199         }
200 
201         builder.setHasRoute(mHasRoute);
202 
203         if (getLaps() != null) {
204             builder.setLaps(ExerciseLapInternal.getExternalLaps(mExerciseLaps));
205         }
206 
207         if (getSegments() != null) {
208             builder.setSegments(ExerciseSegmentInternal.getExternalSegments(mExerciseSegments));
209         }
210 
211         builder.setPlannedExerciseSessionId(
212                 mPlannedExerciseSessionId == null ? null : mPlannedExerciseSessionId.toString());
213         return builder.buildWithoutValidation();
214     }
215 
216     @Override
populateIntervalRecordTo(@onNull Parcel parcel)217     public void populateIntervalRecordTo(@NonNull Parcel parcel) {
218         parcel.writeString(mNotes);
219         parcel.writeInt(mExerciseType);
220         parcel.writeString(mTitle);
221         parcel.writeBoolean(mHasRoute);
222         ExerciseRouteInternal.writeToParcel(mExerciseRoute, parcel);
223         ExerciseLapInternal.writeLapsToParcel(mExerciseLaps, parcel);
224         ExerciseSegmentInternal.writeSegmentsToParcel(mExerciseSegments, parcel);
225         parcel.writeString(
226                 mPlannedExerciseSessionId == null ? null : mPlannedExerciseSessionId.toString());
227     }
228 
229     @SuppressWarnings("NullAway") // TODO(b/317029272): fix this suppression
230     @Override
populateIntervalRecordFrom(@onNull Parcel parcel)231     public void populateIntervalRecordFrom(@NonNull Parcel parcel) {
232         mNotes = parcel.readString();
233         mExerciseType = parcel.readInt();
234         mTitle = parcel.readString();
235         mHasRoute = parcel.readBoolean();
236         mExerciseRoute = ExerciseRouteInternal.readFromParcel(parcel);
237         mExerciseLaps = ExerciseLapInternal.populateLapsFromParcel(parcel);
238         mExerciseSegments = ExerciseSegmentInternal.populateSegmentsFromParcel(parcel);
239         String uuid = parcel.readString();
240         mPlannedExerciseSessionId = uuid == null ? null : UUID.fromString(uuid);
241     }
242 
243     /** Add route location to the session */
addRouteLocation(ExerciseRouteInternal.LocationInternal location)244     public void addRouteLocation(ExerciseRouteInternal.LocationInternal location) {
245         if (mExerciseRoute == null) {
246             mExerciseRoute = new ExerciseRouteInternal(List.of(location));
247             mHasRoute = true;
248         } else {
249             mExerciseRoute.addLocation(location);
250         }
251     }
252 }
253