Android Notification - 灯光提示

先给出问题:Android从O升级到P之后,未接来电无呼吸灯闪烁提示了。

处理未接来电的逻辑在platform/package/service/Telecomm里面,有未接来电时,会向系统发送通知,具体代码位置:

Telecomm/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java

    private void showMissedCallNotification(@NonNull CallInfo callInfo, UserHandle userHandle) {
        Log.i(this, "showMissedCallNotification: userHandle=%d", userHandle.getIdentifier());
        mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0));
        int missCallCounts = mMissedCallCounts.get(userHandle).incrementAndGet();

        if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
            sendNotificationThroughDefaultDialer(callInfo, userHandle);
            return;
        }

        final int titleResId;
        final String expandedText;  // The text in the notification's line 1 and 2.

        // Display the first line of the notification:
        // 1 missed call: <caller name || handle>
        // More than 1 missed call: <number of calls> + "missed calls"
        if (missCallCounts == 1) {
            expandedText = getNameForMissedCallNotification(callInfo);

            CallerInfo ci = callInfo.getCallerInfo();
            if (ci != null && ci.userType == CallerInfo.USER_TYPE_WORK) {
                titleResId = R.string.notification_missedWorkCallTitle;
            } else {
                titleResId = R.string.notification_missedCallTitle;
            }
        } else {
            titleResId = R.string.notification_missedCallsTitle;
            expandedText =
                    mContext.getString(R.string.notification_missedCallsMsg, missCallCounts);
        }

        // Create a public viewable version of the notification, suitable for display when sensitive
        // notification content is hidden.
        // We use user's context here to make sure notification is badged if it is a managed user.
        Context contextForUser = getContextForUser(userHandle);
        Notification.Builder publicBuilder = mNotificationBuilderFactory.getBuilder(contextForUser);
        publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
                .setColor(mContext.getResources().getColor(R.color.theme_color))
                .setWhen(callInfo.getCreationTimeMillis())
                .setShowWhen(true)
                // Show "Phone" for notification title.
                .setContentTitle(mContext.getText(R.string.userCallActivityLabel))
                // Notification details shows that there are missed call(s), but does not reveal
                // the missed caller information.
                .setContentText(mContext.getText(titleResId))
                .setContentIntent(createCallLogPendingIntent(userHandle))
                .setAutoCancel(true)
                .setDeleteIntent(createClearMissedCallsPendingIntent(userHandle));

        // Create the notification suitable for display when sensitive information is showing.
        Notification.Builder builder = mNotificationBuilderFactory.getBuilder(contextForUser);
        builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
                .setColor(mContext.getResources().getColor(R.color.theme_color))
                .setWhen(callInfo.getCreationTimeMillis())
                .setShowWhen(true)
                .setContentTitle(mContext.getText(titleResId))
                .setContentText(expandedText)
                .setContentIntent(createCallLogPendingIntent(userHandle))
                .setAutoCancel(true)
                .setDeleteIntent(createClearMissedCallsPendingIntent(userHandle))
                // Include a public version of the notification to be shown when the missed call
                // notification is shown on the user's lock screen and they have chosen to hide
                // sensitive notification information.
                .setPublicVersion(publicBuilder.build())
                .setChannelId(NotificationChannelManager.CHANNEL_ID_MISSED_CALLS);

        Uri handleUri = callInfo.getHandle();
        String handle = callInfo.getHandleSchemeSpecificPart();

        // Add additional actions when there is only 1 missed call, like call-back and SMS.
        if (missCallCounts == 1) {
            Log.d(this, "Add actions with number %s.", Log.piiHandle(handle));

            if (!TextUtils.isEmpty(handle)
                    && !TextUtils.equals(handle, mContext.getString(R.string.handle_restricted))) {
                builder.addAction(R.drawable.ic_phone_24dp,
                        mContext.getString(R.string.notification_missedCall_call_back),
                        createCallBackPendingIntent(handleUri, userHandle));

                if (canRespondViaSms(callInfo)) {
                    builder.addAction(R.drawable.ic_message_24dp,
                            mContext.getString(R.string.notification_missedCall_message),
                            createSendSmsFromNotificationPendingIntent(handleUri, userHandle));
                }
            }

            Bitmap photoIcon = callInfo.getCallerInfo() == null ?
                    null : callInfo.getCallerInfo().cachedPhotoIcon;
            if (photoIcon != null) {
                builder.setLargeIcon(photoIcon);
            } else {
                Drawable photo = callInfo.getCallerInfo() == null ?
                        null : callInfo.getCallerInfo().cachedPhoto;
                if (photo != null && photo instanceof BitmapDrawable) {
                    builder.setLargeIcon(((BitmapDrawable) photo).getBitmap());
                }
            }
        } else {
            Log.d(this, "Suppress actions. handle: %s, missedCalls: %d.", Log.piiHandle(handle),
                    missCallCounts);
        }

        Notification notification = builder.build();

        //这里会配置是否闪烁呼吸灯
        configureLedOnNotification(notification);

        Log.i(this, "Adding missed call notification for %s.", Log.pii(callInfo.getHandle()));
        long token = Binder.clearCallingIdentity();
        try {
            mNotificationManager.notifyAsUser(
                    NOTIFICATION_TAG, MISSED_CALL_NOTIFICATION_ID, notification, userHandle);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

 配置通知是否显示灯光

private void configureLedOnNotification(Notification notification) {
        notification.flags |= Notification.FLAG_SHOW_LIGHTS;
        notification.defaults |= Notification.DEFAULT_LIGHTS;
}

到此发送端已完成,接下来看看处理部分。

处理是否显示呼吸灯的逻辑在NotificationManagerService.java中。

处理流程 enqueueNotificationInternal ==> EnqueueNotificationRunnable ==> PostNotificationRunnable ==> buzzBeepBlinkLocked ==> updateLightsLocked

其中最后一步updateLightsLocked 就是控制呼吸灯的地方。

但是我遇到的问题是在上一层调用buzzBeepBlinkLocked中,贴出部分代码。系统维护了一个需要显示呼吸灯的通知容器,即mLights。只要mLights不为空,那么关掉屏幕的时候就会有呼吸灯显示。

 

    void buzzBeepBlinkLocked(NotificationRecord record) {
        ......        
        if (wasBuzz && !hasValidVibrate) {
            clearVibrateLocked();
        }

        // light
        // release the light
        boolean wasShowLights = mLights.remove(key);
        if (canShowLightsLocked(record, aboveThreshold)) {
            mLights.add(key);
            updateLightsLocked();
            if (mUseAttentionLight) {
                mAttentionLight.pulse();
            }
            blink = true;
        } else if (wasShowLights) {
            updateLightsLocked();
        }
        ......
    }

在调用canShowLightsLocked的时候返回了false,导致当前通知的key未被加入到mLights中,继续看看这个函数的实现

    boolean canShowLightsLocked(final NotificationRecord record, boolean aboveThreshold) {
        // device lacks light
        if (!mHasLight) {
            return false;
        }
        // user turned lights off globally
        if (!mNotificationPulseEnabled) {
            return false;
        }
        // the notification/channel has no light
        if (record.getLight() == null) {
            return false;
        }
        // unimportant notification
        if (!aboveThreshold) {
            return false;
        }
        // suppressed due to DND
        if ((record.getSuppressedVisualEffects() & SUPPRESSED_EFFECT_LIGHTS) != 0) {
            return false;
        }
        // Suppressed because it's a silent update
        final Notification notification = record.getNotification();
        if (record.isUpdate && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0) {
            return false;
        }
        // Suppressed because another notification in its group handles alerting
        if (record.sbn.isGroup() && record.getNotification().suppressAlertingDueToGrouping()) {
            return false;
        }
        // not if in call or the screen's on
        if (mInCall || mScreenOn) {
            return false;
        }

        return true;
    }

每一种条件都写了注释,十分清晰 ,看上去没什么问题,但是最后一个条件中在mScreenOn的时候则不显示呼吸灯,问题就出在此处。当有未接来电的时候,屏幕一定是打开的,因此未接来电就不可能满足这个条件。估计修改者原意是当屏幕打开的时候,那么说明用户正在操作设备,就不显示呼吸灯提示。忽略了未接来电这种特殊情形。

因此只需要对未接来电进行单独处理即可。我是通过Notification中的channel id来区别未接来电的。

--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -264,6 +264,10 @@ public class NotificationManagerService extends SystemService {
     // notification led colour blue
     private static final int LED_BLUE = 0x000000ff;
 
+    // channel id for missed call, it was defined in
+    // packages/apps/Dialer/java/com/android/dialer/notification/NotificationChannelId.java
+    private static final String CHANNEL_ID_MISSED_CALL = "phone_missed_call";
+
     // ranking thread messages
     private static final int MESSAGE_RECONSIDER_RANKING = 1000;
     private static final int MESSAGE_RANKING_SORT = 1001;
@@ -4982,7 +4986,12 @@ public class NotificationManagerService extends SystemService {
         }
         // not if in call or the screen's on
         if (mInCall || mScreenOn) {
-            return false;
+            // When missed a phone call, "mScreenOn" will be always true
+            // at that time, so if it was a notification of MISSED_CALL, we allow the led show
+            // even if mScreenOn was true.
+            if (mIncall || !CHANNEL_ID_MISSED_CALL.equals(record.getChannel().getId())) {
+                return false;
+            }
         }
 
         return true;

修改后验证成功。 

希望能对遇到同样问题的朋友有所帮助。

原文地址:https://blog.csdn.net/chengchaooppo/article/details/105529783

 

 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值