Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21ad5e0c authored by Sam Mortimer's avatar Sam Mortimer Committed by Christopher N. Hesse
Browse files

frameworks/base: Battery and Notification Lights

A reworked implementation based on code in the
commits listed further below.

Key changes from the original implementation:

*) Settings observation and most lineage lights
   feature specific code has been moved to the
   lineage-sdk.

*) Battery and notification frameworks services
   call out to the sdk to allow our features
   to make changes to lights values.

Original commits:

Author: DvTonder <david.vantonder@gmail.com>
Author: Ricardo Cerqueira <cyanogenmod@cerqueira.org>
Date:   Mon Nov 4 00:57:51 2013 +0000
Framework: Port CM9 features to CM10
Change-Id: Ibd63116df90b06f6ce6adb8a0343059bbb999bfb

Author: Pawit Pornkitprasan <p.pawit@gmail.com>
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 <sam@mortimer.me.uk>
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 <mikeioannina@gmail.com>
Date:   Mon Aug 18 04:56:28 2014 +0300
Add support for single color notification LED (1/2)
Change-Id: I367af77036da9e87c6dd0df552ce4c56d945a44d

Author: Danesh M <daneshm90@gmail.com>
Date:   Thu, 12 Nov 2015 10:52:11 -0800
Framework : Move System settings to CMSettings
Change-Id: I4e9fb06db7b9ba05e4a7bbe11916bb2271af4727

Author: Adnan Begovic <adnan@cyngn.com>
Date:   Mon, 9 Nov 2015 16:26:00 -0800
fw: Move battery light settings to CMSettings.
Change-Id: I28e60473356b2a9af152df82d34ad7abc9918682

Author: Steve Kondik <steve@cyngn.com>
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 <radian.dc@gmail.com>
Date:   Sat Oct 14 23:08:47 2017 +0200
fw: Rebrand to LineageOS and cleanup for Android Oreo
Change-Id: I21d424433bb52a17eea7974c4ea29a3a16fe1be5

Author: AdrianDC <radian.dc@gmail.com>
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 <baddaemon87@gmail.com>
Date:   Mon Oct 9 22:04:00 2017 +0200
Core: Battery warning levels are inclusive, not exclusive
Change-Id: Ib35b154b6117f7e26b4a3a5aee9254dda3adda12

Author: Adrian DC <radian.dc@gmail.com>
Date:   Sat Oct 14 23:08:47 2017 +0200
fw: Rebrand to LineageOS and cleanup for Android Oreo
Change-Id: I845f866891386aee808a4e7e80f4ab7c3ad48830

Change-Id: I564fdb6ca6c5bbcf890afa4b9a2f9fa7bf8b9604
parent d39f02cb
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1202,6 +1202,13 @@ public class Notification implements Parcelable
    public static final int BADGE_ICON_LARGE = 2;
    private int mBadgeIcon = BADGE_ICON_NONE;

    /**
     * Used by light picker in Settings to force
     * notification lights on when screen is on
     * @hide
     */
    public static final String EXTRA_FORCE_SHOW_LIGHTS = "android.forceShowLights";

    /**
     * Structure to encapsulate a named action that can be shown as part of this notification.
     * It must include an icon, a label, and a {@link PendingIntent} to be fired when the action is
+42 −22
Original line number Diff line number Diff line
@@ -55,6 +55,9 @@ import android.util.EventLog;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import org.lineageos.internal.notification.LedValues;
import org.lineageos.internal.notification.LineageBatteryLights;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
@@ -156,6 +159,8 @@ public final class BatteryService extends SystemService {

    private ActivityManagerInternal mActivityManagerInternal;

    private LineageBatteryLights mLineageBatteryLights;

    public BatteryService(Context context) {
        super(context);

@@ -227,6 +232,16 @@ public final class BatteryService extends SystemService {
                        false, obs, UserHandle.USER_ALL);
                updateBatteryWarningLevelLocked();
            }
        } else if (phase == PHASE_BOOT_COMPLETED) {
            mLineageBatteryLights = new LineageBatteryLights(mContext,
                    new LineageBatteryLights.LedUpdater() {
                public void update() {
                    updateLedPulse();
                }
            });

            // Update light state now that mLineageBatteryLights has been initialized.
            updateLedPulse();
        }
    }

@@ -878,6 +893,10 @@ public final class BatteryService extends SystemService {
        proto.flush();
    }

    private synchronized void updateLedPulse() {
        mLed.updateLightsLocked();
    }

    private final class Led {
        private final Light mBatteryLight;

@@ -906,29 +925,30 @@ public final class BatteryService extends SystemService {
         * Synchronize on BatteryService.
         */
        public void updateLightsLocked() {
            final int level = mBatteryProps.batteryLevel;
            final int status = mBatteryProps.batteryStatus;
            if (level < mLowBatteryWarningLevel) {
                if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
                    // Solid red when battery is charging
                    mBatteryLight.setColor(mBatteryLowARGB);
                } else {
                    // Flash red when battery is low and not charging
                    mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
                            mBatteryLedOn, mBatteryLedOff);
                }
            } else if (status == BatteryManager.BATTERY_STATUS_CHARGING
                    || status == BatteryManager.BATTERY_STATUS_FULL) {
                if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
                    // Solid green when full or charging and nearly full
                    mBatteryLight.setColor(mBatteryFullARGB);
                } else {
                    // Solid orange when charging and halfway full
                    mBatteryLight.setColor(mBatteryMediumARGB);
            // mBatteryProps could be null on startup (called by SettingsObserver)
            if (mBatteryProps == null) {
                Slog.w(TAG, "updateLightsLocked: mBatteryProps is null; skipping");
                return;
            }
            } else {
                // No lights if not charging and not low
            // mLineageBatteryLights is initialized during PHASE_BOOT_COMPLETED
            // This means we don't have Lineage battery settings yet so skip.
            if (mLineageBatteryLights == null) {
                Slog.w(TAG, "updateLightsLocked: mLineageBatteryLights is not yet ready; skipping");
                return;
            }

            LedValues ledValues = new LedValues(0 /* color */, mBatteryLedOn, mBatteryLedOff);
            mLineageBatteryLights.calcLights(ledValues,
                    mBatteryProps.batteryLevel, mBatteryProps.batteryStatus,
                    mBatteryProps.batteryLevel <= mLowBatteryWarningLevel);

            if (!ledValues.isEnabled()) {
                mBatteryLight.turnOff();
            } else if (ledValues.isPulsed()) {
                mBatteryLight.setFlashing(ledValues.getColor(), Light.LIGHT_FLASH_TIMED,
                        ledValues.getOnMs(), ledValues.getOffMs());
            } else {
                mBatteryLight.setColor(ledValues.getColor());
            }
        }
    }
