From fdb387454b12531b3394879f833e20542eb7f5cb Mon Sep 17 00:00:00 2001 From: Sam Mortimer Date: Sun, 22 Oct 2017 14:43:46 -0700 Subject: [PATCH] lineage-sdk internal: Add LineageNotificationLights Migrate frameworks/base lineage feature code handling to lineage-sdk. *) Settings observation and most lineage lights feature specific code has been moved to the lineage-sdk. *) Battery frameworks services call out to the sdk to allow our features to make changes to lights values. Based on the commits: Author: DvTonder Author: Ricardo Cerqueira Date: Mon Nov 4 00:57:51 2013 +0000 Framework: Port CM9 features to CM10 Change-Id: Ibd63116df90b06f6ce6adb8a0343059bbb999bfb Author: Pawit Pornkitprasan Date: Sun Dec 8 15:24:41 2013 +0700 BatteryService: fix FC on boot until battery stat is present updateLightsLocked() can be called from CM's added SettingsObserver when battery stat is not present, causing an FC and a loop until battery stat is present. Change-Id: Ic4438fe50e98f1aa05ae1d0d26240bf9410fd92f Author: Sam Mortimer Date: Tue Dec 31 16:22:05 2013 -0800 [2/2] Framework: instant led test Adds support a new notification extra boolean EXTRA_FORCE_SHOW_LIGHTS. Used by settings notification light picker to force lights on when the screen is on. Change-Id: If0a42d32b28fe8c02ef5f7dd148db7eb478fac17 Author: Michael Bestas Date: Mon Aug 18 04:56:28 2014 +0300 Add support for single color notification LED (1/2) Change-Id: I367af77036da9e87c6dd0df552ce4c56d945a44d Author: Danesh M Date: Thu, 12 Nov 2015 10:52:11 -0800 Framework : Move System settings to CMSettings Change-Id: I4e9fb06db7b9ba05e4a7bbe11916bb2271af4727 Author: Adnan Begovic Date: Mon, 9 Nov 2015 16:26:00 -0800 fw: Move battery light settings to CMSettings. Change-Id: I28e60473356b2a9af152df82d34ad7abc9918682 Author: Steve Kondik Date: Thu Sep 24 11:27:59 2015 -0700 lights: Automatically generate an LED color for notifications Change-Id: I7288e52499819a6a6c75ed9d9ba7cfa1b13c5669 nms: Only generate LED colors if the device has a multicolored LED * Check the overlay value before doing any of this stuff. Change-Id: Iedfceba6bfc86b2761d8af57ecab51026bfa4c19 Change-Id: I7288e52499819a6a6c75ed9d9ba7cfa1b13c5669 Author: Adrian DC Date: Sat Oct 14 23:08:47 2017 +0200 fw: Rebrand to LineageOS and cleanup for Android Oreo Change-Id: I21d424433bb52a17eea7974c4ea29a3a16fe1be5 Author: AdrianDC Date: Sat Jul 18 12:20:51 2015 +0200 Lights with Screen On [1/2]: Optional allowment of lights Change-Id: I2071147d8ddab80ba0e1e7310e785ac3e03b3c7c Lights with screen on: Don't disable leds after the lockscreen Change-Id: If8f5b867a34be09fb061bb7ad040b16730f4e263 Framework : Move System settings to CMSettings Change-Id: I4e9fb06db7b9ba05e4a7bbe11916bb2271af4727 fw: Rebrand to LineageOS and port for Android Oreo Change-Id: I65cfeb659fe516ef43aa84b1c6d2eb6876df202a Change-Id: If8f5b867a34be09fb061bb7ad040b16730f4e263 Author: Michael W Date: Mon Oct 9 22:04:00 2017 +0200 Core: Battery warning levels are inclusive, not exclusive Change-Id: Ib35b154b6117f7e26b4a3a5aee9254dda3adda12 Author: Adrian DC Date: Sat Oct 14 23:08:47 2017 +0200 fw: Rebrand to LineageOS and cleanup for Android Oreo Change-Id: I845f866891386aee808a4e7e80f4ab7c3ad48830 Change-Id: Ic4c9c5f5602515ccbc2c6b8b10bcf3d2c6e34b4b --- lineage/res/res/values/arrays.xml | 9 + lineage/res/res/values/symbols.xml | 3 + .../LineageNotificationLights.java | 354 ++++++++++++++++++ 3 files changed, 366 insertions(+) create mode 100644 sdk/src/java/org/lineageos/internal/notification/LineageNotificationLights.java diff --git a/lineage/res/res/values/arrays.xml b/lineage/res/res/values/arrays.xml index 44d29c6a..95b1c838 100644 --- a/lineage/res/res/values/arrays.xml +++ b/lineage/res/res/values/arrays.xml @@ -108,4 +108,13 @@ @string/accessibility_quick_settings_live_display_changed_outdoor + + + + com.google.android.gsf|com.google.android.talk + + diff --git a/lineage/res/res/values/symbols.xml b/lineage/res/res/values/symbols.xml index 417818f5..0256c23e 100644 --- a/lineage/res/res/values/symbols.xml +++ b/lineage/res/res/values/symbols.xml @@ -118,4 +118,7 @@ + + + diff --git a/sdk/src/java/org/lineageos/internal/notification/LineageNotificationLights.java b/sdk/src/java/org/lineageos/internal/notification/LineageNotificationLights.java new file mode 100644 index 00000000..beb10d90 --- /dev/null +++ b/sdk/src/java/org/lineageos/internal/notification/LineageNotificationLights.java @@ -0,0 +1,354 @@ +/** + * Copyright (C) 2015 The CyanogenMod Project + * Copyright (C) 2017 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.internal.notification; + +import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF; +import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON; + +import android.app.KeyguardManager; +import android.app.Notification; +import android.content.Context; +import android.content.ContentResolver; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.UserHandle; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Slog; + +import lineageos.providers.LineageSettings; +import lineageos.util.ColorUtils; + +import org.lineageos.internal.notification.LedValues; +import org.lineageos.internal.notification.LightsCapabilities; + +import java.util.Map; + +public final class LineageNotificationLights { + private static final String TAG = "LineageNotificationLights"; + private static final boolean DEBUG = false; + + // Light capabilities + private boolean mAdjustableNotificationLedBrightness; + private boolean mMultiColorNotificationLed; + + // Light config + private boolean mAutoGenerateNotificationColor; + private boolean mScreenOnEnabled; + private int mNotificationLedBrightnessLevel; + private int mDefaultNotificationColor; + private int mDefaultNotificationLedOn; + private int mDefaultNotificationLedOff; + + private ArrayMap mNotificationPulseCustomLedValues; + private Map mPackageNameMappings; + private final ArrayMap mGeneratedPackageLedColors = + new ArrayMap(); + + // For checking lockscreen status + private KeyguardManager mKeyguardManager; + + private final SettingsObserver mSettingsObserver; + + private final Context mContext; + + public interface LedUpdater { + public void update(); + } + private final LedUpdater mLedUpdater; + + public LineageNotificationLights(Context context, LedUpdater ledUpdater) { + mContext = context; + mLedUpdater = ledUpdater; + + final Resources res = mContext.getResources(); + + mAdjustableNotificationLedBrightness = LightsCapabilities.supports( + mContext, LightsCapabilities.LIGHTS_ADJUSTABLE_NOTIFICATION_LED_BRIGHTNESS); + + mDefaultNotificationColor = res.getColor( + com.android.internal.R.color.config_defaultNotificationColor); + mDefaultNotificationLedOn = res.getInteger( + com.android.internal.R.integer.config_defaultNotificationLedOn); + mDefaultNotificationLedOff = res.getInteger( + com.android.internal.R.integer.config_defaultNotificationLedOff); + + mMultiColorNotificationLed = LightsCapabilities.supports( + mContext, LightsCapabilities.LIGHTS_RGB_NOTIFICATION_LED); + + mNotificationPulseCustomLedValues = new ArrayMap(); + + mPackageNameMappings = new ArrayMap(); + final String[] defaultMapping = res.getStringArray( + org.lineageos.platform.internal.R.array.notification_light_package_mapping); + for (String mapping : defaultMapping) { + String[] map = mapping.split("\\|"); + mPackageNameMappings.put(map[0], map[1]); + } + + mKeyguardManager = + (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + + mSettingsObserver = new SettingsObserver(new Handler()); + mSettingsObserver.observe(); + } + + // Whether we should show lights if the screen is on. + public boolean showLightsScreenOn() { + return mScreenOnEnabled; + } + + // Used by NotificationManagerService to help determine + // when lights should / should not be cleared. + // TODO: put this somewhere else + public boolean isKeyguardLocked() { + return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); + } + + private void parseNotificationPulseCustomValuesString(String customLedValuesString) { + if (TextUtils.isEmpty(customLedValuesString)) { + return; + } + + for (String packageValuesString : customLedValuesString.split("\\|")) { + String[] packageValues = packageValuesString.split("="); + if (packageValues.length != 2) { + Slog.e(TAG, "Error parsing custom led values for unknown package"); + continue; + } + String packageName = packageValues[0]; + String[] values = packageValues[1].split(";"); + if (values.length != 3) { + Slog.e(TAG, "Error parsing custom led values '" + + packageValues[1] + "' for " + packageName); + continue; + } + LedValues ledValues; + try { + // color, onMs, offMs + ledValues = new LedValues(Integer.parseInt(values[0]), + Integer.parseInt(values[1]), Integer.parseInt(values[2])); + } catch (NumberFormatException e) { + Slog.e(TAG, "Error parsing custom led values '" + + packageValues[1] + "' for " + packageName); + continue; + } + mNotificationPulseCustomLedValues.put(packageName, ledValues); + } + } + + private LedValues getLedValuesForPackageName(String packageName) { + return mNotificationPulseCustomLedValues.get(mapPackage(packageName)); + } + + private int generateLedColorForPackageName(String packageName) { + if (!mAutoGenerateNotificationColor) { + return mDefaultNotificationColor; + } + if (!mMultiColorNotificationLed) { + return mDefaultNotificationColor; + } + final String mapping = mapPackage(packageName); + int color = mDefaultNotificationColor; + + if (mGeneratedPackageLedColors.containsKey(mapping)) { + return mGeneratedPackageLedColors.get(mapping); + } + + PackageManager pm = mContext.getPackageManager(); + Drawable icon; + try { + icon = pm.getApplicationIcon(mapping); + } catch (NameNotFoundException e) { + Slog.e(TAG, e.getMessage(), e); + return color; + } + + color = ColorUtils.generateAlertColorFromDrawable(icon); + mGeneratedPackageLedColors.put(mapping, color); + + return color; + } + + private String mapPackage(String pkg) { + if (!mPackageNameMappings.containsKey(pkg)) { + return pkg; + } + return mPackageNameMappings.get(pkg); + } + + // Called by NotificationManagerService updateLightsLocked(). + // Takes the lights values as requested by a notification and + // updates them according to the active Lineage feature settings. + public void calcLights(LedValues ledValues, String packageName, boolean forcedOn, + boolean screenOn, boolean inCall, boolean isDefaultLights, int suppressedEffects) { + if (DEBUG) { + Slog.i(TAG, "calcLights input: ledValues={ " + ledValues + " } packageName=" + + packageName + " forcedOn=" + forcedOn + " screenOn=" + screenOn + + " inCall=" + inCall + " isDefaultLights=" + isDefaultLights + + " suppressedEffects=" + suppressedEffects); + } + + final boolean suppressScreenOff = + (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_OFF) != 0; + final boolean suppressScreenOn = + (suppressedEffects & SUPPRESSED_EFFECT_SCREEN_ON) != 0; + final boolean screenActive = screenOn || inCall; + final boolean enableLed; + if (forcedOn) { + // Forced on always enables + enableLed = true; + } else if (screenActive && (!mScreenOnEnabled || suppressScreenOn)) { + // Screen on cases where we disable + enableLed = false; + } else if (!screenActive && suppressScreenOff) { + // Screen off cases where we disable + enableLed = false; + } else { + // Enable by default fallthrough + enableLed = true; + } + if (!enableLed) { + ledValues.setEnabled(false); + return; + } + + ledValues.setBrightness(mAdjustableNotificationLedBrightness ? + mNotificationLedBrightnessLevel : LedValues.LIGHT_BRIGHTNESS_MAXIMUM); + + final LedValues ledValuesPkg = getLedValuesForPackageName(packageName); + + // Use package specific values that the user has chosen. + if (ledValuesPkg != null) { + ledValues.setColor(ledValuesPkg.getColor() != 0 ? + ledValuesPkg.getColor() : generateLedColorForPackageName(packageName)); + ledValues.setOnMs(ledValuesPkg.getOnMs() >= 0 ? + ledValuesPkg.getOnMs() : mDefaultNotificationLedOn); + ledValues.setOffMs(ledValuesPkg.getOffMs() >= 0 ? + ledValuesPkg.getOffMs() : mDefaultNotificationLedOff); + } else if (isDefaultLights) { + ledValues.setColor(generateLedColorForPackageName(packageName)); + ledValues.setOnMs(mDefaultNotificationLedOn); + ledValues.setOffMs(mDefaultNotificationLedOff); + } + if (DEBUG) { + Slog.i(TAG, "calcLights output: ledValues={ " + ledValues + " }"); + } + } + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_SCREEN_ON), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_COLOR_AUTO), false, + this, UserHandle.USER_ALL); + if (mAdjustableNotificationLedBrightness) { + resolver.registerContentObserver(LineageSettings.System.getUriFor( + LineageSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL), + false, this, UserHandle.USER_ALL); + } + + update(); + } + + @Override + public void onChange(boolean selfChange) { + update(); + } + + private void update() { + ContentResolver resolver = mContext.getContentResolver(); + Resources res = mContext.getResources(); + + // Automatically pick a color for LED if not set + mAutoGenerateNotificationColor = LineageSettings.System.getIntForUser(resolver, + LineageSettings.System.NOTIFICATION_LIGHT_COLOR_AUTO, + 1, UserHandle.USER_CURRENT) != 0; + + // LED default color + mDefaultNotificationColor = LineageSettings.System.getIntForUser(resolver, + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR, + mDefaultNotificationColor, UserHandle.USER_CURRENT); + + // LED default on MS + mDefaultNotificationLedOn = LineageSettings.System.getIntForUser(resolver, + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON, + mDefaultNotificationLedOn, UserHandle.USER_CURRENT); + + // LED default off MS + mDefaultNotificationLedOff = LineageSettings.System.getIntForUser(resolver, + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF, + mDefaultNotificationLedOff, UserHandle.USER_CURRENT); + + // LED generated notification colors + mGeneratedPackageLedColors.clear(); + + // LED custom notification colors + mNotificationPulseCustomLedValues.clear(); + if (LineageSettings.System.getIntForUser(resolver, + LineageSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, 0, + UserHandle.USER_CURRENT) != 0) { + parseNotificationPulseCustomValuesString(LineageSettings.System.getStringForUser( + resolver, LineageSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES, + UserHandle.USER_CURRENT)); + } + + // Notification LED brightness + if (mAdjustableNotificationLedBrightness) { + mNotificationLedBrightnessLevel = LineageSettings.System.getIntForUser(resolver, + LineageSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL, + LedValues.LIGHT_BRIGHTNESS_MAXIMUM, UserHandle.USER_CURRENT); + } + + // Notification lights with screen on + mScreenOnEnabled = (LineageSettings.System.getIntForUser(resolver, + LineageSettings.System.NOTIFICATION_LIGHT_SCREEN_ON, 0, + UserHandle.USER_CURRENT) != 0); + + mLedUpdater.update(); + } + } +}