• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.health.connect.datatypes;
17 
18 import static android.health.connect.datatypes.RecordTypeIdentifier.RECORD_TYPE_MINDFULNESS_SESSION;
19 import static android.health.connect.datatypes.RecordUtils.isEqualNullableCharSequences;
20 import static android.health.connect.datatypes.validation.ValidationUtils.validateIntDefValue;
21 
22 import static com.android.healthfitness.flags.Flags.FLAG_MINDFULNESS;
23 
24 import android.annotation.FlaggedApi;
25 import android.annotation.IntDef;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.health.connect.internal.datatypes.MindfulnessSessionRecordInternal;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.time.Instant;
33 import java.time.ZoneOffset;
34 import java.util.Objects;
35 import java.util.Set;
36 
37 /**
38  * Captures a mindfulness session.
39  *
40  * <p>For example: yoga, meditation, guided breathing, etc.
41  *
42  * <p>Each record needs a start time, end time and a mindfulness session type. In addition, each
43  * record has an optional title and notes.
44  */
45 @FlaggedApi(FLAG_MINDFULNESS)
46 @Identifier(recordIdentifier = RecordTypeIdentifier.RECORD_TYPE_MINDFULNESS_SESSION)
47 public final class MindfulnessSessionRecord extends IntervalRecord {
48 
49     /**
50      * Metric identifier to retrieve total mindfulness session duration using aggregate APIs in
51      * {@link android.health.connect.HealthConnectManager}. Calculated in milliseconds.
52      */
53     @NonNull
54     public static final AggregationType<Long> MINDFULNESS_DURATION_TOTAL =
55             new AggregationType<>(
56                     AggregationType.AggregationTypeIdentifier.MINDFULNESS_SESSION_DURATION_TOTAL,
57                     AggregationType.SUM,
58                     RECORD_TYPE_MINDFULNESS_SESSION,
59                     Long.class);
60 
61     /** Use this type if the mindfulness session type is unknown. */
62     public static final int MINDFULNESS_SESSION_TYPE_UNKNOWN = 0;
63 
64     /** Meditation mindfulness session. */
65     public static final int MINDFULNESS_SESSION_TYPE_MEDITATION = 1;
66 
67     /** Other mindfulness session. */
68     public static final int MINDFULNESS_SESSION_TYPE_OTHER = 2;
69 
70     /** Guided breathing mindfulness session. */
71     public static final int MINDFULNESS_SESSION_TYPE_BREATHING = 3;
72 
73     /** Music/soundscapes mindfulness session. */
74     public static final int MINDFULNESS_SESSION_TYPE_MUSIC = 4;
75 
76     /** Stretches/movement mindfulness session. */
77     public static final int MINDFULNESS_SESSION_TYPE_MOVEMENT = 5;
78 
79     /** Unguided mindfulness session. */
80     public static final int MINDFULNESS_SESSION_TYPE_UNGUIDED = 6;
81 
82     /**
83      * Valid set of values for this IntDef. Update this set when add new type or deprecate existing
84      * type.
85      */
86     private static final Set<Integer> VALID_MINDFULNESS_SESSION_TYPES =
87             Set.of(
88                     MINDFULNESS_SESSION_TYPE_UNKNOWN,
89                     MINDFULNESS_SESSION_TYPE_MEDITATION,
90                     MINDFULNESS_SESSION_TYPE_OTHER,
91                     MINDFULNESS_SESSION_TYPE_BREATHING,
92                     MINDFULNESS_SESSION_TYPE_MUSIC,
93                     MINDFULNESS_SESSION_TYPE_MOVEMENT,
94                     MINDFULNESS_SESSION_TYPE_UNGUIDED);
95 
96     private final int mMindfulnessSessionType;
97     @Nullable private final CharSequence mTitle;
98     @Nullable private final CharSequence mNotes;
99 
100     /**
101      * Builds {@link MindfulnessSessionRecord} instance
102      *
103      * @param metadata Metadata to be associated with the record. See {@link Metadata}.
104      * @param startTime Start time of this activity
105      * @param startZoneOffset Zone offset of the user when the session started
106      * @param endTime End time of this activity
107      * @param endZoneOffset Zone offset of the user when the session finished
108      * @param mindfulnessSessionType type of the session.
109      * @param title Title of the session. Optional field.
110      * @param notes Additional notes for the session. Optional field.
111      * @param skipValidation Boolean flag to skip validation of record values.
112      */
MindfulnessSessionRecord( @onNull Metadata metadata, @NonNull Instant startTime, @NonNull ZoneOffset startZoneOffset, @NonNull Instant endTime, @NonNull ZoneOffset endZoneOffset, @MindfulnessSessionType int mindfulnessSessionType, @Nullable CharSequence title, @Nullable CharSequence notes, boolean skipValidation)113     private MindfulnessSessionRecord(
114             @NonNull Metadata metadata,
115             @NonNull Instant startTime,
116             @NonNull ZoneOffset startZoneOffset,
117             @NonNull Instant endTime,
118             @NonNull ZoneOffset endZoneOffset,
119             @MindfulnessSessionType int mindfulnessSessionType,
120             @Nullable CharSequence title,
121             @Nullable CharSequence notes,
122             boolean skipValidation) {
123         super(
124                 metadata,
125                 startTime,
126                 startZoneOffset,
127                 endTime,
128                 endZoneOffset,
129                 skipValidation,
130                 /* enforceFutureTimeRestrictions= */ true);
131         if (!skipValidation) {
132             validateIntDefValue(
133                     mindfulnessSessionType,
134                     VALID_MINDFULNESS_SESSION_TYPES,
135                     MindfulnessSessionType.class.getSimpleName());
136         }
137         mMindfulnessSessionType = mindfulnessSessionType;
138         mTitle = title;
139         mNotes = notes;
140     }
141 
142     /** Returns type of the mindfulness session. */
143     @MindfulnessSessionType
getMindfulnessSessionType()144     public int getMindfulnessSessionType() {
145         return mMindfulnessSessionType;
146     }
147 
148     /** Returns title of the mindfulness session. Returns null if no title was specified. */
149     @Nullable
getTitle()150     public CharSequence getTitle() {
151         return mTitle;
152     }
153 
154     /** Returns notes for the mindfulness session. Returns null if no notes was specified. */
155     @Nullable
getNotes()156     public CharSequence getNotes() {
157         return mNotes;
158     }
159 
160     @Override
equals(@ullable Object o)161     public boolean equals(@Nullable Object o) {
162         if (this == o) return true;
163         if (!(o instanceof MindfulnessSessionRecord)) return false;
164         if (!super.equals(o)) return false;
165         MindfulnessSessionRecord that = (MindfulnessSessionRecord) o;
166         return getMindfulnessSessionType() == that.getMindfulnessSessionType()
167                 && isEqualNullableCharSequences(getTitle(), that.getTitle())
168                 && isEqualNullableCharSequences(getNotes(), that.getNotes());
169     }
170 
171     @Override
hashCode()172     public int hashCode() {
173         return Objects.hash(super.hashCode(), getMindfulnessSessionType(), getTitle(), getNotes());
174     }
175 
176     /** @hide */
177     @IntDef({
178         MINDFULNESS_SESSION_TYPE_UNKNOWN,
179         MINDFULNESS_SESSION_TYPE_MEDITATION,
180         MINDFULNESS_SESSION_TYPE_OTHER,
181         MINDFULNESS_SESSION_TYPE_BREATHING,
182         MINDFULNESS_SESSION_TYPE_MUSIC,
183         MINDFULNESS_SESSION_TYPE_MOVEMENT,
184         MINDFULNESS_SESSION_TYPE_UNGUIDED
185     })
186     @Retention(RetentionPolicy.SOURCE)
187     public @interface MindfulnessSessionType {}
188 
189     /** Builder class for {@link MindfulnessSessionRecord} */
190     public static final class Builder {
191         private final Metadata mMetadata;
192         private final Instant mStartTime;
193         private final Instant mEndTime;
194         private ZoneOffset mStartZoneOffset;
195         private ZoneOffset mEndZoneOffset;
196         private final int mMindfulnessSessionType;
197         @Nullable private CharSequence mTitle;
198         @Nullable private CharSequence mNotes;
199 
200         /**
201          * @param metadata Metadata to be associated with the record. See {@link Metadata}.
202          * @param startTime Start time of this mindfulness session
203          * @param endTime End time of this mindfulness session
204          */
Builder( @onNull Metadata metadata, @NonNull Instant startTime, @NonNull Instant endTime, @MindfulnessSessionType int mindfulnessSessionType)205         public Builder(
206                 @NonNull Metadata metadata,
207                 @NonNull Instant startTime,
208                 @NonNull Instant endTime,
209                 @MindfulnessSessionType int mindfulnessSessionType) {
210             Objects.requireNonNull(metadata);
211             Objects.requireNonNull(startTime);
212             Objects.requireNonNull(endTime);
213             mMetadata = metadata;
214             mStartTime = startTime;
215             mEndTime = endTime;
216             mMindfulnessSessionType = mindfulnessSessionType;
217             mStartZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(startTime);
218             mEndZoneOffset = ZoneOffset.systemDefault().getRules().getOffset(endTime);
219         }
220 
221         /** Sets the zone offset of the user when the activity started */
222         @NonNull
setStartZoneOffset(@onNull ZoneOffset startZoneOffset)223         public Builder setStartZoneOffset(@NonNull ZoneOffset startZoneOffset) {
224             Objects.requireNonNull(startZoneOffset);
225 
226             mStartZoneOffset = startZoneOffset;
227             return this;
228         }
229 
230         /** Sets the zone offset of the user when the activity ended */
231         @NonNull
setEndZoneOffset(@onNull ZoneOffset endZoneOffset)232         public Builder setEndZoneOffset(@NonNull ZoneOffset endZoneOffset) {
233             Objects.requireNonNull(endZoneOffset);
234             mEndZoneOffset = endZoneOffset;
235             return this;
236         }
237 
238         /**
239          * Sets a title of this activity
240          *
241          * @param title Title of the session. Optional field.
242          */
243         @NonNull
setTitle(@ullable CharSequence title)244         public Builder setTitle(@Nullable CharSequence title) {
245             mTitle = title;
246             return this;
247         }
248 
249         /**
250          * Sets notes for this activity
251          *
252          * @param notes Additional notes for the session. Optional field.
253          */
254         @NonNull
setNotes(@ullable CharSequence notes)255         public Builder setNotes(@Nullable CharSequence notes) {
256             mNotes = notes;
257             return this;
258         }
259 
260         /**
261          * @return Object of {@link MindfulnessSessionRecord} without validating the values.
262          * @hide
263          */
264         @NonNull
buildWithoutValidation()265         public MindfulnessSessionRecord buildWithoutValidation() {
266             return new MindfulnessSessionRecord(
267                     mMetadata,
268                     mStartTime,
269                     mStartZoneOffset,
270                     mEndTime,
271                     mEndZoneOffset,
272                     mMindfulnessSessionType,
273                     mTitle,
274                     mNotes,
275                     true);
276         }
277 
278         /** Returns {@link MindfulnessSessionRecord} */
279         @NonNull
build()280         public MindfulnessSessionRecord build() {
281             return new MindfulnessSessionRecord(
282                     mMetadata,
283                     mStartTime,
284                     mStartZoneOffset,
285                     mEndTime,
286                     mEndZoneOffset,
287                     mMindfulnessSessionType,
288                     mTitle,
289                     mNotes,
290                     false);
291         }
292     }
293 
294     /** @hide */
295     @Override
toRecordInternal()296     public MindfulnessSessionRecordInternal toRecordInternal() {
297         MindfulnessSessionRecordInternal recordInternal =
298                 (MindfulnessSessionRecordInternal)
299                         new MindfulnessSessionRecordInternal()
300                                 .setUuid(getMetadata().getId())
301                                 .setPackageName(getMetadata().getDataOrigin().getPackageName())
302                                 .setLastModifiedTime(
303                                         getMetadata().getLastModifiedTime().toEpochMilli())
304                                 .setClientRecordId(getMetadata().getClientRecordId())
305                                 .setClientRecordVersion(getMetadata().getClientRecordVersion())
306                                 .setManufacturer(getMetadata().getDevice().getManufacturer())
307                                 .setModel(getMetadata().getDevice().getModel())
308                                 .setDeviceType(getMetadata().getDevice().getType())
309                                 .setRecordingMethod(getMetadata().getRecordingMethod());
310         recordInternal.setStartTime(getStartTime().toEpochMilli());
311         recordInternal.setEndTime(getEndTime().toEpochMilli());
312         recordInternal.setStartZoneOffset(getStartZoneOffset().getTotalSeconds());
313         recordInternal.setEndZoneOffset(getEndZoneOffset().getTotalSeconds());
314         recordInternal.setMindfulnessSessionType(getMindfulnessSessionType());
315         if (getTitle() != null) {
316             recordInternal.setTitle(getTitle().toString());
317         }
318         if (getNotes() != null) {
319             recordInternal.setNotes(getNotes().toString());
320         }
321         return recordInternal;
322     }
323 }
324