AR 眼镜之-系统通知定制(通知中心)-实现方案

目录

[📂 前言](#📂 前言)

[AR 眼镜系统版本](#AR 眼镜系统版本)

系统通知定制

[1. 🔱 技术方案](#1. 🔱 技术方案)

[1.1 技术方案概述](#1.1 技术方案概述)

[1.2 实现方案](#1.2 实现方案)

1)通知弹窗消失

2)通知中心显示

[2. 💠 通知弹窗消失](#2. 💠 通知弹窗消失)

[2.1 通知弹窗显示时长到期后自动消失](#2.1 通知弹窗显示时长到期后自动消失)

[2.2 将通知添加到通知中心](#2.2 将通知添加到通知中心)

[3. ⚛️ 通知中心显示](#3. ⚛️ 通知中心显示)

[3.1 统一处理通知](#3.1 统一处理通知)

1)统一处理通知中心的通知添加

[2)处理 AR 眼镜通知添加](#2)处理 AR 眼镜通知添加)

3)处理手机端通知添加

[4)通知信息数据类 NotifyInfo](#4)通知信息数据类 NotifyInfo)

[3.2 分类显示 AR 眼镜以及手机端的通知](#3.2 分类显示 AR 眼镜以及手机端的通知)

[4. ✅ 小结](#4. ✅ 小结)

附录1:通知中心代码


📂 前言

AR 眼镜系统版本

W517 Android9。

系统通知定制

系统通知的底层 实现主要依赖 Android 原生通知模块 NotificationManagerService ,系统通知的上层 UI 主要依赖于继承 NotificationListenerService 去实现,实现过程如下图所示,主要分为三步:1)应用 A 通过 sendNotification 发送通知;2)Android 通知模块 NotificationManagerService 接收到通知;3、应用 B 通过继承 NotificationListenerService监听到系统通知。对于底层实现感兴趣的同学可自行去深入了解,本文所讨论的系统通知实现方案主要针对于上层 UI。

那么,Android 原生系统通知是怎样实现的呢?答案很简单:通过 SystemUI 应用实现,SystemUI 通过继承 NotificationListenerService 监听系统通知,然后显示在通知栏。

但是,AR 眼镜系统与传统 Android 2D 存在较大显示与交互差异,且根据产品需求综合来看,本文采用类似 SystemUI 的方案,通知应用 通过继承 NotificationListenerService 实现系统通知的监听与显示。

1. 🔱 技术方案

1.1 技术方案概述

通知应用 通过继承 NotificationListenerService 实现系统通知的监听与显示,上层 UI 主要包括:通知弹窗、通知中心,系统通知定制的实现方案将分为两个篇章展开,分别是 通知弹窗篇 与 通知中心篇。

1.2 实现方案

接续上篇章 AR 眼镜之-系统通知定制(通知弹窗)-实现方案,showNotification 显示通知弹窗 View 后,需要处理通知弹窗消失的逻辑。

1)通知弹窗消失
  1. 通知弹窗显示时长到期后自动消失;

  2. 将通知添加到通知中心。

2)通知中心显示
  1. 统一处理通知;

  2. 分类显示 AR 眼镜以及手机端的通知。

2. 💠 通知弹窗消失

2.1 通知弹窗显示时长到期后自动消失

一般通知弹窗会规定显示时长,比如 3s 后自动消失。

复制代码
/**
 * 通知浮窗消失时间。单位:ms
 */
const val NOTIFICATION_DIALOG_TIME_OUT = 3000L

var timeOut: CountDownTimer? = null
timeOut = object : CountDownTimer(NOTIFICATION_DIALOG_TIME_OUT, 1000) {
    override fun onTick(millisUntilFinished: Long) {}
    override fun onFinish() {
        addNotificationBar(context, notificationManagerBean)
        timeOut?.cancel()
        timeOut = null
    }
}.start()

2.2 将通知添加到通知中心

复制代码
// 弹窗消息通知消失后,添加到通知中心
addNotificationBar(context, notificationManagerBean)

/**
 * 将通知添加到通知列表
 */
private fun addNotificationBar(
    context: Context,
    notificationManagerBean: NotificationManagerBean,
) {
    if (notificationManagerBean.from == NotificationManagerBean.FROM_GLASS) {
        notificationManagerBean.glassNotification?.let {
            NotificationBarHelper.addNotification(context, it)
        }
    } else {
        notificationManagerBean.phoneNotification?.let {
            NotificationBarHelper.addNotification(context, it)
        }
    }
}

3. ⚛️ 通知中心显示

3.1 统一处理通知

1)统一处理通知中心的通知添加
复制代码
object NotificationBarHelper {

    private val TAG = "NotificationBarHelper"

    private var instance: NotificationBar? = null

    @JvmStatic
    fun getInstance(context: Context): NotificationBar? {
        if (instance == null) {
            instance = NotificationBar(context, null)
        }
        return instance
    }

    @JvmStatic
    fun release() {
        instance = null
    }

    /**
     * AR 眼镜通知添加
     */
    @JvmStatic
    fun addNotification(context: Context, sbn: StatusBarNotification){}

    /**
     * 手机端通知添加
     */
    @JvmStatic
    fun addNotification(context: Context, msg: MessageReqMsgNoti){}

}
2)处理 AR 眼镜通知添加
复制代码
fun addNotification(context: Context, sbn: StatusBarNotification) {
        val notifyInfo = NotifyInfo()

        val packageName = sbn.packageName
        val postTime = sbn.postTime

        sbn.notification.apply {
            val smallIcon = smallIcon?.loadDrawable(context)
//            val largeIcon = getLargeIcon()?.loadDrawable(context)
            val title = extras.getString(Notification.EXTRA_TITLE, "")
            val content = sbn.notification.extras.getCharSequence(Notification.EXTRA_TEXT, "")
            val progress = extras.getInt(Notification.EXTRA_PROGRESS)
//            val progressMax = extras.getInt(Notification.EXTRA_PROGRESS_MAX)
//            val progressInd = extras.getBoolean(Notification.EXTRA_PROGRESS_INDETERMINATE)
//            val priority = priority

            val contentIntent = contentIntent
//            val deleteIntent = deleteIntent
//            val whenTime = `when`
//            val showWhen = extras.getBoolean(Notification.EXTRA_SHOW_WHEN)
//            val channelID = extras.getString(Notification.EXTRA_CHANNEL_ID, "")
//            val contentView = contentView
//            val bigContentView = bigContentView
//            val headsUpContentView = headsUpContentView

//            Log.e(
//                TAG,
//                "addNotification: notification\n" + "packageName = $packageName\n" + "postTime = $postTime\n" + "smallIcon = $smallIcon\n" + "largeIcon = $largeIcon\n" + "title = $title\n" + "content = $content\n" + "progress = $progress\n" + "progressMax = $progressMax\n" + "progressInd = $progressInd\n" + "priority = $priority\n" + "contentIntent = $contentIntent\n" + "deleteIntent = $deleteIntent\n" + "whenTime = $whenTime\n" + "showWhen = $showWhen\n" + "channelID = $channelID\n" + "contentView = $contentView\n" + "bigContentView = $bigContentView\n" + "headsUpContentView = $headsUpContentView\n"
//            )

//            actions?.forEach { action ->
                val actionIcon = action.getIcon()?.loadDrawable(context)
//                val actionIcon = null
//                val actionTitle = action.title
//                val actionIntent = action.actionIntent
//                Log.e(
//                    TAG,
//                    "addNotification: action\n" + "actionIcon = $actionIcon\n" + "actionTitle = $actionTitle\n" + "actionIntent = $actionIntent\n"
//                )
//            }

            notifyInfo.title = title
            notifyInfo.packageName = packageName
            notifyInfo.icon = smallIcon
            notifyInfo.msg = content.toString()
            notifyInfo.time = postTime
            notifyInfo.contentIntent = contentIntent

            val style = extras.getString(Notification.EXTRA_TEMPLATE, "")
            if (style.isNotEmpty()) {
                when (style) {
                    Notification.BigTextStyle::class.java.name -> {
                        val bigContentTitle =
                            sbn.notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG)
                        val summaryText =
                            sbn.notification.extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT)
                        val bigText =
                            sbn.notification.extras.getCharSequence(Notification.EXTRA_BIG_TEXT)
//                        Log.e(
//                            TAG,
//                            "addNotification: BigTextStyle\nbigContentTitle = $bigContentTitle\nsummaryText = $summaryText\nbigText = $bigText\n"
//                        )

                        if (bigContentTitle?.isNotEmpty() == true) notifyInfo.title =
                            bigContentTitle.toString()
                        if (bigText?.isNotEmpty() == true) notifyInfo.msg = bigText.toString()

                    }

                    Notification.BigPictureStyle::class.java.name -> {
                        val bigContentTitle =
                            sbn.notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG)
                        val summaryText =
                            sbn.notification.extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT)
                        val bigPicture =
                            sbn.notification.extras.getParcelable<Bitmap>(Notification.EXTRA_PICTURE)
//                        Log.e(
//                            TAG,
//                            "addNotification: BigPictureStyle\nbigContentTitle = $bigContentTitle\nsummaryText = $summaryText\nbigPicture = $bigPicture\n"
//                        )

                        if (bigContentTitle?.isNotEmpty() == true) notifyInfo.title =
                            bigContentTitle.toString()
                        if (bigPicture != null) notifyInfo.picture = bigPicture

                    }

                    Notification.InboxStyle::class.java.name -> {
                        val bigContentTitle =
                            sbn.notification.extras.getCharSequence(Notification.EXTRA_TITLE_BIG)
                        val summaryText =
                            sbn.notification.extras.getCharSequence(Notification.EXTRA_SUMMARY_TEXT)
                        val textLinesArray =
                            sbn.notification.extras.getCharSequenceArray(Notification.EXTRA_TEXT_LINES)
                        var textLines = ""
                        if (!textLinesArray.isNullOrEmpty()) {
                            val sb: StringBuilder = StringBuilder()
                            for (msg in textLinesArray) if (msg.isNotEmpty()) {
                                sb.append(msg.toString())
                                sb.append('\n')
                            }
                            textLines = sb.toString().trim { it <= ' ' }
                        }
//                        Log.e(
//                            TAG,
//                            "addNotification: BigPictureStyle\nbigContentTitle = $bigContentTitle\nsummaryText = $summaryText\ntextLines = $textLines\n"
//                        )

                        if (bigContentTitle?.isNotEmpty() == true) notifyInfo.title =
                            bigContentTitle.toString()
                        if (textLines.isNotEmpty()) notifyInfo.msg = textLines
                    }

                    Notification.MediaStyle::class.java.name -> {

                    }

                    Notification.DecoratedCustomViewStyle::class.java.name -> {

                    }

                    Notification.DecoratedMediaCustomViewStyle::class.java.name -> {

                    }

                    Notification.MessagingStyle::class.java.name -> {

                    }

//                Notification.CallStyle::class.java.name -> {
//
//                }

                    else -> {}
                }
            }

