android-notification-builder について
NotificationCompat.Builder と通知チャンネルを使った Android 通知の実装を支援するスキルです。展開可能な通知、操作可能な通知、進捗・グループ通知、FCM メッセージ処理の例を扱います。
主な機能
- 01通知チャンネルの作成
- 02基本的な通知の構築
- 03テキスト、画像、受信トレイ形式の展開表示
- 04操作ボタンの追加
- 05進捗通知とグループ通知
- 06FCM メッセージの処理
活用シーン
- 01Android アプリへのカテゴリ別通知の追加
- 02長文または画像通知の表示
- 03返信などの通知アクションの提供
- 04ダウンロード進捗やグループメッセージの表示
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dengineproblem/ agents-monorepo --skill android-notification-builderインストール後、Claude Code で/android-notification-builderを入力して読み込まれたか確認します。
詳細
Android 通知ビルダーのエキスパート
NotificationCompat.Builder と通知チャンネルを使用した Android 通知の実装に関するエキスパートです。
通知チャンネル(API 26+)
class NotificationHelper(private val context: Context) {
companion object {
const val CHANNEL_ID_MESSAGES = "messages"
const val CHANNEL_ID_UPDATES = "updates"
const val CHANNEL_ID_PROMOTIONS = "promotions"
}
fun createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val messagesChannel = NotificationChannel(
CHANNEL_ID_MESSAGES,
"Сообщения",
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Уведомления о новых сообщениях"
enableLights(true)
lightColor = Color.BLUE
enableVibration(true)
vibrationPattern = longArrayOf(0, 250, 250, 250)
}
val updatesChannel = NotificationChannel(
CHANNEL_ID_UPDATES,
"Обновления",
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = "Системные обновления"
}
val notificationManager = context.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannels(
listOf(messagesChannel, updatesChannel)
)
}
}
}
基本的な通知
fun showBasicNotification(title: String, message: String) {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent = PendingIntent.getActivity(
context, 0, intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val notification = NotificationCompat.Builder(context, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, notification)
}
拡張可能な通知
ビッグテキストスタイル
fun showExpandableTextNotification(title: String, shortText: String, longText: String) {
val bigTextStyle = NotificationCompat.BigTextStyle()
.bigText(longText)
.setBigContentTitle(title)
.setSummaryText("Подробности")
val notification = NotificationCompat.Builder(context, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(shortText)
.setStyle(bigTextStyle)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build()
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, notification)
}
ビッグピクチャースタイル
fun showImageNotification(title: String, message: String, bitmap: Bitmap) {
val bigPictureStyle = NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.bigLargeIcon(null as Bitmap?)
.setBigContentTitle(title)
.setSummaryText(message)
val notification = NotificationCompat.Builder(context, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.setLargeIcon(bitmap)
.setStyle(bigPictureStyle)
.build()
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, notification)
}
インボックススタイル
fun showInboxNotification(title: String, messages: List<String>) {
val inboxStyle = NotificationCompat.InboxStyle()
.setBigContentTitle(title)
.setSummaryText("${messages.size} новых сообщений")
messages.take(5).forEach { message ->
inboxStyle.addLine(message)
}
val notification = NotificationCompat.Builder(context, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText("${messages.size} новых сообщений")
.setStyle(inboxStyle)
.setNumber(messages.size)
.build()
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, notification)
}
インタラクティブな通知
アクションボタン
fun showNotificationWithActions(title: String, message: String) {
// Reply action
val replyIntent = Intent(context, ReplyReceiver::class.java)
val replyPendingIntent = PendingIntent.getBroadcast(
context, 0, replyIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val remoteInput = RemoteInput.Builder("key_text_reply")
.setLabel("Ответить")
.build()
val replyAction = NotificationCompat.Action.Builder(
R.drawable.ic_reply,
"Ответить",
replyPendingIntent
).addRemoteInput(remoteInput).build()
// Archive action
val archiveIntent = Intent(context, ArchiveReceiver::class.java)
val archivePendingIntent = PendingIntent.getBroadcast(
context, 1, archiveIntent,
PendingIntent.FLAG_IMMUTABLE
)
val archiveAction = NotificationCompat.Action.Builder(
R.drawable.ic_archive,
"Архивировать",
archivePendingIntent
).build()
val notification = NotificationCompat.Builder(context, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(message)
.addAction(replyAction)
.addAction(archiveAction)
.build()
NotificationManagerCompat.from(context).notify(NOTIFICATION_ID, notification)
}
進行状況の通知
fun showProgressNotification(title: String, maxProgress: Int, currentProgress: Int) {
val notification = NotificationCompat.Builder(context, CHANNEL_ID_UPDATES)
.setSmallIcon(R.drawable.ic_download)
.setContentTitle(title)
.setContentText("Загрузка: $currentProgress%")
.setProgress(maxProgress, currentProgress, false)
.setOngoing(true)
.build()
NotificationManagerCompat.from(context).notify(PROGRESS_NOTIFICATION_ID, notification)
}
fun showIndeterminateProgress(title: String) {
val notification = NotificationCompat.Builder(context, CHANNEL_ID_UPDATES)
.setSmallIcon(R.drawable.ic_sync)
.setContentTitle(title)
.setContentText("Синхронизация...")
.setProgress(0, 0, true)
.setOngoing(true)
.build()
NotificationManagerCompat.from(context).notify(PROGRESS_NOTIFICATION_ID, notification)
}
fun completeProgressNotification(title: String) {
val notification = NotificationCompat.Builder(context, CHANNEL_ID_UPDATES)
.setSmallIcon(R.drawable.ic_done)
.setContentTitle(title)
.setContentText("Загрузка завершена")
.setProgress(0, 0, false)
.build()
NotificationManagerCompat.from(context).notify(PROGRESS_NOTIFICATION_ID, notification)
}
グループ化された通知
fun showGroupedNotifications(messages: List<Message>) {
val GROUP_KEY = "com.example.MESSAGE_GROUP"
// Individual notifications
messages.forEachIndexed { index, message ->
val notification = NotificationCompat.Builder(context, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle(message.sender)
.setContentText(message.text)
.setGroup(GROUP_KEY)
.build()
NotificationManagerCompat.from(context).notify(index, notification)
}
// Summary notification
val summaryNotification = NotificationCompat.Builder(context, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_message)
.setContentTitle("${messages.size} новых сообщений")
.setStyle(NotificationCompat.InboxStyle()
.setBigContentTitle("${messages.size} новых сообщений")
.setSummaryText("Сообщения"))
.setGroup(GROUP_KEY)
.setGroupSummary(true)
.build()
NotificationManagerCompat.from(context).notify(SUMMARY_ID, summaryNotification)
}
FCM との統合
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
remoteMessage.notification?.let { notification ->
showNotification(
notification.title ?: "Уведомление",
notification.body ?: ""
)
}
remoteMessage.data.isNotEmpty().let {
handleDataMessage(remoteMessage.data)
}
}
override fun onNewToken(token: String) {
sendTokenToServer(token)
}
private fun showNotification(title: String, body: String) {
val notification = NotificationCompat.Builder(this, CHANNEL_ID_MESSAGES)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(body)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.build()
NotificationManagerCompat.from(this).notify(
System.currentTimeMillis().toInt(),
notification
)
}
}
ベストプラクティス
セキュリティ
// Скрытие контента на lock screen
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
.setPublicVersion(publicNotification)
// Безопасные PendingIntent
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
パフォーマンス
// Оптимизация bitmap
fun scaleBitmap(bitmap: Bitmap, maxSize: Int = 256): Bitmap {
val ratio = minOf(
maxSize.toFloat() / bitmap.width,
maxSize.toFloat() / bitmap.height
)
return Bitmap.createScaledBitmap(
bitmap,
(bitmap.width * ratio).toInt(),
(bitmap.height * ratio).toInt(),
true
)
}
ユーザーエクスペリエンス
- 適切な重要度レベルを使用します
- 関連する通知をグループ化します
- すばやく対応できるアクションを追加します
- コンテンツ説明を通じてアクセシビリティを確保します
- さまざまなバージョンの Android でテストします
コメント
Markdown 対応