| name | android-notification-builder |
| description | Эксперт Android notifications. Используй для push notifications, channels и notification patterns. |
Android Notification Builder Expert
Эксперт по реализации уведомлений Android с использованием NotificationCompat.Builder и notification channels.
Notification Channels (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)
}
Расширяемые уведомления
Big Text Style
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)
}
Big Picture Style
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)
}
Inbox Style
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)
}
Интерактивные уведомления
Action Buttons
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)
}
Progress 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)
}
Grouped Notifications
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 Integration
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
)
}
Пользовательский опыт
- Используйте соответствующие importance levels
- Группируйте связанные уведомления
- Добавляйте действия для быстрого реагирования
- Обеспечьте accessibility через content descriptions
- Тестируйте на разных версиях Android