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.internal.datatypes;
18 
19 import android.annotation.NonNull;
20 import android.health.connect.datatypes.ExerciseCompletionGoal;
21 import android.health.connect.datatypes.units.Energy;
22 import android.health.connect.datatypes.units.Length;
23 import android.os.Parcel;
24 
25 import java.time.Duration;
26 
27 /**
28  * Internal ExerciseBlock. Part of {@link PlannedExerciseSessionRecordInternal}.
29  *
30  * @hide
31  */
32 public abstract class ExerciseCompletionGoalInternal {
33 
34     /** Convert to {@link ExerciseCompletionGoal}. */
35     @NonNull
toExternalObject()36     public abstract ExerciseCompletionGoal toExternalObject();
37 
38     /** Subclass identifier used during serialization/deserialization. */
getTypeId()39     public abstract int getTypeId();
40 
writeFieldsToParcel(@onNull Parcel parcel)41     abstract void writeFieldsToParcel(@NonNull Parcel parcel);
42 
43     /** Convert from external representation. */
fromExternalObject( ExerciseCompletionGoal externalObject)44     public static ExerciseCompletionGoalInternal fromExternalObject(
45             ExerciseCompletionGoal externalObject) {
46         if (externalObject instanceof ExerciseCompletionGoal.DistanceGoal) {
47             ExerciseCompletionGoal.DistanceGoal goal =
48                     (ExerciseCompletionGoal.DistanceGoal) externalObject;
49             return new DistanceGoalInternal(goal.getDistance());
50         } else if (externalObject instanceof ExerciseCompletionGoal.DistanceWithVariableRestGoal) {
51             ExerciseCompletionGoal.DistanceWithVariableRestGoal goal =
52                     (ExerciseCompletionGoal.DistanceWithVariableRestGoal) externalObject;
53             return new DistanceWithVariableRestGoalInternal(goal.getDistance(), goal.getDuration());
54         } else if (externalObject instanceof ExerciseCompletionGoal.StepsGoal) {
55             ExerciseCompletionGoal.StepsGoal goal =
56                     (ExerciseCompletionGoal.StepsGoal) externalObject;
57             return new StepsGoalInternal(goal.getSteps());
58         } else if (externalObject instanceof ExerciseCompletionGoal.DurationGoal) {
59             ExerciseCompletionGoal.DurationGoal goal =
60                     (ExerciseCompletionGoal.DurationGoal) externalObject;
61             return new DurationGoalInternal(goal.getDuration());
62         } else if (externalObject instanceof ExerciseCompletionGoal.RepetitionsGoal) {
63             ExerciseCompletionGoal.RepetitionsGoal goal =
64                     (ExerciseCompletionGoal.RepetitionsGoal) externalObject;
65             return new RepetitionsGoalInternal(goal.getRepetitions());
66         } else if (externalObject instanceof ExerciseCompletionGoal.TotalCaloriesBurnedGoal) {
67             ExerciseCompletionGoal.TotalCaloriesBurnedGoal goal =
68                     (ExerciseCompletionGoal.TotalCaloriesBurnedGoal) externalObject;
69             return new TotalCaloriesBurnedGoalInternal(goal.getTotalCalories());
70         } else if (externalObject instanceof ExerciseCompletionGoal.ActiveCaloriesBurnedGoal) {
71             ExerciseCompletionGoal.ActiveCaloriesBurnedGoal goal =
72                     (ExerciseCompletionGoal.ActiveCaloriesBurnedGoal) externalObject;
73             return new ActiveCaloriesBurnedGoalInternal(goal.getActiveCalories());
74         } else if (externalObject instanceof ExerciseCompletionGoal.UnspecifiedGoal) {
75             return UnspecifiedGoalInternal.INSTANCE;
76         } else {
77             return UnknownGoalInternal.INSTANCE;
78         }
79     }
80 
81     /** Serialize to parcel. */
writeToParcel(@onNull Parcel parcel)82     public void writeToParcel(@NonNull Parcel parcel) {
83         parcel.writeInt(getTypeId());
84         writeFieldsToParcel(parcel);
85     }
86 
87     /** Deserialize from parcel. */
88     @NonNull
readFromParcel(@onNull Parcel parcel)89     public static ExerciseCompletionGoalInternal readFromParcel(@NonNull Parcel parcel) {
90         int goalTypeId = parcel.readInt();
91         switch (goalTypeId) {
92             case DistanceGoalInternal.DISTANCE_GOAL_TYPE_ID:
93                 return DistanceGoalInternal.readFieldsFromParcel(parcel);
94             case StepsGoalInternal.STEPS_GOAL_TYPE_ID:
95                 return StepsGoalInternal.readFieldsFromParcel(parcel);
96             case DurationGoalInternal.DURATION_GOAL_TYPE_ID:
97                 return DurationGoalInternal.readFieldsFromParcel(parcel);
98             case RepetitionsGoalInternal.REPETITIONS_GOAL_TYPE_ID:
99                 return RepetitionsGoalInternal.readFieldsFromParcel(parcel);
100             case TotalCaloriesBurnedGoalInternal.TOTAL_CALORIES_BURNED_GOAL_TYPE_ID:
101                 return TotalCaloriesBurnedGoalInternal.readFieldsFromParcel(parcel);
102             case ActiveCaloriesBurnedGoalInternal.ACTIVE_CALORIES_BURNED_GOAL_TYPE_ID:
103                 return ActiveCaloriesBurnedGoalInternal.readFieldsFromParcel(parcel);
104             case DistanceWithVariableRestGoalInternal.DISTANCE_WITH_VARIABLE_REST_GOAL_TYPE_ID:
105                 return DistanceWithVariableRestGoalInternal.readFieldsFromParcel(parcel);
106             case UnspecifiedGoalInternal.UNSPECIFIED_GOAL_TYPE_ID:
107                 return UnspecifiedGoalInternal.INSTANCE;
108             case UnknownGoalInternal.UNKNOWN_GOAL_TYPE_ID:
109                 // Fall through.
110             default:
111                 // Can never happen. Client side and service side always have consistent version.
112                 return UnknownGoalInternal.INSTANCE;
113         }
114     }
115 
116     public static final class DistanceGoalInternal extends ExerciseCompletionGoalInternal {
117         public static final int DISTANCE_GOAL_TYPE_ID = 2;
118 
119         @Override
writeFieldsToParcel(@onNull Parcel parcel)120         void writeFieldsToParcel(@NonNull Parcel parcel) {
121             parcel.writeDouble(this.mDistance.getInMeters());
122         }
123 
124         /** Read from parcel. */
125         @NonNull
readFieldsFromParcel(@onNull Parcel parcel)126         public static DistanceGoalInternal readFieldsFromParcel(@NonNull Parcel parcel) {
127             return new DistanceGoalInternal(Length.fromMeters(parcel.readDouble()));
128         }
129 
130         private final Length mDistance;
131 
DistanceGoalInternal(@onNull Length distance)132         public DistanceGoalInternal(@NonNull Length distance) {
133             this.mDistance = distance;
134         }
135 
136         @NonNull
getDistance()137         public Length getDistance() {
138             return mDistance;
139         }
140 
141         @Override
142         @NonNull
toExternalObject()143         public ExerciseCompletionGoal toExternalObject() {
144             return new ExerciseCompletionGoal.DistanceGoal(mDistance);
145         }
146 
147         @Override
getTypeId()148         public int getTypeId() {
149             return DISTANCE_GOAL_TYPE_ID;
150         }
151     }
152 
153     public static final class StepsGoalInternal extends ExerciseCompletionGoalInternal {
154         public static final int STEPS_GOAL_TYPE_ID = 3;
155 
156         @Override
writeFieldsToParcel(@onNull Parcel parcel)157         void writeFieldsToParcel(@NonNull Parcel parcel) {
158             parcel.writeInt(this.mSteps);
159         }
160 
161         /** Read from parcel. */
162         @NonNull
readFieldsFromParcel(@onNull Parcel parcel)163         public static StepsGoalInternal readFieldsFromParcel(@NonNull Parcel parcel) {
164             return new StepsGoalInternal(parcel.readInt());
165         }
166 
167         private final int mSteps;
168 
StepsGoalInternal(int steps)169         public StepsGoalInternal(int steps) {
170             this.mSteps = steps;
171         }
172 
getSteps()173         public int getSteps() {
174             return mSteps;
175         }
176 
177         @Override
178         @NonNull
toExternalObject()179         public ExerciseCompletionGoal toExternalObject() {
180             return new ExerciseCompletionGoal.StepsGoal(mSteps);
181         }
182 
183         @Override
getTypeId()184         public int getTypeId() {
185             return STEPS_GOAL_TYPE_ID;
186         }
187     }
188 
189     public static final class DurationGoalInternal extends ExerciseCompletionGoalInternal {
190         public static final int DURATION_GOAL_TYPE_ID = 4;
191 
192         @Override
writeFieldsToParcel(@onNull Parcel parcel)193         void writeFieldsToParcel(@NonNull Parcel parcel) {
194             parcel.writeLong(this.mDuration.toMillis());
195         }
196 
197         /** Read from parcel. */
198         @NonNull
readFieldsFromParcel(@onNull Parcel parcel)199         public static DurationGoalInternal readFieldsFromParcel(@NonNull Parcel parcel) {
200             return new DurationGoalInternal(Duration.ofMillis(parcel.readLong()));
201         }
202 
203         private final Duration mDuration;
204 
DurationGoalInternal(@onNull Duration duration)205         public DurationGoalInternal(@NonNull Duration duration) {
206             this.mDuration = duration;
207         }
208 
209         @NonNull
getDuration()210         public Duration getDuration() {
211             return mDuration;
212         }
213 
214         @Override
215         @NonNull
toExternalObject()216         public ExerciseCompletionGoal toExternalObject() {
217             return new ExerciseCompletionGoal.DurationGoal(mDuration);
218         }
219 
220         @Override
getTypeId()221         public int getTypeId() {
222             return DURATION_GOAL_TYPE_ID;
223         }
224     }
225 
226     public static final class RepetitionsGoalInternal extends ExerciseCompletionGoalInternal {
227         public static final int REPETITIONS_GOAL_TYPE_ID = 5;
228 
229         @Override
writeFieldsToParcel(@onNull Parcel parcel)230         void writeFieldsToParcel(@NonNull Parcel parcel) {
231             parcel.writeInt(this.mReps);
232         }
233 
234         /** Read from parcel. */
235         @NonNull
readFieldsFromParcel(@onNull Parcel parcel)236         public static RepetitionsGoalInternal readFieldsFromParcel(@NonNull Parcel parcel) {
237             return new RepetitionsGoalInternal(parcel.readInt());
238         }
239 
240         private final int mReps;
241 
RepetitionsGoalInternal(int reps)242         public RepetitionsGoalInternal(int reps) {
243             this.mReps = reps;
244         }
245 
getReps()246         public int getReps() {
247             return mReps;
248         }
249 
250         @Override
251         @NonNull
toExternalObject()252         public ExerciseCompletionGoal toExternalObject() {
253             return new ExerciseCompletionGoal.RepetitionsGoal(mReps);
254         }
255 
256         @Override
getTypeId()257         public int getTypeId() {
258             return REPETITIONS_GOAL_TYPE_ID;
259         }
260     }
261 
262     public static final class TotalCaloriesBurnedGoalInternal
263             extends ExerciseCompletionGoalInternal {
264         public static final int TOTAL_CALORIES_BURNED_GOAL_TYPE_ID = 6;
265 
266         @Override
writeFieldsToParcel(@onNull Parcel parcel)267         void writeFieldsToParcel(@NonNull Parcel parcel) {
268             parcel.writeDouble(this.mTotalCalories.getInCalories());
269         }
270 
271         /** Read from parcel. */
272         @NonNull
readFieldsFromParcel(@onNull Parcel parcel)273         public static TotalCaloriesBurnedGoalInternal readFieldsFromParcel(@NonNull Parcel parcel) {
274             return new TotalCaloriesBurnedGoalInternal(Energy.fromCalories(parcel.readDouble()));
275         }
276 
277         private final Energy mTotalCalories;
278 
TotalCaloriesBurnedGoalInternal(@onNull Energy totalCalories)279         public TotalCaloriesBurnedGoalInternal(@NonNull Energy totalCalories) {
280             this.mTotalCalories = totalCalories;
281         }
282 
283         @NonNull
getTotalCalories()284         public Energy getTotalCalories() {
285             return mTotalCalories;
286         }
287 
288         @Override
toExternalObject()289         public ExerciseCompletionGoal toExternalObject() {
290             return new ExerciseCompletionGoal.TotalCaloriesBurnedGoal(mTotalCalories);
291         }
292 
293         @Override
getTypeId()294         public int getTypeId() {
295             return TOTAL_CALORIES_BURNED_GOAL_TYPE_ID;
296         }
297     }
298 
299     public static final class ActiveCaloriesBurnedGoalInternal
300             extends ExerciseCompletionGoalInternal {
301         public static final int ACTIVE_CALORIES_BURNED_GOAL_TYPE_ID = 7;
302 
303         @Override
writeFieldsToParcel(@onNull Parcel parcel)304         void writeFieldsToParcel(@NonNull Parcel parcel) {
305             parcel.writeDouble(this.mActiveCalories.getInCalories());
306         }
307 
308         /** Read from parcel. */
309         @NonNull
readFieldsFromParcel( @onNull Parcel parcel)310         public static ActiveCaloriesBurnedGoalInternal readFieldsFromParcel(
311                 @NonNull Parcel parcel) {
312             return new ActiveCaloriesBurnedGoalInternal(Energy.fromCalories(parcel.readDouble()));
313         }
314 
315         private final Energy mActiveCalories;
316 
ActiveCaloriesBurnedGoalInternal(@onNull Energy activeCalories)317         public ActiveCaloriesBurnedGoalInternal(@NonNull Energy activeCalories) {
318             this.mActiveCalories = activeCalories;
319         }
320 
321         @NonNull
getActiveCalories()322         public Energy getActiveCalories() {
323             return mActiveCalories;
324         }
325 
326         @Override
327         @NonNull
toExternalObject()328         public ExerciseCompletionGoal toExternalObject() {
329             return new ExerciseCompletionGoal.ActiveCaloriesBurnedGoal(mActiveCalories);
330         }
331 
332         @Override
getTypeId()333         public int getTypeId() {
334             return ACTIVE_CALORIES_BURNED_GOAL_TYPE_ID;
335         }
336     }
337 
338     public static final class DistanceWithVariableRestGoalInternal
339             extends ExerciseCompletionGoalInternal {
340         public static final int DISTANCE_WITH_VARIABLE_REST_GOAL_TYPE_ID = 8;
341 
342         @Override
writeFieldsToParcel(@onNull Parcel parcel)343         void writeFieldsToParcel(@NonNull Parcel parcel) {
344             parcel.writeDouble(this.mDistance.getInMeters());
345             parcel.writeLong(this.mDuration.toMillis());
346         }
347 
348         /** Read from parcel. */
349         @NonNull
readFieldsFromParcel( @onNull Parcel parcel)350         public static DistanceWithVariableRestGoalInternal readFieldsFromParcel(
351                 @NonNull Parcel parcel) {
352             return new DistanceWithVariableRestGoalInternal(
353                     Length.fromMeters(parcel.readDouble()), Duration.ofMillis(parcel.readLong()));
354         }
355 
356         private final Length mDistance;
357 
358         private final Duration mDuration;
359 
DistanceWithVariableRestGoalInternal( @onNull Length distance, @NonNull Duration duration)360         public DistanceWithVariableRestGoalInternal(
361                 @NonNull Length distance, @NonNull Duration duration) {
362             this.mDistance = distance;
363             this.mDuration = duration;
364         }
365 
366         @NonNull
getDistance()367         public Length getDistance() {
368             return mDistance;
369         }
370 
371         @NonNull
getDuration()372         public Duration getDuration() {
373             return mDuration;
374         }
375 
376         @Override
377         @NonNull
toExternalObject()378         public ExerciseCompletionGoal toExternalObject() {
379             return new ExerciseCompletionGoal.DistanceWithVariableRestGoal(mDistance, mDuration);
380         }
381 
382         @Override
getTypeId()383         public int getTypeId() {
384             return DISTANCE_WITH_VARIABLE_REST_GOAL_TYPE_ID;
385         }
386     }
387 
388     /**
389      * Represents a goal that is not recognised by the platform. This could happen when e.g. a
390      * version rollback occurs, i.e. a version is stored in the database that is not supported by
391      * the current (older) version.
392      */
393     public static final class UnknownGoalInternal extends ExerciseCompletionGoalInternal {
394         public static final int UNKNOWN_GOAL_TYPE_ID = 0;
395 
396         public static final UnknownGoalInternal INSTANCE = new UnknownGoalInternal();
397 
398         @Override
writeFieldsToParcel(@onNull Parcel parcel)399         void writeFieldsToParcel(@NonNull Parcel parcel) {
400             // No fields to write.
401         }
402 
UnknownGoalInternal()403         UnknownGoalInternal() {}
404 
405         @Override
406         @NonNull
toExternalObject()407         public ExerciseCompletionGoal toExternalObject() {
408             return ExerciseCompletionGoal.UnknownGoal.INSTANCE;
409         }
410 
411         @Override
getTypeId()412         public int getTypeId() {
413             return UNKNOWN_GOAL_TYPE_ID;
414         }
415     }
416 
417     /**
418      * Represents an open goal that has no specific metric associated with completion. It is up to
419      * the user to determine when the goal is complete.
420      */
421     public static final class UnspecifiedGoalInternal extends ExerciseCompletionGoalInternal {
422         public static final int UNSPECIFIED_GOAL_TYPE_ID = 1;
423 
424         public static final UnspecifiedGoalInternal INSTANCE = new UnspecifiedGoalInternal();
425 
426         @Override
writeFieldsToParcel(@onNull Parcel parcel)427         void writeFieldsToParcel(@NonNull Parcel parcel) {
428             // No fields to write.
429         }
430 
UnspecifiedGoalInternal()431         UnspecifiedGoalInternal() {}
432 
433         @Override
434         @NonNull
toExternalObject()435         public ExerciseCompletionGoal toExternalObject() {
436             return ExerciseCompletionGoal.UnspecifiedGoal.INSTANCE;
437         }
438 
439         @Override
getTypeId()440         public int getTypeId() {
441             return UNSPECIFIED_GOAL_TYPE_ID;
442         }
443     }
444 }
445