package com.gh.common.util import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import android.os.Build import androidx.core.app.NotificationCompat import com.gh.common.xapk.XapkInstaller import com.gh.common.xapk.XapkUnzipStatus import com.gh.gamecenter.R import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.EntranceConsts import com.gh.gamecenter.common.utils.getMetaExtra import com.gh.gamecenter.common.utils.isSimulatorGame import com.gh.gamecenter.common.utils.isVGame import com.gh.gamecenter.common.utils.tryCatchInRelease import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.utils.SpeedUtils import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity import com.lightgame.download.DownloadStatus object DownloadNotificationHelper { private const val DOWNLOAD_GROUP_KEY = "download_group_key" private const val DOWNLOAD_CHANNEL_ID = "download" private const val DOWNLOAD_NOTIFICATION_FOLD_ID = 889 private const val DOWNLOAD_NOTIFICATION_ID = 888 private const val PROGRESS_MAX = 100 const val ACTION_INSTALL = "com.gh.gamecenter.INSTALL" const val ACTION_DOWNLOAD = "com.gh.gamecenter.DOWNLOAD" const val ACTION_VDOWNLOAD = "com.gh.gamecenter.VDOWNLOAD" private val mNotifyMap: MutableMap by lazy { mutableMapOf() } private fun getNotificationManager(): NotificationManager { return HaloApp.getInstance().application.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } @JvmStatic @Synchronized fun addOrUpdateDownloadNotification(entity: DownloadEntity) { var requireUpdateNotificationGroupDelay = false val notificationManager = getNotificationManager() val downloadNotificationId = (entity.gameId + entity.packageName).hashCode() val xapkStatus = entity.meta[XapkInstaller.XAPK_UNZIP_STATUS] if (entity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.SILENT_UPDATE) return if (entity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.SIMULATOR_DOWNLOAD) return val intent = Intent() if (entity.status == DownloadStatus.done && xapkStatus != XapkUnzipStatus.FAILURE.name) { intent.putExtra(EntranceConsts.KEY_URL, entity.url) intent.putExtra(EntranceConsts.KEY_PATH, entity.path) intent.action = ACTION_INSTALL } else if (entity.isVGame()) { intent.action = ACTION_VDOWNLOAD } else { intent.action = ACTION_DOWNLOAD } val pendingIntent = PendingIntent.getBroadcast( HaloApp.getInstance().application, downloadNotificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel(DOWNLOAD_CHANNEL_ID, DOWNLOAD_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW) notificationManager.createNotificationChannel(channel) } val whenTime = 1000 * 60 * (System.currentTimeMillis() / 1000 / 60) val builder = NotificationCompat.Builder(HaloApp.getInstance().application, DOWNLOAD_CHANNEL_ID) .setContentTitle(entity.name) .setSmallIcon(getNotificationIcon()) .setContentIntent(pendingIntent) .setGroup(DOWNLOAD_GROUP_KEY) .setWhen(whenTime) if (xapkStatus == XapkUnzipStatus.FAILURE.name) { builder.setContentText("《" + entity.name + "》解压失败,点击查看详情~") } else { when (entity.status) { DownloadStatus.downloading -> builder.setContentText( String.format( "%s(剩%s)", SpeedUtils.getSpeed(entity.speed), SpeedUtils.getRemainTime(entity.size, entity.progress, entity.speed * 1024) ) ) DownloadStatus.done -> builder.setContentText("下载完成,点击立即安装") DownloadStatus.waiting -> builder.setContentText("等待中") DownloadStatus.subscribe, DownloadStatus.timeout, DownloadStatus.diskisfull, DownloadStatus.neterror -> builder.setContentText("已暂停,连接WiFi自动下载") else -> builder.setContentText("暂停中") } builder.setProgress(PROGRESS_MAX, entity.percent.toInt(), false) } when (entity.status) { DownloadStatus.done -> { builder.setSortKey("A") builder.setOngoing(true) // 垃圾华为 sortKey 不起效 priority 也不起效,要将下载完成任务的通知置顶只能设置为 ongoing,喷了 } DownloadStatus.downloading -> builder.setSortKey("B") else -> builder.setSortKey("C") } tryCatchInRelease { val notification = builder.build() // 可能会抛出异常 notification.flags = notification.flags or Notification.FLAG_NO_CLEAR if (xapkStatus == XapkUnzipStatus.FAILURE.name) { notification.flags = notification.flags or Notification.FLAG_AUTO_CANCEL } else { notification.flags = notification.flags or Notification.FLAG_NO_CLEAR } if (entity.status == DownloadStatus.delete || entity.status == DownloadStatus.cancel || entity.status == DownloadStatus.hijack || entity.status == DownloadStatus.unqualified || entity.status == DownloadStatus.unavailable || entity.status == DownloadStatus.banned || entity.status == DownloadStatus.uncertificated || entity.status == DownloadStatus.notfound || entity.status == DownloadStatus.overflow || (entity.status == DownloadStatus.done // 触发安装事件以后也 cancel 掉通知 && !entity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION].isNullOrEmpty() && xapkStatus != XapkUnzipStatus.FAILURE.name) || entity.status == DownloadStatus.done && entity.isSimulatorGame() ) {//模拟器游戏下载完需要cancel掉通知 requireUpdateNotificationGroupDelay = true notificationManager.cancel(entity.path, DOWNLOAD_NOTIFICATION_ID) } else { if (entity.status != DownloadStatus.downloading) { notificationManager.notify(entity.path, DOWNLOAD_NOTIFICATION_ID, notification) } else { val time = mNotifyMap[entity.path] val curTime = System.currentTimeMillis() if (time == null || curTime - time > 2000) { mNotifyMap[entity.path] = curTime notificationManager.notify(entity.path, DOWNLOAD_NOTIFICATION_ID, notification) } } } } if (requireUpdateNotificationGroupDelay) { // 虽然运行到这里时 notification 已经被 cancel 了,但在下面的 notificationManager.getActiveNotifications 里它有可能还是 active 状态, // 这里延时 100 ms 避免出现所有的任务都取消了以后依旧有一条 notification group 常驻 AppExecutor.uiExecutor.executeWithDelay(Runnable { updateNotificationGroup() }, 100) } else { updateNotificationGroup() } } private fun updateNotificationGroup() { // 部分华为设备调用 getActiveNotifications() 方法时会触发方法内的空指针,这里整体包裹处理了 tryCatchInRelease { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val notificationManager = getNotificationManager() val activeNotifications = notificationManager.activeNotifications var downloadNotificationSize = 0 var downloadGroupNotificationSize = 0 for (activeNotification in activeNotifications) { if (activeNotification.id == DOWNLOAD_NOTIFICATION_ID) { downloadNotificationSize++ } if (activeNotification.id == DOWNLOAD_NOTIFICATION_FOLD_ID) { downloadGroupNotificationSize++ } } if (downloadNotificationSize == 0 && downloadGroupNotificationSize != 0) { // 删除组可能会把组内所有通知一并删除 notificationManager.cancel(DOWNLOAD_NOTIFICATION_FOLD_ID) } else if (downloadNotificationSize != 0 && downloadGroupNotificationSize == 0) { val groupBuilder = NotificationCompat.Builder(HaloApp.getInstance().application, DOWNLOAD_CHANNEL_ID) .setSmallIcon(getNotificationIcon()) .setGroup(DOWNLOAD_GROUP_KEY) .setGroupSummary(true) .setStyle(NotificationCompat.BigTextStyle().bigText("下载任务")) val groupNotification = groupBuilder.build() groupNotification.flags = groupNotification.flags or Notification.FLAG_NO_CLEAR notificationManager.notify(DOWNLOAD_NOTIFICATION_FOLD_ID, groupNotification) } } } } private fun getNotificationIcon(): Int { return R.mipmap.logo } }