//            if (title.isNotEmpty()) {
            if (progress > 0) {
                notifyInfo.style = NOTIFICATION_STYLE_PROGRESS
                Log.i(TAG, "addNotification: update progress.progress = $progress,style = $style\n")
                instance?.updateGlassNotifyInfo(notifyInfo) ?: Log.e(
                    TAG, "addNotification: NotificationBar has not init..."
                )
            } else {
                Log.i(TAG, "addNotification: add notify.style = $style\n")
                instance?.addGlassNotifyInfo(notifyInfo) ?: Log.e(
                    TAG, "addNotification: NotificationBar has not init..."
                )
            }
//            } else {
//                Log.e(TAG, "addNotification: title is empty!!!")
//            }
        }
    }
3)处理手机端通知添加
复制代码
fun addNotification(context: Context, msg: MessageReqMsgNoti) {
        val notifyInfo = NotifyInfo(
            msg.title,
            AppUtils.getPhoneAppName(context, msg),
            AppUtils.getPhoneAppIcon(context, msg),
            msg.text,
            msg.timeStamp
        )

        if (msg.title.isNotEmpty()) {
            instance?.addPhoneNotifyInfo(notifyInfo) ?: Log.e(
                TAG, "addNotification: phone ------ NotificationBar has not init..."
            )
        } else {
            Log.e(TAG, "addNotification: phone ------ title is empty!!!")
        }
    }
