203 lines
9.7 KiB
Kotlin
203 lines
9.7 KiB
Kotlin
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<String, Long> 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
|
||
}
|
||
} |