blob: 0b1911bb15fc88e1fa48910357c90c9be66bfd72 [file] [log] [blame]
Jason Monk5dbd4aa2016-02-07 13:13:39 -05001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
13 */
14
15package com.android.systemui.statusbar.policy;
16
17import libcore.util.Objects;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.opengl.Matrix;
24import android.provider.Settings;
25import android.provider.Settings.Secure;
26import android.util.MathUtils;
27
28import com.android.systemui.tuner.TunerService;
29
30import java.util.ArrayList;
31
32/**
33 * Listens for changes to twilight from the TwilightService.
34 *
35 * Also pushes the current matrix to accessibility based on the current twilight
36 * and various tuner settings.
37 */
38public class NightModeController implements TunerService.Tunable {
39
40 public static final String NIGHT_MODE_ADJUST_TINT = "tuner_night_mode_adjust_tint";
41 private static final String COLOR_MATRIX_CUSTOM_VALUES = "tuner_color_custom_values";
42
43 private static final String ACTION_TWILIGHT_CHANGED = "android.intent.action.TWILIGHT_CHANGED";
44
45 private static final String EXTRA_IS_NIGHT = "isNight";
46 private static final String EXTRA_AMOUNT = "amount";
47
48 // Night mode ~= 3400 K
49 private static final float[] NIGHT_VALUES = new float[] {
50 1, 0, 0, 0,
51 0, .754f, 0, 0,
52 0, 0, .516f, 0,
53 0, 0, 0, 1,
54 };
55 public static final float[] IDENTITY_MATRIX = new float[] {
56 1, 0, 0, 0,
57 0, 1, 0, 0,
58 0, 0, 1, 0,
59 0, 0, 0, 1,
60 };
61
62 private final ArrayList<Listener> mListeners = new ArrayList<>();
63
64 private final Context mContext;
65
66 // This is whether or not this is the main NightMode controller in SysUI that should be
67 // updating relevant color matrixes or if its in the tuner process getting current state
68 // for UI.
69 private final boolean mUpdateMatrix;
70
71 private float[] mCustomMatrix;
72 private boolean mListening;
73 private boolean mAdjustTint;
74
75 private boolean mIsNight;
76 private float mAmount;
77 private boolean mIsAuto;
78
79 public NightModeController(Context context) {
80 this(context, false);
81 }
82
83 public NightModeController(Context context, boolean updateMatrix) {
84 mContext = context;
85 mUpdateMatrix = updateMatrix;
86 TunerService.get(mContext).addTunable(this, NIGHT_MODE_ADJUST_TINT,
87 COLOR_MATRIX_CUSTOM_VALUES, Secure.TWILIGHT_MODE);
88 }
89
90 public void setNightMode(boolean isNight) {
91 if (mIsAuto) {
92 if (mIsNight != isNight) {
93 TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight
94 ? Secure.TWILIGHT_MODE_AUTO_OVERRIDE_ON
95 : Secure.TWILIGHT_MODE_AUTO_OVERRIDE_OFF);
96 } else {
97 TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE,
98 Secure.TWILIGHT_MODE_AUTO);
99 }
100 } else {
101 TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight
102 ? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF);
103 }
104 }
105
106 public void setAuto(boolean auto) {
107 mIsAuto = auto;
108 if (auto) {
109 TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_AUTO);
110 } else {
111 // Lock into the current state
112 TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, mIsNight
113 ? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF);
114 }
115 }
116
117 public boolean isAuto() {
118 return mIsAuto;
119 }
120
121 public void setAdjustTint(Boolean newValue) {
122 TunerService.get(mContext).setValue(NIGHT_MODE_ADJUST_TINT, ((Boolean) newValue) ? 1 : 0);
123 }
124
125 public void addListener(Listener listener) {
126 mListeners.add(listener);
127 listener.onNightModeChanged();
128 updateListening();
129 }
130
131 public void removeListener(Listener listener) {
132 mListeners.remove(listener);
133 updateListening();
134 }
135
136 private void updateListening() {
137 boolean shouldListen = mListeners.size() != 0 || (mUpdateMatrix && mAdjustTint);
138 if (shouldListen == mListening) return;
139 mListening = shouldListen;
140 if (mListening) {
141 mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_TWILIGHT_CHANGED));
142 } else {
143 mContext.unregisterReceiver(mReceiver);
144 }
145 }
146
147 public boolean isEnabled() {
148 if (!mListening) {
149 updateNightMode(mContext.registerReceiver(null,
150 new IntentFilter(ACTION_TWILIGHT_CHANGED)));
151 }
152 return mIsNight;
153 }
154
155 public String getCustomValues() {
156 return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_VALUES);
157 }
158
159 public void setCustomValues(String values) {
160 TunerService.get(mContext).setValue(COLOR_MATRIX_CUSTOM_VALUES, values);
161 }
162
163 @Override
164 public void onTuningChanged(String key, String newValue) {
165 if (COLOR_MATRIX_CUSTOM_VALUES.equals(key)) {
166 mCustomMatrix = newValue != null ? toValues(newValue) : null;
167 updateCurrentMatrix();
168 } else if (NIGHT_MODE_ADJUST_TINT.equals(key)) {
169 mAdjustTint = newValue == null || Integer.parseInt(newValue) != 0;
170 updateListening();
171 updateCurrentMatrix();
172 } else if (Secure.TWILIGHT_MODE.equals(key)) {
173 mIsAuto = newValue != null && Integer.parseInt(newValue) >= Secure.TWILIGHT_MODE_AUTO;
174 }
175 }
176
177 private void updateCurrentMatrix() {
178 if (!mUpdateMatrix) return;
179 if ((!mAdjustTint || mAmount == 0) && mCustomMatrix == null) {
180 TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null);
181 return;
182 }
183 float[] values = scaleValues(IDENTITY_MATRIX, NIGHT_VALUES, mAdjustTint ? mAmount : 0);
184 if (mCustomMatrix != null) {
185 values = multiply(values, mCustomMatrix);
186 }
187 TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
188 toString(values));
189 }
190
191 private void updateNightMode(Intent intent) {
192 mIsNight = intent.getBooleanExtra(EXTRA_IS_NIGHT, false);
193 mAmount = intent.getFloatExtra(EXTRA_AMOUNT, 0);
194 }
195
196 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
197 @Override
198 public void onReceive(Context context, Intent intent) {
199 if (ACTION_TWILIGHT_CHANGED.equals(intent.getAction())) {
200 updateNightMode(intent);
201 updateCurrentMatrix();
202 for (int i = 0; i < mListeners.size(); i++) {
203 mListeners.get(i).onNightModeChanged();
204 }
205 }
206 }
207 };
208
209 public interface Listener {
210 void onNightModeChanged();
211 void onTwilightAutoChanged();
212 }
213
214 private static float[] multiply(float[] matrix, float[] other) {
215 if (matrix == null) {
216 return other;
217 }
218 float[] result = new float[16];
219 Matrix.multiplyMM(result, 0, matrix, 0, other, 0);
220 return result;
221 }
222
223 private float[] scaleValues(float[] identityMatrix, float[] nightValues, float amount) {
224 float[] values = new float[identityMatrix.length];
225 for (int i = 0; i < values.length; i++) {
226 values[i] = MathUtils.lerp(identityMatrix[i], nightValues[i], amount);
227 }
228 return values;
229 }
230
231 public static String toString(float[] values) {
232 StringBuilder builder = new StringBuilder();
233 for (int i = 0; i < values.length; i++) {
234 if (builder.length() != 0) {
235 builder.append(',');
236 }
237 builder.append(values[i]);
238 }
239 return builder.toString();
240 }
241
242 public static float[] toValues(String customValues) {
243 String[] strValues = customValues.split(",");
244 float[] values = new float[strValues.length];
245 for (int i = 0; i < values.length; i++) {
246 values[i] = Float.parseFloat(strValues[i]);
247 }
248 return values;
249 }
250}