4)通知信息数据类 NotifyInfo
复制代码
data class NotifyInfo(
    var title: String = "",
    var packageName: String = "",
    var icon: Drawable? = null,
    var msg: String = "",
    var time: Long = System.currentTimeMillis(),
    var style: String = "", // ProgressStyle
    var type: Int = 0,
    var location: String = "",
    var picture: Bitmap? = null,
    var foldInfoList: MutableList<NotifyInfo> = mutableListOf(),
    var contentIntent: PendingIntent? = null,
)

3.2 分类显示 AR 眼镜以及手机端的通知

此部分主要在 UI 布局设计这块,如下图所示:

通知中心,由于没有特殊复杂的产品设计,本文不再赘述,主要注意如下几点即可:

  1. 使用两个 Table 页去区分 AR 眼镜与手机端的通知;

  2. 然后使用 RecyclerView 列表去展示对应通知;

  3. 同时处理点击 Table 页时对应通知的数据切换;

  4. 处理清除按钮对通知的清除;

具体通知中心的代码,参考附录1。

4. ✅ 小结

对于系统通知定制(通知中心),本文只是一个基础实现方案,更多业务细节请参考产品逻辑去实现。

另外,由于本人能力有限,如有错误,敬请批评指正,谢谢。


附录1:通知中心代码

