|
| 1 | +package com.snownamida.touchgrass.track |
| 2 | + |
| 3 | +import android.app.Notification |
| 4 | +import android.app.NotificationChannel |
| 5 | +import android.app.NotificationManager |
| 6 | +import android.content.Context |
| 7 | +import android.graphics.drawable.Icon |
| 8 | +import android.os.Bundle |
| 9 | +import com.snownamida.touchgrass.R |
| 10 | +import org.json.JSONObject |
| 11 | + |
| 12 | +/** |
| 13 | + * 会话进行中的常驻通知: |
| 14 | + * - 所有机型:通知栏/锁屏显示自走秒的计时(chronometer) |
| 15 | + * - HyperOS:附带焦点通知/超级岛参数(miui.focus.param)。官方协议公开但 |
| 16 | + * 渲染上岛需要小米授予权限,没有权限时系统按普通通知显示——优雅降级。 |
| 17 | + */ |
| 18 | +object TimerNotification { |
| 19 | + private const val CHANNEL_ID = "timer" |
| 20 | + private const val NOTIF_ID = 42 |
| 21 | + private var sequence = 0L |
| 22 | + |
| 23 | + fun show(context: Context, ruleName: String, elapsedSec: Long) { |
| 24 | + val nm = context.getSystemService(NotificationManager::class.java) |
| 25 | + if (nm.getNotificationChannel(CHANNEL_ID) == null) { |
| 26 | + nm.createNotificationChannel( |
| 27 | + NotificationChannel(CHANNEL_ID, "刷屏计时", NotificationManager.IMPORTANCE_LOW) |
| 28 | + ) |
| 29 | + } |
| 30 | + |
| 31 | + val minutes = elapsedSec / 60 |
| 32 | + val content = if (minutes > 0) "这一波已刷 $minutes 分钟" else "开始计时…" |
| 33 | + |
| 34 | + val builder = Notification.Builder(context, CHANNEL_ID) |
| 35 | + .setSmallIcon(R.drawable.ic_stat_grass) |
| 36 | + .setContentTitle("咋还在刷 · $ruleName") |
| 37 | + .setContentText(content) |
| 38 | + .setWhen(System.currentTimeMillis() - elapsedSec * 1000) |
| 39 | + .setUsesChronometer(true) |
| 40 | + .setOngoing(true) |
| 41 | + .setOnlyAlertOnce(true) |
| 42 | + |
| 43 | + val pics = Bundle() |
| 44 | + pics.putParcelable( |
| 45 | + "miui.focus.pic_grass", |
| 46 | + Icon.createWithResource(context, R.drawable.ic_stat_grass) |
| 47 | + ) |
| 48 | + val extras = Bundle() |
| 49 | + extras.putBundle("miui.focus.pics", pics) |
| 50 | + builder.addExtras(extras) |
| 51 | + |
| 52 | + val notification = builder.build() |
| 53 | + notification.extras.putString("miui.focus.param", islandParam(ruleName, content)) |
| 54 | + // 用户没给通知权限时 notify 会被系统拒绝,静默即可(悬浮计时药丸不受影响) |
| 55 | + runCatching { nm.notify(NOTIF_ID, notification) } |
| 56 | + } |
| 57 | + |
| 58 | + fun hide(context: Context) { |
| 59 | + runCatching { context.getSystemService(NotificationManager::class.java).cancel(NOTIF_ID) } |
| 60 | + } |
| 61 | + |
| 62 | + /** HyperOS 焦点通知/超级岛协议(param_v2),字段参考小米官方与阿里云 EMAS 文档。 */ |
| 63 | + private fun islandParam(ruleName: String, content: String): String { |
| 64 | + sequence += 1 |
| 65 | + val picInfo = JSONObject().put("type", 1).put("pic", "miui.focus.pic_grass") |
| 66 | + val textInfo = JSONObject().put("title", "咋还在刷").put("content", content) |
| 67 | + return JSONObject().put( |
| 68 | + "param_v2", |
| 69 | + JSONObject() |
| 70 | + .put("protocol", 1) |
| 71 | + .put("business", "timer") |
| 72 | + .put("updatable", true) |
| 73 | + .put("sequence", sequence) |
| 74 | + .put("baseInfo", JSONObject().put("type", 1).put("title", "咋还在刷 · $ruleName").put("content", content)) |
| 75 | + .put( |
| 76 | + "param_island", |
| 77 | + JSONObject() |
| 78 | + .put("islandProperty", 1) |
| 79 | + .put("smallIslandArea", JSONObject().put("picInfo", picInfo)) |
| 80 | + .put( |
| 81 | + "bigIslandArea", |
| 82 | + JSONObject().put( |
| 83 | + "imageTextInfoLeft", |
| 84 | + JSONObject().put("type", 1).put("picInfo", picInfo).put("textInfo", textInfo) |
| 85 | + ) |
| 86 | + ) |
| 87 | + .put("shareData", JSONObject().put("title", "咋还在刷计时")) |
| 88 | + ) |
| 89 | + ).toString() |
| 90 | + } |
| 91 | +} |
0 commit comments