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