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 */ AmbientBrightnessDayStats(@onNull LocalDate localDate, @NonNull float[] bucketBoundaries)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 */ AmbientBrightnessDayStats(@onNull LocalDate localDate, @NonNull float[] bucketBoundaries, float[] stats)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 */ getLocalDate()97 public LocalDate getLocalDate() { 98 return mLocalDate; 99 } 100 101 /** 102 * @return Aggregated stats of time spent (in seconds) in various buckets. 103 */ getStats()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 */ getBucketBoundaries()114 public float[] getBucketBoundaries() { 115 return mBucketBoundaries; 116 } 117 AmbientBrightnessDayStats(Parcel source)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 equals(Object obj)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 hashCode()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 toString()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 describeContents()183 public int describeContents() { 184 return 0; 185 } 186 187 @Override writeToParcel(Parcel dest, int flags)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 */ log(float ambientBrightness, float durationSec)202 public void log(float ambientBrightness, float durationSec) { 203 int bucketIndex = getBucketIndex(ambientBrightness); 204 if (bucketIndex >= 0) { 205 mStats[bucketIndex] += durationSec; 206 } 207 } 208 getBucketIndex(float ambientBrightness)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 checkSorted(float[] values)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