From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Sergii Pylypenko Date: Sun, 8 Apr 2018 17:55:02 -0700 Subject: [PATCH] SystemUI: Smart Pixels [1/2] SystemUI: screen-dimmer-pixel-filter Major credits to Sergii Pylypenko Change-Id: Ib2d7e18ad8fe2313dbf7593bf55a2cfec03ce567 Signed-off-by: Pranav Vashi SystemUI: Smart Pixels [1/2] Disables a percentage of pixels on screen to reduce power consumption. If enabled with battery saver, don't scale brightness at 0.5f for UX. Includes: - Option to enable on battery saver - User chosen grid - Burn-in protection Configurable via overlay and disabled by defualt: "config_supportSmartPixels" Change-Id: Id3c78548cb090ab2da11f543da31c5a408fb9fe9 Signed-off-by: Adin Kwok Signed-off-by: Pranav Vashi Signed-off-by: minaripenguin Smart Pixels: Switch to registered receiver Switching to a registered receiver allows to properly handle updates on enabling of battery saver mode and switching of users. Also only update screen filter with burn-in protection when the device is in an interactive state. Test: Service starts after rebooting with it enabled Service starts on battery saver mode (user toggle) Service starts on battery saver mode (auto-enabled) Service re-adjusts on user switch to current user settings Filter updates after selected timeout Change-Id: Iced17fd5cc49e0163754bf75782f8465b54e859b Signed-off-by: Pranav Vashi Smart Pixels: Dynamically register receiver Don't keep the receiver registered if it isn't enabled. Change-Id: If6975df536598ee19d0ee17ec4150ae1b055e18c Signed-off-by: Pranav Vashi SmartPixels: Use CoreStartable interface for receiver * Also clean up and add check whether smart pixels is supported. Signed-off-by: Pranav Vashi SystemUI: Add Smart Pixels tile Single tap enables/disables. Long press opens Smart Pixels settings. User is not allowed to enable or disable when Smart Pixels has been auto-enabled on battery saver. Change-Id: I535c84a0ca6360e1db351391e9d0fb9095896ee3 Signed-off-by: Pranav Vashi Signed-off-by: minaripenguin Smart Pixels: Update default grid pattern Change-Id: I826a5a2fdc3aaa9c64f59fbe8b28c8757ca31c58 Signed-off-by: Pranav Vashi SystemUI: mark smartpixels as a trusted overlay Change-Id: I1b5e17f5b4397e61350746b161d58366a19a1fc9 Signed-off-by: Pranav Vashi Fix long click intent for Smart Pixels tile [1/2] Co-authored-by: Joe Maples Co-authored-by: Pranav Vashi Co-authored-by: Adin Kwok Co-authored-by: Anay Wadhera Change-Id: Idf80bd3b54ddfde50ed0993c99076cbdf6b838f1 Signed-off-by: minaripenguin Signed-off-by: Anushek Prasal --- core/java/android/provider/Settings.java | 24 ++ core/res/res/values/config.xml | 3 + core/res/res/values/symbols.xml | 3 + packages/SystemUI/AndroidManifest.xml | 4 + .../res/drawable/ic_qs_smart_pixels.xml | 8 + packages/SystemUI/res/values/strings.xml | 4 + .../dagger/SystemUICoreStartableModule.kt | 7 + .../android/systemui/lineage/LineageModule.kt | 7 + .../systemui/qs/tiles/SmartPixelsTile.java | 176 +++++++++++++ .../android/systemui/smartpixels/Grids.java | 148 +++++++++++ .../smartpixels/SmartPixelsReceiver.java | 168 ++++++++++++ .../smartpixels/SmartPixelsService.java | 244 ++++++++++++++++++ .../display/DisplayPowerController.java | 17 +- 13 files changed, 810 insertions(+), 3 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_qs_smart_pixels.xml create mode 100644 packages/SystemUI/src/com/android/systemui/qs/tiles/SmartPixelsTile.java create mode 100644 packages/SystemUI/src/com/android/systemui/smartpixels/Grids.java create mode 100644 packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsReceiver.java create mode 100644 packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsService.java diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3241e6e3f0cb..cb5826aff17a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5530,6 +5530,30 @@ public final class Settings { @Readable public static final String VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control"; + /** + * Whether to enable Smart Pixels + * @hide + */ + public static final String SMART_PIXELS_ENABLE = "smart_pixels_enable"; + + /** + * Smart Pixels pattern + * @hide + */ + public static final String SMART_PIXELS_PATTERN = "smart_pixels_pattern"; + + /** + * Smart Pixels Shift Timeout + * @hide + */ + public static final String SMART_PIXELS_SHIFT_TIMEOUT = "smart_pixels_shift_timeout"; + + /** + * Whether Smart Pixels should enable on power saver mode + * @hide + */ + public static final String SMART_PIXELS_ON_POWER_SAVE = "smart_pixels_on_power_save"; + /** * IMPORTANT: If you add a new public settings you also have to add it to * PUBLIC_SETTINGS below. If the new setting is hidden you have to add diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index dbd475b52f02..fd0f91dd26e5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -6142,4 +6142,7 @@ false + + + true diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7d8e2f818ce9..a2efd0e8a12f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4916,4 +4916,7 @@ + + + diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 286155953bed..d7580661df40 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -427,6 +427,10 @@ + + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ba806e591c1c..7932b8a4cadc 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2867,4 +2867,8 @@ Priority mode on + + + Smart Pixels + Auto-enabled Smart Pixels diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 947888bfb187..6f9c0480cfea 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -45,6 +45,7 @@ import com.android.systemui.reardisplay.RearDisplayDialogController import com.android.systemui.recents.Recents import com.android.systemui.settings.dagger.MultiUserUtilsModule import com.android.systemui.shortcut.ShortcutKeyDispatcher +import com.android.systemui.smartpixels.SmartPixelsReceiver import com.android.systemui.statusbar.notification.fsi.FsiChromeRepo import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.notification.fsi.FsiChromeViewModelFactory @@ -305,4 +306,10 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(DreamMonitor::class) abstract fun bindDreamMonitor(sysui: DreamMonitor): CoreStartable + + /** Inject into SmartPixelsReceiver. */ + @Binds + @IntoMap + @ClassKey(SmartPixelsReceiver::class) + abstract fun bindSmartPixelsReceiver(sysui: SmartPixelsReceiver): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/lineage/LineageModule.kt b/packages/SystemUI/src/com/android/systemui/lineage/LineageModule.kt index f301530733a0..b9bb328fc5e0 100644 --- a/packages/SystemUI/src/com/android/systemui/lineage/LineageModule.kt +++ b/packages/SystemUI/src/com/android/systemui/lineage/LineageModule.kt @@ -25,6 +25,7 @@ import com.android.systemui.qs.tiles.HeadsUpTile import com.android.systemui.qs.tiles.PowerShareTile import com.android.systemui.qs.tiles.ProfilesTile import com.android.systemui.qs.tiles.ReadingModeTile +import com.android.systemui.qs.tiles.SmartPixelsTile import com.android.systemui.qs.tiles.SyncTile import com.android.systemui.qs.tiles.UsbTetherTile import com.android.systemui.qs.tiles.VpnTile @@ -85,6 +86,12 @@ interface LineageModule { @StringKey(ReadingModeTile.TILE_SPEC) fun bindReadingModeTile(readingModeTile: ReadingModeTile): QSTileImpl<*> + /** Inject SmartPixelsTile into tileMap in QSModule */ + @Binds + @IntoMap + @StringKey(SmartPixelsTile.TILE_SPEC) + fun bindSmartPixelsTile(smartPixelsTile: SmartPixelsTile): QSTileImpl<*> + /** Inject SyncTile into tileMap in QSModule */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SmartPixelsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SmartPixelsTile.java new file mode 100644 index 000000000000..041d83b6a1e3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SmartPixelsTile.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2018 CarbonROM + * Copyright (C) 2018 Adin Kwok (adinkwok) + * + * 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.qs.tiles; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.quicksettings.Tile; +import android.view.View; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.BatteryController; + +import javax.inject.Inject; + +public class SmartPixelsTile extends QSTileImpl implements + BatteryController.BatteryStateChangeCallback { + + public static final String TILE_SPEC = "smartpixels"; + + + private static final Intent SMART_PIXELS_SETTINGS = new Intent("android.settings.SMART_PIXELS_SETTINGS"); + + private final BatteryController mBatteryController; + + private boolean mSmartPixelsEnable; + private boolean mSmartPixelsOnPowerSave; + private boolean mLowPowerMode; + private boolean mListening; + + @Inject + public SmartPixelsTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + BatteryController batteryController) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mBatteryController = batteryController; + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + public void handleSetListening(boolean listening) { + if (listening) { + mBatteryController.addCallback(this); + } else { + mBatteryController.removeCallback(this); + } + } + + @Override + public boolean isAvailable() { + return mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_supportSmartPixels); + } + + @Override + public void handleClick(View v) { + mSmartPixelsEnable = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT) == 1); + mSmartPixelsOnPowerSave = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT) == 1); + if (mLowPowerMode && mSmartPixelsOnPowerSave) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT); + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + } else if (!mSmartPixelsEnable) { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 1, UserHandle.USER_CURRENT); + } else { + Settings.System.putIntForUser(mContext.getContentResolver(), + Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + } + refreshState(); + } + + @Override + public Intent getLongClickIntent() { + return SMART_PIXELS_SETTINGS; + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + mSmartPixelsEnable = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT) == 1); + mSmartPixelsOnPowerSave = (Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT) == 1); + state.icon = ResourceIcon.get(R.drawable.ic_qs_smart_pixels); + if (state.slash == null) { + state.slash = new SlashState(); + } + if (mLowPowerMode && mSmartPixelsOnPowerSave) { + state.label = mContext.getString(R.string.quick_settings_smart_pixels_on_power_save); + state.value = true; + } else if (mSmartPixelsEnable) { + state.label = mContext.getString(R.string.quick_settings_smart_pixels); + state.value = true; + } else { + state.label = mContext.getString(R.string.quick_settings_smart_pixels); + state.value = false; + } + state.slash.isSlashed = !state.value; + state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_smart_pixels); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.QS_BATTERY_TILE; + } + + @Override + public void onBatteryLevelChanged(int level, boolean plugged, boolean charging) { + // yurt + } + + @Override + public void onPowerSaveChanged(boolean active) { + mLowPowerMode = active; + refreshState(); + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/smartpixels/Grids.java b/packages/SystemUI/src/com/android/systemui/smartpixels/Grids.java new file mode 100644 index 000000000000..e29381c38485 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/smartpixels/Grids.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2015, Sergii Pylypenko + * (c) 2018, Joe Maples + * (c) 2018, CarbonROM + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of screen-dimmer-pixel-filter nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.systemui.smartpixels; + +public class Grids { + + public static final int GridSize = 64; + public static final int GridSideSize = 8; + + public static String[] PatternNames = new String[] { + "12%", + "25%", + "38%", + "50%", + "62%", + "75%", + "88%", + }; + + public static byte[][] Patterns = new byte[][] { + { + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, + }, + { + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1, + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1, + }, + { + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 0, 0, 1, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 0, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + }, + { + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 0, 1, 0, 1, 0, 1, + }, + { + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 0, 1, 0, 1, 0, + }, + { + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 0, + }, + { + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 1, + 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 0, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 1, 0, 1, 1, 1, + }, + }; + + // Indexes to shift screen pattern in both vertical and horizontal directions + public static byte[] GridShift = new byte[] { + 0, 1, 8, 9, 2, 3, 10, 11, + 4, 5, 12, 13, 6, 7, 14, 15, + 16, 17, 24, 25, 18, 19, 26, 27, + 20, 21, 28, 29, 22, 23, 30, 31, + 32, 33, 40, 41, 34, 35, 42, 43, + 36, 37, 44, 45, 38, 39, 46, 47, + 48, 49, 56, 57, 50, 51, 58, 59, + 52, 53, 60, 61, 54, 55, 62, 63, + }; + + public static int[] ShiftTimeouts = new int[] { // In milliseconds + 15 * 1000, + 30 * 1000, + 60 * 1000, + 2 * 60 * 1000, + 5 * 60 * 1000, + 10 * 60 * 1000, + 20 * 60 * 1000, + 30 * 60 * 1000, + 60 * 60 * 1000, + }; + +} diff --git a/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsReceiver.java b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsReceiver.java new file mode 100644 index 000000000000..5b6b8ee89e50 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsReceiver.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2018 CarbonROM + * + * 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.smartpixels; + +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.os.Handler; +import android.os.PowerManager; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.Log; + +import com.android.systemui.CoreStartable; +import com.android.systemui.dagger.SysUISingleton; + +import javax.inject.Inject; + +@SysUISingleton +public class SmartPixelsReceiver extends BroadcastReceiver implements CoreStartable { + private static final String TAG = "SmartPixelsReceiver"; + + private Context mContext; + private Handler mHandler = new Handler(); + private ContentResolver mResolver; + private PowerManager mPowerManager; + private SettingsObserver mSettingsObserver; + private Intent mSmartPixelsService; + private IntentFilter mFilter; + + private boolean mEnabled; + private boolean mOnPowerSave; + private boolean mPowerSave; + private boolean mServiceRunning = false; + private boolean mRegisteredReceiver = false; + + @Inject + public SmartPixelsReceiver(Context context) { + mContext = context; + } + + @Override + public void start() { + if (!mContext.getResources(). + getBoolean(com.android.internal.R.bool.config_supportSmartPixels)) + return; + + mSmartPixelsService = new Intent(mContext, + com.android.systemui.smartpixels.SmartPixelsService.class); + + mFilter = new IntentFilter(); + mFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + mFilter.addAction(Intent.ACTION_USER_FOREGROUND); + + initiateSettingsObserver(); + } + + private void registerReceiver() { + mContext.registerReceiver(this, mFilter); + mRegisteredReceiver = true; + } + + private void unregisterReceiver() { + mContext.unregisterReceiver(this); + mRegisteredReceiver = false; + } + + private void initiateSettingsObserver() { + mResolver = mContext.getContentResolver(); + mSettingsObserver = new SettingsObserver(mHandler); + mSettingsObserver.observe(); + mSettingsObserver.update(); + } + + private class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + } + + void observe() { + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_ENABLE), + false, this, UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_ON_POWER_SAVE), + false, this, UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_PATTERN), + false, this, UserHandle.USER_ALL); + mResolver.registerContentObserver(Settings.System.getUriFor( + Settings.System.SMART_PIXELS_SHIFT_TIMEOUT), + false, this, UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + public void update() { + mEnabled = (Settings.System.getIntForUser( + mResolver, Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT) == 1); + mOnPowerSave = (Settings.System.getIntForUser( + mResolver, Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT) == 1); + mPowerSave = mPowerManager.isPowerSaveMode(); + + if (mEnabled || mOnPowerSave) { + if (!mRegisteredReceiver) + registerReceiver(); + } else if (mRegisteredReceiver) { + unregisterReceiver(); + } + + if (!mEnabled && mOnPowerSave) { + if (mPowerSave && !mServiceRunning) { + mContext.startService(mSmartPixelsService); + mServiceRunning = true; + Log.d(TAG, "Started Smart Pixels Service by Power Save enable"); + } else if (!mPowerSave && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mServiceRunning = false; + Log.d(TAG, "Stopped Smart Pixels Service by Power Save disable"); + } else if (mPowerSave && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mContext.startService(mSmartPixelsService); + Log.d(TAG, "Restarted Smart Pixels Service by Power Save enable"); + } + } else if (mEnabled && !mServiceRunning) { + mContext.startService(mSmartPixelsService); + mServiceRunning = true; + Log.d(TAG, "Started Smart Pixels Service by enable"); + } else if (!mEnabled && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mServiceRunning = false; + Log.d(TAG, "Stopped Smart Pixels Service by disable"); + } else if (mEnabled && mServiceRunning) { + mContext.stopService(mSmartPixelsService); + mContext.startService(mSmartPixelsService); + Log.d(TAG, "Restarted Smart Pixels Service"); + } + } + } + + @Override + public void onReceive(final Context context, Intent intent) { + mSettingsObserver.update(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsService.java b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsService.java new file mode 100644 index 000000000000..77ec1c4ba235 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/smartpixels/SmartPixelsService.java @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2015, Sergii Pylypenko + * (c) 2018, Joe Maples + * (c) 2018, Adin Kwok + * (c) 2018, CarbonROM + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of screen-dimmer-pixel-filter nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +package com.android.systemui.smartpixels; + +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; + +import android.Manifest; +import android.app.Service; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.os.Handler; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; + +import com.android.systemui.R; + +public class SmartPixelsService extends Service { + public static final String LOG = "SmartPixelsService"; + + private WindowManager windowManager; + private ImageView view = null; + private Bitmap bmp; + + private boolean destroyed = false; + public static boolean running = false; + + private int startCounter = 0; + private Context mContext; + + // Pixel Filter Settings + private int mPattern = 3; + private int mShiftTimeout = 4; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + running = true; + mContext = this; + updateSettings(); + Log.d(LOG, "Service started"); + startFilter(); + } + + public void startFilter() { + if (view != null) { + return; + } + + windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); + + view = new ImageView(this); + DisplayMetrics metrics = new DisplayMetrics(); + windowManager.getDefaultDisplay().getRealMetrics(metrics); + bmp = Bitmap.createBitmap(Grids.GridSideSize, Grids.GridSideSize, Bitmap.Config.ARGB_4444); + + updatePattern(); + BitmapDrawable draw = new BitmapDrawable(bmp); + draw.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); + draw.setFilterBitmap(false); + draw.setAntiAlias(false); + draw.setTargetDensity(metrics.densityDpi); + + view.setBackground(draw); + + WindowManager.LayoutParams params = getLayoutParams(); + params.privateFlags |= PRIVATE_FLAG_TRUSTED_OVERLAY; + try { + windowManager.addView(view, params); + } catch (Exception e) { + running = false; + view = null; + return; + } + + startCounter++; + final int handlerStartCounter = startCounter; + final Handler handler = new Handler(); + final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (view == null || destroyed || handlerStartCounter != startCounter) { + return; + } else if (pm.isInteractive()) { + updatePattern(); + view.invalidate(); + } + if (!destroyed) { + handler.postDelayed(this, Grids.ShiftTimeouts[mShiftTimeout]); + } + } + }, Grids.ShiftTimeouts[mShiftTimeout]); + } + + public void stopFilter() { + if (view == null) { + return; + } + + startCounter++; + + windowManager.removeView(view); + view = null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + destroyed = true; + stopFilter(); + Log.d(LOG, "Service stopped"); + running = false; + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + Log.d(LOG, "Screen orientation changed, updating window layout"); + WindowManager.LayoutParams params = getLayoutParams(); + windowManager.updateViewLayout(view, params); + } + + private WindowManager.LayoutParams getLayoutParams() + { + Point displaySize = new Point(); + windowManager.getDefaultDisplay().getRealSize(displaySize); + Point windowSize = new Point(); + windowManager.getDefaultDisplay().getRealSize(windowSize); + Resources res = getResources(); + int mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height); + displaySize.x += displaySize.x - windowSize.x + (mStatusBarHeight * 2); + displaySize.y += displaySize.y - windowSize.y + (mStatusBarHeight * 2); + + WindowManager.LayoutParams params = new WindowManager.LayoutParams( + displaySize.x, + displaySize.y, + 0, + 0, + WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | + WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | + WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN | + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + PixelFormat.TRANSPARENT + ); + + // Use the rounded corners overlay to hide it from screenshots. See 132c9f514. + params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; + + params.dimAmount = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE; + params.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE; + return params; + } + + private int getShift() { + long shift = (System.currentTimeMillis() / Grids.ShiftTimeouts[mShiftTimeout]) % Grids.GridSize; + return Grids.GridShift[(int)shift]; + } + + private void updatePattern() { + int shift = getShift(); + int shiftX = shift % Grids.GridSideSize; + int shiftY = shift / Grids.GridSideSize; + for (int i = 0; i < Grids.GridSize; i++) { + int x = (i + shiftX) % Grids.GridSideSize; + int y = ((i / Grids.GridSideSize) + shiftY) % Grids.GridSideSize; + int color = (Grids.Patterns[mPattern][i] == 0) ? Color.TRANSPARENT : Color.BLACK; + bmp.setPixel(x, y, color); + } + } + + private void updateSettings() { + mPattern = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_PATTERN, + 5, UserHandle.USER_CURRENT); + mShiftTimeout = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_SHIFT_TIMEOUT, + 4, UserHandle.USER_CURRENT); + } +} diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 864ea43c8b01..99f778fed20d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -21,6 +21,7 @@ import android.animation.ObjectAnimator; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.ParceledListSlice; import android.content.res.Resources; @@ -1657,10 +1658,20 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call slowChange = false; mAppliedDimming = false; } - // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor - // as long as it is above the minimum threshold. + + // If low power mode is enabled and Smart Pixels Service is stopped, + // scale brightness by screenLowPowerBrightnessFactor + // as long as it is above the minimum threshold + final int mSmartPixelsEnable = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ENABLE, + 0, UserHandle.USER_CURRENT); + final int mSmartPixelsOnPowerSave = Settings.System.getIntForUser( + mContext.getContentResolver(), Settings.System.SMART_PIXELS_ON_POWER_SAVE, + 0, UserHandle.USER_CURRENT); + if (mPowerRequest.lowPowerMode) { - if (brightnessState > PowerManager.BRIGHTNESS_MIN) { + if ((brightnessState > PowerManager.BRIGHTNESS_MIN) && + ((mSmartPixelsEnable == 0) || (mSmartPixelsOnPowerSave == 0))) { final float brightnessFactor = Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1); final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor);