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
    }

}
相关推荐
ItJavawfc8 天前
Android12_13左上角状态栏数字时间显示右移动
systemui·时间右移动
Swuagg15 天前
AR 眼镜之-系统通知定制(通知弹窗)-实现方案
notification·systemui·系统通知·通知弹窗
小先生Zcutie5 个月前
【Android】SystemUI通知栏过滤指定应用的通知
android·systemui
hehui09215 个月前
android11 SystemUI入門之KeyguardPatternView解析
systemui
JohnnyDeng9410 个月前
StatusBarManager中的相关标志位
systemui
Jon_Lo1 年前
Android SystemUI setSystemUiVisibility()参数Flag详解
android·导航栏·systemui·1024程序员节·状态栏·fullscreen
xhBruce1 年前
SystemUI状态栏
systemui·状态栏
xhBruce1 年前
SystemUI导航栏
导航栏·systemui
一化十1 年前
Android 6.0 Settings中添加虚拟键开关
android·settings·mtk·systemui