复制代码
class NotificationBar(context: Context, attrs: AttributeSet?) : ConstraintLayout(context, attrs),
    BarMonitor {

    val glassNotifyInfoList: MutableList<NotifyInfo> = mutableListOf()
    val phoneNotifyInfoList: MutableList<NotifyInfo> = mutableListOf()

    private lateinit var mRootView: View
    private lateinit var notifyClear: View
    private lateinit var notifyRecyclerView: AGGRecyclerView
    private lateinit var notifyNoMsgLayout: LinearLayout
    private lateinit var notifyNoMsgText: AGGTextView

    private lateinit var glassTitle: AGGTextView
    private lateinit var phoneTitle: AGGTextView
    private lateinit var foldPage: NotificationFoldPage

    private val TAG = NotificationBar::class.java.simpleName
    private val NOTIFY_COUNT_NUM = 100
    private val NOTIFY_FOLD_COUNT_NUM = 3
    private val notifyAdapter by lazy { NotifyAdapter() }

    private var isClickNotifyGlassBtn = false
    private var curOpenedFoldViewNotifyInfo: NotifyInfo? = null

    init {
        initView()
        initNotifyTitle()
        initLiveDataBus()
        initViewStatus()
    }

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        if (event.keyCode == KeyEvent.KEYCODE_BACK) {
            Log.i(TAG, "dispatchKeyEvent: KEYCODE_BACK")
            LiveDataBus.get().with(Constants.NOTIFICATION_EVENT_BUS_CLOSE_FOLD_PAGE).value = true
        }
        return super.dispatchKeyEvent(event)
    }

    override fun isBarVisible(): Boolean = mRootView.visibility == VISIBLE

    fun updateBarVisible(visible: Boolean) {
        if (visible) {
            if (mRootView.visibility != VISIBLE) mRootView.visibility = VISIBLE
        } else {
            if (mRootView.visibility == VISIBLE) mRootView.visibility = INVISIBLE
        }
        callBarStateChanged()
    }

    fun addGlassNotifyInfo(notifyInfo: NotifyInfo) {
        judgeFoldNotifyInfo(notifyInfo, glassNotifyInfoList)
        if (isClickNotifyGlassBtn) {
            notifyAdapter.addInfoList(glassNotifyInfoList)
            notifyDataSetChanged()
        }
        notifyClear.visibility = VISIBLE
    }

    fun addPhoneNotifyInfo(notifyInfo: NotifyInfo) {
        judgeFoldNotifyInfo(notifyInfo, phoneNotifyInfoList)
        if (!isClickNotifyGlassBtn) {
            notifyAdapter.addInfoList(phoneNotifyInfoList)
            notifyDataSetChanged()
        }
        notifyClear.visibility = VISIBLE
    }

    fun updateGlassNotifyInfo(notifyInfo: NotifyInfo) {
        var isUpdate = false
        for (i in 0 until glassNotifyInfoList.size) {
            if (glassNotifyInfoList[i].title == notifyInfo.title) {
                glassNotifyInfoList[i] = notifyInfo
                isUpdate = true
                break
            }
        }

        if (isClickNotifyGlassBtn && isUpdate) {
            notifyAdapter.updateInfo(notifyInfo)
            notifyDataSetChanged()
        }

        if (!isUpdate) addGlassNotifyInfo(notifyInfo)
    }

    @SuppressLint("NotifyDataSetChanged")
    fun notifyDataSetChanged() {
        if (glassNotifyInfoList.isEmpty() && phoneNotifyInfoList.isEmpty()) {
            notifyRecyclerView.visibility = INVISIBLE
            notifyNoMsgLayout.visibility = VISIBLE
            notifyClear.visibility = INVISIBLE
        } else {
            notifyAdapter.notifyDataSetChanged()
            notifyRecyclerView.scrollToPosition(0)
            notifyRecyclerView.visibility = VISIBLE
            notifyNoMsgLayout.visibility = INVISIBLE
            notifyClear.visibility = VISIBLE
        }
    }

    fun initViewStatus() {
        phoneTitle.setBackgroundResource(R.drawable.notification_bar_title_item_bg_select)
        glassTitle.setBackgroundColor(Color.TRANSPARENT)
        notifyAdapter.addInfoList(phoneNotifyInfoList)
        if (phoneNotifyInfoList.isEmpty()) {
            notifyRecyclerView.visibility = INVISIBLE
            notifyNoMsgLayout.visibility = VISIBLE
            updateNoMsgText()
            if (glassNotifyInfoList.isEmpty()) notifyClear.visibility = INVISIBLE
            else notifyClear.visibility = VISIBLE
        } else {
            notifyDataSetChanged()
            notifyRecyclerView.visibility = VISIBLE
            notifyNoMsgLayout.visibility = INVISIBLE
        }
    }

    private fun initView() {
        mRootView = LayoutInflater.from(context)
            .inflate(R.layout.notification_layout_notificationbar, this, true)
        notifyClear = mRootView.findViewById(R.id.notify_clear)
        notifyRecyclerView = mRootView.findViewById(R.id.notify_list)
        notifyNoMsgLayout = mRootView.findViewById(R.id.notify_no_msg_layout)
        notifyNoMsgText = mRootView.findViewById(R.id.notify_no_msg_text)
        glassTitle = mRootView.findViewById(R.id.glassTitle)
        phoneTitle = mRootView.findViewById(R.id.phoneTitle)
        notifyRecyclerView.apply {
            layoutManager = LinearLayoutManager(context)
            addItemDecoration(SpacesItemDecoration((10 * context.resources.displayMetrics.density + 0.5f).toInt()))
            adapter = notifyAdapter
            isFocusableInTouchMode = true
        }
        foldPage = mRootView.findViewById(R.id.fold_page)

        mRootView.postDelayed({
            registerPackageRemoved(context)
            register(context)
        }, 3000)
        // 重启/开机后,AR眼镜自动连接上手机时,通知中心不一定收到ACTION_LINK_STATE_CHANGED状态的回调,于是在重启/开机一定时间后主动获取状态。
        mRootView.postDelayed({ updateNoMsgText() }, 15000)
    }

    @SuppressLint("NotifyDataSetChanged")
    private fun initNotifyTitle() {
        notifyClear.setOnClickListener {
            SoundPoolTools.playNotifyClear(context.applicationContext)

            if (notifyAdapter.itemCount == 0) {
                handleClearNotification()
            } else {
                notifyRecyclerView.clearAllItem(object : AnimatorListener {
                    override fun onAnimationStart(p0: Animator) {}

                    override fun onAnimationEnd(p0: Animator) {
                        handleClearNotification()
                    }

                    override fun onAnimationCancel(p0: Animator) {}

                    override fun onAnimationRepeat(p0: Animator) {}
                })
            }
        }
        glassTitle.setOnHoverListener { v, event ->
            if (event.action == MotionEvent.ACTION_HOVER_ENTER) {
                glassTitle.setBackgroundResource(R.drawable.notification_bar_title_item_bg_foc)
            } else if (event.action == MotionEvent.ACTION_HOVER_EXIT) {
                if (isClickNotifyGlassBtn) {
                    glassTitle.setBackgroundResource(R.drawable.notification_bar_title_item_bg_select)
                } else {
                    glassTitle.setBackgroundColor(Color.TRANSPARENT)
                }
            }
            false
        }
        glassTitle.setOnClickListener {
            if (!isClickNotifyGlassBtn) {
                isClickNotifyGlassBtn = true
                glassTitle.setBackgroundResource(R.drawable.notification_bar_title_item_bg_select)
                phoneTitle.setBackgroundColor(Color.TRANSPARENT)
                notifyAdapter.addInfoList(glassNotifyInfoList)
                if (glassNotifyInfoList.isEmpty()) {
                    notifyRecyclerView.visibility = INVISIBLE
                    notifyNoMsgLayout.visibility = VISIBLE
                    updateNoMsgText()
                    if (phoneNotifyInfoList.isEmpty()) notifyClear.visibility = INVISIBLE
                    else notifyClear.visibility = VISIBLE
                } else {
                    notifyDataSetChanged()

                    notifyRecyclerView.visibility = VISIBLE
                    notifyNoMsgLayout.visibility = INVISIBLE
                }
            }
        }
        phoneTitle.setOnHoverListener { v, event ->
            if (event.action == MotionEvent.ACTION_HOVER_ENTER) {
                phoneTitle.setBackgroundResource(R.drawable.notification_bar_title_item_bg_foc)
            } else if (event.action == MotionEvent.ACTION_HOVER_EXIT) {
                if (isClickNotifyGlassBtn) {
                    phoneTitle.setBackgroundColor(Color.TRANSPARENT)
                } else {
                    phoneTitle.setBackgroundResource(R.drawable.notification_bar_title_item_bg_select)
                }
            }
            false
        }
        phoneTitle.setOnClickListener {
            if (isClickNotifyGlassBtn) {
                isClickNotifyGlassBtn = false
                initViewStatus()
            }
        }
    }

    private fun initLiveDataBus() {
        LiveDataBus.get().with(
            Constants.NOTIFICATION_EVENT_BUS_OPEN_FOLD_PAGE, NotifyInfo::class.java
        ).observeForever {
            curOpenedFoldViewNotifyInfo = it
            foldPage.visibility = VISIBLE
            foldPage.addFoldNotifyInfo(it.foldInfoList)
        }
        LiveDataBus.get()
            .with(Constants.NOTIFICATION_EVENT_BUS_CLOSE_FOLD_PAGE, Boolean::class.java)
            .observeForever {
                curOpenedFoldViewNotifyInfo = null
                foldPage.visibility = GONE
            }
        LiveDataBus.get().with(Constants.NOTIFICATION_EVENT_BUS_CLEAR_FOLD_VIEW, String::class.java)
            .observeForever {
                curOpenedFoldViewNotifyInfo = null
                foldPage.visibility = GONE
                clearCurFoldItem(
                    if (isClickNotifyGlassBtn) glassNotifyInfoList else phoneNotifyInfoList, it
                )
            }
        LiveEventBus.get(NOTIFICATION_PHONE_LINK_STATE_CHANGED, Boolean::class.java)
            .observeForever { updateNoMsgText() }
    }

    private fun registerPackageRemoved(context: Context) {
        val filter = IntentFilter()
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
        filter.addDataScheme("package")
        context.registerReceiver(object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                val pkgName = intent.data?.schemeSpecificPart
                Log.d("NotificationBar", "onReceive: $pkgName removed")

                val tempRemoveList: MutableList<NotifyInfo> = mutableListOf()
                for (i in 0 until glassNotifyInfoList.size) {
                    if (glassNotifyInfoList[i].packageName == pkgName) {
                        tempRemoveList.add(glassNotifyInfoList[i])
                    }
                }

                if (tempRemoveList.isNotEmpty()) {
                    glassNotifyInfoList.removeAll(tempRemoveList)
                    if (isClickNotifyGlassBtn) {
                        if (glassNotifyInfoList.isEmpty() && phoneNotifyInfoList.isEmpty()) {
                            notifyClear.visibility = INVISIBLE
                        } else {
                            notifyClear.visibility = VISIBLE
                        }
                        notifyAdapter.addInfoList(glassNotifyInfoList)
                        notifyDataSetChanged()
                        updateNoMsgText()
                    }

                    curOpenedFoldViewNotifyInfo?.let {
                        if (pkgName == it.packageName) {
                            LiveDataBus.get().with(Constants.NOTIFICATION_EVENT_BUS_CLEAR_FOLD_VIEW)
                                .setValue(pkgName)
                        }
                    }
                }
            }
        }, filter, null, Handler())
    }

    private fun register(context: Context) {
        val filter = IntentFilter()
        filter.addAction(Intent.ACTION_TIME_TICK)
        filter.addAction(Intent.ACTION_TIME_CHANGED)
        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED)
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
        context.registerReceiver(object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                notifyAllTime()
            }
        }, filter, null, Handler())
    }

    private fun updateNoMsgText() {
        if (notifyNoMsgLayout.isVisible) {
            if (isClickNotifyGlassBtn) {
                notifyNoMsgText.text = context.getString(R.string.notification_glass_no_msg)
            } else {
                if (PhoneNotificationHelper.isConnectPhone()) {
                    notifyNoMsgText.text = context.getString(R.string.notification_glass_no_msg)
                } else {
                    notifyNoMsgText.text = context.getString(R.string.notification_phone_no_msg)
                }
            }
        }
    }

    private fun notifyAllTime() {
        if (glassNotifyInfoList.isNotEmpty() || phoneNotifyInfoList.isNotEmpty()) {
            notifyAdapter.notifyItemRangeChanged(0, notifyAdapter.itemCount, "update_time")
        }
    }

    /**
     * 同一应用3条及以上通知,折叠显示判断
     */
    private fun judgeFoldNotifyInfo(info: NotifyInfo, infoList: MutableList<NotifyInfo>) {
        val foldInfoList: MutableList<NotifyInfo> = mutableListOf()
        for (i in 0 until infoList.size) {
            if (infoList[i].packageName == info.packageName) {
                if (infoList[i].foldInfoList.size >= NOTIFY_FOLD_COUNT_NUM) { // 已经折叠
                    if (infoList[i].foldInfoList.size == NOTIFY_COUNT_NUM) infoList[i].foldInfoList.remove(
                        infoList[i].foldInfoList[NOTIFY_COUNT_NUM - 1]
                    )
                    infoList[i].foldInfoList.add(0, info)

                    // 移到最前方
                    val notifyInfoTemp = infoList[i]
                    infoList.remove(notifyInfoTemp)
                    infoList.add(0, notifyInfoTemp)

                    //  更新折叠页面
                    curOpenedFoldViewNotifyInfo?.let {
                        if (it.packageName == info.packageName) {
                            foldPage.updateFoldNotifyInfo(notifyInfoTemp.foldInfoList)
                            curOpenedFoldViewNotifyInfo = notifyInfoTemp
                        }
                    }
                    return
                } else {
                    foldInfoList.add(infoList[i])
                }
            }
        }
        foldInfoList.add(0, info)

        // 判断是否折叠通知
        if (foldInfoList.size >= NOTIFY_FOLD_COUNT_NUM) {
            info.type = Constants.NOTIFICATION_TYPE_FOLD
            info.foldInfoList = foldInfoList
            infoList.removeAll(foldInfoList)
            infoList.add(0, info)
        } else {
            if (infoList.size == NOTIFY_COUNT_NUM) infoList.remove(infoList[NOTIFY_COUNT_NUM - 1])
            infoList.add(0, info)
        }
    }

    /**
     * 清除当前的折叠通知Item项
     */
    @SuppressLint("NotifyDataSetChanged")
    private fun clearCurFoldItem(notifyInfoList: MutableList<NotifyInfo>, pkgName: String) {
        val tempNotifyInfoList: MutableList<NotifyInfo> = mutableListOf()
        for (i in 0 until notifyInfoList.size) {
            if (notifyInfoList[i].packageName == pkgName) {
                tempNotifyInfoList.add(notifyInfoList[i])
            }
        }
        notifyInfoList.removeAll(tempNotifyInfoList)
        notifyAdapter.addInfoList(notifyInfoList)
        if (notifyInfoList.isEmpty()) {
            notifyAdapter.notifyDataSetChanged()
            notifyRecyclerView.visibility = INVISIBLE
            notifyNoMsgLayout.visibility = VISIBLE
            updateNoMsgText()
            if (glassNotifyInfoList.isEmpty() && phoneNotifyInfoList.isEmpty()) {
                notifyClear.visibility = INVISIBLE
            } else {
                notifyClear.visibility = VISIBLE
            }
        } else {
            notifyDataSetChanged()
        }
    }

    private fun handleClearNotification() {
        glassNotifyInfoList.clear()
        phoneNotifyInfoList.clear()
        notifyAdapter.clear()

        notifyRecyclerView.visibility = INVISIBLE
        notifyNoMsgLayout.visibility = VISIBLE
        updateNoMsgText()
        notifyClear.visibility = INVISIBLE
    }

}
相关推荐
AFinalStone18 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
AFinalStone18 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
AFinalStone18 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui
AFinalStone19 天前
Android7 SystemUI 源码解析(一)全景概览与架构设计
android·systemui
AFinalStone19 天前
Android7 SystemUI 源码解析(六)QuickSettings快捷设置面板
android·systemui
贾伟康1 个月前
【时光清单|04】HarmonyOS ArkTS 通知服务实战:管理权限、渠道和点击跳转
harmonyos·arkts·系统通知·notificationkit·wantagent
陆业聪6 个月前
SystemUI 里做 Launcher App 列表:四种方案的 Framework 原理与工程取舍
android·framework·systemui·launcher
Just_Paranoid9 个月前
【TaskbarDelegate】屏蔽上滑返回桌面手势功能
android·systemui·navigation·launcher·gesture
Just_Paranoid10 个月前
【SystemUI】基于 Android R 实现下拉状态栏毛玻璃背景
android·canvas·systemui·renderscript
ItJavawfc10 个月前
SystemUI-下拉去掉后去掉用户切换-去掉电源按键中的紧急呼叫
systemui·systemui去除紧急呼叫·去除紧急呼叫·去除用户切换·去除用户选择