blob: 0b1911bb15fc88e1fa48910357c90c9be66bfd72 [file] [log] [blame]
/*
* Copyright (C) 2016 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 com.android.systemui.statusbar.policy;
import libcore.util.Objects;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.opengl.Matrix;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.util.MathUtils;
import com.android.systemui.tuner.TunerService;
import java.util.ArrayList;
/**
* Listens for changes to twilight from the TwilightService.
*
* Also pushes the current matrix to accessibility based on the current twilight
* and various tuner settings.
*/
public class NightModeController implements TunerService.Tunable {
public static final String NIGHT_MODE_ADJUST_TINT = "tuner_night_mode_adjust_tint";
private static final String COLOR_MATRIX_CUSTOM_VALUES = "tuner_color_custom_values";
private static final String ACTION_TWILIGHT_CHANGED = "android.intent.action.TWILIGHT_CHANGED";
private static final String EXTRA_IS_NIGHT = "isNight";
private static final String EXTRA_AMOUNT = "amount";
// Night mode ~= 3400 K
private static final float[] NIGHT_VALUES = new float[] {
1, 0, 0, 0,
0, .754f, 0, 0,
0, 0, .516f, 0,
0, 0, 0, 1,
};
public static final float[] IDENTITY_MATRIX = new float[] {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
private final ArrayList<Listener> mListeners = new ArrayList<>();
private final Context mContext;
// This is whether or not this is the main NightMode controller in SysUI that should be
// updating relevant color matrixes or if its in the tuner process getting current state
// for UI.
private final boolean mUpdateMatrix;
private float[] mCustomMatrix;
private boolean mListening;
private boolean mAdjustTint;
private boolean mIsNight;
private float mAmount;
private boolean mIsAuto;
public NightModeController(Context context) {
this(context, false);
}
public NightModeController(Context context, boolean updateMatrix) {
mContext = context;
mUpdateMatrix = updateMatrix;
TunerService.get(mContext).addTunable(this, NIGHT_MODE_ADJUST_TINT,
COLOR_MATRIX_CUSTOM_VALUES, Secure.TWILIGHT_MODE);
}
public void setNightMode(boolean isNight) {
if (mIsAuto) {
if (mIsNight != isNight) {
TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight
? Secure.TWILIGHT_MODE_AUTO_OVERRIDE_ON
: Secure.TWILIGHT_MODE_AUTO_OVERRIDE_OFF);
} else {
TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE,
Secure.TWILIGHT_MODE_AUTO);
}
} else {
TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight
? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF);
}
}
public void setAuto(boolean auto) {
mIsAuto = auto;
if (auto) {
TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_AUTO);
} else {
// Lock into the current state
TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, mIsNight
? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF);
}
}
public boolean isAuto() {
return mIsAuto;
}
public void setAdjustTint(Boolean newValue) {
TunerService.get(mContext).setValue(NIGHT_MODE_ADJUST_TINT, ((Boolean) newValue) ? 1 : 0);
}
public void addListener(Listener listener) {
mListeners.add(listener);
listener.onNightModeChanged();
updateListening();
}
public void removeListener(Listener listener) {
mListeners.remove(listener);
updateListening();
}
private void updateListening() {
boolean shouldListen = mListeners.size() != 0 || (mUpdateMatrix && mAdjustTint);
if (shouldListen == mListening) return;
mListening = shouldListen;
if (mListening) {
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_TWILIGHT_CHANGED));
} else {
mContext.unregisterReceiver(mReceiver);
}
}
public boolean isEnabled() {
if (!mListening) {
updateNightMode(mContext.registerReceiver(null,
new IntentFilter(ACTION_TWILIGHT_CHANGED)));
}
return mIsNight;
}
public String getCustomValues() {
return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_VALUES);
}
public void setCustomValues(String values) {
TunerService.get(mContext).setValue(COLOR_MATRIX_CUSTOM_VALUES, values);
}
@Override
public void onTuningChanged(String key, String newValue) {
if (COLOR_MATRIX_CUSTOM_VALUES.equals(key)) {
mCustomMatrix = newValue != null ? toValues(newValue) : null;
updateCurrentMatrix();
} else if (NIGHT_MODE_ADJUST_TINT.equals(key)) {
mAdjustTint = newValue == null || Integer.parseInt(newValue) != 0;
updateListening();
updateCurrentMatrix();
} else if (Secure.TWILIGHT_MODE.equals(key)) {
mIsAuto = newValue != null && Integer.parseInt(newValue) >= Secure.TWILIGHT_MODE_AUTO;
}
}
private void updateCurrentMatrix() {
if (!mUpdateMatrix) return;
if ((!mAdjustTint || mAmount == 0) && mCustomMatrix == null) {
TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null);
return;
}
float[] values = scaleValues(IDENTITY_MATRIX, NIGHT_VALUES, mAdjustTint ? mAmount : 0);
if (mCustomMatrix != null) {
values = multiply(values, mCustomMatrix);
}
TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX,
toString(values));
}
private void updateNightMode(Intent intent) {
mIsNight = intent.getBooleanExtra(EXTRA_IS_NIGHT, false);
mAmount = intent.getFloatExtra(EXTRA_AMOUNT, 0);
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_TWILIGHT_CHANGED.equals(intent.getAction())) {
updateNightMode(intent);
updateCurrentMatrix();
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onNightModeChanged();
}
}
}
};
public interface Listener {
void onNightModeChanged();
void onTwilightAutoChanged();
}
private static float[] multiply(float[] matrix, float[] other) {
if (matrix == null) {
return other;
}
float[] result = new float[16];
Matrix.multiplyMM(result, 0, matrix, 0, other, 0);
return result;
}
private float[] scaleValues(float[] identityMatrix, float[] nightValues, float amount) {
float[] values = new float[identityMatrix.length];
for (int i = 0; i < values.length; i++) {
values[i] = MathUtils.lerp(identityMatrix[i], nightValues[i], amount);
}
return values;
}
public static String toString(float[] values) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < values.length; i++) {
if (builder.length() != 0) {
builder.append(',');
}
builder.append(values[i]);
}
return builder.toString();
}
public static float[] toValues(String customValues) {
String[] strValues = customValues.split(",");
float[] values = new float[strValues.length];
for (int i = 0; i < values.length; i++) {
values[i] = Float.parseFloat(strValues[i]);
}
return values;
}
}