Jason Monk | 5dbd4aa | 2016-02-07 13:13:39 -0500 | [diff] [blame] | 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 | |
Jason Monk | 5dbd4aa | 2016-02-07 13:13:39 -0500 | [diff] [blame] | 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; |
Jason Monk | 5dbd4aa | 2016-02-07 13:13:39 -0500 | [diff] [blame] | 22 | import android.provider.Settings.Secure; |
| 23 | import android.util.MathUtils; |
Jason Monk | 5dbd4aa | 2016-02-07 13:13:39 -0500 | [diff] [blame] | 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 | |
| 75 | public NightModeController(Context context) { |
| 76 | this(context, false); |
| 77 | } |
| 78 | |
| 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 | |
| 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 | |
| 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 | |
| 113 | public boolean isAuto() { |
| 114 | return mIsAuto; |
| 115 | } |
| 116 | |
| 117 | public void setAdjustTint(Boolean newValue) { |
| 118 | TunerService.get(mContext).setValue(NIGHT_MODE_ADJUST_TINT, ((Boolean) newValue) ? 1 : 0); |
| 119 | } |
| 120 | |
| 121 | public void addListener(Listener listener) { |
| 122 | mListeners.add(listener); |
| 123 | listener.onNightModeChanged(); |
| 124 | updateListening(); |
| 125 | } |
| 126 | |
| 127 | public void removeListener(Listener listener) { |
| 128 | mListeners.remove(listener); |
| 129 | updateListening(); |
| 130 | } |
| 131 | |
| 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 | |
| 143 | public boolean isEnabled() { |
| 144 | if (!mListening) { |
| 145 | updateNightMode(mContext.registerReceiver(null, |
| 146 | new IntentFilter(ACTION_TWILIGHT_CHANGED))); |
| 147 | } |
| 148 | return mIsNight; |
| 149 | } |
| 150 | |
| 151 | public String getCustomValues() { |
| 152 | return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_VALUES); |
| 153 | } |
| 154 | |
| 155 | public void setCustomValues(String values) { |
| 156 | TunerService.get(mContext).setValue(COLOR_MATRIX_CUSTOM_VALUES, values); |
| 157 | } |
| 158 | |
| 159 | @Override |
| 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 | |
| 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 | |
| 187 | private void updateNightMode(Intent intent) { |
Jason Monk | 46dbfb4 | 2016-02-25 14:59:20 -0500 | [diff] [blame] | 188 | mIsNight = intent != null && intent.getBooleanExtra(EXTRA_IS_NIGHT, false); |
| 189 | mAmount = intent != null ? intent.getFloatExtra(EXTRA_AMOUNT, 0) : 0; |
Jason Monk | 5dbd4aa | 2016-02-07 13:13:39 -0500 | [diff] [blame] | 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 { |
| 206 | void onNightModeChanged(); |
| 207 | void onTwilightAutoChanged(); |
| 208 | } |
| 209 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | } |