Irbisa · Шпаргалка
Уведомления и push
NotificationCompat, каналы, POST_NOTIFICATIONS, FCM, data- и notification-сообщения, deep links
Что такое notification channel и почему повышение его важности в коде позже ничего не меняет?
class App : Application() { override fun onCreate() { super.onCreate() // Idempotent: safe on every launch, must run before the first notify() NotificationManagerCompat.from(this).createNotificationChannel( NotificationChannelCompat.Builder("delivery_v2", NotificationManagerCompat.IMPORTANCE_HIGH)…Что минимально нужно
NotificationCompat.Builder, чтобы показать рабочее уведомление?val tap = PendingIntent.getActivity( ctx, 0, Intent(Intent.ACTION_VIEW, "myapp://orders/42".toUri(), ctx, MainActivity::class.java), PendingIntent.FLAG_IMMUTABLE)…Какие стили уведомлений есть в
NotificationCompatи какую часть уведомления меняет стиль?val release = NotificationCompat.Builder(ctx, "news") .setSmallIcon(R.drawable.ic_stat_news) .setContentTitle("Release 4.2 is out") .setContentText("Offline sync, faster search, 40 fixes") // collapsed row .setStyle(NotificationCompat.BigTextStyle().bigText(fullChangelog)) // expanded only .build()…Как обновить или убрать уже показанное уведомление?
val nm = NotificationManagerCompat.from(ctx)val base = NotificationCompat.Builder(ctx, "orders") .setSmallIcon(R.drawable.ic_stat_order) .setContentTitle("Order #42") // Same id: updates the row in place, quietly…Что изменилось с
POST_NOTIFICATIONSв Android 13 и что делать, если пользователь отказал?// AndroidManifest.xml: <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> @Composablefun PriceAlertButton(onEnabled: () -> Unit) { val ctx = LocalContext.current val launcher = rememberLauncherForActivityResult(…Что определяет громкость уведомления: важность канала,
setPriorityили приоритет сообщения FCM?Content intent уведомления указывает на этот receiver. На Android 12+ экран так и не открывается. Почему и что делать вместо этого?
// setContentIntent(PendingIntent.getBroadcast(ctx, 0, openIntent, FLAG_IMMUTABLE))class OpenReceiver : BroadcastReceiver() { override fun onReceive(ctx: Context, intent: Intent) { val route = intent.getStringExtra("route") analytics.log("notification_open", route) ctx.startActivity( Intent(ctx, MainActivity::class.java) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra("route", route) ) }}Найдите баг: в шторке два уведомления из разных чатов, а тап по любому из них открывает чат с последним сообщением.
fun showChatNotification(ctx: Context, chat: Chat, text: String) { val open = Intent(ctx, MainActivity::class.java) .putExtra("chat_id", chat.id) val pi = PendingIntent.getActivity( ctx, 0, open, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val notification = NotificationCompat.Builder(ctx, CHANNEL_CHATS) .setSmallIcon(R.drawable.ic_stat_chat) .setContentTitle(chat.title) .setContentText(text) .setContentIntent(pi) .setAutoCancel(true) .build() NotificationManagerCompat.from(ctx).notify(chat.id, notification)}Как сгруппировать уведомления под сводкой, чтобы десять результатов давали один звук, а не десять?
private const val GROUP_SYNC = "sync_results"private const val SUMMARY_ID = 0 fun postSyncResults(ctx: Context, files: List<SyncedFile>) { val nm = NotificationManagerCompat.from(ctx) files.forEach { file ->…Эта загрузка обновляет уведомление с прогрессом на каждом чанке. Что услышит пользователь и почему полоса остаётся полной, а текст «Готово» так и не появляется?
suspend fun download(ctx: Context, file: RemoteFile) { val nm = NotificationManagerCompat.from(ctx) val builder = NotificationCompat.Builder(ctx, "downloads") // IMPORTANCE_DEFAULT .setSmallIcon(R.drawable.ic_stat_download) .setContentTitle(file.name) file.chunks().collectIndexed { i, chunk -> sink.write(chunk) builder.setProgress(100, (i + 1) * 100 / file.chunkCount, false) nm.notify(DOWNLOAD_ID, builder.build()) } builder.setContentText("Done").setProgress(0, 0, false) nm.notify(DOWNLOAD_ID, builder.build())}В этом direct reply
getResultsFromIntentвозвращаетnull, а после исправления поле ответа бесконечно крутится. Что не так?val replyPi = PendingIntent.getBroadcast( ctx, 0, Intent(ctx, ReplyReceiver::class.java), PendingIntent.FLAG_IMMUTABLE)val action = NotificationCompat.Action.Builder(R.drawable.ic_send, "Reply", replyPi) .addRemoteInput(RemoteInput.Builder(KEY_REPLY).setLabel("Message").build()) .build() class ReplyReceiver : BroadcastReceiver() { override fun onReceive(ctx: Context, intent: Intent) { val text = RemoteInput.getResultsFromIntent(intent) ?.getCharSequence(KEY_REPLY) ?: return SendReplyWorker.enqueue(ctx, text.toString()) }}Что нужно уведомлению чата, чтобы попасть в раздел «Разговоры» и поддерживать bubbles?
val sender = Person.Builder().setName("Dana").setKey("user:88").build()val shortcutId = "chat:88" // 1. Long-lived shortcut: without it there is no Conversations sectionShortcutManagerCompat.pushDynamicShortcut( ctx,…Чем notification-сообщения FCM отличаются от data-сообщений и когда на самом деле вызывается
onMessageReceived?class AppMessagingService : FirebaseMessagingService() { // Data-only: runs in every app state. With a "notification" block: // runs only while the app is in the foreground. override fun onMessageReceived(message: RemoteMessage) { val convId = message.data["conversation_id"] ?: return repository.insert(message.data)…Как управлять жизненным циклом регистрационных токенов FCM на клиенте и на сервере?
class AppMessagingService : FirebaseMessagingService() { // Install, data clear, restore, rotation; often before anyone signed in override fun onNewToken(token: String) { TokenStore.savePending(this, token) TokenUploadWorker.enqueue(this) // retryable: the network may be down }…Как убрать одноразовый код с экрана блокировки и что вместо вас решают пользователь и администратор?
fun otpNotification(ctx: Context, code: String, tap: PendingIntent): Notification { val redacted = NotificationCompat.Builder(ctx, "security") .setSmallIcon(R.drawable.ic_lock) .setContentTitle("New sign-in code") // says what it is, never the code .setContentIntent(tap) // same tap target .build()…Что такое full-screen intent и что изменилось с
USE_FULL_SCREEN_INTENTв Android 14?fun ensureFullScreenIntent(activity: Activity) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) return val nm = activity.getSystemService(NotificationManager::class.java) if (nm.canUseFullScreenIntent()) return activity.startActivity( Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT, "package:${activity.packageName}".toUri())…Пуш из чата приходит, пока пользователь читает именно этот разговор. Что должно произойти и где в архитектуре это решается?
Как
collapse_key,ttlи приоритет сообщения в FCM влияют на доставку и как выбирать их для разных типов сообщений?// Server, Firebase Admin SDK: one shape per message typefun driverArrived(token: String, rideId: String): Message = Message.builder() .setToken(token) .putData("ride_id", rideId) .setAndroidConfig(AndroidConfig.builder() .setPriority(AndroidConfig.Priority.HIGH) // wake it out of Doze…Пользователи некоторых OEM-телефонов говорят, что уведомления не приходят, а у вас не воспроизводится. Как найти, где они теряются?