diff options
28 files changed, 1195 insertions, 803 deletions
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index aa697eadcbca..13ba6cc37d22 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -211,6 +211,9 @@ public abstract class DisplayManagerInternal { public int dozeScreenBrightness; public int dozeScreenState; + // If true, use twilight to affect the brightness. + public boolean useTwilight; + public DisplayPowerRequest() { policy = POLICY_BRIGHT; useProximitySensor = false; @@ -242,6 +245,7 @@ public abstract class DisplayManagerInternal { boostScreenBrightness = other.boostScreenBrightness; dozeScreenBrightness = other.dozeScreenBrightness; dozeScreenState = other.dozeScreenState; + useTwilight = other.useTwilight; } @Override @@ -262,7 +266,8 @@ public abstract class DisplayManagerInternal { && lowPowerMode == other.lowPowerMode && boostScreenBrightness == other.boostScreenBrightness && dozeScreenBrightness == other.dozeScreenBrightness - && dozeScreenState == other.dozeScreenState; + && dozeScreenState == other.dozeScreenState + && useTwilight == other.useTwilight; } @Override @@ -282,7 +287,8 @@ public abstract class DisplayManagerInternal { + ", lowPowerMode=" + lowPowerMode + ", boostScreenBrightness=" + boostScreenBrightness + ", dozeScreenBrightness=" + dozeScreenBrightness - + ", dozeScreenState=" + Display.stateToString(dozeScreenState); + + ", dozeScreenState=" + Display.stateToString(dozeScreenState) + + ", useTwilight=" + useTwilight; } public static String policyToString(int policy) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5ab2b001614a..46048e4ad48b 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5922,6 +5922,52 @@ public final class Settings { "camera_double_tap_power_gesture_disabled"; /** + + /** + * Behavior of twilight on the device. + * One of {@link #TWILIGHT_MODE_LOCKED_OFF}, {@link #TWILIGHT_MODE_LOCKED_ON} + * or {@link #TWILIGHT_MODE_AUTO}. + * @hide + */ + public static final String TWILIGHT_MODE = "twilight_mode"; + + /** + * Twilight mode always off. + * @hide + */ + public static final int TWILIGHT_MODE_LOCKED_OFF = 0; + + /** + * Twilight mode always on. + * @hide + */ + public static final int TWILIGHT_MODE_LOCKED_ON = 1; + + /** + * Twilight mode auto. + * @hide + */ + public static final int TWILIGHT_MODE_AUTO = 2; + + /** + * Twilight mode auto, temporarily overriden to on. + * @hide + */ + public static final int TWILIGHT_MODE_AUTO_OVERRIDE_OFF = 3; + + /** + * Twilight mode auto, temporarily overriden to off. + * @hide + */ + public static final int TWILIGHT_MODE_AUTO_OVERRIDE_ON = 4; + + /** + * Whether brightness should automatically adjust based on twilight state. + * @hide + */ + public static final String BRIGHTNESS_USE_TWILIGHT = "brightness_use_twilight"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9b989b94d49f..bb31a0207148 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -452,6 +452,8 @@ <protected-broadcast android:name="android.bluetooth.input.profile.action.HANDSHAKE" /> <protected-broadcast android:name="android.bluetooth.input.profile.action.REPORT" /> + <protected-broadcast android:name="android.intent.action.TWILIGHT_CHANGED" /> + <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> <!-- ====================================================================== --> diff --git a/packages/SystemUI/res/drawable/ic_colorize.xml b/packages/SystemUI/res/drawable/ic_night_mode.xml index 79fd6d907264..caa7a47ee5b4 100644 --- a/packages/SystemUI/res/drawable/ic_colorize.xml +++ b/packages/SystemUI/res/drawable/ic_night_mode.xml @@ -1,5 +1,5 @@ <!-- - Copyright (C) 2015 The Android Open Source Project + Copyright (C) 2016 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml b/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml new file mode 100644 index 000000000000..010815a41819 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_night_mode_disabled.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24.0dp" + android:height="24.0dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#4DFFFFFF" + android:pathData="M20.71,5.63l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-3.12,3.12 -1.93,-1.91 -1.41,1.41 1.42,1.42L3.0,16.25L3.0,21.0l4.75,0.0l8.92,-8.92 1.42,1.42 1.41,-1.41 -1.92,-1.92 3.12,-3.12c0.4,0.0 0.4,-1.0 0.01,-1.42zM6.92,19.0L5.0,17.08l8.06,-8.06 1.92,1.92L6.92,19.0z"/> +</vector> diff --git a/packages/SystemUI/res/layout/preference_matrix.xml b/packages/SystemUI/res/layout/calibrate_sliders.xml index 1f6066e5eaf0..0dec8a18caa4 100644 --- a/packages/SystemUI/res/layout/preference_matrix.xml +++ b/packages/SystemUI/res/layout/calibrate_sliders.xml @@ -94,11 +94,4 @@ android:layout_weight="1" /> </LinearLayout> - <Button - android:id="@+id/apply" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="end" - android:text="@string/color_apply" /> - </LinearLayout> diff --git a/packages/SystemUI/res/layout/color_matrix_settings.xml b/packages/SystemUI/res/layout/night_mode_settings.xml index 3725e78cc481..3725e78cc481 100644 --- a/packages/SystemUI/res/layout/color_matrix_settings.xml +++ b/packages/SystemUI/res/layout/night_mode_settings.xml diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index fa5b1a9ffa92..fe3391c53fa5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1240,29 +1240,43 @@ <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] --> <string name="notification_done">Done</string> - <!-- Label for no color transform [CHAR LIMIT=30] --> - <string name="color_matrix_none">Normal colors</string> + <!-- SysUI Tuner: Color and appearance screen title [CHAR LIMIT=50] --> + <string name="color_and_appearance">Color and appearance</string> - <!-- Label for night color transform [CHAR LIMIT=30] --> - <string name="color_matrix_night">Night colors</string> + <!-- SysUI Tuner: Name of the night mode feature [CHAR LIMIT=30] --> + <string name="night_mode">Night mode</string> - <!-- Label for custom color transform [CHAR LIMIT=30] --> - <string name="color_matrix_custom">Custom colors</string> + <!-- SysUI Tuner: Name of calibrate display dialog [CHAR LIMIT=30] --> + <string name="calibrate_display">Calibrate display</string> - <!-- Label for auto color transforms [CHAR LIMIT=30] --> - <string name="color_matrix_auto">Auto</string> + <!-- SysUI Tuner: Summary of night mode when its on [CHAR LIMIT=NONE] --> + <string name="night_mode_on">On</string> - <!-- Label for unknown color transform [CHAR LIMIT=30] --> - <string name="color_matrix_unknown">Unknown colors</string> + <!-- SysUI Tuner: Summary of night mode when its off [CHAR LIMIT=NONE] --> + <string name="night_mode_off">Off</string> - <!-- Title for color transform [CHAR LIMIT=30] --> - <string name="color_transform">Color modification</string> + <!-- SysUI Tuner: Label for switch to turn on night mode automatically [CHAR LIMIT=50] --> + <string name="turn_on_automatically">Turn on automatically</string> - <!-- Title for setting to show Quick Settings tile [CHAR LIMIT=60] --> - <string name="color_matrix_show_qs">Show Quick Settings tile</string> + <!-- SysUI Tuner: Summary for switch to turn on night mode automatically [CHAR LIMIT=NONE] --> + <string name="turn_on_auto_summary">Switch into Night Mode as appropriate for location and time of day</string> - <!-- Title for switch to enable custom color transform [CHAR LIMIT=60] --> - <string name="color_enable_custom">Enable custom transform</string> + <!-- SysUI Tuner: Label for section controlling what night mode does [CHAR LIMIT=60] --> + <string name="when_night_mode_on">When Night Mode is on</string> + + <!-- SysUI Tuner: Switch controlling whether dark theme is turned on with night mode [CHAR LIMIT=45] --> + <string name="use_dark_theme">Use dark theme for Android OS</string> + + <!-- SysUI Tuner: Switch controlling whether tint is changed with night mode [CHAR LIMIT=45] --> + <string name="adjust_tint">Adjust tint</string> + + <!-- SysUI Tuner: Switch controlling whether brightness is changed with night mode [CHAR LIMIT=45] --> + <string name="adjust_brightness">Adjust brightness</string> + + <!-- SysUI Tuner: Disclaimer about using dark theme with night mode [CHAR LIMIT=NONE] --> + <string name="night_mode_disclaimer">The dark theme is applied to + core areas of Android OS that are normally displayed in a light theme, + such as Settings and notifications.</string> <!-- Button to apply settings [CHAR LIMIT=30] --> <string name="color_apply">Apply</string> @@ -1344,6 +1358,9 @@ <!-- Label for feature switch [CHAR LIMIT=30] --> <string name="switch_bar_on">On</string> + <!-- Label for feature switch [CHAR LIMIT=30] --> + <string name="switch_bar_off">Off</string> + <!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] --> <string name="nav_bar">Navigation bar</string> diff --git a/packages/SystemUI/res/xml/color_and_appearance.xml b/packages/SystemUI/res/xml/color_and_appearance.xml new file mode 100644 index 000000000000..21f890eb058d --- /dev/null +++ b/packages/SystemUI/res/xml/color_and_appearance.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:title="@string/color_and_appearance"> + + <Preference + android:key="night_mode" + android:title="@string/night_mode" + android:fragment="com.android.systemui.tuner.NightModeFragment" /> + + <com.android.systemui.tuner.CalibratePreference + android:key="calibrate" + android:title="@string/calibrate_display" /> + +</PreferenceScreen> diff --git a/packages/SystemUI/res/xml/night_mode.xml b/packages/SystemUI/res/xml/night_mode.xml new file mode 100644 index 000000000000..d5f5333a513b --- /dev/null +++ b/packages/SystemUI/res/xml/night_mode.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:title="@string/night_mode"> + + <SwitchPreference + android:key="auto" + android:title="@string/turn_on_automatically" + android:summary="@string/turn_on_auto_summary" /> + + <PreferenceCategory + android:title="@string/when_night_mode_on"> + + <SwitchPreference + android:key="dark_theme" + android:title="@string/use_dark_theme" /> + + <SwitchPreference + android:key="adjust_tint" + android:title="@string/adjust_tint" /> + + <SwitchPreference + android:key="adjust_brightness" + android:title="@string/adjust_brightness" /> + + </PreferenceCategory> + + <Preference + android:selectable="false" + android:summary="@string/night_mode_disclaimer" /> + +</PreferenceScreen> diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index 39281bcb0809..800762ebf7ff 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -142,8 +142,8 @@ <Preference android:key="color_transform" - android:title="@string/color_transform" - android:fragment="com.android.systemui.tuner.ColorMatrixFragment" /> + android:title="@string/color_and_appearance" + android:fragment="com.android.systemui.tuner.ColorAndAppearanceFragment" /> <PreferenceScreen android:key="volume_and_do_not_disturb" diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index e363b767ab9f..6b94195ce5a6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.DisplayController; +import com.android.systemui.statusbar.policy.NightModeController; import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardMonitor; @@ -400,7 +400,7 @@ public abstract class QSTile<TState extends State> implements Listenable { UserInfoController getUserInfoController(); BatteryController getBatteryController(); TileServices getTileServices(); - DisplayController getDisplayController(); + NightModeController getNightModeController(); void removeTile(String tileSpec); ManagedProfileController getManagedProfileController(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 5c34cebe428c..45f2d75b6233 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -53,9 +53,9 @@ public class TileQueryHelper { } private void addSystemTiles(QSTileHost host) { - boolean hasColorMod = host.getDisplayController().isEnabled(); + boolean hasColorMod = host.getNightModeController().isEnabled(); String possible = mContext.getString(R.string.quick_settings_tiles_default) - + ",hotspot,inversion,saver,work,cast" + (hasColorMod ? ",colors" : ""); + + ",hotspot,inversion,saver,work,cast" + (hasColorMod ? ",night" : ""); String[] possibleTiles = possible.split(","); final Handler qsHandler = new Handler(host.getLooper()); final Handler mainHandler = new Handler(Looper.getMainLooper()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 2af2009ac505..80afb9a3c705 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -57,7 +57,7 @@ import com.android.systemui.qs.tiles.WorkModeTile; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.DisplayController; +import com.android.systemui.statusbar.policy.NightModeController; import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardMonitor; @@ -68,7 +68,7 @@ import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.tuner.ColorMatrixTile; +import com.android.systemui.tuner.NightModeTile; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -108,7 +108,7 @@ public final class QSTileHost implements QSTile.Host, Tunable { private final TileServices mServices; private final List<Callback> mCallbacks = new ArrayList<>(); - private final DisplayController mDisplayController; + private final NightModeController mNightModeController; private final AutoTileManager mAutoTiles; private final ManagedProfileController mProfileController; private View mHeader; @@ -137,7 +137,7 @@ public final class QSTileHost implements QSTile.Host, Tunable { mSecurity = security; mBattery = battery; mIconController = iconController; - mDisplayController = new DisplayController(mContext); + mNightModeController = new NightModeController(mContext, true); mProfileController = new ManagedProfileController(this); final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(), @@ -292,8 +292,8 @@ public final class QSTileHost implements QSTile.Host, Tunable { return mIconController; } - public DisplayController getDisplayController() { - return mDisplayController; + public NightModeController getNightModeController() { + return mNightModeController; } public ManagedProfileController getManagedProfileController() { @@ -422,8 +422,8 @@ public final class QSTileHost implements QSTile.Host, Tunable { else if (tileSpec.equals("user")) return new UserTile(this); else if (tileSpec.equals("battery")) return new BatteryTile(this); else if (tileSpec.equals("saver")) return new DataSaverTile(this); - else if (tileSpec.equals(ColorMatrixTile.COLOR_MATRIX_SPEC)) - return new ColorMatrixTile(this); + else if (tileSpec.equals(NightModeTile.NIGHT_MODE_SPEC)) + return new NightModeTile(this); // Intent tiles. else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec); else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DisplayController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DisplayController.java deleted file mode 100644 index d47050c18519..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DisplayController.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.android.systemui.statusbar.policy; - -import libcore.util.Objects; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Configuration; -import android.provider.Settings; - -import com.android.systemui.R; -import com.android.systemui.tuner.TunerService; - -import java.util.ArrayList; - -public class DisplayController implements TunerService.Tunable { - - public static final String COLOR_MATRIX_CUSTOM_ENABLED = "tuner_color_custom_enabled"; - public static final String COLOR_MATRIX_CUSTOM_VALUES = "tuner_color_custom_values"; - - public static final String COLOR_STATE = "sysui_color_matrix_state"; - - public static final int COLOR_STATE_DISABLED = 0; - public static final int COLOR_STATE_ENABLED = 1; - public static final int COLOR_STATE_AUTO = 2; - - public static final String AUTO_STRING = "auto_mode"; - public static final String NONE_STRING = "none"; - - public static final int AUTO_INDEX = 2; - public static final int CUSTOM_INDEX = 3; - - // Night mode ~= 3400 K - private static final float[] NIGHT_VALUES = new float[] { - 1, 0, 0, 0, - 0, .754f, 0, 0, - 0, 0, .516f, 0, - 0, 0, 0, 1, - }; - public static final float[] IDENTITY_MATRIX = new float[] { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - }; - - private final ArrayList<Listener> mListeners = new ArrayList<>(); - - private final Context mContext; - - private String mCurrentValue; - private boolean mListening; - - public DisplayController(Context context) { - mContext = context; - TunerService.get(mContext).addTunable(this, COLOR_STATE, - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX); - } - - public void addListener(Listener listener) { - mListeners.add(listener); - listener.onCurrentMatrixChanged(); - } - - public void removeListener(Listener listener) { - mListeners.remove(listener); - } - - public boolean isEnabled() { - return TunerService.get(mContext).getValue(COLOR_STATE, COLOR_STATE_DISABLED) - != COLOR_STATE_DISABLED; - } - - public boolean isAuto() { - return mListening; - } - - public void setAuto(boolean auto) { - TunerService.get(mContext).setValue(COLOR_STATE, auto ? COLOR_STATE_AUTO - : COLOR_STATE_DISABLED); - } - - public boolean isCustomSet() { - return isCustomEnabled() && Objects.equal(getCurrentMatrix(), getCustomValues()); - } - - public String getCurrentMatrix() { - return mCurrentValue; - } - - public String getCustomValues() { - return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_VALUES); - } - - public boolean isCustomEnabled() { - return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_ENABLED, 0) != 0; - } - - @Override - public void onTuningChanged(String key, String newValue) { - if (Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX.equals(key)) { - mCurrentValue = newValue; - for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onCurrentMatrixChanged(); - } - } else if (COLOR_STATE.equals(key)) { - final boolean listening = newValue != null - && Integer.parseInt(newValue) == COLOR_STATE_AUTO; - if (listening && !mListening) { - mListening = true; - mContext.registerReceiver(mReceiver, - new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); - updateNightMode(); - } else if (!listening && mListening) { - mListening = false; - mContext.unregisterReceiver(mReceiver); - } - for (int i = 0; i < mListeners.size(); i++) { - mListeners.get(i).onCurrentMatrixChanged(); - } - } - } - - private void updateNightMode() { - final int uiMode = mContext.getResources().getConfiguration().uiMode; - final boolean isNightMode = (uiMode & Configuration.UI_MODE_NIGHT_MASK) - == Configuration.UI_MODE_NIGHT_YES; - String value = null; - if (isNightMode) { - value = toString(NIGHT_VALUES); - } - TunerService.get(mContext).setValue(Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, - value); - } - - private final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) { - updateNightMode(); - } - } - }; - - public interface Listener { - void onCurrentMatrixChanged(); - } - - public static String[] getColorTransforms(Context context) { - return new String[] { - NONE_STRING, - toString(NIGHT_VALUES), - AUTO_STRING, // Blank spot for auto values - null, // Blank spot for custom values - }; - } - - public static CharSequence[] getColorTitles(Context context) { - // TODO: Move to string array resource. - return new CharSequence[]{ - context.getString(R.string.color_matrix_none), - context.getString(R.string.color_matrix_night), - context.getString(R.string.color_matrix_auto), - context.getString(R.string.color_matrix_custom), - }; - } - - public static String toString(float[] values) { - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < values.length; i++) { - if (builder.length() != 0) { - builder.append(','); - } - builder.append(values[i]); - } - return builder.toString(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NightModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NightModeController.java new file mode 100644 index 000000000000..0b1911bb15fc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NightModeController.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import libcore.util.Objects; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.opengl.Matrix; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.util.MathUtils; + +import com.android.systemui.tuner.TunerService; + +import java.util.ArrayList; + +/** + * Listens for changes to twilight from the TwilightService. + * + * Also pushes the current matrix to accessibility based on the current twilight + * and various tuner settings. + */ +public class NightModeController implements TunerService.Tunable { + + public static final String NIGHT_MODE_ADJUST_TINT = "tuner_night_mode_adjust_tint"; + private static final String COLOR_MATRIX_CUSTOM_VALUES = "tuner_color_custom_values"; + + private static final String ACTION_TWILIGHT_CHANGED = "android.intent.action.TWILIGHT_CHANGED"; + + private static final String EXTRA_IS_NIGHT = "isNight"; + private static final String EXTRA_AMOUNT = "amount"; + + // Night mode ~= 3400 K + private static final float[] NIGHT_VALUES = new float[] { + 1, 0, 0, 0, + 0, .754f, 0, 0, + 0, 0, .516f, 0, + 0, 0, 0, 1, + }; + public static final float[] IDENTITY_MATRIX = new float[] { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, + }; + + private final ArrayList<Listener> mListeners = new ArrayList<>(); + + private final Context mContext; + + // This is whether or not this is the main NightMode controller in SysUI that should be + // updating relevant color matrixes or if its in the tuner process getting current state + // for UI. + private final boolean mUpdateMatrix; + + private float[] mCustomMatrix; + private boolean mListening; + private boolean mAdjustTint; + + private boolean mIsNight; + private float mAmount; + private boolean mIsAuto; + + public NightModeController(Context context) { + this(context, false); + } + + public NightModeController(Context context, boolean updateMatrix) { + mContext = context; + mUpdateMatrix = updateMatrix; + TunerService.get(mContext).addTunable(this, NIGHT_MODE_ADJUST_TINT, + COLOR_MATRIX_CUSTOM_VALUES, Secure.TWILIGHT_MODE); + } + + public void setNightMode(boolean isNight) { + if (mIsAuto) { + if (mIsNight != isNight) { + TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight + ? Secure.TWILIGHT_MODE_AUTO_OVERRIDE_ON + : Secure.TWILIGHT_MODE_AUTO_OVERRIDE_OFF); + } else { + TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, + Secure.TWILIGHT_MODE_AUTO); + } + } else { + TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, isNight + ? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF); + } + } + + public void setAuto(boolean auto) { + mIsAuto = auto; + if (auto) { + TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_AUTO); + } else { + // Lock into the current state + TunerService.get(mContext).setValue(Secure.TWILIGHT_MODE, mIsNight + ? Secure.TWILIGHT_MODE_LOCKED_ON : Secure.TWILIGHT_MODE_LOCKED_OFF); + } + } + + public boolean isAuto() { + return mIsAuto; + } + + public void setAdjustTint(Boolean newValue) { + TunerService.get(mContext).setValue(NIGHT_MODE_ADJUST_TINT, ((Boolean) newValue) ? 1 : 0); + } + + public void addListener(Listener listener) { + mListeners.add(listener); + listener.onNightModeChanged(); + updateListening(); + } + + public void removeListener(Listener listener) { + mListeners.remove(listener); + updateListening(); + } + + private void updateListening() { + boolean shouldListen = mListeners.size() != 0 || (mUpdateMatrix && mAdjustTint); + if (shouldListen == mListening) return; + mListening = shouldListen; + if (mListening) { + mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_TWILIGHT_CHANGED)); + } else { + mContext.unregisterReceiver(mReceiver); + } + } + + public boolean isEnabled() { + if (!mListening) { + updateNightMode(mContext.registerReceiver(null, + new IntentFilter(ACTION_TWILIGHT_CHANGED))); + } + return mIsNight; + } + + public String getCustomValues() { + return TunerService.get(mContext).getValue(COLOR_MATRIX_CUSTOM_VALUES); + } + + public void setCustomValues(String values) { + TunerService.get(mContext).setValue(COLOR_MATRIX_CUSTOM_VALUES, values); + } + + @Override + public void onTuningChanged(String key, String newValue) { + if (COLOR_MATRIX_CUSTOM_VALUES.equals(key)) { + mCustomMatrix = newValue != null ? toValues(newValue) : null; + updateCurrentMatrix(); + } else if (NIGHT_MODE_ADJUST_TINT.equals(key)) { + mAdjustTint = newValue == null || Integer.parseInt(newValue) != 0; + updateListening(); + updateCurrentMatrix(); + } else if (Secure.TWILIGHT_MODE.equals(key)) { + mIsAuto = newValue != null && Integer.parseInt(newValue) >= Secure.TWILIGHT_MODE_AUTO; + } + } + + private void updateCurrentMatrix() { + if (!mUpdateMatrix) return; + if ((!mAdjustTint || mAmount == 0) && mCustomMatrix == null) { + TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null); + return; + } + float[] values = scaleValues(IDENTITY_MATRIX, NIGHT_VALUES, mAdjustTint ? mAmount : 0); + if (mCustomMatrix != null) { + values = multiply(values, mCustomMatrix); + } + TunerService.get(mContext).setValue(Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, + toString(values)); + } + + private void updateNightMode(Intent intent) { + mIsNight = intent.getBooleanExtra(EXTRA_IS_NIGHT, false); + mAmount = intent.getFloatExtra(EXTRA_AMOUNT, 0); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_TWILIGHT_CHANGED.equals(intent.getAction())) { + updateNightMode(intent); + updateCurrentMatrix(); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onNightModeChanged(); + } + } + } + }; + + public interface Listener { + void onNightModeChanged(); + void onTwilightAutoChanged(); + } + + private static float[] multiply(float[] matrix, float[] other) { + if (matrix == null) { + return other; + } + float[] result = new float[16]; + Matrix.multiplyMM(result, 0, matrix, 0, other, 0); + return result; + } + + private float[] scaleValues(float[] identityMatrix, float[] nightValues, float amount) { + float[] values = new float[identityMatrix.length]; + for (int i = 0; i < values.length; i++) { + values[i] = MathUtils.lerp(identityMatrix[i], nightValues[i], amount); + } + return values; + } + + public static String toString(float[] values) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < values.length; i++) { + if (builder.length() != 0) { + builder.append(','); + } + builder.append(values[i]); + } + return builder.toString(); + } + + public static float[] toValues(String customValues) { + String[] strValues = customValues.split(","); + float[] values = new float[strValues.length]; + for (int i = 0; i < values.length; i++) { + values[i] = Float.parseFloat(strValues[i]); + } + return values; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/CalibratePreference.java b/packages/SystemUI/src/com/android/systemui/tuner/CalibratePreference.java new file mode 100644 index 000000000000..ff7be1367388 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/CalibratePreference.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.tuner; + +import android.content.Context; +import android.support.v7.preference.DialogPreference; +import android.util.AttributeSet; + +public class CalibratePreference extends DialogPreference { + public CalibratePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ColorAndAppearanceFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/ColorAndAppearanceFragment.java new file mode 100644 index 000000000000..9f11325c7d15 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/ColorAndAppearanceFragment.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.tuner; + + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.support.v14.preference.PreferenceFragment; +import android.support.v7.preference.Preference; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.SeekBar; + +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.NightModeController; + +public class ColorAndAppearanceFragment extends PreferenceFragment { + + private static final String KEY_CALIBRATE = "calibrate"; + + private static final long RESET_DELAY = 10000; + private static final CharSequence KEY_NIGHT_MODE = "night_mode"; + + private NightModeController mNightModeController; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNightModeController = new NightModeController(getContext()); + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.color_and_appearance); + } + + @Override + public void onResume() { + super.onResume(); + // TODO: Figure out better title model for Tuner, to avoid any more of this. + getActivity().setTitle(R.string.color_and_appearance); + + Preference nightMode = findPreference(KEY_NIGHT_MODE); + nightMode.setSummary(mNightModeController.isEnabled() + ? R.string.night_mode_on : R.string.night_mode_off); + } + + @Override + public void onDisplayPreferenceDialog(Preference preference) { + if (preference instanceof CalibratePreference) { + CalibrateDialog.show(this); + } else { + super.onDisplayPreferenceDialog(preference); + } + } + + private void startRevertTimer() { + getView().postDelayed(mResetColorMatrix, RESET_DELAY); + } + + private void onApply() { + mNightModeController.setCustomValues(Settings.Secure.getString( + getContext().getContentResolver(), Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX)); + getView().removeCallbacks(mResetColorMatrix); + } + + private void onRevert() { + getView().removeCallbacks(mResetColorMatrix); + mResetColorMatrix.run(); + } + + private final Runnable mResetColorMatrix = new Runnable() { + @Override + public void run() { + ((DialogFragment) getFragmentManager().findFragmentByTag("RevertWarning")).dismiss(); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null); + } + }; + + public static class CalibrateDialog extends DialogFragment implements + DialogInterface.OnClickListener { + private float[] mValues; + private NightModeController mNightModeController; + + public static void show(ColorAndAppearanceFragment fragment) { + CalibrateDialog dialog = new CalibrateDialog(); + dialog.setTargetFragment(fragment, 0); + dialog.show(fragment.getFragmentManager(), "Calibrate"); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNightModeController = new NightModeController(getContext()); + String customValues = mNightModeController.getCustomValues(); + if (customValues == null) { + // Generate this as a string because its the easiest way to generate a copy of the + // identity. + customValues = NightModeController.toString(NightModeController.IDENTITY_MATRIX); + } + mValues = NightModeController.toValues(customValues); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + View v = LayoutInflater.from(getContext()).inflate(R.layout.calibrate_sliders, null); + bindView(v.findViewById(R.id.r_group), 0); + bindView(v.findViewById(R.id.g_group), 5); + bindView(v.findViewById(R.id.b_group), 10); + return new AlertDialog.Builder(getContext()) + .setTitle(R.string.calibrate_display) + .setView(v) + .setPositiveButton(R.string.color_apply, this) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } + + private void bindView(View view, final int index) { + SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); + seekBar.setMax(1000); + seekBar.setProgress((int) (1000 * mValues[index])); + seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + mValues[index] = progress / 1000f; + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mValues[0] == 1 && mValues[5] == 1 && mValues[10] == 1) { + // Allow removal of matrix by all values set to highest. + mNightModeController.setCustomValues(null); + return; + } + ((ColorAndAppearanceFragment) getTargetFragment()).startRevertTimer(); + Settings.Secure.putString(getContext().getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, + NightModeController.toString(mValues)); + RevertWarning.show((ColorAndAppearanceFragment) getTargetFragment()); + } + } + + public static class RevertWarning extends DialogFragment + implements DialogInterface.OnClickListener { + + public static void show(ColorAndAppearanceFragment fragment) { + RevertWarning warning = new RevertWarning(); + warning.setTargetFragment(fragment, 0); + warning.show(fragment.getFragmentManager(), "RevertWarning"); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog alertDialog = new AlertDialog.Builder(getContext()) + .setTitle(R.string.color_revert_title) + .setMessage(R.string.color_revert_message) + .setPositiveButton(R.string.ok, this) + .create(); + alertDialog.setCanceledOnTouchOutside(true); + return alertDialog; + } + + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + ((ColorAndAppearanceFragment) getTargetFragment()).onRevert(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + ((ColorAndAppearanceFragment) getTargetFragment()).onApply(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixFragment.java deleted file mode 100644 index dfacd033a90e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixFragment.java +++ /dev/null @@ -1,341 +0,0 @@ -/** - * Copyright (c) 2015, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.tuner; - -import android.annotation.Nullable; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.ContentResolver; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.provider.Settings; -import android.support.v14.preference.PreferenceFragment; -import android.support.v14.preference.SwitchPreference; -import android.support.v7.preference.DropDownPreference; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceViewHolder; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.SeekBar; -import android.widget.Switch; - -import com.android.systemui.R; -import com.android.systemui.statusbar.phone.QSTileHost; -import com.android.systemui.statusbar.policy.DisplayController; - -import java.util.Objects; - -public class ColorMatrixFragment extends PreferenceFragment implements TunerService.Tunable { - - private static final String TAG = "ColorMatrixFragment"; - - private static final long RESET_DELAY = 10000; - - private boolean mCustomEnabled; - private DropDownPreference mSelectPreference; - private String mCurrentValue; - private String mCustomValues; - private SwitchPreference mEnableCustomPreference; - private MatrixPreference mCustomPreference; - private int mState; - private Switch mSwitch; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Context context = getContext(); - TunerService.get(context).addTunable(this, DisplayController.COLOR_MATRIX_CUSTOM_ENABLED, - DisplayController.COLOR_MATRIX_CUSTOM_VALUES, DisplayController.COLOR_STATE, - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - final View view = LayoutInflater.from(getContext()).inflate( - R.layout.color_matrix_settings, container, false); - ((ViewGroup) view).addView(super.onCreateView(inflater, container, savedInstanceState)); - return view; - } - - @Override - public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - final Context context = getPreferenceManager().getContext(); - setPreferenceScreen(getPreferenceManager().createPreferenceScreen(context)); - - mSelectPreference = new DropDownPreference(context); - mSelectPreference.setTitle(R.string.color_transform); - mSelectPreference.setSummary("%s"); - mSelectPreference.setOnPreferenceChangeListener( - new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (Objects.equals(newValue, DisplayController.AUTO_STRING)) { - Settings.Secure.putInt(context.getContentResolver(), - DisplayController.COLOR_STATE, - DisplayController.COLOR_STATE_AUTO); - return true; - } - if (Objects.equals(newValue, DisplayController.NONE_STRING)) { - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null); - return true; - } - Settings.Secure.putInt(context.getContentResolver(), - DisplayController.COLOR_STATE, - DisplayController.COLOR_STATE_ENABLED); - final String value = (String) newValue; - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, - value); - return true; - } - }); - getPreferenceScreen().addPreference(mSelectPreference); - - mEnableCustomPreference = new SwitchPreference(context); - mEnableCustomPreference.setTitle(R.string.color_enable_custom); - mEnableCustomPreference.setOnPreferenceChangeListener( - new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - boolean enabled = (Boolean) newValue; - if (!enabled && Objects.equals(mCurrentValue, mCustomValues)) { - Settings.Secure.putString(context.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null); - } - Settings.Secure.putInt(context.getContentResolver(), - DisplayController.COLOR_MATRIX_CUSTOM_ENABLED, enabled ? 1 : 0); - return true; - } - }); - getPreferenceScreen().addPreference(mEnableCustomPreference); - - mCustomPreference = new MatrixPreference(context); - getPreferenceScreen().addPreference(mCustomPreference); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - View switchBar = view.findViewById(R.id.switch_bar); - mSwitch = (Switch) switchBar.findViewById(android.R.id.switch_widget); - mSwitch.setChecked(mState != DisplayController.COLOR_STATE_DISABLED); - switchBar.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int newState = mState != DisplayController.COLOR_STATE_DISABLED - ? DisplayController.COLOR_STATE_DISABLED - : DisplayController.COLOR_STATE_ENABLED; - ContentResolver contentResolver = getContext().getContentResolver(); - if (newState == DisplayController.COLOR_STATE_DISABLED) { - String tiles = Settings.Secure.getString(contentResolver, - QSTileHost.TILES_SETTING); - if (tiles != null) { - if (tiles.contains(",colors")) { - tiles = tiles.replace(",colors", ""); - } else if (tiles.contains("colors,")) { - tiles = tiles.replace("colors,", ""); - } - Settings.Secure.putString(contentResolver, QSTileHost.TILES_SETTING, - tiles); - } - } - Settings.Secure.putInt(contentResolver, - DisplayController.COLOR_STATE, newState); - } - }); - } - - @Override - public void onDestroy() { - super.onDestroy(); - TunerService.get(getContext()).removeTunable(this); - } - - @Override - public void onTuningChanged(String key, String newValue) { - if (DisplayController.COLOR_MATRIX_CUSTOM_ENABLED.equals(key)) { - mCustomEnabled = newValue != null && Integer.parseInt(newValue) != 0; - mEnableCustomPreference.setChecked(mCustomEnabled); - mCustomPreference.setEnabled(mCustomEnabled - && mState != DisplayController.COLOR_STATE_DISABLED); - updateSelectOptions(); - } else if (DisplayController.COLOR_MATRIX_CUSTOM_VALUES.equals(key)) { - mCustomValues = newValue; - if (mCustomValues == null) { - mCustomValues = DisplayController.toString(DisplayController.IDENTITY_MATRIX); - } - mCustomPreference.setValues(mCustomValues); - updateSelectOptions(); - } else if (DisplayController.COLOR_STATE.equals(key)) { - mState = newValue != null ? Integer.parseInt(newValue) : 0; - if (mSwitch != null) { - mSwitch.setChecked(mState != DisplayController.COLOR_STATE_DISABLED); - } - mSelectPreference.setEnabled(mState != DisplayController.COLOR_STATE_DISABLED); - mEnableCustomPreference.setEnabled(mState != DisplayController.COLOR_STATE_DISABLED); - mCustomPreference.setEnabled(mCustomEnabled - && mState != DisplayController.COLOR_STATE_DISABLED); - } else { - mCurrentValue = newValue; - updateSelectOptions(); - } - } - - private void updateSelectOptions() { - final int N = DisplayController.CUSTOM_INDEX + (mCustomEnabled ? 1 : 0); - String[] values = new String[N]; - CharSequence[] names = new CharSequence[N]; - CharSequence[] totalNames = DisplayController.getColorTitles(getContext()); - String[] entries = DisplayController.getColorTransforms(getContext()); - entries[DisplayController.CUSTOM_INDEX] = mCustomValues != null ? mCustomValues : ""; - for (int i = 0; i < N; i++) { - values[i] = entries[i]; - names[i] = totalNames[i]; - } - mSelectPreference.setEntries(names); - mSelectPreference.setEntryValues(values); - int index = 0; - if (mState == DisplayController.COLOR_STATE_AUTO) { - index = DisplayController.AUTO_INDEX; - } else if (mCustomValues != null && Objects.equals(mCurrentValue, mCustomValues)) { - index = DisplayController.CUSTOM_INDEX; - } else if (Objects.equals(mCurrentValue, entries[1])) { - index = 1; - } - mSelectPreference.setValueIndex(index); - mSelectPreference.setSummary("%s"); - return; - } - - private void startRevertTimer() { - getView().postDelayed(mResetColorMatrix, RESET_DELAY); - } - - private void onApply() { - Settings.Secure.putString(getContext().getContentResolver(), - DisplayController.COLOR_MATRIX_CUSTOM_VALUES, mCurrentValue); - getView().removeCallbacks(mResetColorMatrix); - } - - private void onRevert() { - getView().removeCallbacks(mResetColorMatrix); - mResetColorMatrix.run(); - } - - private final Runnable mResetColorMatrix = new Runnable() { - @Override - public void run() { - ((DialogFragment) getFragmentManager().findFragmentByTag("RevertWarning")).dismiss(); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, null); - } - }; - - private class MatrixPreference extends Preference implements View.OnClickListener { - private float[] mValues; - - public MatrixPreference(Context context) { - super(context); - setLayoutResource(R.layout.preference_matrix); - } - - public void setValues(String customValues) { - String[] strValues = customValues.split(","); - mValues = new float[strValues.length]; - for (int i = 0; i < mValues.length; i++) { - mValues[i] = Float.parseFloat(strValues[i]); - } - notifyChanged(); - } - - @Override - public void onBindViewHolder(PreferenceViewHolder holder) { - super.onBindViewHolder(holder); - bindView(holder.findViewById(R.id.r_group), 0); - bindView(holder.findViewById(R.id.g_group), 5); - bindView(holder.findViewById(R.id.b_group), 10); - holder.findViewById(R.id.apply).setOnClickListener(this); - } - - private void bindView(View view, final int index) { - SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); - seekBar.setMax(1000); - seekBar.setProgress((int) (1000 * mValues[index])); - seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - mValues[index] = progress / 1000f; - } - - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - } - - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - } - - @Override - public void onClick(View v) { - startRevertTimer(); - Settings.Secure.putString(getContext().getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, - DisplayController.toString(mValues)); - RevertWarning.show(ColorMatrixFragment.this); - } - - } - - public static class RevertWarning extends DialogFragment - implements DialogInterface.OnClickListener { - - public static void show(ColorMatrixFragment fragment) { - RevertWarning warning = new RevertWarning(); - warning.setTargetFragment(fragment, 0); - warning.show(fragment.getFragmentManager(), "RevertWarning"); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - AlertDialog alertDialog = new AlertDialog.Builder(getContext()) - .setTitle(R.string.color_revert_title) - .setMessage(R.string.color_revert_message) - .setPositiveButton(R.string.ok, this) - .create(); - alertDialog.setCanceledOnTouchOutside(true); - return alertDialog; - } - - @Override - public void onCancel(DialogInterface dialog) { - super.onCancel(dialog); - ((ColorMatrixFragment) getTargetFragment()).onRevert(); - } - - @Override - public void onClick(DialogInterface dialog, int which) { - ((ColorMatrixFragment) getTargetFragment()).onApply(); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java b/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java deleted file mode 100644 index d8cf2e2bcbdc..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tuner/ColorMatrixTile.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) 2015, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.tuner; - -import android.app.ActivityManager; -import android.provider.Settings; - -import com.android.internal.logging.MetricsProto.MetricsEvent; -import com.android.systemui.R; -import com.android.systemui.qs.QSTile; -import com.android.systemui.statusbar.policy.DisplayController; - -import java.util.Objects; - - -public class ColorMatrixTile extends QSTile<QSTile.State> implements DisplayController.Listener { - - public static final String COLOR_MATRIX_SPEC = "colors"; - - private final DisplayController mDisplayController; - - private int mIndex; - private String mCurrentValue; - - private boolean mCustomEnabled; - private String[] mValues; - private CharSequence[] mValueTitles; - - public ColorMatrixTile(Host host) { - super(host); - mDisplayController = host.getDisplayController(); - } - - @Override - public void setListening(boolean listening) { - if (listening) { - mValues = DisplayController.getColorTransforms(mContext); - mValueTitles = DisplayController.getColorTitles(mContext); - mDisplayController.addListener(this); - } else { - mDisplayController.removeListener(this); - } - } - - @Override - public State newTileState() { - return new State(); - } - - @Override - protected void handleClick() { - mIndex++; - if (mIndex == DisplayController.AUTO_INDEX) { - mDisplayController.setAuto(true); - } else { - mDisplayController.setAuto(false); - if (!mDisplayController.isCustomEnabled() - && (mIndex == DisplayController.CUSTOM_INDEX)) { - mIndex++; - } - if (mIndex == mValues.length - 1) { - mIndex = 0; - } - Settings.Secure.putStringForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, mValues[mIndex], - ActivityManager.getCurrentUser()); - } - refreshState(); - } - - @Override - protected void handleUpdateState(State state, Object arg) { - if (mDisplayController.isAuto()) { - mIndex = DisplayController.AUTO_INDEX; - } else if (mDisplayController.isCustomSet()) { - mIndex = DisplayController.CUSTOM_INDEX; - } else { - mIndex = Objects.equals(mDisplayController.getCurrentMatrix(), mValues[1]) ? 1 : 0; - } - state.icon = ResourceIcon.get(R.drawable.ic_colorize); - state.label = mValueTitles[mIndex]; - state.contentDescription = mValueTitles[mIndex]; - } - - @Override - public void onCurrentMatrixChanged() { - refreshState(); - } - - @Override - public int getMetricsCategory() { - return MetricsEvent.QS_COLOR_MATRIX; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java new file mode 100644 index 000000000000..1377407cb2d9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/NightModeFragment.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.tuner; + +import android.annotation.Nullable; +import android.app.UiModeManager; +import android.content.ContentResolver; +import android.content.Context; +import android.os.Bundle; +import android.provider.Settings; +import android.provider.Settings.Secure; +import android.support.v14.preference.PreferenceFragment; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.DropDownPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Switch; + +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.QSTileHost; +import com.android.systemui.statusbar.policy.NightModeController; +import com.android.systemui.statusbar.policy.NightModeController.Listener; +import com.android.systemui.tuner.TunerService.Tunable; + +public class NightModeFragment extends PreferenceFragment implements Tunable, + Listener, OnPreferenceChangeListener { + + private static final String TAG = "NightModeFragment"; + private static final CharSequence KEY_AUTO = "auto"; + private static final CharSequence KEY_DARK_THEME = "dark_theme"; + private static final CharSequence KEY_ADJUST_TINT = "adjust_tint"; + private static final CharSequence KEY_ADJUST_BRIGHTNESS = "adjust_brightness"; + + private Switch mSwitch; + + private NightModeController mNightModeController; + private SwitchPreference mAutoSwitch; + private SwitchPreference mDarkTheme; + private SwitchPreference mAdjustTint; + private SwitchPreference mAdjustBrightness; + private UiModeManager mUiModeManager; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mNightModeController = new NightModeController(getContext()); + mUiModeManager = getContext().getSystemService(UiModeManager.class); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View view = LayoutInflater.from(getContext()).inflate( + R.layout.night_mode_settings, container, false); + ((ViewGroup) view).addView(super.onCreateView(inflater, container, savedInstanceState)); + return view; + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + final Context context = getPreferenceManager().getContext(); + + addPreferencesFromResource(R.xml.night_mode); + mAutoSwitch = (SwitchPreference) findPreference(KEY_AUTO); + mAutoSwitch.setOnPreferenceChangeListener(this); + mDarkTheme = (SwitchPreference) findPreference(KEY_DARK_THEME); + mDarkTheme.setOnPreferenceChangeListener(this); + mAdjustTint = (SwitchPreference) findPreference(KEY_ADJUST_TINT); + mAdjustTint.setOnPreferenceChangeListener(this); + mAdjustBrightness = (SwitchPreference) findPreference(KEY_ADJUST_BRIGHTNESS); + mAdjustBrightness.setOnPreferenceChangeListener(this); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + View switchBar = view.findViewById(R.id.switch_bar); + mSwitch = (Switch) switchBar.findViewById(android.R.id.switch_widget); + mSwitch.setChecked(mNightModeController.isEnabled()); + switchBar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean newState = !mNightModeController.isEnabled(); + mNightModeController.setNightMode(newState); + mSwitch.setChecked(newState); + } + }); + } + + @Override + public void onResume() { + super.onResume(); + mNightModeController.addListener(this); + TunerService.get(getContext()).addTunable(this, Secure.BRIGHTNESS_USE_TWILIGHT, + NightModeController.NIGHT_MODE_ADJUST_TINT); + mDarkTheme.setChecked(mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO); + calculateDisabled(); + } + + @Override + public void onPause() { + super.onPause(); + mNightModeController.removeListener(this); + TunerService.get(getContext()).removeTunable(this); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (mAutoSwitch == preference) { + mNightModeController.setAuto((Boolean) newValue); + } else if (mDarkTheme == preference) { + mUiModeManager.setNightMode(((Boolean) newValue) ? UiModeManager.MODE_NIGHT_AUTO + : UiModeManager.MODE_NIGHT_NO); + postCalculateDisabled(); + } else if (mAdjustTint == preference) { + mNightModeController.setAdjustTint((Boolean) newValue); + postCalculateDisabled(); + } else if (mAdjustBrightness == preference) { + TunerService.get(getContext()).setValue(Secure.BRIGHTNESS_USE_TWILIGHT, + ((Boolean) newValue) ? 1 : 0); + postCalculateDisabled(); + } else { + return false; + } + return true; + } + + private void postCalculateDisabled() { + // Post this because its the easiest way to wait for all state to be calculated. + getView().post(new Runnable() { + @Override + public void run() { + calculateDisabled(); + } + }); + } + + private void calculateDisabled() { + int enabledCount = (mDarkTheme.isChecked() ? 1 : 0) + + (mAdjustTint.isChecked() ? 1 : 0) + + (mAdjustBrightness.isChecked() ? 1 : 0); + if (enabledCount == 1) { + if (mDarkTheme.isChecked()) { + mDarkTheme.setEnabled(false); + } else if (mAdjustTint.isChecked()) { + mAdjustTint.setEnabled(false); + } else { + mAdjustBrightness.setEnabled(false); + } + } else { + mDarkTheme.setEnabled(true); + mAdjustTint.setEnabled(true); + mAdjustBrightness.setEnabled(true); + } + } + + @Override + public void onTuningChanged(String key, String newValue) { + if (Secure.BRIGHTNESS_USE_TWILIGHT.equals(key)) { + mAdjustBrightness.setChecked(newValue != null && Integer.parseInt(newValue) != 0); + } else if (NightModeController.NIGHT_MODE_ADJUST_TINT.equals(key)) { + // Default on. + mAdjustTint.setChecked(newValue == null || Integer.parseInt(newValue) != 0); + } + } + + @Override + public void onNightModeChanged() { + mSwitch.setChecked(mNightModeController.isEnabled()); + } + + @Override + public void onTwilightAutoChanged() { + mAutoSwitch.setChecked(mNightModeController.isAuto()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java new file mode 100644 index 000000000000..1311f307c7dc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tuner/NightModeTile.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.tuner; + +import android.app.ActivityManager; +import android.provider.Settings; + +import com.android.internal.logging.MetricsProto.MetricsEvent; +import com.android.systemui.R; +import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.policy.NightModeController; + +import java.util.Objects; + + +public class NightModeTile extends QSTile<QSTile.State> implements NightModeController.Listener { + + public static final String NIGHT_MODE_SPEC = "night"; + + private final NightModeController mNightModeController; + + private int mIndex; + private String mCurrentValue; + + private boolean mCustomEnabled; + private String[] mValues; + private CharSequence[] mValueTitles; + + public NightModeTile(Host host) { + super(host); + mNightModeController = host.getNightModeController(); + } + + @Override + public void setListening(boolean listening) { + if (listening) { + mNightModeController.addListener(this); + refreshState(); + } else { + mNightModeController.removeListener(this); + } + } + + @Override + public State newTileState() { + return new State(); + } + + @Override + protected void handleClick() { + mNightModeController.setNightMode(!mNightModeController.isEnabled()); + refreshState(); + } + + @Override + protected void handleUpdateState(State state, Object arg) { + // TODO: Right now this is just a dropper, needs an actual night icon. + boolean enabled = mNightModeController.isEnabled(); + state.icon = ResourceIcon.get(enabled ? R.drawable.ic_night_mode + : R.drawable.ic_night_mode_disabled); + state.label = mContext.getString(R.string.night_mode); + state.contentDescription = mContext.getString(R.string.night_mode); + } + + @Override + public void onNightModeChanged() { + refreshState(); + } + + @Override + public void onTwilightAutoChanged() { + // Don't care. + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.QS_COLOR_MATRIX; + } +} diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 1908f72dfd51..5c80d04da10e 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -60,22 +60,8 @@ class AutomaticBrightnessController { // non-zero, which in turn ensures that the total weight is non-zero. private static final long AMBIENT_LIGHT_PREDICTION_TIME_MILLIS = 100; - // If true, enables the use of the current time as an auto-brightness adjustment. - // The basic idea here is to expand the dynamic range of auto-brightness - // when it is especially dark outside. The light sensor tends to perform - // poorly at low light levels so we compensate for it by making an - // assumption about the environment. - private static final boolean USE_TWILIGHT_ADJUSTMENT = - PowerManager.useTwilightAdjustmentFeature(); - // Specifies the maximum magnitude of the time of day adjustment. - private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1.5f; - - // The amount of time after or before sunrise over which to start adjusting - // the gamma. We want the change to happen gradually so that it is below the - // threshold of perceptibility and so that the adjustment has maximum effect - // well after dusk. - private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2; + private static final float TWILIGHT_ADJUSTMENT_MAX_GAMMA = 1f; // Debounce for sampling user-initiated changes in display brightness to ensure // the user is satisfied with the result before storing the sample. @@ -193,6 +179,8 @@ class AutomaticBrightnessController { private int mBrightnessAdjustmentSampleOldBrightness; private float mBrightnessAdjustmentSampleOldGamma; + private boolean mUseTwilight; + public AutomaticBrightnessController(Callbacks callbacks, Looper looper, SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor, @@ -221,10 +209,6 @@ class AutomaticBrightnessController { if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) { mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); } - - if (USE_TWILIGHT_ADJUSTMENT) { - mTwilight.registerListener(mTwilightListener, mHandler); - } } public int getAutomaticScreenBrightness() { @@ -235,7 +219,7 @@ class AutomaticBrightnessController { } public void configure(boolean enable, float adjustment, boolean dozing, - boolean userInitiatedChange) { + boolean userInitiatedChange, boolean useTwilight) { // While dozing, the application processor may be suspended which will prevent us from // receiving new information from the light sensor. On some devices, we may be able to // switch to a wake-up light sensor instead but for now we will simply disable the sensor @@ -244,6 +228,7 @@ class AutomaticBrightnessController { mDozing = dozing; boolean changed = setLightSensorEnabled(enable && !dozing); changed |= setScreenAutoBrightnessAdjustment(adjustment); + changed |= setUseTwilight(useTwilight); if (changed) { updateAutoBrightness(false /*sendUpdate*/); } @@ -252,6 +237,17 @@ class AutomaticBrightnessController { } } + private boolean setUseTwilight(boolean useTwilight) { + if (mUseTwilight == useTwilight) return false; + if (useTwilight) { + mTwilight.registerListener(mTwilightListener, mHandler); + } else { + mTwilight.unregisterListener(mTwilightListener); + } + mUseTwilight = useTwilight; + return true; + } + public void dump(PrintWriter pw) { pw.println(); pw.println("Automatic Brightness Controller Configuration:"); @@ -484,18 +480,13 @@ class AutomaticBrightnessController { } } - if (USE_TWILIGHT_ADJUSTMENT) { + if (mUseTwilight) { TwilightState state = mTwilight.getCurrentState(); if (state != null && state.isNight()) { final long now = System.currentTimeMillis(); - final float earlyGamma = - getTwilightGamma(now, state.getYesterdaySunset(), state.getTodaySunrise()); - final float lateGamma = - getTwilightGamma(now, state.getTodaySunset(), state.getTomorrowSunrise()); - gamma *= earlyGamma * lateGamma; + gamma *= 1 + state.getAmount() * TWILIGHT_ADJUSTMENT_MAX_GAMMA; if (DEBUG) { - Slog.d(TAG, "updateAutoBrightness: earlyGamma=" + earlyGamma - + ", lateGamma=" + lateGamma); + Slog.d(TAG, "updateAutoBrightness: twilight amount=" + state.getAmount()); } } } @@ -579,25 +570,6 @@ class AutomaticBrightnessController { } } - private static float getTwilightGamma(long now, long lastSunset, long nextSunrise) { - if (lastSunset < 0 || nextSunrise < 0 - || now < lastSunset || now > nextSunrise) { - return 1.0f; - } - - if (now < lastSunset + TWILIGHT_ADJUSTMENT_TIME) { - return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA, - (float)(now - lastSunset) / TWILIGHT_ADJUSTMENT_TIME); - } - - if (now > nextSunrise - TWILIGHT_ADJUSTMENT_TIME) { - return MathUtils.lerp(1.0f, TWILIGHT_ADJUSTMENT_MAX_GAMMA, - (float)(nextSunrise - now) / TWILIGHT_ADJUSTMENT_TIME); - } - - return TWILIGHT_ADJUSTMENT_MAX_GAMMA; - } - private final class AutomaticBrightnessHandler extends Handler { public AutomaticBrightnessHandler(Looper looper) { super(looper, null, true /*async*/); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1038d975ef55..1ed7070d2586 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -615,7 +615,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && mPowerRequest.brightnessSetByUser; mAutomaticBrightnessController.configure(autoBrightnessEnabled, mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON, - userInitiatedChange); + userInitiatedChange, mPowerRequest.useTwilight); } // Apply brightness boost. diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index a1f24f772c3c..dbaa598fb1b2 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -49,6 +49,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; +import android.provider.Settings.Secure; import android.service.dreams.DreamManagerInternal; import android.util.EventLog; import android.util.Slog; @@ -468,6 +469,9 @@ public final class PowerManagerService extends SystemService private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>(); + // True if brightness should be affected by twilight. + private boolean mBrightnessUseTwilight; + private native void nativeInit(); private static native void nativeAcquireSuspendBlocker(String name); @@ -620,6 +624,9 @@ public final class PowerManagerService extends SystemService resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.DOUBLE_TAP_TO_WAKE), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Secure.BRIGHTNESS_USE_TWILIGHT), + false, mSettingsObserver, UserHandle.USER_ALL); // Go. readConfigurationLocked(); updateSettingsLocked(); @@ -726,6 +733,9 @@ public final class PowerManagerService extends SystemService Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); + mBrightnessUseTwilight = Settings.Secure.getIntForUser(resolver, + Secure.BRIGHTNESS_USE_TWILIGHT, 0, UserHandle.USER_CURRENT) != 0; + final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE, 0) != 0; final boolean autoLowPowerModeConfigured = Settings.Global.getInt(resolver, @@ -1997,6 +2007,7 @@ public final class PowerManagerService extends SystemService mDisplayPowerRequest.useProximitySensor = shouldUseProximitySensorLocked(); mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled; mDisplayPowerRequest.boostScreenBrightness = mScreenBrightnessBoostInProgress; + mDisplayPowerRequest.useTwilight = mBrightnessUseTwilight; if (mDisplayPowerRequest.policy == DisplayPowerRequest.POLICY_DOZE) { mDisplayPowerRequest.dozeScreenState = mDozeScreenStateOverrideFromDreamManager; diff --git a/services/core/java/com/android/server/twilight/TwilightManager.java b/services/core/java/com/android/server/twilight/TwilightManager.java index b3de58b279af..56137a406757 100644 --- a/services/core/java/com/android/server/twilight/TwilightManager.java +++ b/services/core/java/com/android/server/twilight/TwilightManager.java @@ -20,5 +20,6 @@ import android.os.Handler; public interface TwilightManager { void registerListener(TwilightListener listener, Handler handler); + void unregisterListener(TwilightListener listener); TwilightState getCurrentState(); } diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java index a71961c8fe9b..ac1ab64a1e81 100644 --- a/services/core/java/com/android/server/twilight/TwilightService.java +++ b/services/core/java/com/android/server/twilight/TwilightService.java @@ -19,12 +19,15 @@ package com.android.server.twilight; import com.android.server.SystemService; import com.android.server.TwilightCalculator; +import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; @@ -33,6 +36,9 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.provider.Settings.Secure; import android.text.format.DateUtils; import android.text.format.Time; import android.util.Slog; @@ -54,6 +60,26 @@ public final class TwilightService extends SystemService { static final String ACTION_UPDATE_TWILIGHT_STATE = "com.android.server.action.UPDATE_TWILIGHT_STATE"; + // The amount of time after or before sunrise over which to start adjusting + // twilight affected things. We want the change to happen gradually so that + // it is below the threshold of perceptibility and so that the adjustment has + // maximum effect well after dusk. + private static final long TWILIGHT_ADJUSTMENT_TIME = DateUtils.HOUR_IN_MILLIS * 2; + + // Broadcast when twilight changes. + public static final String ACTION_TWILIGHT_CHANGED = "android.intent.action.TWILIGHT_CHANGED"; + + public static final String EXTRA_IS_NIGHT = "isNight"; + public static final String EXTRA_AMOUNT = "amount"; + + // Amount of time the TwilightService will stay locked in an override state before switching + // back to auto. + private static final long RESET_TIME = DateUtils.HOUR_IN_MILLIS * 2; + private static final String EXTRA_RESET_USER = "user"; + + private static final String ACTION_RESET_TWILIGHT_AUTO = + "com.android.server.action.RESET_TWILIGHT_AUTO"; + final Object mLock = new Object(); AlarmManager mAlarmManager; @@ -65,6 +91,10 @@ public final class TwilightService extends SystemService { TwilightState mTwilightState; + private int mCurrentUser; + private boolean mLocked; + private boolean mBootCompleted; + public TwilightService(Context context) { super(context); } @@ -75,14 +105,93 @@ public final class TwilightService extends SystemService { mLocationManager = (LocationManager) getContext().getSystemService( Context.LOCATION_SERVICE); mLocationHandler = new LocationHandler(); + mCurrentUser = ActivityManager.getCurrentUser(); IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(ACTION_UPDATE_TWILIGHT_STATE); - getContext().registerReceiver(mUpdateLocationReceiver, filter); + getContext().registerReceiver(mReceiver, filter); publishLocalService(TwilightManager.class, mService); + getContext().getContentResolver().registerContentObserver( + Secure.getUriFor(Secure.TWILIGHT_MODE), false, mContentObserver, mCurrentUser); + mContentObserver.onChange(true); + } + + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_BOOT_COMPLETED) { + mBootCompleted = true; + sendBroadcast(); + } + } + + private void reregisterSettingObserver() { + final ContentResolver contentResolver = getContext().getContentResolver(); + contentResolver.unregisterContentObserver(mContentObserver); + contentResolver.registerContentObserver(Secure.getUriFor(Secure.TWILIGHT_MODE), false, + mContentObserver, mCurrentUser); + mContentObserver.onChange(true); + } + + private void setLockedState(TwilightState state) { + synchronized (mLock) { + // Make sure we aren't locked so we can set the state. + mLocked = false; + setTwilightState(state); + // Make sure we leave the state locked, so it cant be changed. + mLocked = true; + // TODO: Don't bother updating state when locked. + } + } + + private void setTwilightState(TwilightState state) { + synchronized (mLock) { + if (mLocked) { + // State has been locked by secure setting, shouldn't be changed. + return; + } + if (!Objects.equal(mTwilightState, state)) { + if (DEBUG) { + Slog.d(TAG, "Twilight state changed: " + state); + } + + mTwilightState = state; + + final int listenerLen = mListeners.size(); + for (int i = 0; i < listenerLen; i++) { + mListeners.get(i).postUpdate(); + } + } + } + sendBroadcast(); + } + + private void sendBroadcast() { + synchronized (mLock) { + if (mTwilightState == null) { + return; + } + if (mBootCompleted) { + Intent intent = new Intent(ACTION_TWILIGHT_CHANGED); + intent.putExtra(EXTRA_IS_NIGHT, mTwilightState.isNight()); + intent.putExtra(EXTRA_AMOUNT, mTwilightState.getAmount()); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); + } + } + } + + private void scheduleReset() { + long resetTime = System.currentTimeMillis() + RESET_TIME; + Intent resetIntent = new Intent(ACTION_RESET_TWILIGHT_AUTO); + resetIntent.putExtra(EXTRA_RESET_USER, mCurrentUser); + PendingIntent pendingIntent = PendingIntent.getBroadcast( + getContext(), 0, resetIntent, 0); + mAlarmManager.cancel(pendingIntent); + mAlarmManager.setExact(AlarmManager.RTC, resetTime, pendingIntent); } private static class TwilightListenerRecord implements Runnable { @@ -133,24 +242,22 @@ public final class TwilightService extends SystemService { } } } - }; - private void setTwilightState(TwilightState state) { - synchronized (mLock) { - if (!Objects.equal(mTwilightState, state)) { - if (DEBUG) { - Slog.d(TAG, "Twilight state changed: " + state); + @Override + public void unregisterListener(TwilightListener listener) { + synchronized (mLock) { + for (int i = 0; i < mListeners.size(); i++) { + if (mListeners.get(i).mListener == listener) { + mListeners.remove(i); + } } - mTwilightState = state; - - final int listenerLen = mListeners.size(); - for (int i = 0; i < listenerLen; i++) { - mListeners.get(i).postUpdate(); + if (mListeners.size() == 0) { + mLocationHandler.disableLocationUpdates(); } } } - } + }; // The user has moved if the accuracy circles of the two locations don't overlap. private static boolean hasMoved(Location from, Location to) { @@ -183,6 +290,7 @@ public final class TwilightService extends SystemService { private static final int MSG_GET_NEW_LOCATION_UPDATE = 2; private static final int MSG_PROCESS_NEW_LOCATION = 3; private static final int MSG_DO_TWILIGHT_UPDATE = 4; + private static final int MSG_DISABLE_LOCATION_UPDATES = 5; private static final long LOCATION_UPDATE_MS = 24 * DateUtils.HOUR_IN_MILLIS; private static final long MIN_LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS; @@ -210,6 +318,10 @@ public final class TwilightService extends SystemService { sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES); } + public void disableLocationUpdates() { + sendEmptyMessage(MSG_DISABLE_LOCATION_UPDATES); + } + public void requestLocationUpdate() { sendEmptyMessage(MSG_GET_NEW_LOCATION_UPDATE); } @@ -311,6 +423,11 @@ public final class TwilightService extends SystemService { } break; + case MSG_DISABLE_LOCATION_UPDATES: + mLocationManager.removeUpdates(mLocationListener); + removeMessages(MSG_ENABLE_LOCATION_UPDATES); + break; + case MSG_DO_TWILIGHT_UPDATE: updateTwilightState(); break; @@ -368,11 +485,6 @@ public final class TwilightService extends SystemService { final long now = System.currentTimeMillis(); - // calculate yesterday's twilight - mTwilightCalculator.calculateTwilight(now - DateUtils.DAY_IN_MILLIS, - mLocation.getLatitude(), mLocation.getLongitude()); - final long yesterdaySunset = mTwilightCalculator.mSunset; - // calculate today's twilight mTwilightCalculator.calculateTwilight(now, mLocation.getLatitude(), mLocation.getLongitude()); @@ -385,9 +497,19 @@ public final class TwilightService extends SystemService { mLocation.getLatitude(), mLocation.getLongitude()); final long tomorrowSunrise = mTwilightCalculator.mSunrise; + float amount = 0; + if (isNight) { + if (todaySunrise == -1 || todaySunset == -1) { + amount = 1; + } else if (now > todaySunset) { + amount = Math.min(1, (now - todaySunset) / (float) TWILIGHT_ADJUSTMENT_TIME); + } else { + amount = Math.max(0, 1 + - (todaySunrise - now) / (float) TWILIGHT_ADJUSTMENT_TIME); + } + } // set twilight state - TwilightState state = new TwilightState(isNight, yesterdaySunset, - todaySunrise, todaySunset, tomorrowSunrise); + TwilightState state = new TwilightState(isNight, amount); if (DEBUG) { Slog.d(TAG, "Updating twilight state: " + state); } @@ -402,12 +524,18 @@ public final class TwilightService extends SystemService { // add some extra time to be on the safe side. nextUpdate += DateUtils.MINUTE_IN_MILLIS; - if (now > todaySunset) { - nextUpdate += tomorrowSunrise; - } else if (now > todaySunrise) { - nextUpdate += todaySunset; + if (amount == 1 || amount == 0) { + if (now > todaySunset) { + nextUpdate += tomorrowSunrise; + } else if (now > todaySunrise) { + nextUpdate += todaySunset; + } else { + nextUpdate += todaySunrise; + } } else { - nextUpdate += todaySunrise; + // This is the update rate while transitioning. + // Leave at 10 min for now (one from above). + nextUpdate += 9 * DateUtils.MINUTE_IN_MILLIS; } } @@ -423,9 +551,37 @@ public final class TwilightService extends SystemService { } } - private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() { + private final ContentObserver mContentObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + int value = Secure.getIntForUser(getContext().getContentResolver(), + Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_LOCKED_OFF, mCurrentUser); + if (value == Secure.TWILIGHT_MODE_LOCKED_OFF) { + setLockedState(new TwilightState(false, 0)); + } else if (value == Secure.TWILIGHT_MODE_LOCKED_ON) { + setLockedState(new TwilightState(true, 1)); + } else if (value == Secure.TWILIGHT_MODE_AUTO_OVERRIDE_OFF) { + setLockedState(new TwilightState(false, 0)); + scheduleReset(); + } else if (value == Secure.TWILIGHT_MODE_AUTO_OVERRIDE_ON) { + setLockedState(new TwilightState(true, 1)); + scheduleReset(); + } else { + mLocked = false; + mLocationHandler.requestTwilightUpdate(); + } + } + }; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { + mCurrentUser = ActivityManager.getCurrentUser(); + reregisterSettingObserver(); + return; + } if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(intent.getAction()) && !intent.getBooleanExtra("state", false)) { // Airplane mode is now off! @@ -433,6 +589,12 @@ public final class TwilightService extends SystemService { return; } + if (ACTION_RESET_TWILIGHT_AUTO.equals(intent.getAction())) { + int user = intent.getIntExtra(EXTRA_RESET_USER, 0); + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Secure.TWILIGHT_MODE, Secure.TWILIGHT_MODE_AUTO, user); + return; + } // Time zone has changed or alarm expired. mLocationHandler.requestTwilightUpdate(); } diff --git a/services/core/java/com/android/server/twilight/TwilightState.java b/services/core/java/com/android/server/twilight/TwilightState.java index 91e24d7d55bf..81abc132011a 100644 --- a/services/core/java/com/android/server/twilight/TwilightState.java +++ b/services/core/java/com/android/server/twilight/TwilightState.java @@ -25,20 +25,11 @@ import java.util.Date; */ public class TwilightState { private final boolean mIsNight; - private final long mYesterdaySunset; - private final long mTodaySunrise; - private final long mTodaySunset; - private final long mTomorrowSunrise; + private final float mAmount; - TwilightState(boolean isNight, - long yesterdaySunset, - long todaySunrise, long todaySunset, - long tomorrowSunrise) { + TwilightState(boolean isNight, float amount) { mIsNight = isNight; - mYesterdaySunset = yesterdaySunset; - mTodaySunrise = todaySunrise; - mTodaySunset = todaySunset; - mTomorrowSunrise = tomorrowSunrise; + mAmount = amount; } /** @@ -49,35 +40,11 @@ public class TwilightState { } /** - * Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase, - * or -1 if the sun never sets. + * For twilight affects that change gradually over time, this is the amount they + * should currently be in effect. */ - public long getYesterdaySunset() { - return mYesterdaySunset; - } - - /** - * Returns the time of today's sunrise in the System.currentTimeMillis() timebase, - * or -1 if the sun never rises. - */ - public long getTodaySunrise() { - return mTodaySunrise; - } - - /** - * Returns the time of today's sunset in the System.currentTimeMillis() timebase, - * or -1 if the sun never sets. - */ - public long getTodaySunset() { - return mTodaySunset; - } - - /** - * Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase, - * or -1 if the sun never rises. - */ - public long getTomorrowSunrise() { - return mTomorrowSunrise; + public float getAmount() { + return mAmount; } @Override @@ -88,10 +55,7 @@ public class TwilightState { public boolean equals(TwilightState other) { return other != null && mIsNight == other.mIsNight - && mYesterdaySunset == other.mYesterdaySunset - && mTodaySunrise == other.mTodaySunrise - && mTodaySunset == other.mTodaySunset - && mTomorrowSunrise == other.mTomorrowSunrise; + && mAmount == other.mAmount; } @Override @@ -103,10 +67,7 @@ public class TwilightState { public String toString() { DateFormat f = DateFormat.getDateTimeInstance(); return "{TwilightState: isNight=" + mIsNight - + ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset)) - + ", mTodaySunrise=" + f.format(new Date(mTodaySunrise)) - + ", mTodaySunset=" + f.format(new Date(mTodaySunset)) - + ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise)) + + ", mAmount=" + mAmount + "}"; } } |