2023-02-04 21:52:26 +01:00
|
|
|
package jp.juggler.subwaytooter.notification
|
|
|
|
|
2023-02-05 16:44:28 +01:00
|
|
|
import android.Manifest
|
2023-06-08 23:23:35 +02:00
|
|
|
import android.annotation.SuppressLint
|
2023-02-04 21:52:26 +01:00
|
|
|
import android.app.NotificationChannel
|
2023-02-05 16:44:28 +01:00
|
|
|
import android.app.PendingIntent
|
2023-02-04 21:52:26 +01:00
|
|
|
import android.content.Context
|
2023-02-05 16:44:28 +01:00
|
|
|
import android.content.pm.PackageManager
|
2023-06-08 23:23:35 +02:00
|
|
|
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
2023-02-05 16:44:28 +01:00
|
|
|
import android.os.Build
|
|
|
|
import androidx.annotation.ColorInt
|
|
|
|
import androidx.annotation.DrawableRes
|
2023-02-04 21:52:26 +01:00
|
|
|
import androidx.annotation.StringRes
|
2023-02-05 16:44:28 +01:00
|
|
|
import androidx.core.app.ActivityCompat
|
2023-02-04 21:52:26 +01:00
|
|
|
import androidx.core.app.NotificationCompat
|
|
|
|
import androidx.core.app.NotificationManagerCompat
|
2023-02-05 16:44:28 +01:00
|
|
|
import androidx.core.content.ContextCompat
|
2023-02-04 21:52:26 +01:00
|
|
|
import androidx.startup.Initializer
|
2023-02-05 16:44:28 +01:00
|
|
|
import androidx.work.ForegroundInfo
|
2023-02-08 20:50:08 +01:00
|
|
|
import jp.juggler.subwaytooter.BuildConfig
|
2023-02-04 21:52:26 +01:00
|
|
|
import jp.juggler.subwaytooter.R
|
2023-02-05 16:44:28 +01:00
|
|
|
import jp.juggler.subwaytooter.pref.LazyContextInitializer
|
2023-02-04 21:52:26 +01:00
|
|
|
import jp.juggler.util.*
|
|
|
|
import jp.juggler.util.log.LogCategory
|
|
|
|
|
|
|
|
private val log = LogCategory("NotificationChannels")
|
|
|
|
|
2023-06-08 23:23:35 +02:00
|
|
|
@SuppressLint("InlinedApi")
|
2023-02-04 21:52:26 +01:00
|
|
|
enum class NotificationChannels(
|
|
|
|
val id: String,
|
|
|
|
@StringRes val titleId: Int,
|
|
|
|
@StringRes val descId: Int,
|
|
|
|
val importance: Int,
|
|
|
|
val priority: Int,
|
2023-06-08 23:23:35 +02:00
|
|
|
// foreground service type,
|
|
|
|
val foregroundServiceType: Int,
|
2023-02-04 21:52:26 +01:00
|
|
|
// 通知ID。(ID+tagでユニーク)
|
|
|
|
val notificationId: Int,
|
|
|
|
// PendingIntentのrequestCode。(ID+intentのdata Uriでユニーク)
|
|
|
|
// pending intent request code for tap
|
|
|
|
val pircTap: Int,
|
|
|
|
// pending intent request code for delete
|
|
|
|
val pircDelete: Int,
|
|
|
|
// 通知削除のUri prefix
|
|
|
|
val uriPrefixDelete: String,
|
2023-02-08 20:50:08 +01:00
|
|
|
// 通知タップのUri prefix
|
|
|
|
val uriPrefixTap: String,
|
2023-02-04 21:52:26 +01:00
|
|
|
) {
|
|
|
|
PullNotification(
|
|
|
|
id = "SnsNotification",
|
|
|
|
titleId = R.string.pull_notification,
|
|
|
|
descId = R.string.pull_notification_desc,
|
|
|
|
importance = NotificationManagerCompat.IMPORTANCE_DEFAULT,
|
|
|
|
priority = NotificationCompat.PRIORITY_DEFAULT,
|
2023-06-08 23:23:35 +02:00
|
|
|
foregroundServiceType = FOREGROUND_SERVICE_TYPE_DATA_SYNC,
|
2023-02-04 21:52:26 +01:00
|
|
|
notificationId = 1,
|
|
|
|
pircTap = 1,
|
|
|
|
pircDelete = 1, // uriでtapとdeleteを区別している
|
2023-02-08 20:50:08 +01:00
|
|
|
uriPrefixDelete = "${BuildConfig.customScheme}://notification_delete/",
|
|
|
|
uriPrefixTap = "${BuildConfig.customScheme}://notification_click/",
|
2023-02-04 21:52:26 +01:00
|
|
|
),
|
2023-02-05 16:44:28 +01:00
|
|
|
PullWorker(
|
2023-02-04 21:52:26 +01:00
|
|
|
id = "PollingForegrounder",
|
2023-02-05 16:44:28 +01:00
|
|
|
titleId = R.string.loading_notification_title,
|
2023-02-04 21:52:26 +01:00
|
|
|
descId = R.string.polling_foregrounder_desc,
|
|
|
|
importance = NotificationManagerCompat.IMPORTANCE_LOW,
|
|
|
|
priority = NotificationCompat.PRIORITY_MIN,
|
2023-06-08 23:23:35 +02:00
|
|
|
foregroundServiceType = FOREGROUND_SERVICE_TYPE_DATA_SYNC,
|
2023-02-04 21:52:26 +01:00
|
|
|
notificationId = 2,
|
|
|
|
pircTap = 2,
|
|
|
|
pircDelete = -1,
|
2023-02-08 20:50:08 +01:00
|
|
|
uriPrefixDelete = "${BuildConfig.customScheme}://checker",
|
|
|
|
uriPrefixTap = "${BuildConfig.customScheme}://checker-tap",
|
2023-02-04 21:52:26 +01:00
|
|
|
),
|
|
|
|
ServerTimeout(
|
|
|
|
id = "ErrorNotification",
|
|
|
|
titleId = R.string.server_timeout,
|
|
|
|
descId = R.string.server_timeout_desc,
|
|
|
|
importance = NotificationManagerCompat.IMPORTANCE_LOW,
|
|
|
|
priority = NotificationCompat.PRIORITY_LOW,
|
2023-06-08 23:23:35 +02:00
|
|
|
foregroundServiceType = FOREGROUND_SERVICE_TYPE_DATA_SYNC,
|
2023-02-04 21:52:26 +01:00
|
|
|
notificationId = 3,
|
|
|
|
pircTap = 3,
|
2023-02-05 16:44:28 +01:00
|
|
|
pircDelete = 4,
|
2023-02-08 20:50:08 +01:00
|
|
|
uriPrefixDelete = "${BuildConfig.customScheme}://server-timeout",
|
|
|
|
uriPrefixTap = "${BuildConfig.customScheme}://server-timeout-tap",
|
2023-02-04 21:52:26 +01:00
|
|
|
),
|
|
|
|
PushMessage(
|
|
|
|
id = "PushMessage",
|
|
|
|
titleId = R.string.push_message,
|
|
|
|
descId = R.string.push_message_desc,
|
|
|
|
importance = NotificationManagerCompat.IMPORTANCE_HIGH,
|
|
|
|
priority = NotificationCompat.PRIORITY_HIGH,
|
2023-06-08 23:23:35 +02:00
|
|
|
foregroundServiceType = FOREGROUND_SERVICE_TYPE_DATA_SYNC,
|
2023-02-05 16:44:28 +01:00
|
|
|
notificationId = 5,
|
|
|
|
pircTap = 5,
|
|
|
|
pircDelete = 6,
|
2023-02-08 20:50:08 +01:00
|
|
|
uriPrefixDelete = "${BuildConfig.customScheme}://pushMessage",
|
|
|
|
uriPrefixTap = "${BuildConfig.customScheme}://notification_click/",
|
2023-02-04 21:52:26 +01:00
|
|
|
),
|
|
|
|
Alert(
|
|
|
|
id = "Alert",
|
|
|
|
titleId = R.string.alert,
|
|
|
|
descId = R.string.alert_notification_desc,
|
|
|
|
importance = NotificationManagerCompat.IMPORTANCE_HIGH,
|
|
|
|
priority = NotificationCompat.PRIORITY_HIGH,
|
2023-06-08 23:23:35 +02:00
|
|
|
foregroundServiceType = FOREGROUND_SERVICE_TYPE_DATA_SYNC,
|
2023-02-05 16:44:28 +01:00
|
|
|
notificationId = 7,
|
|
|
|
pircTap = 7,
|
|
|
|
pircDelete = 8,
|
2023-02-08 20:50:08 +01:00
|
|
|
uriPrefixDelete = "${BuildConfig.customScheme}://alert",
|
|
|
|
uriPrefixTap = "${BuildConfig.customScheme}://alert-tap",
|
2023-02-04 21:52:26 +01:00
|
|
|
),
|
2023-02-05 16:44:28 +01:00
|
|
|
PushWorker(
|
2023-02-04 21:52:26 +01:00
|
|
|
id = "PushMessageWorker",
|
|
|
|
titleId = R.string.push_worker,
|
|
|
|
descId = R.string.push_worker_desc,
|
|
|
|
importance = NotificationManagerCompat.IMPORTANCE_LOW,
|
|
|
|
priority = NotificationCompat.PRIORITY_LOW,
|
2023-06-08 23:23:35 +02:00
|
|
|
foregroundServiceType = FOREGROUND_SERVICE_TYPE_DATA_SYNC,
|
2023-02-05 16:44:28 +01:00
|
|
|
notificationId = 9,
|
|
|
|
pircTap = 9,
|
|
|
|
pircDelete = 10,
|
2023-02-08 20:50:08 +01:00
|
|
|
uriPrefixDelete = "${BuildConfig.customScheme}://pushWorker",
|
|
|
|
uriPrefixTap = "${BuildConfig.customScheme}://pushWorker-tag",
|
2023-02-04 21:52:26 +01:00
|
|
|
),
|
2023-02-05 16:44:28 +01:00
|
|
|
|
|
|
|
;
|
|
|
|
|
2023-02-08 10:55:49 +01:00
|
|
|
fun isDisabled(context: Context) = !isEnabled(context)
|
2023-02-05 16:44:28 +01:00
|
|
|
|
2023-06-08 23:23:35 +02:00
|
|
|
private fun isEnabled(context: Context): Boolean {
|
2023-02-05 16:44:28 +01:00
|
|
|
if (Build.VERSION.SDK_INT >= 33) {
|
|
|
|
if (ActivityCompat.checkSelfPermission(
|
|
|
|
context,
|
|
|
|
Manifest.permission.POST_NOTIFICATIONS
|
|
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
|
|
) {
|
|
|
|
log.w("[$name] missing POST_NOTIFICATIONS.")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NotificationManagerCompat.from(context).isChannelEnabled(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
fun notify(
|
|
|
|
context: Context,
|
|
|
|
tag: String? = null,
|
|
|
|
iniitalizer: NotificationCompat.Builder.() -> Unit,
|
|
|
|
) {
|
|
|
|
if (Build.VERSION.SDK_INT >= 33) {
|
|
|
|
if (ActivityCompat.checkSelfPermission(
|
|
|
|
context,
|
|
|
|
Manifest.permission.POST_NOTIFICATIONS
|
|
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
|
|
) {
|
|
|
|
log.w("[$name] missing POST_NOTIFICATIONS.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val nc = this
|
|
|
|
val notificationManager = NotificationManagerCompat.from(context)
|
|
|
|
if (!notificationManager.isChannelEnabled(nc.id)) {
|
|
|
|
log.w("[$name] notification channel is disabled.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
val builder = NotificationCompat.Builder(context, nc.id).apply {
|
|
|
|
priority = nc.priority
|
|
|
|
iniitalizer()
|
|
|
|
}
|
|
|
|
notificationManager.notify(tag, notificationId, builder.build())
|
|
|
|
}
|
|
|
|
|
|
|
|
fun createForegroundInfo(
|
|
|
|
context: Context,
|
|
|
|
@DrawableRes iconId: Int =
|
|
|
|
R.drawable.ic_refresh,
|
|
|
|
@ColorInt color: Int =
|
|
|
|
ContextCompat.getColor(context, R.color.colorOsNotificationAccent),
|
|
|
|
title: String? = context.getString(titleId),
|
|
|
|
text: String? = context.getString(descId),
|
|
|
|
piTap: PendingIntent? = null,
|
|
|
|
piDelete: PendingIntent? = null,
|
2023-02-08 20:50:08 +01:00
|
|
|
force: Boolean = false,
|
2023-02-05 16:44:28 +01:00
|
|
|
): ForegroundInfo? {
|
2023-02-08 10:55:49 +01:00
|
|
|
val notificationManager = NotificationManagerCompat.from(context)
|
|
|
|
|
2023-02-08 20:50:08 +01:00
|
|
|
if (!force) {
|
2023-02-08 10:55:49 +01:00
|
|
|
if (Build.VERSION.SDK_INT >= 33) {
|
|
|
|
if (ActivityCompat.checkSelfPermission(
|
|
|
|
context,
|
|
|
|
Manifest.permission.POST_NOTIFICATIONS
|
|
|
|
) != PackageManager.PERMISSION_GRANTED
|
|
|
|
) {
|
|
|
|
log.w("[$id] missing POST_NOTIFICATIONS.")
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!notificationManager.isChannelEnabled(id)) {
|
|
|
|
log.w("[$id] notification channel is disabled.")
|
2023-02-05 16:44:28 +01:00
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val nc = this
|
|
|
|
val builder = NotificationCompat.Builder(context, nc.id).apply {
|
|
|
|
priority = nc.priority
|
|
|
|
setSmallIcon(iconId)
|
|
|
|
setColor(color)
|
|
|
|
title?.let { setContentTitle(it) }
|
|
|
|
text?.let { setContentText(it) }
|
|
|
|
piTap?.let { setContentIntent(piTap) }
|
|
|
|
piDelete?.let { setDeleteIntent(piDelete) }
|
|
|
|
setWhen(System.currentTimeMillis())
|
|
|
|
setOngoing(true)
|
|
|
|
}
|
2023-07-06 07:19:40 +02:00
|
|
|
val useForegroundServiceType = false
|
|
|
|
return if (useForegroundServiceType && Build.VERSION.SDK_INT >= 34) {
|
2023-06-29 17:24:17 +02:00
|
|
|
ForegroundInfo(
|
|
|
|
nc.notificationId,
|
|
|
|
builder.build(),
|
|
|
|
nc.foregroundServiceType,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// WorkManagerのサービスにforegroundServiceTypeが定義されてないので、
|
|
|
|
// foregroundServiceTypeを渡すと怒られる
|
|
|
|
ForegroundInfo(
|
|
|
|
nc.notificationId,
|
|
|
|
builder.build(),
|
|
|
|
)
|
|
|
|
}
|
2023-02-05 16:44:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fun cancel(context: Context, tag: String? = null) {
|
|
|
|
NotificationManagerCompat.from(context).cancel(tag, notificationId)
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
fun NotificationManagerCompat.isChannelEnabled(channelId: String): Boolean {
|
|
|
|
val importance = getNotificationChannel(channelId)?.importance
|
|
|
|
log.i("isChannelEnabled: importance=$importance")
|
|
|
|
return when (importance) {
|
|
|
|
null, NotificationManagerCompat.IMPORTANCE_NONE -> false
|
|
|
|
else -> true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-04 21:52:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 通知チャネルの初期化を
|
|
|
|
* androidx app startupのイニシャライザとして実装したもの
|
|
|
|
*/
|
|
|
|
@Suppress("unused")
|
|
|
|
class NotificationChannelsInitializer : Initializer<Boolean> {
|
|
|
|
override fun dependencies(): List<Class<out Initializer<*>>> =
|
2023-02-05 16:44:28 +01:00
|
|
|
listOf(LazyContextInitializer::class.java)
|
2023-02-04 21:52:26 +01:00
|
|
|
|
|
|
|
override fun create(context: Context): Boolean {
|
|
|
|
context.run {
|
|
|
|
val list = NotificationChannels.values()
|
|
|
|
log.i("createNotificationChannel(s) size=${list.size}")
|
|
|
|
val notificationManager = NotificationManagerCompat.from(this)
|
2023-02-05 16:44:28 +01:00
|
|
|
for (nc in list) {
|
|
|
|
val channel = NotificationChannel(
|
|
|
|
nc.id,
|
|
|
|
getString(nc.titleId),
|
|
|
|
nc.importance,
|
2023-02-04 21:52:26 +01:00
|
|
|
).apply {
|
2023-02-05 16:44:28 +01:00
|
|
|
description = getString(nc.descId)
|
2023-02-04 21:52:26 +01:00
|
|
|
}
|
2023-02-05 16:44:28 +01:00
|
|
|
notificationManager.createNotificationChannel(channel)
|
2023-02-04 21:52:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|