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