blob: b25ef8d6a88fb748d161806846fc35b6c6fafce7 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.display;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
import java.time.LocalDate;
import java.util.Arrays;
/**
* AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
* {@see DisplayManager.getAmbientBrightnessStats()}
*
* @hide
*/
@SystemApi
@TestApi
public final class AmbientBrightnessDayStats implements Parcelable {
/** The localdate for which brightness stats are being tracked */
private final LocalDate mLocalDate;
/** Ambient brightness values for creating bucket boundaries from */
private final float[] mBucketBoundaries;
/** Stats of how much time (in seconds) was spent in each of the buckets */
private final float[] mStats;
/**
* Initialize day stats from the given state. The time spent in each of the bucket is
* initialized to 0.
*
* @param localDate The date for which stats are being tracked
* @param bucketBoundaries Bucket boundaries used from creating the buckets from
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@NonNull float[] bucketBoundaries) {
this(localDate, bucketBoundaries, null);
}
/**
* Initialize day stats from the given state
*
* @param localDate The date for which stats are being tracked
* @param bucketBoundaries Bucket boundaries used from creating the buckets from
* @param stats Time spent in each of the buckets (in seconds)
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@NonNull float[] bucketBoundaries, float[] stats) {
Preconditions.checkNotNull(localDate);
Preconditions.checkNotNull(bucketBoundaries);
Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
"bucketBoundaries");
if (bucketBoundaries.length < 1) {
throw new IllegalArgumentException("Bucket boundaries must contain at least 1 value");
}
checkSorted(bucketBoundaries);
if (stats == null) {
stats = new float[bucketBoundaries.length];
} else {
Preconditions.checkArrayElementsInRange(stats, 0, Float.MAX_VALUE, "stats");
if (bucketBoundaries.length != stats.length) {
throw new IllegalArgumentException(
"Bucket boundaries and stats must be of same size.");
}
}
mLocalDate = localDate;
mBucketBoundaries = bucketBoundaries;
mStats = stats;
}
/**
* @return The {@link LocalDate} for which brightness stats are being tracked.
*/
public LocalDate getLocalDate() {
return mLocalDate;
}
/**
* @return Aggregated stats of time spent (in seconds) in various buckets.
*/
public float[] getStats() {
return mStats;
}
/**
* Returns the bucket boundaries (in lux) used for creating buckets. For eg., if the bucket
* boundaries array is {b1, b2, b3}, the buckets will be [b1, b2), [b2, b3), [b3, inf).
*
* @return The list of bucket boundaries.
*/
public float[] getBucketBoundaries() {
return mBucketBoundaries;
}
private AmbientBrightnessDayStats(Parcel source) {
mLocalDate = LocalDate.parse(source.readString());
mBucketBoundaries = source.createFloatArray();
mStats = source.createFloatArray();
}
public static final @android.annotation.NonNull Creator<AmbientBrightnessDayStats> CREATOR =
new Creator<AmbientBrightnessDayStats>() {
@Override
public AmbientBrightnessDayStats createFromParcel(Parcel source) {
return new AmbientBrightnessDayStats(source);
}
@Override
public AmbientBrightnessDayStats[] newArray(int size) {
return new AmbientBrightnessDayStats[size];
}
};
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
AmbientBrightnessDayStats other = (AmbientBrightnessDayStats) obj;
return mLocalDate.equals(other.mLocalDate) && Arrays.equals(mBucketBoundaries,
other.mBucketBoundaries) && Arrays.equals(mStats, other.mStats);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = result * prime + mLocalDate.hashCode();
result = result * prime + Arrays.hashCode(mBucketBoundaries);
result = result * prime + Arrays.hashCode(mStats);
return result;
}
@Override
public String toString() {
StringBuilder bucketBoundariesString = new StringBuilder();
StringBuilder statsString = new StringBuilder();
for (int i = 0; i < mBucketBoundaries.length; i++) {
if (i != 0) {
bucketBoundariesString.append(", ");
statsString.append(", ");
}
bucketBoundariesString.append(mBucketBoundaries[i]);
statsString.append(mStats[i]);
}
return new StringBuilder()
.append(mLocalDate).append(" ")
.append("{").append(bucketBoundariesString).append("} ")
.append("{").append(statsString).append("}").toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mLocalDate.toString());
dest.writeFloatArray(mBucketBoundaries);
dest.writeFloatArray(mStats);
}
/**
* Updates the stats by incrementing the time spent for the appropriate bucket based on ambient
* brightness reading.
*
* @param ambientBrightness Ambient brightness reading (in lux)
* @param durationSec Time spent with the given reading (in seconds)
* @hide
*/
public void log(float ambientBrightness, float durationSec) {
int bucketIndex = getBucketIndex(ambientBrightness);
if (bucketIndex >= 0) {
mStats[bucketIndex] += durationSec;
}
}
private int getBucketIndex(float ambientBrightness) {
if (ambientBrightness < mBucketBoundaries[0]) {
return -1;
}
int low = 0;
int high = mBucketBoundaries.length - 1;
while (low < high) {
int mid = (low + high) / 2;
if (mBucketBoundaries[mid] <= ambientBrightness
&& ambientBrightness < mBucketBoundaries[mid + 1]) {
return mid;
} else if (mBucketBoundaries[mid] < ambientBrightness) {
low = mid + 1;
} else if (mBucketBoundaries[mid] > ambientBrightness) {
high = mid - 1;
}
}
return low;
}
private static void checkSorted(float[] values) {
if (values.length <= 1) {
return;
}
float prevValue = values[0];
for (int i = 1; i < values.length; i++) {
Preconditions.checkState(prevValue < values[i]);
prevValue = values[i];
}
return;
}
}