diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2018-12-19 17:14:53 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-12-19 17:14:53 +0000 |
| commit | 7b2b4c838be94e6ca7cfbb255523c800da4a0cce (patch) | |
| tree | dd8d72d57320dffc6052353fd85f0d9dc8190f40 /core/java | |
| parent | b39b21b7cd2f4de92cb47332e166636bc7d2081b (diff) | |
| parent | 54478d9cab3634482b12e8a834db89ac4788437b (diff) | |
Merge "Added app context to smart screen brightness."
Diffstat (limited to 'core/java')
3 files changed, 589 insertions, 4 deletions
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index 7e52ca331f9f..be054297c769 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -19,26 +19,54 @@ package android.hardware.display; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.content.pm.ApplicationInfo; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; import com.android.internal.util.Preconditions; +import com.android.internal.util.XmlUtils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; /** @hide */ @SystemApi @TestApi public final class BrightnessConfiguration implements Parcelable { + private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve"; + private static final String TAG_BRIGHTNESS_POINT = "brightness-point"; + private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections"; + private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction"; + private static final String ATTR_LUX = "lux"; + private static final String ATTR_NITS = "nits"; + private static final String ATTR_DESCRIPTION = "description"; + private static final String ATTR_PACKAGE_NAME = "package-name"; + private static final String ATTR_CATEGORY = "category"; + private final float[] mLux; private final float[] mNits; + private final Map<String, BrightnessCorrection> mCorrectionsByPackageName; + private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory; private final String mDescription; - private BrightnessConfiguration(float[] lux, float[] nits, String description) { + private BrightnessConfiguration(float[] lux, float[] nits, + Map<String, BrightnessCorrection> correctionsByPackageName, + Map<Integer, BrightnessCorrection> correctionsByCategory, String description) { mLux = lux; mNits = nits; + mCorrectionsByPackageName = correctionsByPackageName; + mCorrectionsByCategory = correctionsByCategory; mDescription = description; } @@ -56,6 +84,38 @@ public final class BrightnessConfiguration implements Parcelable { } /** + * Returns a brightness correction by app, or null. + * + * @param packageName + * The app's package name. + * + * @return The matching brightness correction, or null. + * + * @hide + */ + @SystemApi + @Nullable + public BrightnessCorrection getCorrectionByPackageName(String packageName) { + return mCorrectionsByPackageName.get(packageName); + } + + /** + * Returns a brightness correction by app category, or null. + * + * @param category + * The app category. + * + * @return The matching brightness correction, or null. + * + * @hide + */ + @SystemApi + @Nullable + public BrightnessCorrection getCorrectionByCategory(int category) { + return mCorrectionsByCategory.get(category); + } + + /** * Returns description string. * @hide */ @@ -67,6 +127,20 @@ public final class BrightnessConfiguration implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeFloatArray(mLux); dest.writeFloatArray(mNits); + dest.writeInt(mCorrectionsByPackageName.size()); + for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) { + final String packageName = entry.getKey(); + final BrightnessCorrection correction = entry.getValue(); + dest.writeString(packageName); + correction.writeToParcel(dest, flags); + } + dest.writeInt(mCorrectionsByCategory.size()); + for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) { + final int category = entry.getKey(); + final BrightnessCorrection correction = entry.getValue(); + dest.writeInt(category); + correction.writeToParcel(dest, flags); + } dest.writeString(mDescription); } @@ -85,7 +159,14 @@ public final class BrightnessConfiguration implements Parcelable { } sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")"); } - sb.append("], '"); + sb.append("], {"); + for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) { + sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", "); + } + for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) { + sb.append(entry.getKey() + ": " + entry.getValue() + ", "); + } + sb.append("}, '"); if (mDescription != null) { sb.append(mDescription); } @@ -98,6 +179,8 @@ public final class BrightnessConfiguration implements Parcelable { int result = 1; result = result * 31 + Arrays.hashCode(mLux); result = result * 31 + Arrays.hashCode(mNits); + result = result * 31 + mCorrectionsByPackageName.hashCode(); + result = result * 31 + mCorrectionsByCategory.hashCode(); if (mDescription != null) { result = result * 31 + mDescription.hashCode(); } @@ -114,6 +197,8 @@ public final class BrightnessConfiguration implements Parcelable { } final BrightnessConfiguration other = (BrightnessConfiguration) o; return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits) + && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName) + && mCorrectionsByCategory.equals(other.mCorrectionsByCategory) && Objects.equals(mDescription, other.mDescription); } @@ -123,7 +208,25 @@ public final class BrightnessConfiguration implements Parcelable { float[] lux = in.createFloatArray(); float[] nits = in.createFloatArray(); Builder builder = new Builder(lux, nits); - builder.setDescription(in.readString()); + + int n = in.readInt(); + for (int i = 0; i < n; i++) { + final String packageName = in.readString(); + final BrightnessCorrection correction = + BrightnessCorrection.CREATOR.createFromParcel(in); + builder.addCorrectionByPackageName(packageName, correction); + } + + n = in.readInt(); + for (int i = 0; i < n; i++) { + final int category = in.readInt(); + final BrightnessCorrection correction = + BrightnessCorrection.CREATOR.createFromParcel(in); + builder.addCorrectionByCategory(category, correction); + } + + final String description = in.readString(); + builder.setDescription(description); return builder.build(); } @@ -133,11 +236,146 @@ public final class BrightnessConfiguration implements Parcelable { }; /** + * Writes the configuration to an XML serializer. + * + * @param serializer + * The XML serializer. + * + * @hide + */ + public void saveToXml(XmlSerializer serializer) throws IOException { + serializer.startTag(null, TAG_BRIGHTNESS_CURVE); + if (mDescription != null) { + serializer.attribute(null, ATTR_DESCRIPTION, mDescription); + } + for (int i = 0; i < mLux.length; i++) { + serializer.startTag(null, TAG_BRIGHTNESS_POINT); + serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i])); + serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i])); + serializer.endTag(null, TAG_BRIGHTNESS_POINT); + } + serializer.endTag(null, TAG_BRIGHTNESS_CURVE); + serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS); + for (Map.Entry<String, BrightnessCorrection> entry : + mCorrectionsByPackageName.entrySet()) { + final String packageName = entry.getKey(); + final BrightnessCorrection correction = entry.getValue(); + serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION); + serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); + correction.saveToXml(serializer); + serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION); + } + for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) { + final int category = entry.getKey(); + final BrightnessCorrection correction = entry.getValue(); + serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION); + serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category)); + correction.saveToXml(serializer); + serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION); + } + serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS); + } + + /** + * Read a configuration from an XML parser. + * + * @param parser + * The XML parser. + * + * @throws IOException + * The parser failed to read the XML file. + * @throws XmlPullParserException + * The parser failed to parse the XML file. + * + * @hide + */ + public static BrightnessConfiguration loadFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + String description = null; + List<Float> luxList = new ArrayList<>(); + List<Float> nitsList = new ArrayList<>(); + Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>(); + Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>(); + final int configDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, configDepth)) { + if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) { + description = parser.getAttributeValue(null, ATTR_DESCRIPTION); + final int curveDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, curveDepth)) { + if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) { + continue; + } + final float lux = loadFloatFromXml(parser, ATTR_LUX); + final float nits = loadFloatFromXml(parser, ATTR_NITS); + luxList.add(lux); + nitsList.add(nits); + } + } + if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) { + final int correctionsDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, correctionsDepth)) { + if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) { + continue; + } + final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); + final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY); + BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser); + if (packageName != null) { + correctionsByPackageName.put(packageName, correction); + } else if (categoryText != null) { + try { + final int category = Integer.parseInt(categoryText); + correctionsByCategory.put(category, correction); + } catch (NullPointerException | NumberFormatException e) { + continue; + } + } + } + } + } + final int n = luxList.size(); + float[] lux = new float[n]; + float[] nits = new float[n]; + for (int i = 0; i < n; i++) { + lux[i] = luxList.get(i); + nits[i] = nitsList.get(i); + } + final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux, + nits); + builder.setDescription(description); + for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) { + final String packageName = entry.getKey(); + final BrightnessCorrection correction = entry.getValue(); + builder.addCorrectionByPackageName(packageName, correction); + } + for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) { + final int category = entry.getKey(); + final BrightnessCorrection correction = entry.getValue(); + builder.addCorrectionByCategory(category, correction); + } + return builder.build(); + } + + private static float loadFloatFromXml(XmlPullParser parser, String attribute) { + final String string = parser.getAttributeValue(null, attribute); + try { + return Float.parseFloat(string); + } catch (NullPointerException | NumberFormatException e) { + return Float.NaN; + } + } + + /** * A builder class for {@link BrightnessConfiguration}s. */ public static class Builder { + private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20; + private static final int MAX_CORRECTIONS_BY_CATEGORY = 20; + private float[] mCurveLux; private float[] mCurveNits; + private Map<String, BrightnessCorrection> mCorrectionsByPackageName; + private Map<Integer, BrightnessCorrection> mCorrectionsByCategory; private String mDescription; /** @@ -169,6 +407,88 @@ public final class BrightnessConfiguration implements Parcelable { checkMonotonic(nits, false /*strictly increasing*/, "nits"); mCurveLux = lux; mCurveNits = nits; + mCorrectionsByPackageName = new HashMap<>(); + mCorrectionsByCategory = new HashMap<>(); + } + + /** + * Returns the maximum number of corrections by package name allowed. + * + * @return The maximum number of corrections by package name allowed. + * + * @hide + */ + @SystemApi + public int getMaxCorrectionsByPackageName() { + return MAX_CORRECTIONS_BY_PACKAGE_NAME; + } + + /** + * Returns the maximum number of corrections by category allowed. + * + * @return The maximum number of corrections by category allowed. + * + * @hide + */ + @SystemApi + public int getMaxCorrectionsByCategory() { + return MAX_CORRECTIONS_BY_CATEGORY; + } + + /** + * Add a brightness correction by app package name. + * This correction is applied whenever an app with this package name has the top activity + * of the focused stack. + * + * @param packageName + * The app's package name. + * @param correction + * The brightness correction. + * + * @return The builder. + * + * @throws IllegalArgumentExceptions + * Maximum number of corrections by package name exceeded (see + * {@link #getMaxCorrectionsByPackageName}). + * + * @hide + */ + @SystemApi + public Builder addCorrectionByPackageName(String packageName, + BrightnessCorrection correction) { + if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) { + throw new IllegalArgumentException("Too many corrections by package name"); + } + mCorrectionsByPackageName.put(packageName, correction); + return this; + } + + /** + * Add a brightness correction by app category. + * This correction is applied whenever an app with this category has the top activity of + * the focused stack, and only if a correction by package name has not been applied. + * + * @param category + * The {@link android.content.pm.ApplicationInfo#category app category}. + * @param correction + * The brightness correction. + * + * @return The builder. + * + * @throws IllegalArgumentException + * Maximum number of corrections by category exceeded (see + * {@link #getMaxCorrectionsByCategory}). + * + * @hide + */ + @SystemApi + public Builder addCorrectionByCategory(@ApplicationInfo.Category int category, + BrightnessCorrection correction) { + if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) { + throw new IllegalArgumentException("Too many corrections by category"); + } + mCorrectionsByCategory.put(category, correction); + return this; } /** @@ -191,7 +511,8 @@ public final class BrightnessConfiguration implements Parcelable { if (mCurveLux == null || mCurveNits == null) { throw new IllegalStateException("A curve must be set!"); } - return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription); + return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName, + mCorrectionsByCategory, mDescription); } private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) { diff --git a/core/java/android/hardware/display/BrightnessCorrection.aidl b/core/java/android/hardware/display/BrightnessCorrection.aidl new file mode 100644 index 000000000000..3abe29cc0076 --- /dev/null +++ b/core/java/android/hardware/display/BrightnessCorrection.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 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; + +parcelable BrightnessCorrection; diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java new file mode 100644 index 000000000000..c4e0e3b723cd --- /dev/null +++ b/core/java/android/hardware/display/BrightnessCorrection.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 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.os.Parcel; +import android.os.Parcelable; +import android.util.MathUtils; + +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; + +/** + * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the + * actual correction scheme. + * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package + * name and category) to corrections that need to be applied to the brightness within that context. + * Corrections are currently done by the app that has the top activity of the focused stack, either + * by its package name, or (if its package name is not mapped to any correction) by its category. + * + * @hide + */ +@SystemApi +public final class BrightnessCorrection implements Parcelable { + + private static final int SCALE_AND_TRANSLATE_LOG = 1; + + private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log"; + + private BrightnessCorrectionImplementation mImplementation; + + // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't + // make this class abstract and use composition instead of inheritence. + private BrightnessCorrection(BrightnessCorrectionImplementation implementation) { + mImplementation = implementation; + } + + /** + * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be + * {@code exp(scale * log(brightness) + translate)}. + * + * @param scale + * How much to scale the log brightness. + * @param translate + * How much to translate the log brightness. + * + * @return A BrightnessCorrection that given {@code brightness}, corrects it to be + * {@code exp(scale * log(brightness) + translate)}. + * + * @throws IllegalArgumentException + * - scale or translate are NaN. + */ + @NonNull + public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) { + BrightnessCorrectionImplementation implementation = + new ScaleAndTranslateLog(scale, translate); + return new BrightnessCorrection(implementation); + } + + /** + * Applies the brightness correction to a given brightness. + * + * @param brightness + * The brightness. + * + * @return The corrected brightness. + */ + public float apply(float brightness) { + return mImplementation.apply(brightness); + } + + /** + * Returns a string representation. + * + * @return A string representation. + */ + public String toString() { + return mImplementation.toString(); + } + + public static final Creator<BrightnessCorrection> CREATOR = + new Creator<BrightnessCorrection>() { + public BrightnessCorrection createFromParcel(Parcel in) { + final int type = in.readInt(); + switch (type) { + case SCALE_AND_TRANSLATE_LOG: + return ScaleAndTranslateLog.readFromParcel(in); + } + return null; + } + + public BrightnessCorrection[] newArray(int size) { + return new BrightnessCorrection[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + mImplementation.writeToParcel(dest); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Writes the correction to an XML serializer. + * + * @param serializer + * The XML serializer. + * + * @hide + */ + public void saveToXml(XmlSerializer serializer) throws IOException { + mImplementation.saveToXml(serializer); + } + + /** + * Read a correction from an XML parser. + * + * @param parser + * The XML parser. + * + * @throws IOException + * The parser failed to read the XML file. + * @throws XmlPullParserException + * The parser failed to parse the XML file. + * + * @hide + */ + public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, + XmlPullParserException { + final int depth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, depth)) { + if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) { + return ScaleAndTranslateLog.loadFromXml(parser); + } + } + return null; + } + + private static float loadFloatFromXml(XmlPullParser parser, String attribute) { + final String string = parser.getAttributeValue(null, attribute); + try { + return Float.parseFloat(string); + } catch (NullPointerException | NumberFormatException e) { + return Float.NaN; + } + } + + private interface BrightnessCorrectionImplementation { + float apply(float brightness); + String toString(); + void writeToParcel(Parcel dest); + void saveToXml(XmlSerializer serializer) throws IOException; + // Package-private static methods: + // static BrightnessCorrection readFromParcel(Parcel in); + // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, + // XmlPullParserException; + } + + /** + * A BrightnessCorrection that given {@code brightness}, corrects it to be + * {@code exp(scale * log(brightness) + translate)}. + */ + private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation { + private static final float MIN_SCALE = 0.5f; + private static final float MAX_SCALE = 2.0f; + private static final float MIN_TRANSLATE = -0.6f; + private static final float MAX_TRANSLATE = 0.7f; + + private static final String ATTR_SCALE = "scale"; + private static final String ATTR_TRANSLATE = "translate"; + + private final float mScale; + private final float mTranslate; + + ScaleAndTranslateLog(float scale, float translate) { + if (Float.isNaN(scale) || Float.isNaN(translate)) { + throw new IllegalArgumentException("scale and translate must be numbers"); + } + mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE); + } + + @Override + public float apply(float brightness) { + return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate); + } + + @Override + public String toString() { + return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")"; + } + + @Override + public void writeToParcel(Parcel dest) { + dest.writeInt(SCALE_AND_TRANSLATE_LOG); + dest.writeFloat(mScale); + dest.writeFloat(mTranslate); + } + + @Override + public void saveToXml(XmlSerializer serializer) throws IOException { + serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG); + serializer.attribute(null, ATTR_SCALE, Float.toString(mScale)); + serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate)); + serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG); + } + + static BrightnessCorrection readFromParcel(Parcel in) { + float scale = in.readFloat(); + float translate = in.readFloat(); + return BrightnessCorrection.createScaleAndTranslateLog(scale, translate); + } + + static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException, + XmlPullParserException { + final float scale = loadFloatFromXml(parser, ATTR_SCALE); + final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE); + return BrightnessCorrection.createScaleAndTranslateLog(scale, translate); + } + } +} |
