NoMan/SysUI: Clear LEDs only when entering the shade

Don't clear notification LEDs when seeing notifications on the
lockscreen.

Also fix a bug where the LED didn't continue flashing after
the screen turned off.

For devices with doze capability, ensure that the LED continuing
to flash after screen off doesn't cause an immediate pulses, but
delay the first pulse by 10s.

Bug: 15449039
Change-Id: Id34d51a2c91ceaf069e49add1ab690bb855f9638
This commit is contained in:
Christoph Studer
2014-11-26 15:32:20 +01:00
parent da39290460
commit 1f32c65697
8 changed files with 96 additions and 25 deletions

View File

@@ -40,8 +40,10 @@ interface IStatusBarService
// You need the STATUS_BAR_SERVICE permission
void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
out int[] switches, out List<IBinder> binders);
void onPanelRevealed();
void onPanelRevealed(boolean clearNotificationEffects);
void onPanelHidden();
// Mark current notifications as "seen" and stop ringing, vibrating, blinking.
void clearNotificationEffects();
void onNotificationClick(String key);
void onNotificationActionClick(String key, int actionIndex);
void onNotificationError(String pkg, String tag, int id,

View File

@@ -28,6 +28,7 @@ public interface DozeHost {
void pulseWhileDozing(@NonNull PulseCallback callback);
void stopDozing();
boolean isPowerSaveActive();
boolean isNotificationLightOn();
public interface Callback {
void onNewNotifications();

View File

@@ -56,6 +56,17 @@ public class DozeService extends DreamService {
private static final String NOTIFICATION_PULSE_ACTION = ACTION_BASE + ".notification_pulse";
private static final String EXTRA_INSTANCE = "instance";
/**
* Earliest time we pulse due to a notification light after the service started.
*
* <p>Incoming notification light events during the blackout period are
* delayed to the earliest time defined by this constant.</p>
*
* <p>This delay avoids a pulse immediately after screen off, at which
* point the notification light is re-enabled again by NoMan.</p>
*/
private static final int EARLIEST_LIGHT_PULSE_AFTER_START_MS = 10 * 1000;
private final String mTag = String.format(TAG + ".%08x", hashCode());
private final Context mContext = this;
private final DozeParameters mDozeParameters = new DozeParameters(mContext);
@@ -77,6 +88,7 @@ public class DozeService extends DreamService {
private boolean mPowerSaveActive;
private boolean mCarMode;
private long mNotificationPulseTime;
private long mEarliestPulseDueToLight;
private int mScheduleResetsRemaining;
public DozeService() {
@@ -159,8 +171,9 @@ public class DozeService extends DreamService {
}
mDreaming = true;
listenForPulseSignals(true);
rescheduleNotificationPulse(false /*predicate*/); // cancel any pending pulse alarms
mEarliestPulseDueToLight = System.currentTimeMillis() + EARLIEST_LIGHT_PULSE_AFTER_START_MS;
listenForPulseSignals(true);
// Ask the host to get things ready to start dozing.
// Once ready, we call startDozing() at which point the CPU may suspend
@@ -295,6 +308,12 @@ public class DozeService extends DreamService {
if (listen) {
resetNotificationResets();
mHost.addCallback(mHostCallback);
// Continue to pulse for existing LEDs.
mNotificationLightOn = mHost.isNotificationLightOn();
if (mNotificationLightOn) {
updateNotificationPulseDueToLight();
}
} else {
mHost.removeCallback(mHostCallback);
}
@@ -305,21 +324,26 @@ public class DozeService extends DreamService {
mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets();
}
private void updateNotificationPulse() {
if (DEBUG) Log.d(mTag, "updateNotificationPulse");
private void updateNotificationPulseDueToLight() {
long timeMs = System.currentTimeMillis();
timeMs = Math.max(timeMs, mEarliestPulseDueToLight);
updateNotificationPulse(timeMs);
}
private void updateNotificationPulse(long notificationTimeMs) {
if (DEBUG) Log.d(mTag, "updateNotificationPulse notificationTimeMs=" + notificationTimeMs);
if (!mDozeParameters.getPulseOnNotifications()) return;
if (mScheduleResetsRemaining <= 0) {
if (DEBUG) Log.d(mTag, "No more schedule resets remaining");
return;
}
final long now = System.currentTimeMillis();
if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) {
if ((notificationTimeMs - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) {
if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
return;
}
mScheduleResetsRemaining--;
if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
mNotificationPulseTime = now;
mNotificationPulseTime = notificationTimeMs;
rescheduleNotificationPulse(true /*predicate*/);
}
@@ -401,14 +425,14 @@ public class DozeService extends DreamService {
private final DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
public void onNewNotifications() {
if (DEBUG) Log.d(mTag, "onNewNotifications");
if (DEBUG) Log.d(mTag, "onNewNotifications (noop)");
// noop for now
}
@Override
public void onBuzzBeepBlinked() {
if (DEBUG) Log.d(mTag, "onBuzzBeepBlinked");
updateNotificationPulse();
updateNotificationPulse(System.currentTimeMillis());
}
@Override
@@ -417,7 +441,7 @@ public class DozeService extends DreamService {
if (mNotificationLightOn == on) return;
mNotificationLightOn = on;
if (mNotificationLightOn) {
updateNotificationPulse();
updateNotificationPulseDueToLight();
}
}

View File

@@ -1642,7 +1642,11 @@ public abstract class BaseStatusBar extends SystemUI implements
protected void handleVisibleToUserChanged(boolean visibleToUser) {
try {
if (visibleToUser) {
mBarService.onPanelRevealed();
// Only stop blinking, vibrating, ringing when the user went into the shade
// manually (SHADE or SHADE_LOCKED).
boolean clearNotificationEffects =
(mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
mBarService.onPanelRevealed(clearNotificationEffects);
} else {
mBarService.onPanelHidden();
}

View File

@@ -3807,6 +3807,16 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
* @param state The {@link StatusBarState} to set.
*/
public void setBarState(int state) {
// If we're visible and switched to SHADE_LOCKED (the user dragged down
// on the lockscreen), clear notification LED, vibration, ringing.
// Other transitions are covered in handleVisibleToUserChanged().
if (mVisible && mState != state && state == StatusBarState.SHADE_LOCKED) {
try {
mBarService.clearNotificationEffects();
} catch (RemoteException e) {
// Ignore.
}
}
mState = state;
mStatusBarWindowManager.setStatusBarState(state);
}
@@ -4138,6 +4148,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final H mHandler = new H();
// Keeps the last reported state by fireNotificationLight.
private boolean mNotificationLightOn;
@Override
public String toString() {
return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
@@ -4156,6 +4169,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
public void fireNotificationLight(boolean on) {
mNotificationLightOn = on;
for (Callback callback : mCallbacks) {
callback.onNotificationLight(on);
}
@@ -4197,6 +4211,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return mBatteryController != null && mBatteryController.isPowerSave();
}
@Override
public boolean isNotificationLightOn() {
return mNotificationLightOn;
}
private void handleStartDozing(@NonNull Runnable ready) {
if (!mDozing) {
mDozing = true;

View File

@@ -26,8 +26,9 @@ public interface NotificationDelegate {
void onNotificationError(int callingUid, int callingPid,
String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
void onPanelRevealed();
void onPanelRevealed(boolean clearEffects);
void onPanelHidden();
void clearEffects();
void onNotificationVisibilityChanged(
String[] newlyVisibleKeys, String[] noLongerVisibleKeys);
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);

View File

@@ -122,7 +122,7 @@ import java.util.Objects;
/** {@hide} */
public class NotificationManagerService extends SystemService {
static final String TAG = "NotificationService";
static final boolean DBG = false;
static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
static final int MAX_PACKAGE_NOTIFICATIONS = 50;
@@ -564,9 +564,23 @@ public class NotificationManagerService extends SystemService {
}
@Override
public void onPanelRevealed() {
public void onPanelRevealed(boolean clearEffects) {
EventLogTags.writeNotificationPanelRevealed();
if (clearEffects) {
clearEffects();
}
}
@Override
public void onPanelHidden() {
EventLogTags.writeNotificationPanelHidden();
}
@Override
public void clearEffects() {
synchronized (mNotificationList) {
if (DBG) Slog.d(TAG, "clearEffects");
// sound
mSoundNotification = null;
@@ -597,11 +611,6 @@ public class NotificationManagerService extends SystemService {
}
}
@Override
public void onPanelHidden() {
EventLogTags.writeNotificationPanelHidden();
}
@Override
public void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id,
int uid, int initialPid, String message, int userId) {
@@ -740,8 +749,10 @@ public class NotificationManagerService extends SystemService {
// Keep track of screen on/off state, but do not turn off the notification light
// until user passes through the lock screen or views the notification.
mScreenOn = true;
updateNotificationPulse();
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
mScreenOn = false;
updateNotificationPulse();
} else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK
.equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE));

View File

@@ -26,7 +26,6 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.util.Slog;
import android.view.WindowManager;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
@@ -495,16 +494,26 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
/**
* The status bar service should call this each time the user brings the panel from
* invisible to visible in order to clear the notification light.
* @param clearNotificationEffects whether to consider notifications as "shown" and stop
* LED, vibration, and ringing
*/
@Override
public void onPanelRevealed() {
public void onPanelRevealed(boolean clearNotificationEffects) {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
// tell the notification manager to turn off the lights.
mNotificationDelegate.onPanelRevealed();
mNotificationDelegate.onPanelRevealed(clearNotificationEffects);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void clearNotificationEffects() throws RemoteException {
enforceStatusBarService();
long identity = Binder.clearCallingIdentity();
try {
mNotificationDelegate.clearEffects();
} finally {
Binder.restoreCallingIdentity(identity);
}