1/*
2 * Copyright (C) 2017 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
17package android.hardware.display;
18
19import android.annotation.Nullable;
20import android.annotation.SystemApi;
21import android.annotation.TestApi;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.util.Pair;
25
26import com.android.internal.util.Preconditions;
27
28import java.util.Arrays;
29import java.util.Objects;
30
31/** @hide */
32@SystemApi
33@TestApi
34public final class BrightnessConfiguration implements Parcelable {
35    private final float[] mLux;
36    private final float[] mNits;
37    private final String mDescription;
38
39    private BrightnessConfiguration(float[] lux, float[] nits, String description) {
40        mLux = lux;
41        mNits = nits;
42        mDescription = description;
43    }
44
45    /**
46     * Gets the base brightness as curve.
47     *
48     * The curve is returned as a pair of float arrays, the first representing all of the lux
49     * points of the brightness curve and the second representing all of the nits values of the
50     * brightness curve.
51     *
52     * @return the control points for the brightness curve.
53     */
54    public Pair<float[], float[]> getCurve() {
55        return Pair.create(Arrays.copyOf(mLux, mLux.length), Arrays.copyOf(mNits, mNits.length));
56    }
57
58    /**
59     * Returns description string.
60     * @hide
61     */
62    public String getDescription() {
63        return mDescription;
64    }
65
66    @Override
67    public void writeToParcel(Parcel dest, int flags) {
68        dest.writeFloatArray(mLux);
69        dest.writeFloatArray(mNits);
70        dest.writeString(mDescription);
71    }
72
73    @Override
74    public int describeContents() {
75        return 0;
76    }
77
78    @Override
79    public String toString() {
80        StringBuilder sb = new StringBuilder("BrightnessConfiguration{[");
81        final int size = mLux.length;
82        for (int i = 0; i < size; i++) {
83            if (i != 0) {
84                sb.append(", ");
85            }
86            sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
87        }
88        sb.append("], '");
89        if (mDescription != null) {
90            sb.append(mDescription);
91        }
92        sb.append("'}");
93        return sb.toString();
94    }
95
96    @Override
97    public int hashCode() {
98        int result = 1;
99        result = result * 31 + Arrays.hashCode(mLux);
100        result = result * 31 + Arrays.hashCode(mNits);
101        if (mDescription != null) {
102            result = result * 31 + mDescription.hashCode();
103        }
104        return result;
105    }
106
107    @Override
108    public boolean equals(Object o) {
109        if (o == this) {
110            return true;
111        }
112        if (!(o instanceof BrightnessConfiguration)) {
113            return false;
114        }
115        final BrightnessConfiguration other = (BrightnessConfiguration) o;
116        return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
117                && Objects.equals(mDescription, other.mDescription);
118    }
119
120    public static final Creator<BrightnessConfiguration> CREATOR =
121            new Creator<BrightnessConfiguration>() {
122        public BrightnessConfiguration createFromParcel(Parcel in) {
123            float[] lux = in.createFloatArray();
124            float[] nits = in.createFloatArray();
125            Builder builder = new Builder(lux, nits);
126            builder.setDescription(in.readString());
127            return builder.build();
128        }
129
130        public BrightnessConfiguration[] newArray(int size) {
131            return new BrightnessConfiguration[size];
132        }
133    };
134
135    /**
136     * A builder class for {@link BrightnessConfiguration}s.
137     */
138    public static class Builder {
139        private float[] mCurveLux;
140        private float[] mCurveNits;
141        private String mDescription;
142
143        /**
144         * STOPSHIP remove when app has stopped using this.
145         * @hide
146         */
147        public Builder() {
148        }
149
150        /**
151         * Constructs the builder with the control points for the brightness curve.
152         *
153         * Brightness curves must have strictly increasing ambient brightness values in lux and
154         * monotonically increasing display brightness values in nits. In addition, the initial
155         * control point must be 0 lux.
156         *
157         * @throws IllegalArgumentException if the initial control point is not at 0 lux.
158         * @throws IllegalArgumentException if the lux levels are not strictly increasing.
159         * @throws IllegalArgumentException if the nit levels are not monotonically increasing.
160         */
161        public Builder(float[] lux, float[] nits) {
162            setCurve(lux, nits);
163        }
164
165        /**
166         * Sets the control points for the brightness curve.
167         *
168         * Brightness curves must have strictly increasing ambient brightness values in lux and
169         * monotonically increasing display brightness values in nits. In addition, the initial
170         * control point must be 0 lux.
171         *
172         * @throws IllegalArgumentException if the initial control point is not at 0 lux.
173         * @throws IllegalArgumentException if the lux levels are not strictly increasing.
174         * @throws IllegalArgumentException if the nit levels are not monotonically increasing.
175         *
176         * STOPSHIP remove when app has stopped using this.
177         * @hide
178         */
179        public Builder setCurve(float[] lux, float[] nits) {
180            Preconditions.checkNotNull(lux);
181            Preconditions.checkNotNull(nits);
182            if (lux.length == 0 || nits.length == 0) {
183                throw new IllegalArgumentException("Lux and nits arrays must not be empty");
184            }
185            if (lux.length != nits.length) {
186                throw new IllegalArgumentException("Lux and nits arrays must be the same length");
187            }
188            if (lux[0] != 0) {
189                throw new IllegalArgumentException("Initial control point must be for 0 lux");
190            }
191            Preconditions.checkArrayElementsInRange(lux, 0, Float.MAX_VALUE, "lux");
192            Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
193            checkMonotonic(lux, true/*strictly increasing*/, "lux");
194            checkMonotonic(nits, false /*strictly increasing*/, "nits");
195            mCurveLux = lux;
196            mCurveNits = nits;
197            return this;
198        }
199
200        /**
201         * Set description of the brightness curve.
202         *
203         * @param description brief text describing the curve pushed. It maybe truncated
204         *                    and will not be displayed in the UI
205         */
206        public Builder setDescription(@Nullable String description) {
207            mDescription = description;
208            return this;
209        }
210
211        /**
212         * Builds the {@link BrightnessConfiguration}.
213         *
214         * A brightness curve <b>must</b> be set before calling this.
215         */
216        public BrightnessConfiguration build() {
217            if (mCurveLux == null || mCurveNits == null) {
218                throw new IllegalStateException("A curve must be set!");
219            }
220            return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
221        }
222
223        private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
224            if (vals.length <= 1) {
225                return;
226            }
227            float prev = vals[0];
228            for (int i = 1; i < vals.length; i++) {
229                if (prev > vals[i] || prev == vals[i] && strictlyIncreasing) {
230                    String condition = strictlyIncreasing ? "strictly increasing" : "monotonic";
231                    throw new IllegalArgumentException(name + " values must be " + condition);
232                }
233                prev = vals[i];
234            }
235        }
236    }
237}
238