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.datatypes;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.health.connect.datatypes.validation.ValidationUtils;
22 import android.health.connect.internal.datatypes.ExerciseSegmentInternal;
23 
24 import java.time.Instant;
25 import java.util.Objects;
26 
27 /**
28  * Represents particular exercise within exercise session (see {@link ExerciseSessionRecord}).
29  *
30  * <p>Each record contains start and end time of the exercise, exercise type and optional number of
31  * repetitions.
32  */
33 public final class ExerciseSegment implements TimeInterval.TimeIntervalHolder {
34     private final TimeInterval mInterval;
35 
36     @ExerciseSegmentType.ExerciseSegmentTypes private final int mSegmentType;
37 
38     private final int mRepetitionsCount;
39 
ExerciseSegment( @onNull TimeInterval interval, @ExerciseSegmentType.ExerciseSegmentTypes int segmentType, @IntRange(from = 0) int repetitionsCount, boolean skipValidation)40     private ExerciseSegment(
41             @NonNull TimeInterval interval,
42             @ExerciseSegmentType.ExerciseSegmentTypes int segmentType,
43             @IntRange(from = 0) int repetitionsCount,
44             boolean skipValidation) {
45         Objects.requireNonNull(interval);
46         mInterval = interval;
47 
48         mSegmentType = segmentType;
49 
50         if (!skipValidation) {
51             ValidationUtils.requireNonNegative(repetitionsCount, "repetitionsCount");
52         }
53         mRepetitionsCount = repetitionsCount;
54     }
55 
56     /*
57      * Returns type of the segment, one of {@link @ExerciseSegmentType.ExerciseSegmentTypes}.
58      */
59     @ExerciseSegmentType.ExerciseSegmentTypes
getSegmentType()60     public int getSegmentType() {
61         return mSegmentType;
62     }
63 
64     /*
65      * Returns number of repetitions in the current segment. Positive value.
66      */
67     @IntRange(from = 0)
getRepetitionsCount()68     public int getRepetitionsCount() {
69         return mRepetitionsCount;
70     }
71 
72     /*
73      * Returns start time of the segment.
74      */
75     @NonNull
getStartTime()76     public Instant getStartTime() {
77         return mInterval.getStartTime();
78     }
79 
80     /*
81      * Returns end time of the segment.
82      */
83     @NonNull
getEndTime()84     public Instant getEndTime() {
85         return mInterval.getEndTime();
86     }
87 
88     /** @hide */
89     @Override
getInterval()90     public TimeInterval getInterval() {
91         return mInterval;
92     }
93 
94     @Override
equals(Object o)95     public boolean equals(Object o) {
96         if (this == o) return true;
97         if (!(o instanceof ExerciseSegment)) return false;
98         ExerciseSegment that = (ExerciseSegment) o;
99         return mSegmentType == that.mSegmentType
100                 && mRepetitionsCount == that.mRepetitionsCount
101                 && Objects.equals(mInterval, that.mInterval);
102     }
103 
104     @Override
hashCode()105     public int hashCode() {
106         return Objects.hash(mSegmentType, mRepetitionsCount, mInterval);
107     }
108 
109     /** @hide */
toSegmentInternal()110     public ExerciseSegmentInternal toSegmentInternal() {
111         return new ExerciseSegmentInternal()
112                 .setStarTime(getStartTime().toEpochMilli())
113                 .setEndTime(getEndTime().toEpochMilli())
114                 .setSegmentType(getSegmentType())
115                 .setRepetitionsCount(getRepetitionsCount());
116     }
117 
118     /** Builder class for {@link ExerciseSegment} */
119     public static final class Builder {
120         private final TimeInterval mInterval;
121 
122         @ExerciseSegmentType.ExerciseSegmentTypes private final int mSegmentType;
123 
124         private int mRepetitionsCount = 0;
125 
Builder( @onNull Instant startTime, @NonNull Instant endTime, @ExerciseSegmentType.ExerciseSegmentTypes int segmentType)126         public Builder(
127                 @NonNull Instant startTime,
128                 @NonNull Instant endTime,
129                 @ExerciseSegmentType.ExerciseSegmentTypes int segmentType) {
130             Objects.requireNonNull(startTime);
131             Objects.requireNonNull(endTime);
132             mInterval = new TimeInterval(startTime, endTime);
133             mSegmentType = segmentType;
134         }
135 
136         /**
137          * Sets the number of repetitions to the current segment. Returns builder instance with
138          * repetitions count set.
139          */
140         @NonNull
setRepetitionsCount(@ntRangefrom = 0) int repetitionsCount)141         public Builder setRepetitionsCount(@IntRange(from = 0) int repetitionsCount) {
142             if (repetitionsCount < 0) {
143                 throw new IllegalArgumentException("Number of repetitions must be non negative.");
144             }
145             mRepetitionsCount = repetitionsCount;
146             return this;
147         }
148 
149         /**
150          * @return Object of {@link ExerciseSegment} without validating the values.
151          * @hide
152          */
153         @NonNull
buildWithoutValidation()154         public ExerciseSegment buildWithoutValidation() {
155             return new ExerciseSegment(mInterval, mSegmentType, mRepetitionsCount, true);
156         }
157 
158         /**
159          * Sets the number repetitions to the current segment. Returns {@link ExerciseSegment}
160          * instance.
161          */
162         @NonNull
build()163         public ExerciseSegment build() {
164             return new ExerciseSegment(mInterval, mSegmentType, mRepetitionsCount, false);
165         }
166     }
167 }
168