Home | History | Annotate | Download | only in display
      1 /*
      2  * Copyright 2018 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.hardware.display;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.SystemApi;
     21 import android.annotation.TestApi;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 
     25 import com.android.internal.util.Preconditions;
     26 
     27 import java.time.LocalDate;
     28 import java.util.Arrays;
     29 
     30 /**
     31  * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
     32  * {@see DisplayManager.getAmbientBrightnessStats()}
     33  *
     34  * @hide
     35  */
     36 @SystemApi
     37 @TestApi
     38 public final class AmbientBrightnessDayStats implements Parcelable {
     39 
     40     /** The localdate for which brightness stats are being tracked */
     41     private final LocalDate mLocalDate;
     42 
     43     /** Ambient brightness values for creating bucket boundaries from */
     44     private final float[] mBucketBoundaries;
     45 
     46     /** Stats of how much time (in seconds) was spent in each of the buckets */
     47     private final float[] mStats;
     48 
     49     /**
     50      * Initialize day stats from the given state. The time spent in each of the bucket is
     51      * initialized to 0.
     52      *
     53      * @param localDate        The date for which stats are being tracked
     54      * @param bucketBoundaries Bucket boundaries used from creating the buckets from
     55      * @hide
     56      */
     57     public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
     58             @NonNull float[] bucketBoundaries) {
     59         this(localDate, bucketBoundaries, null);
     60     }
     61 
     62     /**
     63      * Initialize day stats from the given state
     64      *
     65      * @param localDate        The date for which stats are being tracked
     66      * @param bucketBoundaries Bucket boundaries used from creating the buckets from
     67      * @param stats            Time spent in each of the buckets (in seconds)
     68      * @hide
     69      */
     70     public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
     71             @NonNull float[] bucketBoundaries, float[] stats) {
     72         Preconditions.checkNotNull(localDate);
     73         Preconditions.checkNotNull(bucketBoundaries);
     74         Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
     75                 "bucketBoundaries");
     76         if (bucketBoundaries.length < 1) {
     77             throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
     78         }
     79         checkSorted(bucketBoundaries);
     80         if (stats == null) {
     81             stats = new float[bucketBoundaries.length];
     82         } else {
     83             Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
     84             if (bucketBoundaries.length != stats.length) {
     85                 throw new IllegalArgumentException(
     86                         "Bucket boundaries and stats must be of same size.");
     87             }
     88         }
     89         mLocalDate = localDate;
     90         mBucketBoundaries = bucketBoundaries;
     91         mStats = stats;
     92     }
     93 
     94     /**
     95      * @return The {@link LocalDate} for which brightness stats are being tracked.
     96      */
     97     public LocalDate getLocalDate() {
     98         return mLocalDate;
     99     }
    100 
    101     /**
    102      * @return Aggregated stats of time spent (in seconds) in various buckets.
    103      */
    104     public float[] getStats() {
    105         return mStats;
    106     }
    107 
    108     /**
    109      * Returns the bucket boundaries (in lux) used for creating buckets. For eg., if the bucket
    110      * boundaries array is {b1, b2, b3}, the buckets will be [b1, b2), [b2, b3), [b3, inf).
    111      *
    112      * @return The list of bucket boundaries.
    113      */
    114     public float[] getBucketBoundaries() {
    115         return mBucketBoundaries;
    116     }
    117 
    118     private AmbientBrightnessDayStats(Parcel source) {
    119         mLocalDate = LocalDate.parse(source.readString());
    120         mBucketBoundaries = source.createFloatArray();
    121         mStats = source.createFloatArray();
    122     }
    123 
    124     public static final Creator<AmbientBrightnessDayStats> CREATOR =
    125             new Creator<AmbientBrightnessDayStats>() {
    126 
    127                 @Override
    128                 public AmbientBrightnessDayStats createFromParcel(Parcel source) {
    129                     return new AmbientBrightnessDayStats(source);
    130                 }
    131 
    132                 @Override
    133                 public AmbientBrightnessDayStats[] newArray(int size) {
    134                     return new AmbientBrightnessDayStats[size];
    135                 }
    136             };
    137 
    138     @Override
    139     public boolean equals(Object obj) {
    140         if (this == obj) {
    141             return true;
    142         }
    143         if (obj == null) {
    144             return false;
    145         }
    146         if (getClass() != obj.getClass()) {
    147             return false;
    148         }
    149         AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
    150         return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
    151                 other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
    152     }
    153 
    154     @Override
    155     public int hashCode() {
    156         final int prime = 31;
    157         int result = 1;
    158         result = result * prime + mLocalDate.hashCode();
    159         result = result * prime + Arrays.hashCode(mBucketBoundaries);
    160         result = result * prime + Arrays.hashCode(mStats);
    161         return result;
    162     }
    163 
    164     @Override
    165     public String toString() {
    166         StringBuilder bucketBoundariesString = new StringBuilder();
    167         StringBuilder statsString = new StringBuilder();
    168         for (int i = 0; i < mBucketBoundaries.length; i++) {
    169             if (i != 0) {
    170                 bucketBoundariesString.append(", ");
    171                 statsString.append(", ");
    172             }
    173             bucketBoundariesString.append(mBucketBoundaries[i]);
    174             statsString.append(mStats[i]);
    175         }
    176         return new StringBuilder()
    177                 .append(mLocalDate).append(" ")
    178                 .append("{").append(bucketBoundariesString).append("} ")
    179                 .append("{").append(statsString).append("}").toString();
    180     }
    181 
    182     @Override
    183     public int describeContents() {
    184         return 0;
    185     }
    186 
    187     @Override
    188     public void writeToParcel(Parcel dest, int flags) {
    189         dest.writeString(mLocalDate.toString());
    190         dest.writeFloatArray(mBucketBoundaries);
    191         dest.writeFloatArray(mStats);
    192     }
    193 
    194     /**
    195      * Updates the stats by incrementing the time spent for the appropriate bucket based on ambient
    196      * brightness reading.
    197      *
    198      * @param ambientBrightness Ambient brightness reading (in lux)
    199      * @param durationSec       Time spent with the given reading (in seconds)
    200      * @hide
    201      */
    202     public void log(float ambientBrightness, float durationSec) {
    203         int bucketIndex = getBucketIndex(ambientBrightness);
    204         if (bucketIndex >= 0) {
    205             mStats[bucketIndex] += durationSec;
    206         }
    207     }
    208 
    209     private int getBucketIndex(float ambientBrightness) {
    210         if (ambientBrightness < mBucketBoundaries[0]) {
    211             return -1;
    212         }
    213         int low = 0;
    214         int high = mBucketBoundaries.length - 1;
    215         while (low < high) {
    216             int mid = (low + high) / 2;
    217             if (mBucketBoundaries[mid] <= ambientBrightness
    218                     && ambientBrightness < mBucketBoundaries[mid + 1]) {
    219                 return mid;
    220             } else if (mBucketBoundaries[mid] < ambientBrightness) {
    221                 low = mid + 1;
    222             } else if (mBucketBoundaries[mid] > ambientBrightness) {
    223                 high = mid - 1;
    224             }
    225         }
    226         return low;
    227     }
    228 
    229     private static void checkSorted(float[] values) {
    230         if (values.length <= 1) {
    231             return;
    232         }
    233         float prevValue = values[0];
    234         for (int i = 1; i < values.length; i++) {
    235             Preconditions.checkState(prevValue < values[i]);
    236             prevValue = values[i];
    237         }
    238         return;
    239     }
    240 }
    241