• 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 
17 package com.android.server.healthconnect.storage.datatypehelpers.aggregation;
18 
19 import static android.health.connect.datatypes.ActivityIntensityRecord.ACTIVITY_INTENSITY_TYPE_MODERATE;
20 import static android.health.connect.datatypes.ActivityIntensityRecord.ACTIVITY_INTENSITY_TYPE_VIGOROUS;
21 import static android.health.connect.datatypes.AggregationType.AggregationTypeIdentifier.ACTIVITY_INTENSITY_DURATION_TOTAL;
22 import static android.health.connect.datatypes.AggregationType.AggregationTypeIdentifier.ACTIVITY_INTENSITY_MINUTES_TOTAL;
23 import static android.health.connect.datatypes.AggregationType.AggregationTypeIdentifier.ACTIVITY_INTENSITY_MODERATE_DURATION_TOTAL;
24 import static android.health.connect.datatypes.AggregationType.AggregationTypeIdentifier.ACTIVITY_INTENSITY_VIGOROUS_DURATION_TOTAL;
25 
26 import static com.android.server.healthconnect.storage.utils.StorageUtils.getCursorInt;
27 
28 import android.database.Cursor;
29 import android.health.connect.datatypes.ActivityIntensityRecord;
30 import android.health.connect.datatypes.AggregationType;
31 
32 import com.android.server.healthconnect.storage.datatypehelpers.ActivityIntensityRecordHelper;
33 
34 import java.util.concurrent.TimeUnit;
35 
36 /**
37  * Helper class to aggregate {@link ActivityIntensityRecord} data.
38  *
39  * <p>Calculates the duration of the overlap between the underlying {@link ActivityIntensityRecord}
40  * and the requested window, and applies the multiplier depending on intensity type of the record.
41  *
42  * @hide
43  */
44 final class ActivityIntensityAggregationData extends AggregationRecordData {
45 
46     private static final long MILLIS_IN_A_MINUTE = TimeUnit.MINUTES.toMillis(1);
47 
48     @AggregationType.AggregationTypeIdentifier private final int mAggregationType;
49     @ActivityIntensityRecord.ActivityIntensityType private int mActivityIntensityType;
50 
ActivityIntensityAggregationData( @ggregationType.AggregationTypeIdentifier int aggregationType)51     ActivityIntensityAggregationData(
52             @AggregationType.AggregationTypeIdentifier int aggregationType) {
53         mAggregationType = aggregationType;
54     }
55 
56     @Override
getResultOnInterval(AggregationTimestamp windowStart, AggregationTimestamp windowEnd)57     double getResultOnInterval(AggregationTimestamp windowStart, AggregationTimestamp windowEnd) {
58         double overlapDurationMillis =
59                 calculateIntervalOverlapDuration(
60                         getStartTime(), windowStart.getTime(), getEndTime(), windowEnd.getTime());
61 
62         // Zero multiplier is used for cases when the intensity type of the underlying record is
63         // different to the intensity type being aggregated, in which case the record duration must
64         // not be included in the aggregation.
65         int multiplier =
66                 switch (mAggregationType) {
67                     case ACTIVITY_INTENSITY_MODERATE_DURATION_TOTAL ->
68                             mActivityIntensityType == ACTIVITY_INTENSITY_TYPE_MODERATE ? 1 : 0;
69                     case ACTIVITY_INTENSITY_VIGOROUS_DURATION_TOTAL ->
70                             mActivityIntensityType == ACTIVITY_INTENSITY_TYPE_VIGOROUS ? 1 : 0;
71                     case ACTIVITY_INTENSITY_DURATION_TOTAL -> 1;
72                     case ACTIVITY_INTENSITY_MINUTES_TOTAL ->
73                             mActivityIntensityType == ACTIVITY_INTENSITY_TYPE_MODERATE ? 1 : 2;
74                     default ->
75                             throw new IllegalStateException(
76                                     "Unsupported aggregation type: " + mAggregationType);
77                 };
78 
79         // TODO(b/373585917): round the resulting INTENSITY_MINUTES to the nearest minute instead
80         // of rounding down to avoid floating point inaccuracy.
81         return overlapDurationMillis
82                 * multiplier
83                 / (mAggregationType == ACTIVITY_INTENSITY_MINUTES_TOTAL ? MILLIS_IN_A_MINUTE : 1);
84     }
85 
86     @Override
populateSpecificAggregationData(Cursor cursor, boolean useLocalTime)87     void populateSpecificAggregationData(Cursor cursor, boolean useLocalTime) {
88         mActivityIntensityType =
89                 getCursorInt(cursor, ActivityIntensityRecordHelper.TYPE_COLUMN_NAME);
90     }
91 }
92