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();
+ }
+ }
+}