+54 −13
Original line number Diff line number Diff line
/*
 * Copyright (C) 2007 The Android Open Source Project
 * 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.
@@ -172,6 +174,9 @@ import com.android.server.policy.PhoneWindowManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.notification.ManagedServices.UserProfiles;

import org.lineageos.internal.notification.LedValues;
import org.lineageos.internal.notification.LineageNotificationLights;

import libcore.io.IoUtils;

import org.json.JSONException;
@@ -358,6 +363,8 @@ public class NotificationManagerService extends SystemService {
    private GroupHelper mGroupHelper;
    private boolean mIsTelevision;

    private LineageNotificationLights mLineageNotificationLights;

    private static class Archive {
        final int mBufferSize;
        final ArrayDeque<StatusBarNotification> mBuffer;
@@ -725,9 +732,12 @@ public class NotificationManagerService extends SystemService {
    @GuardedBy("mNotificationLock")
    private void clearLightsLocked() {
        // light
        // clear only if lockscreen is not active
        if (!mLineageNotificationLights.isKeyguardLocked()) {
            mLights.clear();
            updateLightsLocked();
        }
    }

    private final BroadcastReceiver mNotificationTimeoutReceiver = new BroadcastReceiver() {
        @Override
@@ -894,7 +904,10 @@ public class NotificationManagerService extends SystemService {
                }
            } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
                // turn off LED when user passes through lock screen
                // if lights with screen on is disabled.
                if (!mLineageNotificationLights.showLightsScreenOn()) {
                    mNotificationLight.turnOff();
                }
            } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
                final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                // reload per-user settings
@@ -1280,6 +1293,13 @@ public class NotificationManagerService extends SystemService {
        timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
        getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);

        mLineageNotificationLights = new LineageNotificationLights(getContext(),
                 new LineageNotificationLights.LedUpdater() {
            public void update() {
                updateNotificationPulse();
            }
        });

        publishBinderService(Context.NOTIFICATION_SERVICE, mService);
        publishLocalService(NotificationManagerInternal.class, mInternalService);
    }
@@ -3805,9 +3825,8 @@ public class NotificationManagerService extends SystemService {
        // light
        // release the light
        boolean wasShowLights = mLights.remove(key);
        if (record.getLight() != null && aboveThreshold
                && ((record.getSuppressedVisualEffects()
                & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
        if (record.getLight() != null
                && (aboveThreshold || isLedNotificationForcedOn(record))) {
            mLights.add(key);
            updateLightsLocked();
            if (mUseAttentionLight) {
@@ -4713,17 +4732,39 @@ public class NotificationManagerService extends SystemService {
            }
        }

        // Don't flash while we are in a call or screen is on
        if (ledNotification == null || mInCall || mScreenOn) {
        NotificationRecord.Light light = ledNotification != null ?
                ledNotification.getLight() : null;
        if (ledNotification == null || mLineageNotificationLights == null ||
                light == null || !mNotificationPulseEnabled) {
            mNotificationLight.turnOff();
            return;
        }

        LedValues ledValues = new LedValues(light.color, light.onMs, light.offMs);
        final String packageName = ledNotification.sbn.getPackageName();
        final boolean defaultLights =
                (ledNotification.sbn.getNotification().defaults
                        & Notification.DEFAULT_LIGHTS) != 0;
        mLineageNotificationLights.calcLights(ledValues, packageName,
                isLedNotificationForcedOn(ledNotification), mScreenOn,
                mInCall, defaultLights, ledNotification.getSuppressedVisualEffects());

        if (!ledValues.isEnabled()) {
            mNotificationLight.turnOff();
        } else {
            NotificationRecord.Light light = ledNotification.getLight();
            if (light != null && mNotificationPulseEnabled) {
                // pulse repeatedly
                mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED,
                        light.onMs, light.offMs);
            mNotificationLight.setFlashing(ledValues.getColor(), Light.LIGHT_FLASH_TIMED,
                    ledValues.getOnMs(), ledValues.getOffMs());
        }
    }

    private boolean isLedNotificationForcedOn(NotificationRecord nr) {
        if (nr != null) {
            final Notification n = nr.sbn.getNotification();
            if (n.extras != null) {
                return n.extras.getBoolean(Notification.EXTRA_FORCE_SHOW_LIGHTS, false);
            }
        }
        return false;
    }

    @GuardedBy("mNotificationLock")