aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/cm_current.txt53
-rw-r--r--cm/lib/main/java/org/cyanogenmod/platform/internal/display/AmbientLuxObserver.java254
-rw-r--r--cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java272
-rw-r--r--cm/lib/main/java/org/cyanogenmod/platform/internal/display/DisplayHardwareController.java435
-rw-r--r--cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayFeature.java158
-rw-r--r--cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java523
-rw-r--r--cm/lib/main/java/org/cyanogenmod/platform/internal/display/OutdoorModeController.java217
-rw-r--r--cm/res/AndroidManifest.xml7
-rw-r--r--cm/res/res/values/config.xml9
-rw-r--r--cm/res/res/values/strings.xml5
-rw-r--r--cm/res/res/values/symbols.xml5
-rw-r--r--sdk/src/java/cyanogenmod/app/CMContextConstants.java15
-rw-r--r--sdk/src/java/cyanogenmod/hardware/ILiveDisplayService.aidl50
-rw-r--r--sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.aidl19
-rw-r--r--sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java244
-rw-r--r--sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java415
-rw-r--r--sdk/src/java/cyanogenmod/providers/CMSettings.java34
-rw-r--r--system-api/cm_system-current.txt53
18 files changed, 2761 insertions, 7 deletions
diff --git a/api/cm_current.txt b/api/cm_current.txt
index ff84505..5992f3f 100644
--- a/api/cm_current.txt
+++ b/api/cm_current.txt
@@ -529,6 +529,53 @@ package cyanogenmod.hardware {
method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
}
+ public class LiveDisplayConfig implements android.os.Parcelable {
+ ctor public LiveDisplayConfig(java.util.BitSet, int, int, int, boolean, boolean, boolean, boolean);
+ method public int describeContents();
+ method public boolean getDefaultAutoContrast();
+ method public boolean getDefaultAutoOutdoorMode();
+ method public boolean getDefaultCABC();
+ method public boolean getDefaultColorEnhancement();
+ method public int getDefaultDayTemperature();
+ method public int getDefaultMode();
+ method public int getDefaultNightTemperature();
+ method public boolean hasFeature(int);
+ method public boolean isAvailable();
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
+ public class LiveDisplayManager {
+ method public float[] getColorAdjustment();
+ method public cyanogenmod.hardware.LiveDisplayConfig getConfig();
+ method public int getDayColorTemperature();
+ method public static synchronized cyanogenmod.hardware.LiveDisplayManager getInstance(android.content.Context);
+ method public int getMode();
+ method public int getNightColorTemperature();
+ method public boolean isAutoContrastEnabled();
+ method public boolean isAutomaticOutdoorModeEnabled();
+ method public boolean isCABCEnabled();
+ method public boolean isColorEnhancementEnabled();
+ method public boolean setAutoContrastEnabled(boolean);
+ method public boolean setAutomaticOutdoorModeEnabled(boolean);
+ method public boolean setCABCEnabled(boolean);
+ method public boolean setColorAdjustment(float[]);
+ method public boolean setColorEnhancementEnabled(boolean);
+ method public boolean setDayColorTemperature(int);
+ method public boolean setMode(int);
+ method public boolean setNightColorTemperature(int);
+ field public static final int FEATURE_AUTO_CONTRAST = 11; // 0xb
+ field public static final int FEATURE_CABC = 10; // 0xa
+ field public static final int FEATURE_COLOR_ADJUSTMENT = 13; // 0xd
+ field public static final int FEATURE_COLOR_ENHANCEMENT = 12; // 0xc
+ field public static final int FEATURE_DISPLAY_MODES = 15; // 0xf
+ field public static final int FEATURE_MANAGED_OUTDOOR_MODE = 14; // 0xe
+ field public static final int MODE_AUTO = 2; // 0x2
+ field public static final int MODE_DAY = 4; // 0x4
+ field public static final int MODE_NIGHT = 1; // 0x1
+ field public static final int MODE_OFF = 0; // 0x0
+ field public static final int MODE_OUTDOOR = 3; // 0x3
+ }
+
public abstract class ThermalListenerCallback extends cyanogenmod.hardware.IThermalListenerCallback.Stub {
ctor public ThermalListenerCallback();
}
@@ -613,6 +660,7 @@ package cyanogenmod.platform {
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
+ field public static final java.lang.String MANAGE_LIVEDISPLAY = "cyanogenmod.permission.MANAGE_LIVEDISPLAY";
field public static final java.lang.String MANAGE_PERSISTENT_STORAGE = "cyanogenmod.permission.MANAGE_PERSISTENT_STORAGE";
field public static final java.lang.String MODIFY_MSIM_PHONE_STATE = "cyanogenmod.permission.MODIFY_MSIM_PHONE_STATE";
field public static final java.lang.String MODIFY_NETWORK_SETTINGS = "cyanogenmod.permission.MODIFY_NETWORK_SETTINGS";
@@ -795,6 +843,7 @@ package cyanogenmod.providers {
public final class CMSettings {
ctor public CMSettings();
field public static final java.lang.String ACTION_DATA_USAGE = "cyanogenmod.settings.ACTION_DATA_USAGE";
+ field public static final java.lang.String ACTION_LIVEDISPLAY_SETTINGS = "cyanogenmod.settings.LIVEDISPLAY_SETTINGS";
field public static final java.lang.String AUTHORITY = "cmsettings";
}
@@ -868,10 +917,12 @@ package cyanogenmod.providers {
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DIALER_OPENCNAM_ACCOUNT_SID = "dialer_opencnam_account_sid";
field public static final java.lang.String DIALER_OPENCNAM_AUTH_TOKEN = "dialer_opencnam_auth_token";
+ field public static final java.lang.String DISPLAY_AUTO_CONTRAST = "display_auto_contrast";
field public static final java.lang.String DISPLAY_AUTO_OUTDOOR_MODE = "display_auto_outdoor_mode";
+ field public static final java.lang.String DISPLAY_CABC = "display_low_power";
field public static final java.lang.String DISPLAY_COLOR_ADJUSTMENT = "display_color_adjustment";
field public static final java.lang.String DISPLAY_COLOR_ENHANCE = "display_color_enhance";
- field public static final java.lang.String DISPLAY_LOW_POWER = "display_low_power";
+ field public static final deprecated java.lang.String DISPLAY_LOW_POWER = "display_low_power";
field public static final java.lang.String DISPLAY_TEMPERATURE_DAY = "display_temperature_day";
field public static final java.lang.String DISPLAY_TEMPERATURE_MODE = "display_temperature_mode";
field public static final java.lang.String DISPLAY_TEMPERATURE_NIGHT = "display_temperature_night";
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/AmbientLuxObserver.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/AmbientLuxObserver.java
new file mode 100644
index 0000000..32c7e70
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/AmbientLuxObserver.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.platform.internal.display;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+public class AmbientLuxObserver {
+
+ private static final String TAG = "AmbientLuxObserver";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Sensor mLightSensor;
+ private final SensorManager mSensorManager;
+
+ private final float mThresholdLux;
+ private final int mThresholdDuration;
+
+ private boolean mLightSensorEnabled = false;
+ private int mLightSensorRate;
+
+ private float mAmbientLux = 0.0f;
+
+ private static final int LOW = 0;
+ private static final int HIGH = 1;
+
+ private int mState = LOW;
+
+ private final AmbientLuxHandler mLuxHandler;
+
+ private TransitionListener mCallback;
+
+ private final TimedMovingAverageRingBuffer mRingBuffer;
+
+ public interface TransitionListener {
+ public void onTransition(int state, float ambientLux);
+ }
+
+ public AmbientLuxObserver(Context context, Looper looper,
+ float thresholdLux, int thresholdDuration) {
+ mLuxHandler = new AmbientLuxHandler(looper);
+ mThresholdLux = thresholdLux;
+ mThresholdDuration = thresholdDuration;
+ mRingBuffer = new TimedMovingAverageRingBuffer(thresholdDuration);
+
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
+ mLightSensorRate = context.getResources().getInteger(
+ com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
+ }
+
+ private class AmbientLuxHandler extends Handler {
+
+ private static final int MSG_UPDATE_LUX = 0;
+ private static final int MSG_TRANSITION = 1;
+
+ AmbientLuxHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ int direction = 0;
+ float lux = 0.0f;
+
+ synchronized (AmbientLuxObserver.this) {
+ switch (msg.what) {
+ case MSG_UPDATE_LUX:
+ lux = (Float) msg.obj;
+ mRingBuffer.add(lux);
+
+ // FALL THRU
+
+ case MSG_TRANSITION:
+ mAmbientLux = mRingBuffer.getAverage();
+
+ if (DEBUG) {
+ Log.d(TAG, "lux= " + lux + " mState=" + mState +
+ " mAmbientLux=" + mAmbientLux);
+ }
+
+ direction = mAmbientLux >= mThresholdLux ? HIGH : LOW;
+ if (mState != direction) {
+ mState = direction;
+ if (mCallback != null) {
+ mCallback.onTransition(mState, mAmbientLux);
+ }
+ }
+
+ // check again in case we didn't get any
+ // more readings because the sensor settled
+ if (mRingBuffer.size() > 1) {
+ sendEmptyMessageDelayed(MSG_TRANSITION, mThresholdDuration / 2);
+ }
+ break;
+ }
+ }
+ }
+
+ void clear() {
+ removeCallbacksAndMessages(null);
+ }
+ };
+
+ private final SensorEventListener mListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mLightSensorEnabled) {
+ Message.obtain(mLuxHandler, AmbientLuxHandler.MSG_UPDATE_LUX,
+ event.values[0]).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+ public synchronized int getState() {
+ return mState;
+ }
+
+ public synchronized void setTransitionListener(TransitionListener callback) {
+ mCallback = callback;
+ enableLightSensor(callback != null);
+ }
+
+ private void enableLightSensor(boolean enable) {
+ if (enable && !mLightSensorEnabled) {
+ mAmbientLux = 0.0f;
+ mState = LOW;
+ mLightSensorEnabled = true;
+ mRingBuffer.clear();
+ mSensorManager.registerListener(mListener, mLightSensor,
+ mLightSensorRate * 1000, mLuxHandler);
+ } else if (!enable && mLightSensorEnabled) {
+ mLightSensorEnabled = false;
+ mSensorManager.unregisterListener(mListener);
+ mLuxHandler.clear();
+ }
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println(" AmbientLuxObserver State:");
+ pw.println(" mLightSensorEnabled=" + mLightSensorEnabled);
+ pw.println(" mState=" + mState);
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mRingBuffer=" + mRingBuffer.toString());
+ }
+
+ /**
+ * Calculates a simple moving average based on a fixed
+ * duration sliding window. This is useful for dampening
+ * erratic sensors and rolling thru transitional periods
+ * smoothly.
+ */
+ private static class TimedMovingAverageRingBuffer {
+
+ private final LinkedList<Sample> mRing = new LinkedList<Sample>();
+
+ private final int mPeriod;
+
+ private float mTotal = 0.0f;
+
+ private static class Sample {
+ public final long mTimestamp;
+ public final float mValue;
+ public Sample (long timestamp, float value) {
+ mTimestamp = timestamp;
+ mValue = value;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + mValue + ", " + mTimestamp + ")";
+ }
+ }
+
+ public TimedMovingAverageRingBuffer(int period) {
+ mPeriod = period;
+ }
+
+ public synchronized void add(float sample) {
+ expire();
+ if (sample == 0.0f && mRing.size() == 0) {
+ return;
+ }
+ mRing.offer(new Sample(System.currentTimeMillis(), sample));
+ mTotal += sample;
+ }
+
+ public synchronized int size() {
+ return mRing.size();
+ }
+
+ public synchronized float getAverage() {
+ expire();
+ return mRing.size() == 0 ? 0.0f : (mTotal / mRing.size());
+ }
+
+ public synchronized void clear() {
+ mRing.clear();
+ mTotal = 0.0f;
+ }
+
+ private void expire() {
+ long now = System.currentTimeMillis();
+ while (mRing.size() > 1 &&
+ ((now - mRing.peek().mTimestamp) > mPeriod)) {
+ mTotal -= mRing.pop().mValue;
+ }
+ }
+
+ @Override
+ public synchronized String toString() {
+ expire();
+ StringBuilder sb = new StringBuilder();
+ for (Iterator<Sample> i = mRing.iterator(); i.hasNext();) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(i.next());
+ }
+ return "average=" + getAverage() + " length=" + mRing.size() +
+ " mRing=[" + sb.toString() + "]";
+ }
+ }
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java
new file mode 100644
index 0000000..9c4253e
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/ColorTemperatureController.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.platform.internal.display;
+
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_AUTO;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_DAY;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_NIGHT;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_OFF;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.text.format.DateUtils;
+import android.util.MathUtils;
+import android.util.Slog;
+
+import com.android.server.twilight.TwilightState;
+
+import java.io.PrintWriter;
+import java.util.BitSet;
+
+import cyanogenmod.providers.CMSettings;
+import cyanogenmod.util.ColorUtils;
+
+public class ColorTemperatureController extends LiveDisplayFeature {
+
+ private ValueAnimator mAnimator;
+ private DisplayHardwareController mDisplayHardware;
+
+ private boolean mUseTemperatureAdjustment;
+
+ private int mDefaultDayTemperature;
+ private int mDefaultNightTemperature;
+
+ private int mDayTemperature;
+ private int mNightTemperature;
+
+ private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 1;
+
+ private static final int OFF_TEMPERATURE = 6500;
+
+ private int mColorTemperature = OFF_TEMPERATURE;
+
+ public ColorTemperatureController(Context context, Handler handler,
+ DisplayHardwareController displayHardware) {
+ super(context, handler);
+ mDisplayHardware = displayHardware;
+ }
+
+ @Override
+ public boolean onStart() {
+ if (!mDisplayHardware.hasColorAdjustment()) {
+ return false;
+ }
+
+ mUseTemperatureAdjustment = true;
+
+ mDefaultDayTemperature = mContext.getResources().getInteger(
+ org.cyanogenmod.platform.internal.R.integer.config_dayColorTemperature);
+ mDefaultNightTemperature = mContext.getResources().getInteger(
+ org.cyanogenmod.platform.internal.R.integer.config_nightColorTemperature);
+
+ registerSettings(
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_TEMPERATURE_DAY),
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_TEMPERATURE_NIGHT));
+ return true;
+ }
+
+ void getCapabilities(final BitSet caps) {
+ if (mUseTemperatureAdjustment) {
+ caps.set(MODE_AUTO);
+ caps.set(MODE_DAY);
+ caps.set(MODE_NIGHT);
+ }
+ }
+
+ int getDefaultDayTemperature() {
+ return mDefaultDayTemperature;
+ }
+
+ int getDefaultNightTemperature() {
+ return mDefaultNightTemperature;
+ }
+
+ int getColorTemperature() {
+ return mColorTemperature;
+ }
+
+ int getDayColorTemperature() {
+ return getInt(CMSettings.System.DISPLAY_TEMPERATURE_DAY,
+ mDefaultDayTemperature);
+ }
+
+ void setDayColorTemperature(int temperature) {
+ putInt(CMSettings.System.DISPLAY_TEMPERATURE_DAY, temperature);
+ }
+
+ int getNightColorTemperature() {
+ return getInt(CMSettings.System.DISPLAY_TEMPERATURE_NIGHT,
+ mDefaultNightTemperature);
+ }
+
+ void setNightColorTemperature(int temperature) {
+ putInt(CMSettings.System.DISPLAY_TEMPERATURE_NIGHT, temperature);
+ }
+
+ @Override
+ public void onModeChanged(int mode) {
+ super.onModeChanged(mode);
+ updateColorTemperature();
+ }
+
+ @Override
+ public synchronized void onSettingsChanged(Uri uri) {
+ mDayTemperature = getDayColorTemperature();
+ mNightTemperature = getNightColorTemperature();
+ updateColorTemperature();
+ }
+
+ @Override
+ public void onTwilightUpdated(TwilightState twilight) {
+ super.onTwilightUpdated(twilight);
+ mHandler.post(mTransitionRunnable);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("ColorTemperatureController Configuration:");
+ pw.println(" mDayTemperature=" + mDayTemperature);
+ pw.println(" mNightTemperature=" + mNightTemperature);
+ pw.println();
+ pw.println(" ColorTemperatureController State:");
+ pw.println(" mColorTemperature=" + mColorTemperature);
+ if (getTwilight() != null) {
+ pw.println(" mTwilight=" + getTwilight().toString());
+ }
+ pw.println(" transitioning=" + mHandler.hasCallbacks(mTransitionRunnable));
+ }
+
+ private final Runnable mTransitionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (ColorTemperatureController.this) {
+ updateColorTemperature();
+
+ boolean transition = getMode() == MODE_AUTO &&
+ mColorTemperature != mDayTemperature &&
+ mColorTemperature != mNightTemperature;
+
+ if (transition) {
+ // fire again in a minute
+ mHandler.postDelayed(mTransitionRunnable, DateUtils.MINUTE_IN_MILLIS);
+ }
+ }
+ }
+ };
+
+ private void updateColorTemperature() {
+ mHandler.removeCallbacks(mTransitionRunnable);
+
+ int temperature = mDayTemperature;
+ int mode = getMode();
+
+ if (mode == MODE_OFF || isLowPowerMode()) {
+ temperature = OFF_TEMPERATURE;
+ } else if (mode == MODE_NIGHT) {
+ temperature = mNightTemperature;
+ } else if (mode == MODE_AUTO) {
+ temperature = getTwilightK();
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "updateColorTemperatureLocked mode=" + mode +
+ " temperature=" + temperature + " mColorTemperature=" + mColorTemperature);
+ }
+
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ mAnimator.removeAllUpdateListeners();
+ }
+ mAnimator = ValueAnimator.ofInt(mColorTemperature, temperature);
+ mAnimator.setDuration(Math.abs(mColorTemperature - temperature) / 2);
+ mAnimator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(final ValueAnimator animation) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ setDisplayTemperature((Integer)animation.getAnimatedValue());
+ }
+ });
+ }
+ });
+ mAnimator.start();
+ }
+
+
+ private synchronized void setDisplayTemperature(int temperature) {
+ mColorTemperature = temperature;
+
+ final float[] rgb = ColorUtils.temperatureToRGB(temperature);
+ mDisplayHardware.setAdditionalAdjustment(rgb);
+
+ if (DEBUG) {
+ Slog.d(TAG, "Adjust display temperature to " + temperature + "K");
+ }
+ }
+
+ /**
+ * Where is the sun anyway? This calculation determines day or night, and scales
+ * the value around sunset/sunrise for a smooth transition.
+ *
+ * @param now
+ * @param sunset
+ * @param sunrise
+ * @return float between 0 and 1
+ */
+ private static float adj(long now, long sunset, long sunrise) {
+ if (sunset < 0 || sunrise < 0
+ || now < sunset || now > sunrise) {
+ return 1.0f;
+ }
+
+ if (now < sunset + TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, 0.0f,
+ (float)(now - sunset) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ if (now > sunrise - TWILIGHT_ADJUSTMENT_TIME) {
+ return MathUtils.lerp(1.0f, 0.0f,
+ (float)(sunrise - now) / TWILIGHT_ADJUSTMENT_TIME);
+ }
+
+ return 0.0f;
+ }
+
+ /**
+ * Determine the color temperature we should use for the display based on
+ * the position of the sun.
+ *
+ * @return color temperature in Kelvin
+ */
+ private int getTwilightK() {
+ float adjustment = 1.0f;
+ final TwilightState twilight = getTwilight();
+
+ if (twilight != null) {
+ final long now = System.currentTimeMillis();
+ adjustment = adj(now, twilight.getYesterdaySunset(), twilight.getTodaySunrise()) *
+ adj(now, twilight.getTodaySunset(), twilight.getTomorrowSunrise());
+ }
+
+ return (int)MathUtils.lerp(mNightTemperature, mDayTemperature, adjustment);
+ }
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/DisplayHardwareController.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/DisplayHardwareController.java
new file mode 100644
index 0000000..0189ccd
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/DisplayHardwareController.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.platform.internal.display;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+
+import cyanogenmod.hardware.CMHardwareManager;
+import cyanogenmod.hardware.LiveDisplayManager;
+import cyanogenmod.providers.CMSettings;
+
+public class DisplayHardwareController extends LiveDisplayFeature {
+
+ private CMHardwareManager mHardware;
+
+ // hardware capabilities
+ private boolean mUseAutoContrast;
+ private boolean mUseColorAdjustment;
+ private boolean mUseColorEnhancement;
+ private boolean mUseCABC;
+
+ // default values
+ private boolean mDefaultAutoContrast;
+ private boolean mDefaultColorEnhancement;
+ private boolean mDefaultCABC;
+
+ // current values
+ private boolean mAutoContrast;
+ private boolean mColorEnhancement;
+ private boolean mCABC;
+
+ // color adjustment holders
+ private final float[] mColorAdjustment = new float[] { 1.0f, 1.0f, 1.0f };
+ private final float[] mAdditionalAdjustment = new float[] { 1.0f, 1.0f, 1.0f };
+ private final float[] mRGB = new float[] { 1.0f, 1.0f, 1.0f };
+
+ // settings uris
+ private static final Uri DISPLAY_AUTO_CONTRAST =
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_AUTO_CONTRAST);
+ private static final Uri DISPLAY_COLOR_ADJUSTMENT =
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_COLOR_ADJUSTMENT);
+ private static final Uri DISPLAY_COLOR_ENHANCE =
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_COLOR_ENHANCE);
+ private static final Uri DISPLAY_CABC =
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_CABC);
+
+ public DisplayHardwareController(Context context, Handler handler) {
+ super(context, handler);
+ }
+
+ @Override
+ public boolean onStart() {
+ final ArrayList<Uri> settings = new ArrayList<Uri>();
+
+ mHardware = CMHardwareManager.getInstance(mContext);
+ mUseCABC = mHardware
+ .isSupported(CMHardwareManager.FEATURE_ADAPTIVE_BACKLIGHT);
+ if (mUseCABC) {
+ mDefaultCABC = mContext.getResources().getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_defaultCABC);
+ mCABC = mHardware.get(CMHardwareManager.FEATURE_ADAPTIVE_BACKLIGHT);
+ settings.add(DISPLAY_CABC);
+ }
+
+ mUseColorEnhancement = mHardware
+ .isSupported(CMHardwareManager.FEATURE_COLOR_ENHANCEMENT);
+ if (mUseColorEnhancement) {
+ mDefaultColorEnhancement = mContext.getResources().getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_defaultColorEnhancement);
+ mColorEnhancement = mHardware.get(CMHardwareManager.FEATURE_COLOR_ENHANCEMENT);
+ settings.add(DISPLAY_COLOR_ENHANCE);
+ }
+
+ mUseAutoContrast = mHardware
+ .isSupported(CMHardwareManager.FEATURE_AUTO_CONTRAST);
+ if (mUseAutoContrast) {
+ mDefaultAutoContrast = mContext.getResources().getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_defaultAutoContrast);
+ mAutoContrast = mHardware.get(CMHardwareManager.FEATURE_AUTO_CONTRAST);
+ settings.add(DISPLAY_AUTO_CONTRAST);
+ }
+
+ mUseColorAdjustment = mHardware
+ .isSupported(CMHardwareManager.FEATURE_DISPLAY_COLOR_CALIBRATION);
+ if (mUseColorAdjustment) {
+ settings.add(DISPLAY_COLOR_ADJUSTMENT);
+ }
+
+ if (settings.size() == 0) {
+ return false;
+ }
+
+ registerSettings(settings.toArray(new Uri[settings.size()]));
+ return true;
+ }
+
+ @Override
+ void getCapabilities(final BitSet caps) {
+ if (mUseAutoContrast) {
+ caps.set(LiveDisplayManager.FEATURE_AUTO_CONTRAST);
+ }
+ if (mUseColorEnhancement) {
+ caps.set(LiveDisplayManager.FEATURE_COLOR_ENHANCEMENT);
+ }
+ if (mUseCABC) {
+ caps.set(LiveDisplayManager.FEATURE_CABC);
+ }
+ if (mUseColorAdjustment) {
+ caps.set(LiveDisplayManager.FEATURE_COLOR_ADJUSTMENT);
+ }
+ }
+
+ @Override
+ public synchronized void onSettingsChanged(Uri uri) {
+ if (uri == null || uri.equals(DISPLAY_CABC)) {
+ updateCABCMode();
+ }
+ if (uri == null || uri.equals(DISPLAY_AUTO_CONTRAST)) {
+ updateAutoContrast();
+ }
+ if (uri == null || uri.equals(DISPLAY_COLOR_ENHANCE)) {
+ updateColorEnhancement();
+ }
+ if (uri == null || uri.equals(DISPLAY_COLOR_ADJUSTMENT)) {
+ System.arraycopy(
+ parseColorAdjustment(getString(CMSettings.System.DISPLAY_COLOR_ADJUSTMENT)),
+ 0, mColorAdjustment, 0, 3);
+ updateColorAdjustment();
+ }
+ }
+
+ @Override
+ public synchronized void onLowPowerModeChanged(boolean lowPowerMode) {
+ updateCABCMode();
+ updateAutoContrast();
+ updateColorEnhancement();
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("DisplayHardwareController Configuration:");
+ pw.println(" mUseAutoContrast=" + mUseAutoContrast);
+ pw.println(" mUseColorAdjustment=" + mUseColorAdjustment);
+ pw.println(" mUseColorEnhancement=" + mUseColorEnhancement);
+ pw.println(" mUseCABC=" + mUseCABC);
+ pw.println();
+ pw.println(" DisplayHardwareController State:");
+ pw.println(" mAutoContrast=" + mAutoContrast);
+ pw.println(" mColorEnhancement=" + mColorEnhancement);
+ pw.println(" mCABC=" + mCABC);
+ pw.println(" mColorAdjustment=" + Arrays.toString(mColorAdjustment));
+ pw.println(" mAdditionalAdjustment=" + Arrays.toString(mAdditionalAdjustment));
+ pw.println(" mRGB=" + Arrays.toString(mRGB));
+ }
+
+ boolean hasColorAdjustment() {
+ return mUseColorAdjustment;
+ }
+
+ /**
+ * Additional adjustments provided by night mode
+ *
+ * @param adj
+ */
+ synchronized void setAdditionalAdjustment(float[] adj) {
+ if (DEBUG) {
+ Slog.d(TAG, "setAdditionalAdjustment: " + Arrays.toString(adj));
+ }
+ // Sanity check this so we don't mangle the display
+ if (adj != null && adj.length == 3 &&
+ !(adj[0] <= 0.0f && adj[1] <= 0.0f && adj[2] <= 0.0f)) {
+ for (int i = 0; i < 3; i++) {
+ if (adj[i] > 1.0f) {
+ adj[i] = 1.0f;
+ }
+ }
+ System.arraycopy(adj, 0, mAdditionalAdjustment, 0, 3);
+ updateColorAdjustment();
+ } else {
+ mAdditionalAdjustment[0] = 1.0f;
+ mAdditionalAdjustment[1] = 1.0f;
+ mAdditionalAdjustment[2] = 1.0f;
+ }
+
+ }
+
+ /**
+ * Automatic contrast optimization
+ */
+ private synchronized void updateAutoContrast() {
+ if (!mUseAutoContrast) {
+ return;
+ }
+
+ boolean value = getInt(CMSettings.System.DISPLAY_AUTO_CONTRAST,
+ (mDefaultAutoContrast ? 1 : 0)) == 1;
+
+ boolean enabled = !isLowPowerMode() && value;
+
+ if (enabled == mAutoContrast) {
+ return;
+ }
+
+ mHardware.set(CMHardwareManager.FEATURE_AUTO_CONTRAST, enabled);
+ mAutoContrast = enabled;
+ }
+
+ /**
+ * Color enhancement is optional
+ */
+ private synchronized void updateColorEnhancement() {
+ if (!mUseColorEnhancement) {
+ return;
+ }
+
+ boolean value = getInt(CMSettings.System.DISPLAY_COLOR_ENHANCE,
+ (mDefaultColorEnhancement ? 1 : 0)) == 1;
+
+ boolean enabled = !isLowPowerMode() && value;
+
+ if (enabled == mColorEnhancement) {
+ return;
+ }
+
+ mHardware.set(CMHardwareManager.FEATURE_COLOR_ENHANCEMENT, enabled);
+ mColorEnhancement = enabled;
+ }
+
+ /**
+ * Adaptive backlight / low power mode. Turn it off when under very bright light.
+ */
+ private synchronized void updateCABCMode() {
+ if (!mUseCABC) {
+ return;
+ }
+
+ boolean value = getInt(CMSettings.System.DISPLAY_CABC,
+ (mDefaultCABC ? 1 : 0)) == 1;
+
+ boolean enabled = !isLowPowerMode() && value;
+
+ if (enabled == mCABC) {
+ return;
+ }
+
+ mHardware.set(CMHardwareManager.FEATURE_ADAPTIVE_BACKLIGHT, value);
+ mCABC = value;
+ }
+
+ private synchronized void updateColorAdjustment() {
+ if (!mUseColorAdjustment) {
+ return;
+ }
+
+ final float[] rgb = new float[] { 1.0f, 1.0f, 1.0f };
+
+ if (!isLowPowerMode()) {
+
+ System.arraycopy(mAdditionalAdjustment, 0, rgb, 0, 3);
+ rgb[0] *= mColorAdjustment[0];
+ rgb[1] *= mColorAdjustment[1];
+ rgb[2] *= mColorAdjustment[2];
+ }
+
+ if (rgb[0] == mRGB[0] && rgb[1] == mRGB[1] && rgb[2] == mRGB[2]) {
+ // no changes
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "updateColorAdjustment: " + Arrays.toString(rgb));
+ }
+
+ int max = mHardware.getDisplayColorCalibrationMax();
+ mHardware.setDisplayColorCalibration(new int[] {
+ (int) Math.ceil(rgb[0] * max),
+ (int) Math.ceil(rgb[1] * max),
+ (int) Math.ceil(rgb[2] * max)
+ });
+ System.arraycopy(rgb, 0, mRGB, 0, 3);
+
+ screenRefresh();
+ }
+
+ /**
+ * Tell SurfaceFlinger to repaint the screen. This is called after updating
+ * hardware registers for display calibration to have an immediate effect.
+ */
+ private void screenRefresh() {
+ try {
+ final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
+ if (flinger != null) {
+ final Parcel data = Parcel.obtain();
+ data.writeInterfaceToken("android.ui.ISurfaceComposer");
+ flinger.transact(1004, data, null, 0);
+ data.recycle();
+ }
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to refresh screen", ex);
+ }
+ }
+
+ boolean getDefaultCABC() {
+ return mDefaultCABC;
+ }
+
+ boolean getDefaultAutoContrast() {
+ return mDefaultAutoContrast;
+ }
+
+ boolean getDefaultColorEnhancement() {
+ return mDefaultColorEnhancement;
+ }
+
+ boolean isAutoContrastEnabled() {
+ return mUseAutoContrast &&
+ getInt(CMSettings.System.DISPLAY_AUTO_CONTRAST,
+ (mDefaultAutoContrast ? 1 : 0)) == 1;
+ }
+
+ boolean setAutoContrastEnabled(boolean enabled) {
+ if (!mUseAutoContrast) {
+ return false;
+ }
+ putInt(CMSettings.System.DISPLAY_AUTO_CONTRAST, enabled ? 1 : 0);
+ return true;
+ }
+
+ boolean isCABCEnabled() {
+ return mUseCABC &&
+ getInt(CMSettings.System.DISPLAY_CABC,
+ (mDefaultCABC ? 1 : 0)) == 1;
+ }
+
+ boolean setCABCEnabled(boolean enabled) {
+ if (!mUseCABC) {
+ return false;
+ }
+ putInt(CMSettings.System.DISPLAY_CABC, enabled ? 1 : 0);
+ return true;
+ }
+
+ boolean isColorEnhancementEnabled() {
+ return mUseColorEnhancement &&
+ getInt(CMSettings.System.DISPLAY_COLOR_ENHANCE,
+ (mDefaultColorEnhancement ? 1 : 0)) == 1;
+ }
+
+ boolean setColorEnhancementEnabled(boolean enabled) {
+ if (!mUseColorEnhancement) {
+ return false;
+ }
+ putInt(CMSettings.System.DISPLAY_COLOR_ENHANCE, enabled ? 1 : 0);
+ return true;
+ }
+
+
+ float[] getColorAdjustment() {
+ if (!mUseColorAdjustment) {
+ return new float[] { 1.0f, 1.0f, 1.0f };
+ }
+ return parseColorAdjustment(getString(CMSettings.System.DISPLAY_COLOR_ADJUSTMENT));
+ }
+
+ boolean setColorAdjustment(float[] adj) {
+ // sanity check
+ if (!mUseColorAdjustment || adj.length != 3 ||
+ adj[0] < 0 || adj[0] > 1.0f ||
+ adj[1] < 0 || adj[1] > 1.0f ||
+ adj[2] < 0 || adj[2] > 1.0f) {
+ return false;
+ }
+ StringBuilder sb = new StringBuilder();
+ sb.append(adj[0]).append(" ").append(adj[1]).append(" ").append(adj[2]);
+
+ putString(CMSettings.System.DISPLAY_COLOR_ADJUSTMENT, sb.toString());
+ return true;
+ }
+
+ /**
+ * Parse and sanity check an RGB triplet from a string.
+ */
+ private float[] parseColorAdjustment(String rgbString) {
+ String[] adj = rgbString == null ? null : rgbString.split(" ");
+ float[] parsed = new float[3];
+
+ if (adj == null || adj.length != 3) {
+ adj = new String[] { "1.0", "1.0", "1.0" };
+ }
+
+ try {
+ parsed[0] = Float.parseFloat(adj[0]);
+ parsed[1] = Float.parseFloat(adj[1]);
+ parsed[2] = Float.parseFloat(adj[2]);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, e.getMessage(), e);
+ parsed[0] = 1.0f;
+ parsed[1] = 1.0f;
+ parsed[2] = 1.0f;
+ }
+
+ // sanity check
+ for (int i = 0; i < parsed.length; i++) {
+ if (parsed[i] <= 0.0f || parsed[i] > 1.0f) {
+ parsed[i] = 1.0f;
+ }
+ }
+ return parsed;
+ }
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayFeature.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayFeature.java
new file mode 100644
index 0000000..5b1e33c
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayFeature.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.platform.internal.display;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.server.pm.UserContentObserver;
+import com.android.server.twilight.TwilightState;
+
+import java.io.PrintWriter;
+import java.util.BitSet;
+
+import cyanogenmod.providers.CMSettings;
+
+public abstract class LiveDisplayFeature {
+
+ protected static final String TAG = "LiveDisplay";
+ protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ protected final Context mContext;
+ protected final Handler mHandler;
+
+ private TwilightState mTwilight;
+ private boolean mLowPowerMode = false;
+ private boolean mScreenOn = false;
+ private int mMode = 0;
+
+ private final SettingsObserver mSettingsObserver;
+
+ public LiveDisplayFeature(Context context, Handler handler) {
+ mContext = context;
+ mHandler = handler;
+ mSettingsObserver = new SettingsObserver(handler);
+ }
+
+ public abstract boolean onStart();
+
+ public abstract void onSettingsChanged(Uri uri);
+
+ public void onModeChanged(int mode) {
+ mMode = mode;
+ }
+
+ public void onDisplayStateChanged(boolean screenOn) {
+ mScreenOn = screenOn;
+ }
+
+ public void onLowPowerModeChanged(boolean lowPowerMode) {
+ mLowPowerMode = lowPowerMode;
+ }
+
+ public void onTwilightUpdated(TwilightState twilight) {
+ mTwilight = twilight;
+ }
+
+ public void onDestroy() {
+ mSettingsObserver.unregister();
+ }
+
+ public abstract void dump(PrintWriter pw);
+
+ abstract void getCapabilities(final BitSet caps);
+
+ protected final void registerSettings(Uri... settings) {
+ mSettingsObserver.register(settings);
+ onSettingsChanged(null);
+ }
+
+ protected final int getInt(String setting, int defaultValue) {
+ return CMSettings.System.getIntForUser(mContext.getContentResolver(),
+ setting, defaultValue, UserHandle.USER_CURRENT);
+ }
+
+ protected final void putInt(String setting, int value) {
+ CMSettings.System.putIntForUser(mContext.getContentResolver(),
+ setting, value, UserHandle.USER_CURRENT);
+ }
+
+ protected final String getString(String setting) {
+ return CMSettings.System.getStringForUser(mContext.getContentResolver(),
+ setting, UserHandle.USER_CURRENT);
+ }
+
+ protected final void putString(String setting, String value) {
+ CMSettings.System.putStringForUser(mContext.getContentResolver(),
+ setting, value, UserHandle.USER_CURRENT);
+ }
+
+ protected final boolean isLowPowerMode() {
+ return mLowPowerMode;
+ }
+
+ protected final int getMode() {
+ return mMode;
+ }
+
+ protected final boolean isScreenOn() {
+ return mScreenOn;
+ }
+
+ protected final TwilightState getTwilight() {
+ return mTwilight;
+ }
+
+ protected final boolean isNight() {
+ return mTwilight != null && mTwilight.isNight();
+ }
+
+ final class SettingsObserver extends UserContentObserver {
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register(Uri... uris) {
+ final ContentResolver cr = mContext.getContentResolver();
+ for (Uri uri : uris) {
+ cr.registerContentObserver(uri, false, this, UserHandle.USER_ALL);
+ }
+
+ observe();
+ }
+
+ public void unregister() {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ unobserve();
+ }
+
+ @Override
+ protected void update() {
+ onSettingsChanged(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ onSettingsChanged(uri);
+ }
+ }
+
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java
new file mode 100644
index 0000000..68e01bb
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/LiveDisplayService.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.platform.internal.display;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManagerInternal;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.Display;
+
+import com.android.server.LocalServices;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+import com.android.server.pm.UserContentObserver;
+import com.android.server.twilight.TwilightListener;
+import com.android.server.twilight.TwilightManager;
+import com.android.server.twilight.TwilightState;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Iterator;
+import java.util.List;
+
+import cyanogenmod.app.CMContextConstants;
+import cyanogenmod.hardware.ILiveDisplayService;
+import cyanogenmod.hardware.LiveDisplayConfig;
+import cyanogenmod.providers.CMSettings;
+
+import static cyanogenmod.hardware.LiveDisplayManager.*;
+
+/**
+ * LiveDisplay is an advanced set of features for improving
+ * display quality under various ambient conditions.
+ *
+ * The service is constructed with a set of LiveDisplayFeatures
+ * which provide capabilities such as outdoor mode, night mode,
+ * and calibration. It interacts with CMHardwareService to relay
+ * changes down to the lower layers.
+ */
+public class LiveDisplayService extends SystemService {
+
+ private static final String TAG = "LiveDisplay";
+
+ private static final int MSG_MODE_CHANGED = 1;
+ private static final int MSG_DISPLAY_CHANGED = 2;
+ private static final int MSG_LOW_POWER_MODE_CHANGED = 3;
+ private static final int MSG_TWILIGHT_UPDATE = 4;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final ServiceThread mHandlerThread;
+
+ private DisplayManager mDisplayManager;
+ private ModeObserver mModeObserver;
+ private TwilightManager mTwilightManager;
+
+ private boolean mInitialized = false;
+ private boolean mAwaitingNudge = true;
+ private boolean mSunset = false;
+
+ private boolean mLowPowerMode;
+ private int mDisplayState = -1;
+
+ private final List<LiveDisplayFeature> mFeatures = new ArrayList<LiveDisplayFeature>();
+
+ private ColorTemperatureController mCTC;
+ private DisplayHardwareController mDHC;
+ private OutdoorModeController mOMC;
+
+ private LiveDisplayConfig mConfig;
+
+ public LiveDisplayService(Context context) {
+ super(context);
+
+ mContext = context;
+
+ // We want a slightly higher priority thread to handle these requests
+ mHandlerThread = new ServiceThread(TAG,
+ Process.THREAD_PRIORITY_DISPLAY + 1, false /*allowIo*/);
+ mHandlerThread.start();
+ mHandler = new LiveDisplayHandler(mHandlerThread.getLooper());
+ }
+
+ @Override
+ public void onStart() {
+ if (mContext.getPackageManager().hasSystemFeature(
+ CMContextConstants.Features.LIVEDISPLAY)) {
+ publishBinderService(CMContextConstants.CM_LIVEDISPLAY_SERVICE, mBinder);
+ } else {
+ Log.wtf(TAG, "CM LiveDisplay service started by system server but feature xml not" +
+ " declared. Not publishing binder service!");
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+
+ mAwaitingNudge = getSunsetCounter() < 1;
+
+ mDHC = new DisplayHardwareController(mContext, mHandler);
+ mFeatures.add(mDHC);
+
+ mCTC = new ColorTemperatureController(mContext, mHandler, mDHC);
+ mFeatures.add(mCTC);
+
+ mOMC = new OutdoorModeController(mContext, mHandler);
+ mFeatures.add(mOMC);
+
+ // Call onStart of each feature and get it's capabilities
+ final BitSet capabilities = new BitSet();
+ for (Iterator<LiveDisplayFeature> it = mFeatures.iterator(); it.hasNext();) {
+ final LiveDisplayFeature feature = it.next();
+ if (feature.onStart()) {
+ feature.getCapabilities(capabilities);
+ } else {
+ it.remove();
+ }
+ }
+
+ int defaultMode = mContext.getResources().getInteger(
+ org.cyanogenmod.platform.internal.R.integer.config_defaultLiveDisplayMode);
+
+ mConfig = new LiveDisplayConfig(capabilities, defaultMode,
+ mCTC.getDefaultDayTemperature(), mCTC.getDefaultNightTemperature(),
+ mOMC.getDefaultAutoOutdoorMode(), mDHC.getDefaultAutoContrast(),
+ mDHC.getDefaultCABC(), mDHC.getDefaultColorEnhancement());
+
+ mDisplayManager = (DisplayManager) getContext().getSystemService(
+ Context.DISPLAY_SERVICE);
+ mDisplayManager.registerDisplayListener(mDisplayListener, null);
+
+ PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
+ pmi.registerLowPowerModeObserver(mLowPowerModeListener);
+
+ mTwilightManager = LocalServices.getService(TwilightManager.class);
+ mTwilightManager.registerListener(mTwilightListener, mHandler);
+ updateTwilight();
+
+ updateDisplayState(mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState());
+
+ mModeObserver = new ModeObserver(mHandler);
+ mModeObserver.update();
+
+ mInitialized = true;
+ }
+ }
+
+ private final IBinder mBinder = new ILiveDisplayService.Stub() {
+
+ @Override
+ public LiveDisplayConfig getConfig() {
+ return mConfig;
+ }
+
+ @Override
+ public int getMode() {
+ return mModeObserver.getMode();
+ }
+
+ @Override
+ public boolean setMode(int mode) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ if (mConfig.hasFeature(mode) && mode >= MODE_FIRST && mode <= MODE_LAST) {
+ putInt(CMSettings.System.DISPLAY_TEMPERATURE_MODE, mode);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public float[] getColorAdjustment() {
+ return mDHC.getColorAdjustment();
+ }
+
+ @Override
+ public boolean setColorAdjustment(float[] adj) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ return mDHC.setColorAdjustment(adj);
+ }
+
+ @Override
+ public boolean isAutoContrastEnabled() {
+ return mDHC.isAutoContrastEnabled();
+ }
+
+ @Override
+ public boolean setAutoContrastEnabled(boolean enabled) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ return mDHC.setAutoContrastEnabled(enabled);
+ }
+
+ @Override
+ public boolean isCABCEnabled() {
+ return mDHC.isCABCEnabled();
+ }
+
+ @Override
+ public boolean setCABCEnabled(boolean enabled) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ return mDHC.setCABCEnabled(enabled);
+ }
+
+ @Override
+ public boolean isColorEnhancementEnabled() {
+ return mDHC.isColorEnhancementEnabled();
+ }
+
+ @Override
+ public boolean setColorEnhancementEnabled(boolean enabled) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ return mDHC.setColorEnhancementEnabled(enabled);
+ }
+
+ @Override
+ public boolean isAutomaticOutdoorModeEnabled() {
+ return mOMC.isAutomaticOutdoorModeEnabled();
+ }
+
+ @Override
+ public boolean setAutomaticOutdoorModeEnabled(boolean enabled) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ return mOMC.setAutomaticOutdoorModeEnabled(enabled);
+ }
+
+ @Override
+ public int getDayColorTemperature() {
+ return mCTC.getDayColorTemperature();
+ }
+
+ @Override
+ public boolean setDayColorTemperature(int temperature) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ mCTC.setDayColorTemperature(temperature);
+ return true;
+ }
+
+ @Override
+ public int getNightColorTemperature() {
+ return mCTC.getNightColorTemperature();
+ }
+
+ @Override
+ public boolean setNightColorTemperature(int temperature) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.MANAGE_LIVEDISPLAY, null);
+ mCTC.setNightColorTemperature(temperature);
+ return true;
+ }
+
+ @Override
+ public int getColorTemperature() {
+ return mCTC.getColorTemperature();
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println();
+ pw.println("LiveDisplay Service State:");
+ pw.println(" mMode=" + mModeObserver.getMode());
+ pw.println(" mDisplayState=" + mDisplayState);
+ pw.println(" mAwaitingNudge=" + mAwaitingNudge);
+ pw.println(" mConfig=" + mConfig.toString());
+
+ for (int i = 0; i < mFeatures.size(); i++) {
+ mFeatures.get(i).dump(pw);
+ }
+ }
+ };
+
+ // Listener for screen on/off events
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ mHandler.obtainMessage(MSG_DISPLAY_CHANGED,
+ mDisplayManager.getDisplay(displayId).getState(), 0).sendToTarget();
+ }
+ }
+ };
+
+
+ // Display postprocessing can have power impact.
+ private PowerManagerInternal.LowPowerModeListener mLowPowerModeListener =
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public void onLowPowerModeChanged(boolean lowPowerMode) {
+ if (lowPowerMode != mLowPowerMode) {
+ mLowPowerMode = lowPowerMode;
+ mHandler.obtainMessage(MSG_LOW_POWER_MODE_CHANGED,
+ (lowPowerMode ? 1 : 0), 0).sendToTarget();
+ }
+ }
+ };
+
+ // Watch for mode changes
+ private final class ModeObserver extends UserContentObserver {
+
+ private final Uri MODE_SETTING =
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_TEMPERATURE_MODE);
+
+ ModeObserver(Handler handler) {
+ super(handler);
+
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(MODE_SETTING, false, this, UserHandle.USER_ALL);
+
+ observe();
+ }
+
+ @Override
+ protected void update() {
+ mHandler.obtainMessage(MSG_MODE_CHANGED, getMode(), 0).sendToTarget();
+ }
+
+ int getMode() {
+ return getInt(CMSettings.System.DISPLAY_TEMPERATURE_MODE,
+ mConfig.getDefaultMode());
+ }
+ }
+
+ // Night watchman
+ private final TwilightListener mTwilightListener = new TwilightListener() {
+ @Override
+ public void onTwilightStateChanged() {
+ mHandler.obtainMessage(MSG_TWILIGHT_UPDATE,
+ mTwilightManager.getCurrentState()).sendToTarget();
+ }
+ };
+
+ private int getSunsetCounter() {
+ // Counter used to determine when we should tell the user about this feature.
+ // If it's not used after 3 sunsets, we'll show the hint once.
+ return CMSettings.System.getIntForUser(mContext.getContentResolver(),
+ CMSettings.System.LIVE_DISPLAY_HINTED,
+ -3,
+ UserHandle.USER_CURRENT);
+ }
+
+
+ private void updateSunsetCounter(int count) {
+ CMSettings.System.putIntForUser(mContext.getContentResolver(),
+ CMSettings.System.LIVE_DISPLAY_HINTED,
+ count,
+ UserHandle.USER_CURRENT);
+ mAwaitingNudge = count > 0;
+ }
+
+ private void stopNudgingMe() {
+ if (mAwaitingNudge) {
+ updateSunsetCounter(1);
+ }
+ }
+
+ /**
+ * Show a friendly notification to the user about the potential benefits of decreasing
+ * blue light at night. Do this only once if the feature has not been used after
+ * three sunsets. It would be great to enable this by default, but we don't want
+ * the change of screen color to be considered a "bug" by a user who doesn't
+ * understand what's happening.
+ *
+ * @param state
+ */
+ private void nudge() {
+ final TwilightState twilight = mTwilightManager.getCurrentState();
+ if (!mAwaitingNudge || twilight == null) {
+ return;
+ }
+
+ int counter = getSunsetCounter();
+
+ // check if we should send the hint only once after sunset
+ boolean transition = twilight.isNight() && !mSunset;
+ mSunset = twilight.isNight();
+ if (!transition) {
+ return;
+ }
+
+ if (counter <= 0) {
+ counter++;
+ updateSunsetCounter(counter);
+ }
+ if (counter == 0) {
+ //show the notification and don't come back here
+ final Intent intent = new Intent(CMSettings.ACTION_LIVEDISPLAY_SETTINGS);
+ PendingIntent result = PendingIntent.getActivity(
+ mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentTitle(mContext.getResources().getString(
+ org.cyanogenmod.platform.internal.R.string.live_display_title))
+ .setContentText(mContext.getResources().getString(
+ org.cyanogenmod.platform.internal.R.string.live_display_hint))
+ .setSmallIcon(org.cyanogenmod.platform.internal.R.drawable.ic_livedisplay_notif)
+ .setStyle(new Notification.BigTextStyle().bigText(mContext.getResources()
+ .getString(
+ org.cyanogenmod.platform.internal.R.string.live_display_hint)))
+ .setContentIntent(result)
+ .setAutoCancel(true);
+
+ NotificationManager nm =
+ (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notifyAsUser(null, 1, builder.build(), UserHandle.CURRENT);
+
+ updateSunsetCounter(1);
+ }
+ }
+
+ private int getInt(String setting, int defValue) {
+ return CMSettings.System.getIntForUser(mContext.getContentResolver(),
+ setting, defValue, UserHandle.USER_CURRENT);
+ }
+
+ private void putInt(String setting, int value) {
+ CMSettings.System.putIntForUser(mContext.getContentResolver(),
+ setting, value, UserHandle.USER_CURRENT);
+ }
+
+ private synchronized void updateTwilight() {
+ final TwilightState twilight = mTwilightManager.getCurrentState();
+ for (int i = 0; i < mFeatures.size(); i++) {
+ mFeatures.get(i).onTwilightUpdated(twilight);
+ }
+ }
+
+ private synchronized void updateDisplayState(int displayState) {
+ if (mDisplayState != displayState) {
+ mDisplayState = displayState;
+
+ for (int i = 0; i < mFeatures.size(); i++) {
+ mFeatures.get(i).onDisplayStateChanged(displayState == Display.STATE_ON);
+ }
+ }
+ }
+
+ private synchronized void updateMode(int mode) {
+ for (int i = 0; i < mFeatures.size(); i++) {
+ mFeatures.get(i).onModeChanged(mode);
+ }
+ }
+
+ private synchronized void updateLowPowerMode(boolean lowPowerMode) {
+ if (mLowPowerMode != lowPowerMode) {
+ mLowPowerMode = lowPowerMode;
+
+ for (int i = 0; i < mFeatures.size(); i++) {
+ mFeatures.get(i).onLowPowerModeChanged(mLowPowerMode);
+ }
+ }
+ }
+
+ private final class LiveDisplayHandler extends Handler {
+ public LiveDisplayHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (!mInitialized) {
+ return;
+ }
+
+ switch (msg.what) {
+ case MSG_DISPLAY_CHANGED:
+ updateDisplayState(msg.arg1);
+ break;
+ case MSG_LOW_POWER_MODE_CHANGED:
+ updateLowPowerMode(msg.arg1 == 1);
+ break;
+ case MSG_TWILIGHT_UPDATE:
+ updateTwilight();
+ nudge();
+ break;
+ case MSG_MODE_CHANGED:
+ stopNudgingMe();
+ updateMode(msg.arg1);
+ break;
+ }
+ }
+ }
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/display/OutdoorModeController.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/OutdoorModeController.java
new file mode 100644
index 0000000..f20979e
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/display/OutdoorModeController.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 org.cyanogenmod.platform.internal.display;
+
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_AUTO;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_DAY;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_OUTDOOR;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+
+import com.android.server.twilight.TwilightState;
+
+import java.io.PrintWriter;
+import java.util.BitSet;
+
+import cyanogenmod.hardware.CMHardwareManager;
+import cyanogenmod.hardware.LiveDisplayManager;
+import cyanogenmod.providers.CMSettings;
+
+public class OutdoorModeController extends LiveDisplayFeature {
+
+ private CMHardwareManager mHardware;
+ private AmbientLuxObserver mLuxObserver;
+
+ // hardware capabilities
+ private boolean mUseOutdoorMode;
+ private boolean mSelfManaged;
+
+ // default values
+ private int mDefaultOutdoorLux;
+ private boolean mDefaultAutoOutdoorMode;
+
+ // current values
+ private boolean mAutoOutdoorMode;
+
+ // internal state
+ private boolean mIsOutdoor;
+ private boolean mIsSensorEnabled;
+
+ // sliding window for sensor event smoothing
+ private static final int SENSOR_WINDOW_MS = 3000;
+
+ public OutdoorModeController(Context context, Handler handler) {
+ super(context, handler);
+ }
+
+ @Override
+ public boolean onStart() {
+ mHardware = CMHardwareManager.getInstance(mContext);
+ if (!mHardware.isSupported(CMHardwareManager.FEATURE_SUNLIGHT_ENHANCEMENT)) {
+ return false;
+ }
+
+ mUseOutdoorMode = true;
+
+ mDefaultOutdoorLux = mContext.getResources().getInteger(
+ org.cyanogenmod.platform.internal.R.integer.config_outdoorAmbientLux);
+ mDefaultAutoOutdoorMode = mContext.getResources().getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_defaultAutoOutdoorMode);
+
+ mSelfManaged = mHardware.isSunlightEnhancementSelfManaged();
+ if (!mSelfManaged) {
+ mLuxObserver = new AmbientLuxObserver(mContext, mHandler.getLooper(),
+ mDefaultOutdoorLux, SENSOR_WINDOW_MS);
+ }
+
+ registerSettings(
+ CMSettings.System.getUriFor(CMSettings.System.DISPLAY_AUTO_OUTDOOR_MODE));
+ return true;
+ }
+
+ @Override
+ void getCapabilities(final BitSet caps) {
+ if (mUseOutdoorMode) {
+ caps.set(LiveDisplayManager.MODE_OUTDOOR);
+ if (mSelfManaged) {
+ caps.set(LiveDisplayManager.FEATURE_MANAGED_OUTDOOR_MODE);
+ }
+ }
+ }
+
+ @Override
+ public void onModeChanged(int mode) {
+ super.onModeChanged(mode);
+ updateOutdoorMode();
+ }
+
+ @Override
+ public void onDisplayStateChanged(boolean screenOn) {
+ super.onDisplayStateChanged(screenOn);
+ if (mSelfManaged) {
+ return;
+ }
+ updateOutdoorMode();
+ }
+
+ @Override
+ public void onLowPowerModeChanged(boolean lowPowerMode) {
+ super.onLowPowerModeChanged(lowPowerMode);
+ updateOutdoorMode();
+ }
+
+ @Override
+ public synchronized void onSettingsChanged(Uri uri) {
+ mAutoOutdoorMode = getInt(CMSettings.System.DISPLAY_AUTO_OUTDOOR_MODE,
+ (mDefaultAutoOutdoorMode ? 1 : 0)) == 1;
+ updateOutdoorMode();
+ }
+
+ @Override
+ public void onTwilightUpdated(TwilightState twilight) {
+ super.onTwilightUpdated(twilight);
+ updateOutdoorMode();
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println();
+ pw.println("OutdoorModeController Configuration:");
+ pw.println(" mSelfManaged=" + mSelfManaged);
+ if (!mSelfManaged) {
+ pw.println(" mDefaultOutdoorLux=" + mDefaultOutdoorLux);
+ pw.println();
+ pw.println(" OutdoorModeController State:");
+ pw.println(" mAutoOutdoorMode=" + mAutoOutdoorMode);
+ pw.println(" mIsOutdoor=" + mIsOutdoor);
+ pw.println(" mIsNight=" + isNight());
+ }
+ mLuxObserver.dump(pw);
+ }
+
+ boolean setAutomaticOutdoorModeEnabled(boolean enabled) {
+ if (!mUseOutdoorMode) {
+ return false;
+ }
+ putInt(CMSettings.System.DISPLAY_AUTO_OUTDOOR_MODE, (enabled ? 1 : 0));
+ return true;
+ }
+
+ boolean isAutomaticOutdoorModeEnabled() {
+ return mUseOutdoorMode;
+ }
+
+ boolean getDefaultAutoOutdoorMode() {
+ return mDefaultAutoOutdoorMode;
+ }
+
+ private void observeAmbientLuxLocked(boolean observe) {
+ mLuxObserver.setTransitionListener(observe ? mListener : null);
+ }
+
+ /**
+ * Outdoor mode is optionally enabled when ambient lux > 10000 and it's daytime
+ * Melt faces!
+ *
+ * TODO: Use the camera or RGB sensor to determine if it's really sunlight
+ */
+ private synchronized void updateOutdoorMode() {
+ /*
+ * Hardware toggle:
+ * Enabled if outdoor mode explictly selected
+ * Enabled if outdoor lux exceeded and day mode or auto mode (if not night)
+ */
+ boolean enabled = !isLowPowerMode() &&
+ (getMode() == MODE_OUTDOOR ||
+ (mAutoOutdoorMode && (mSelfManaged || mIsOutdoor) &&
+ ((getMode() == MODE_AUTO && !isNight()) || getMode() == MODE_DAY)));
+ mHardware.set(CMHardwareManager.FEATURE_SUNLIGHT_ENHANCEMENT, enabled);
+
+ /* Sensor:
+ * Enabled in day mode
+ * Enabled in auto mode if it's not night
+ * Disabled if outdoor mode explicitly selected
+ * Disabled in low power mode
+ * Disabled if screen is off
+ */
+ boolean sensorEnabled = !isLowPowerMode() && isScreenOn() &&
+ getMode() != MODE_OUTDOOR && mAutoOutdoorMode &&
+ ((getMode() == MODE_AUTO && !isNight()) || getMode() == MODE_DAY);
+ if (mIsSensorEnabled != sensorEnabled) {
+ mIsSensorEnabled = sensorEnabled;
+ observeAmbientLuxLocked(sensorEnabled);
+ }
+ }
+
+ private final AmbientLuxObserver.TransitionListener mListener =
+ new AmbientLuxObserver.TransitionListener() {
+ @Override
+ public void onTransition(final int state, float ambientLux) {
+ final boolean outdoor = state == 1;
+ synchronized (OutdoorModeController.this) {
+ if (mIsOutdoor == outdoor) {
+ return;
+ }
+
+ mIsOutdoor = outdoor;
+ updateOutdoorMode();
+ }
+ }
+ };
+
+}
diff --git a/cm/res/AndroidManifest.xml b/cm/res/AndroidManifest.xml
index 3565673..c8fc50a 100644
--- a/cm/res/AndroidManifest.xml
+++ b/cm/res/AndroidManifest.xml
@@ -219,6 +219,13 @@
android:description="@string/permdesc_weather_access_mgr"
android:protectionLevel="normal"/>
+ <!-- Allows an application to manage LiveDisplay -->
+ <permission android:name="cyanogenmod.permission.MANAGE_LIVEDISPLAY"
+ android:label="@string/permlab_manageLiveDisplay"
+ android:description="@string/permdesc_manageLiveDisplay"
+ android:icon="@drawable/ic_launcher_cyanogenmod"
+ android:protectionLevel="normal" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/cm/res/res/values/config.xml b/cm/res/res/values/config.xml
index 3a5b7d2..3755e4b 100644
--- a/cm/res/res/values/config.xml
+++ b/cm/res/res/values/config.xml
@@ -51,10 +51,16 @@
<integer name="config_proximityCheckTimeout">250</integer>
<bool name="config_proximityCheckOnWakeEnabledByDefault">false</bool>
- <!-- Default values for display color temperature -->
+ <!-- Default values for LiveDisplay -->
<integer name="config_dayColorTemperature">6500</integer>
<integer name="config_nightColorTemperature">4500</integer>
<integer name="config_outdoorAmbientLux">9000</integer>
+ <integer name="config_defaultLiveDisplayMode">0</integer>
+
+ <bool name="config_defaultAutoContrast">false</bool>
+ <bool name="config_defaultAutoOutdoorMode">true</bool>
+ <bool name="config_defaultColorEnhancement">true</bool>
+ <bool name="config_defaultCABC">true</bool>
<!-- Is the notification LED brightness adjustable ?
Used to decide if the user can set LED brightness -->
@@ -91,5 +97,6 @@
<item>org.cyanogenmod.platform.internal.IconCacheManagerService</item>
<item>org.cyanogenmod.platform.internal.LiveLockScreenServiceBroker</item>
<item>org.cyanogenmod.platform.internal.CMWeatherManagerService</item>
+ <item>org.cyanogenmod.platform.internal.display.LiveDisplayService</item>
</string-array>
</resources>
diff --git a/cm/res/res/values/strings.xml b/cm/res/res/values/strings.xml
index 425044d..bc0b22a 100644
--- a/cm/res/res/values/strings.xml
+++ b/cm/res/res/values/strings.xml
@@ -198,4 +198,9 @@
<string name="permlab_dataUsageRead">read data usage database</string>
<!-- DataUsageProvider read permission description -->
<string name="permdesc_dataUsageRead">Allows an app to read content from the data usage database.</string>
+
+ <!-- LiveDisplay manager permission -->
+ <string name="permlab_manageLiveDisplay">manage livedisplay settings</string>
+ <string name="permdesc_manageLiveDisplay">Allows an app to configure advanced display settings.</string>
+
</resources>
diff --git a/cm/res/res/values/symbols.xml b/cm/res/res/values/symbols.xml
index 7465ad3..9d52390 100644
--- a/cm/res/res/values/symbols.xml
+++ b/cm/res/res/values/symbols.xml
@@ -69,6 +69,11 @@
<java-symbol type="integer" name="config_dayColorTemperature" />
<java-symbol type="integer" name="config_nightColorTemperature" />
<java-symbol type="integer" name="config_outdoorAmbientLux" />
+ <java-symbol type="integer" name="config_defaultLiveDisplayMode" />
+ <java-symbol type="bool" name="config_defaultAutoContrast" />
+ <java-symbol type="bool" name="config_defaultAutoOutdoorMode" />
+ <java-symbol type="bool" name="config_defaultColorEnhancement" />
+ <java-symbol type="bool" name="config_defaultCABC" />
<!-- Notification and battery light -->
<java-symbol type="bool" name="config_adjustableNotificationLedBrightness" />
diff --git a/sdk/src/java/cyanogenmod/app/CMContextConstants.java b/sdk/src/java/cyanogenmod/app/CMContextConstants.java
index 98171b8..07a2980 100644
--- a/sdk/src/java/cyanogenmod/app/CMContextConstants.java
+++ b/sdk/src/java/cyanogenmod/app/CMContextConstants.java
@@ -132,6 +132,13 @@ public final class CMContextConstants {
public static final String CM_WEATHER_SERVICE = "cmweather";
/**
+ * Manages display color adjustments
+ *
+ * @hide
+ */
+ public static final String CM_LIVEDISPLAY_SERVICE = "cmlivedisplay";
+
+ /**
* Features supported by the CMSDK.
*/
public static class Features {
@@ -214,5 +221,13 @@ public final class CMContextConstants {
*/
@SdkConstant(SdkConstant.SdkConstantType.FEATURE)
public static final String WEATHER_SERVICES = "org.cyanogenmod.weather";
+
+ /**
+ * Feature for {@link PackageManager#getSystemAvailableFeatures} and
+ * {@link PackageManager#hasSystemFeature}: The device includes the LiveDisplay service
+ * utilized by the cmsdk.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.FEATURE)
+ public static final String LIVEDISPLAY = "org.cyanogenmod.livedisplay";
}
}
diff --git a/sdk/src/java/cyanogenmod/hardware/ILiveDisplayService.aidl b/sdk/src/java/cyanogenmod/hardware/ILiveDisplayService.aidl
new file mode 100644
index 0000000..68e2b86
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/hardware/ILiveDisplayService.aidl
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016, The CyanogenMod 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 cyanogenmod.hardware;
+
+import cyanogenmod.hardware.LiveDisplayConfig;
+
+/** @hide */
+interface ILiveDisplayService {
+ LiveDisplayConfig getConfig();
+
+ int getMode();
+ boolean setMode(int mode);
+
+ float[] getColorAdjustment();
+ boolean setColorAdjustment(in float[] adj);
+
+ boolean isAutoContrastEnabled();
+ boolean setAutoContrastEnabled(boolean enabled);
+
+ boolean isCABCEnabled();
+ boolean setCABCEnabled(boolean enabled);
+
+ boolean isColorEnhancementEnabled();
+ boolean setColorEnhancementEnabled(boolean enabled);
+
+ int getDayColorTemperature();
+ boolean setDayColorTemperature(int temperature);
+
+ int getNightColorTemperature();
+ boolean setNightColorTemperature(int temperature);
+
+ int getColorTemperature();
+
+ boolean isAutomaticOutdoorModeEnabled();
+ boolean setAutomaticOutdoorModeEnabled(boolean enabled);
+}
diff --git a/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.aidl b/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.aidl
new file mode 100644
index 0000000..326e298
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 cyanogenmod.hardware;
+
+parcelable LiveDisplayConfig;
diff --git a/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java b/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java
new file mode 100644
index 0000000..0a569d3
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/hardware/LiveDisplayConfig.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 cyanogenmod.hardware;
+
+import static cyanogenmod.hardware.LiveDisplayManager.FEATURE_FIRST;
+import static cyanogenmod.hardware.LiveDisplayManager.FEATURE_LAST;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_FIRST;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_LAST;
+import static cyanogenmod.hardware.LiveDisplayManager.MODE_OFF;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.BitSet;
+
+import cyanogenmod.os.Build;
+import cyanogenmod.os.Concierge;
+import cyanogenmod.os.Concierge.ParcelInfo;
+
+/**
+ * Holder class for LiveDisplay static configuration.
+ *
+ * This class holds various defaults and hardware capabilities
+ * which are involved with LiveDisplay.
+ */
+public class LiveDisplayConfig implements Parcelable {
+
+ private final BitSet mCapabilities;
+ private final int mDefaultDayTemperature;
+ private final int mDefaultNightTemperature;
+ private final int mDefaultMode;
+
+ private final boolean mDefaultAutoContrast;
+ private final boolean mDefaultAutoOutdoorMode;
+ private final boolean mDefaultCABC;
+ private final boolean mDefaultColorEnhancement;
+
+ public LiveDisplayConfig(BitSet capabilities, int defaultMode,
+ int defaultDayTemperature, int defaultNightTemperature,
+ boolean defaultAutoOutdoorMode, boolean defaultAutoContrast,
+ boolean defaultCABC, boolean defaultColorEnhancement) {
+ super();
+ mCapabilities = (BitSet) capabilities.clone();
+ mDefaultMode = defaultMode;
+ mDefaultDayTemperature = defaultDayTemperature;
+ mDefaultNightTemperature = defaultNightTemperature;
+ mDefaultAutoContrast = defaultAutoContrast;
+ mDefaultAutoOutdoorMode = defaultAutoOutdoorMode;
+ mDefaultCABC = defaultCABC;
+ mDefaultColorEnhancement = defaultColorEnhancement;
+ }
+
+ private LiveDisplayConfig(Parcel parcel) {
+ // Read parcelable version via the Concierge
+ ParcelInfo parcelInfo = Concierge.receiveParcel(parcel);
+ int parcelableVersion = parcelInfo.getParcelVersion();
+
+ // temp vars
+ long capabilities = 0;
+ int defaultMode = 0;
+ int defaultDayTemperature = -1;
+ int defaultNightTemperature = -1;
+ boolean defaultAutoContrast = false;
+ boolean defaultAutoOutdoorMode = false;
+ boolean defaultCABC = false;
+ boolean defaultColorEnhancement = false;
+
+ if (parcelableVersion >= Build.CM_VERSION_CODES.FIG) {
+ capabilities = parcel.readLong();
+ defaultMode = parcel.readInt();
+ defaultDayTemperature = parcel.readInt();
+ defaultNightTemperature = parcel.readInt();
+ defaultAutoContrast = parcel.readInt() == 1;
+ defaultAutoOutdoorMode = parcel.readInt() == 1;
+ defaultCABC = parcel.readInt() == 1;
+ defaultColorEnhancement = parcel.readInt() == 1;
+ }
+
+ // set temps
+ mCapabilities = BitSet.valueOf(new long[] { capabilities });
+ mDefaultMode = defaultMode;
+ mDefaultDayTemperature = defaultDayTemperature;
+ mDefaultNightTemperature = defaultNightTemperature;
+ mDefaultAutoContrast = defaultAutoContrast;
+ mDefaultAutoOutdoorMode = defaultAutoOutdoorMode;
+ mDefaultCABC = defaultCABC;
+ mDefaultColorEnhancement = defaultColorEnhancement;
+
+ // Complete parcel info for the concierge
+ parcelInfo.complete();
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("capabilities=").append(mCapabilities.toString());
+ sb.append(" defaultMode=").append(mDefaultMode);
+ sb.append(" defaultDayTemperature=").append(mDefaultDayTemperature);
+ sb.append(" defaultNightTemperature=").append(mDefaultNightTemperature);
+ sb.append(" defaultAutoOutdoorMode=").append(mDefaultAutoOutdoorMode);
+ sb.append(" defaultAutoContrast=").append(mDefaultAutoContrast);
+ sb.append(" defaultCABC=").append(mDefaultCABC);
+ sb.append(" defaultColorEnhancement=").append(mDefaultColorEnhancement);
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ // Tell the concierge to prepare the parcel
+ ParcelInfo parcelInfo = Concierge.prepareParcel(out);
+
+ // ==== FIG =====
+ out.writeLong(mCapabilities.toLongArray()[0]);
+ out.writeInt(mDefaultMode);
+ out.writeInt(mDefaultDayTemperature);
+ out.writeInt(mDefaultNightTemperature);
+ out.writeInt(mDefaultAutoContrast ? 1 : 0);
+ out.writeInt(mDefaultAutoOutdoorMode ? 1 : 0);
+ out.writeInt(mDefaultCABC ? 1 : 0);
+ out.writeInt(mDefaultColorEnhancement ? 1 : 0);
+
+ // Complete the parcel info for the concierge
+ parcelInfo.complete();
+ }
+
+ /**
+ * Checks if a particular feature or mode is supported by the system.
+ *
+ * @param feature
+ * @return true if capable
+ */
+ public boolean hasFeature(int feature) {
+ return ((feature >= MODE_FIRST && feature <= MODE_LAST) ||
+ (feature >= FEATURE_FIRST && feature <= FEATURE_LAST)) &&
+ (feature == MODE_OFF || mCapabilities.get(feature));
+ }
+
+ /**
+ * Checks if LiveDisplay is available for use on this device.
+ *
+ * @return true if any feature is enabled
+ */
+ public boolean isAvailable() {
+ return !mCapabilities.isEmpty();
+ }
+
+ /**
+ * Gets the default color temperature to use in the daytime. This is typically
+ * set to 6500K, however this may not be entirely accurate. Use this value for
+ * resetting controls to the default.
+ *
+ * @return the default day temperature in K
+ */
+ public int getDefaultDayTemperature() {
+ return mDefaultDayTemperature;
+ }
+
+ /**
+ * Gets the default color temperature to use at night. This is typically set
+ * to 4500K, but this may not be entirely accurate. Use this value for resetting
+ * controls to defaults.
+ *
+ * @return the default night color temperature
+ */
+ public int getDefaultNightTemperature() {
+ return mDefaultNightTemperature;
+ }
+
+ /**
+ * Get the default adaptive mode.
+ *
+ * @return the default mode
+ */
+ public int getDefaultMode() {
+ return mDefaultMode;
+ }
+
+ /**
+ * Get the default value for auto contrast
+ *
+ * @return true if enabled
+ */
+ public boolean getDefaultAutoContrast() {
+ return mDefaultAutoContrast;
+ }
+
+ /**
+ * Get the default value for automatic outdoor mode
+ *
+ * @return true if enabled
+ */
+ public boolean getDefaultAutoOutdoorMode() {
+ return mDefaultAutoOutdoorMode;
+ }
+
+ /**
+ * Get the default value for CABC
+ *
+ * @return true if enabled
+ */
+ public boolean getDefaultCABC() {
+ return mDefaultCABC;
+ }
+
+ /**
+ * Get the default value for color enhancement
+ *
+ * @return true if enabled
+ */
+ public boolean getDefaultColorEnhancement() {
+ return mDefaultColorEnhancement;
+ }
+
+ /** @hide */
+ public static final Parcelable.Creator<LiveDisplayConfig> CREATOR =
+ new Parcelable.Creator<LiveDisplayConfig>() {
+ public LiveDisplayConfig createFromParcel(Parcel in) {
+ return new LiveDisplayConfig(in);
+ }
+
+ @Override
+ public LiveDisplayConfig[] newArray(int size) {
+ return new LiveDisplayConfig[size];
+ }
+ };
+}
diff --git a/sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java b/sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java
new file mode 100644
index 0000000..0e4f7a1
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/hardware/LiveDisplayManager.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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 cyanogenmod.hardware;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import cyanogenmod.app.CMContextConstants;
+
+/**
+ * LiveDisplay is an advanced set of features for improving
+ * display quality under various ambient conditions.
+ *
+ * The backend service is constructed with a set of LiveDisplayFeatures
+ * which provide capabilities such as outdoor mode, night mode,
+ * and calibration. It interacts with CMHardwareService to relay
+ * changes down to the lower layers.
+ *
+ * Multiple adaptive modes are supported, and various hardware
+ * features such as CABC, ACO and color enhancement are also
+ * managed by LiveDisplay.
+ */
+public class LiveDisplayManager {
+
+ /**
+ * Disable all LiveDisplay adaptive features
+ */
+ public static final int MODE_OFF = 0;
+
+ /**
+ * Change color temperature to night mode
+ */
+ public static final int MODE_NIGHT = 1;
+
+ /**
+ * Enable automatic detection of appropriate mode
+ */
+ public static final int MODE_AUTO = 2;
+
+ /**
+ * Increase brightness/contrast/saturation for sunlight
+ */
+ public static final int MODE_OUTDOOR = 3;
+
+ /**
+ * Change color temperature to day mode, and allow
+ * detection of outdoor conditions
+ */
+ public static final int MODE_DAY = 4;
+
+ /** @hide */
+ public static final int MODE_FIRST = MODE_OFF;
+ /** @hide */
+ public static final int MODE_LAST = MODE_DAY;
+
+ /**
+ * Content adaptive backlight control, adjust images to
+ * increase brightness in order to reduce backlight level
+ */
+ public static final int FEATURE_CABC = 10;
+
+ /**
+ * Adjust images to increase contrast
+ */
+ public static final int FEATURE_AUTO_CONTRAST = 11;
+
+ /**
+ * Adjust image to improve saturation and color
+ */
+ public static final int FEATURE_COLOR_ENHANCEMENT = 12;
+
+ /**
+ * Capable of adjusting RGB levels
+ */
+ public static final int FEATURE_COLOR_ADJUSTMENT = 13;
+
+ /**
+ * System supports outdoor mode, but environmental sensing
+ * is done by an external application.
+ */
+ public static final int FEATURE_MANAGED_OUTDOOR_MODE = 14;
+
+ /**
+ * System supports multiple display calibrations
+ * for different viewing intents.
+ */
+ public static final int FEATURE_DISPLAY_MODES = 15;
+
+ /** @hide */
+ public static final int FEATURE_FIRST = FEATURE_CABC;
+ /** @hide */
+ public static final int FEATURE_LAST = FEATURE_DISPLAY_MODES;
+
+ private static final String TAG = "LiveDisplay";
+
+ private final Context mContext;
+ private final LiveDisplayConfig mConfig;
+
+ private static LiveDisplayManager sInstance;
+ private static ILiveDisplayService sService;
+
+ /**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ private LiveDisplayManager(Context context) {
+ Context appContext = context.getApplicationContext();
+ if (appContext != null) {
+ mContext = appContext;
+ } else {
+ mContext = context;
+ }
+ sService = getService();
+
+ if (context.getPackageManager().hasSystemFeature(
+ CMContextConstants.Features.LIVEDISPLAY) && !checkService()) {
+ throw new RuntimeException("Unable to get LiveDisplayService. The service either" +
+ " crashed, was not started, or the interface has been called to early in" +
+ " SystemServer init");
+ }
+
+ try {
+ mConfig = sService.getConfig();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Unable to fetch LiveDisplay configuration!", e);
+ }
+ }
+
+ /**
+ * Get or create an instance of the {@link cyanogenmod.hardware.LiveDisplayManager}
+ * @param context
+ * @return {@link LiveDisplayManager}
+ */
+ public synchronized static LiveDisplayManager getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new LiveDisplayManager(context);
+ }
+ return sInstance;
+ }
+
+ /** @hide */
+ public static ILiveDisplayService getService() {
+ if (sService != null) {
+ return sService;
+ }
+ IBinder b = ServiceManager.getService(CMContextConstants.CM_LIVEDISPLAY_SERVICE);
+ if (b != null) {
+ sService = ILiveDisplayService.Stub.asInterface(b);
+ return sService;
+ }
+ return null;
+ }
+
+ /**
+ * @return true if service is valid
+ */
+ private boolean checkService() {
+ if (sService == null) {
+ Log.w(TAG, "not connected to CMHardwareManagerService");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Gets the static configuration and settings.
+ *
+ * @return the configuration
+ */
+ public LiveDisplayConfig getConfig() {
+ return mConfig;
+ }
+
+ /**
+ * Returns the current adaptive mode.
+ *
+ * @return id of the selected mode
+ */
+ public int getMode() {
+ try {
+ return checkService() ? sService.getMode() : MODE_OFF;
+ } catch (RemoteException e) {
+ return MODE_OFF;
+ }
+ }
+
+ /**
+ * Selects a new adaptive mode.
+ *
+ * @param mode
+ * @return true if the mode was selected
+ */
+ public boolean setMode(int mode) {
+ try {
+ return checkService() && sService.setMode(mode);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the auto contrast optimization feature is enabled.
+ *
+ * @return true if enabled
+ */
+ public boolean isAutoContrastEnabled() {
+ try {
+ return checkService() && sService.isAutoContrastEnabled();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the state of auto contrast optimization
+ *
+ * @param enabled
+ * @return true if state was changed
+ */
+ public boolean setAutoContrastEnabled(boolean enabled) {
+ try {
+ return checkService() && sService.setAutoContrastEnabled(enabled);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the CABC feature is enabled
+ *
+ * @return true if enabled
+ */
+ public boolean isCABCEnabled() {
+ try {
+ return checkService() && sService.isCABCEnabled();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the state of CABC
+ *
+ * @param enabled
+ * @return true if state was changed
+ */
+ public boolean setCABCEnabled(boolean enabled) {
+ try {
+ return checkService() && sService.setCABCEnabled(enabled);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the color enhancement feature is enabled
+ *
+ * @return true if enabled
+ */
+ public boolean isColorEnhancementEnabled() {
+ try {
+ return checkService() && sService.isColorEnhancementEnabled();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the state of color enhancement
+ *
+ * @param enabled
+ * @return true if state was changed
+ */
+ public boolean setColorEnhancementEnabled(boolean enabled) {
+ try {
+ return checkService() && sService.setColorEnhancementEnabled(enabled);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the user-specified color temperature to use in the daytime.
+ *
+ * @return the day color temperature
+ */
+ public int getDayColorTemperature() {
+ try {
+ return checkService() ? sService.getDayColorTemperature() : -1;
+ } catch (RemoteException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Sets the color temperature to use in the daytime.
+ *
+ * @param temperature
+ * @return true if state was changed
+ */
+ public boolean setDayColorTemperature(int temperature) {
+ try {
+ return checkService() && sService.setDayColorTemperature(temperature);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the user-specified color temperature to use at night.
+ *
+ * @return the night color temperature
+ */
+ public int getNightColorTemperature() {
+ try {
+ return checkService() ? sService.getNightColorTemperature() : -1;
+ } catch (RemoteException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Sets the color temperature to use at night.
+ *
+ * @param temperature
+ * @return true if state was changed
+ */
+ public boolean setNightColorTemperature(int temperature) {
+ try {
+ return checkService() && sService.setNightColorTemperature(temperature);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks if outdoor mode should be enabled automatically when under extremely high
+ * ambient light. This is typically around 12000 lux.
+ *
+ * @return if outdoor conditions should be detected
+ */
+ public boolean isAutomaticOutdoorModeEnabled() {
+ try {
+ return checkService() && sService.isAutomaticOutdoorModeEnabled();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Enables automatic detection of outdoor conditions. Outdoor mode is triggered
+ * when high ambient light is detected and it's not night.
+ *
+ * @param enabled
+ * @return true if state was changed
+ */
+ public boolean setAutomaticOutdoorModeEnabled(boolean enabled) {
+ try {
+ return checkService() && sService.setAutomaticOutdoorModeEnabled(enabled);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Gets the current RGB triplet which is applied as a color adjustment.
+ * The values are floats between 0 and 1. A setting of { 1.0, 1.0, 1.0 }
+ * means that no adjustment is made.
+ *
+ * @return array of { R, G, B } offsets
+ */
+ public float[] getColorAdjustment() {
+ try {
+ if (checkService()) {
+ return sService.getColorAdjustment();
+ }
+ } catch (RemoteException e) {
+ }
+ return new float[] { 1.0f, 1.0f, 1.0f };
+ }
+
+ /**
+ * Sets the color adjustment to use. This can be set by the user to calibrate
+ * their screen. This should be sent in the format { R, G, B } as floats from
+ * 0 to 1. A setting of { 1.0, 1.0, 1.0 } means that no adjustment is made.
+ * The hardware implementation may refuse certain values which make the display
+ * unreadable, such as { 0, 0, 0 }. This calibration will be combined with other
+ * internal adjustments, such as night mode, if necessary.
+ *
+ * @param array of { R, G, B } offsets
+ * @return true if state was changed
+ */
+ public boolean setColorAdjustment(float[] adj) {
+ try {
+ return checkService() && sService.setColorAdjustment(adj);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+}
diff --git a/sdk/src/java/cyanogenmod/providers/CMSettings.java b/sdk/src/java/cyanogenmod/providers/CMSettings.java
index 47cc524..2b3b07a 100644
--- a/sdk/src/java/cyanogenmod/providers/CMSettings.java
+++ b/sdk/src/java/cyanogenmod/providers/CMSettings.java
@@ -68,6 +68,16 @@ public final class CMSettings {
*/
public static final String ACTION_DATA_USAGE = "cyanogenmod.settings.ACTION_DATA_USAGE";
+ /**
+ * Activity Action: Show LiveDisplay settings
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ public static final String ACTION_LIVEDISPLAY_SETTINGS =
+ "cyanogenmod.settings.LIVEDISPLAY_SETTINGS";
+
// region Call Methods
/**
@@ -1318,10 +1328,15 @@ public final class CMSettings {
* Use display power saving features such as CABC or CABL
* 0 = 0ff, 1 = on
*/
- public static final String DISPLAY_LOW_POWER = "display_low_power";
+ public static final String DISPLAY_CABC = "display_low_power";
+
+ /**
+ * @deprecated
+ */
+ public static final String DISPLAY_LOW_POWER = DISPLAY_CABC;
/** @hide */
- public static final Validator DISPLAY_LOW_POWER_VALIDATOR =
+ public static final Validator DISPLAY_CABC_VALIDATOR =
sBooleanValidator;
/**
@@ -1335,6 +1350,16 @@ public final class CMSettings {
sBooleanValidator;
/**
+ * Use auto contrast optimization feature of display
+ * 0 = 0ff, 1 = on
+ */
+ public static final String DISPLAY_AUTO_CONTRAST = "display_auto_contrast";
+
+ /** @hide */
+ public static final Validator DISPLAY_AUTO_CONTRAST_VALIDATOR =
+ sBooleanValidator;
+
+ /**
* Manual display color adjustments (RGB values as floats, separated by spaces)
*/
public static final String DISPLAY_COLOR_ADJUSTMENT = "display_color_adjustment";
@@ -1827,7 +1852,7 @@ public final class CMSettings {
CMSettings.System.DISPLAY_TEMPERATURE_NIGHT,
CMSettings.System.DISPLAY_TEMPERATURE_MODE,
CMSettings.System.DISPLAY_AUTO_OUTDOOR_MODE,
- CMSettings.System.DISPLAY_LOW_POWER,
+ CMSettings.System.DISPLAY_CABC,
CMSettings.System.DISPLAY_COLOR_ENHANCE,
CMSettings.System.DISPLAY_COLOR_ADJUSTMENT,
CMSettings.System.LIVE_DISPLAY_HINTED,
@@ -1965,8 +1990,9 @@ public final class CMSettings {
VALIDATORS.put(DISPLAY_TEMPERATURE_DAY, DISPLAY_TEMPERATURE_DAY_VALIDATOR);
VALIDATORS.put(DISPLAY_TEMPERATURE_NIGHT, DISPLAY_TEMPERATURE_NIGHT_VALIDATOR);
VALIDATORS.put(DISPLAY_TEMPERATURE_MODE, DISPLAY_TEMPERATURE_MODE_VALIDATOR);
+ VALIDATORS.put(DISPLAY_AUTO_CONTRAST, DISPLAY_AUTO_CONTRAST_VALIDATOR);
VALIDATORS.put(DISPLAY_AUTO_OUTDOOR_MODE, DISPLAY_AUTO_OUTDOOR_MODE_VALIDATOR);
- VALIDATORS.put(DISPLAY_LOW_POWER, DISPLAY_LOW_POWER_VALIDATOR);
+ VALIDATORS.put(DISPLAY_CABC, DISPLAY_CABC_VALIDATOR);
VALIDATORS.put(DISPLAY_COLOR_ENHANCE, DISPLAY_COLOR_ENHANCE_VALIDATOR);
VALIDATORS.put(DISPLAY_COLOR_ADJUSTMENT, DISPLAY_COLOR_ADJUSTMENT_VALIDATOR);
VALIDATORS.put(LIVE_DISPLAY_HINTED, LIVE_DISPLAY_HINTED_VALIDATOR);
diff --git a/system-api/cm_system-current.txt b/system-api/cm_system-current.txt
index ff84505..5992f3f 100644
--- a/system-api/cm_system-current.txt
+++ b/system-api/cm_system-current.txt
@@ -529,6 +529,53 @@ package cyanogenmod.hardware {
method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
}
+ public class LiveDisplayConfig implements android.os.Parcelable {
+ ctor public LiveDisplayConfig(java.util.BitSet, int, int, int, boolean, boolean, boolean, boolean);
+ method public int describeContents();
+ method public boolean getDefaultAutoContrast();
+ method public boolean getDefaultAutoOutdoorMode();
+ method public boolean getDefaultCABC();
+ method public boolean getDefaultColorEnhancement();
+ method public int getDefaultDayTemperature();
+ method public int getDefaultMode();
+ method public int getDefaultNightTemperature();
+ method public boolean hasFeature(int);
+ method public boolean isAvailable();
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
+ public class LiveDisplayManager {
+ method public float[] getColorAdjustment();
+ method public cyanogenmod.hardware.LiveDisplayConfig getConfig();
+ method public int getDayColorTemperature();
+ method public static synchronized cyanogenmod.hardware.LiveDisplayManager getInstance(android.content.Context);
+ method public int getMode();
+ method public int getNightColorTemperature();
+ method public boolean isAutoContrastEnabled();
+ method public boolean isAutomaticOutdoorModeEnabled();
+ method public boolean isCABCEnabled();
+ method public boolean isColorEnhancementEnabled();
+ method public boolean setAutoContrastEnabled(boolean);
+ method public boolean setAutomaticOutdoorModeEnabled(boolean);
+ method public boolean setCABCEnabled(boolean);
+ method public boolean setColorAdjustment(float[]);
+ method public boolean setColorEnhancementEnabled(boolean);
+ method public boolean setDayColorTemperature(int);
+ method public boolean setMode(int);
+ method public boolean setNightColorTemperature(int);
+ field public static final int FEATURE_AUTO_CONTRAST = 11; // 0xb
+ field public static final int FEATURE_CABC = 10; // 0xa
+ field public static final int FEATURE_COLOR_ADJUSTMENT = 13; // 0xd
+ field public static final int FEATURE_COLOR_ENHANCEMENT = 12; // 0xc
+ field public static final int FEATURE_DISPLAY_MODES = 15; // 0xf
+ field public static final int FEATURE_MANAGED_OUTDOOR_MODE = 14; // 0xe
+ field public static final int MODE_AUTO = 2; // 0x2
+ field public static final int MODE_DAY = 4; // 0x4
+ field public static final int MODE_NIGHT = 1; // 0x1
+ field public static final int MODE_OFF = 0; // 0x0
+ field public static final int MODE_OUTDOOR = 3; // 0x3
+ }
+
public abstract class ThermalListenerCallback extends cyanogenmod.hardware.IThermalListenerCallback.Stub {
ctor public ThermalListenerCallback();
}
@@ -613,6 +660,7 @@ package cyanogenmod.platform {
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
+ field public static final java.lang.String MANAGE_LIVEDISPLAY = "cyanogenmod.permission.MANAGE_LIVEDISPLAY";
field public static final java.lang.String MANAGE_PERSISTENT_STORAGE = "cyanogenmod.permission.MANAGE_PERSISTENT_STORAGE";
field public static final java.lang.String MODIFY_MSIM_PHONE_STATE = "cyanogenmod.permission.MODIFY_MSIM_PHONE_STATE";
field public static final java.lang.String MODIFY_NETWORK_SETTINGS = "cyanogenmod.permission.MODIFY_NETWORK_SETTINGS";
@@ -795,6 +843,7 @@ package cyanogenmod.providers {
public final class CMSettings {
ctor public CMSettings();
field public static final java.lang.String ACTION_DATA_USAGE = "cyanogenmod.settings.ACTION_DATA_USAGE";
+ field public static final java.lang.String ACTION_LIVEDISPLAY_SETTINGS = "cyanogenmod.settings.LIVEDISPLAY_SETTINGS";
field public static final java.lang.String AUTHORITY = "cmsettings";
}
@@ -868,10 +917,12 @@ package cyanogenmod.providers {
field public static final android.net.Uri CONTENT_URI;
field public static final java.lang.String DIALER_OPENCNAM_ACCOUNT_SID = "dialer_opencnam_account_sid";
field public static final java.lang.String DIALER_OPENCNAM_AUTH_TOKEN = "dialer_opencnam_auth_token";
+ field public static final java.lang.String DISPLAY_AUTO_CONTRAST = "display_auto_contrast";
field public static final java.lang.String DISPLAY_AUTO_OUTDOOR_MODE = "display_auto_outdoor_mode";
+ field public static final java.lang.String DISPLAY_CABC = "display_low_power";
field public static final java.lang.String DISPLAY_COLOR_ADJUSTMENT = "display_color_adjustment";
field public static final java.lang.String DISPLAY_COLOR_ENHANCE = "display_color_enhance";
- field public static final java.lang.String DISPLAY_LOW_POWER = "display_low_power";
+ field public static final deprecated java.lang.String DISPLAY_LOW_POWER = "display_low_power";
field public static final java.lang.String DISPLAY_TEMPERATURE_DAY = "display_temperature_day";
field public static final java.lang.String DISPLAY_TEMPERATURE_MODE = "display_temperature_mode";
field public static final java.lang.String DISPLAY_TEMPERATURE_NIGHT = "display_temperature_night";