{"id":"d0a334b6-a9ac-4ae8-b8c3-a4a3b8ccfe16","shortId":"z5DSYU","kind":"skill","title":"notifications","tagline":"27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.","description":"# Android Notifications\r\n\r\n## Rule 1: Always create channels before posting (Android 8+)\r\n\r\n```kotlin\r\n// ✅ Channel setup — do this in Application.onCreate()\r\nclass MyApplication : Application() {\r\n    override fun onCreate() {\r\n        super.onCreate()\r\n        createNotificationChannels()\r\n    }\r\n\r\n    private fun createNotificationChannels() {\r\n        val channels = listOf(\r\n            NotificationChannelCompat.Builder(\r\n                NotificationChannels.MESSAGES,\r\n                NotificationManagerCompat.IMPORTANCE_HIGH\r\n            ).apply {\r\n                setName(\"Messages\")\r\n                setDescription(\"Direct messages from other users\")\r\n                setVibrationEnabled(true)\r\n                setLightsEnabled(true)\r\n            }.build(),\r\n\r\n            NotificationChannelCompat.Builder(\r\n                NotificationChannels.UPDATES,\r\n                NotificationManagerCompat.IMPORTANCE_DEFAULT\r\n            ).apply {\r\n                setName(\"Updates\")\r\n                setDescription(\"App updates and announcements\")\r\n            }.build(),\r\n\r\n            NotificationChannelCompat.Builder(\r\n                NotificationChannels.BACKGROUND_SYNC,\r\n                NotificationManagerCompat.IMPORTANCE_MIN\r\n            ).apply {\r\n                setName(\"Background sync\")\r\n                setDescription(\"Silent background sync status\")\r\n            }.build()\r\n        )\r\n\r\n        NotificationManagerCompat.from(this).createNotificationChannelsCompat(channels)\r\n    }\r\n}\r\n\r\nobject NotificationChannels {\r\n    const val MESSAGES = \"messages\"\r\n    const val UPDATES = \"updates\"\r\n    const val BACKGROUND_SYNC = \"background_sync\"\r\n}\r\n```\r\n\r\n## Rule 2: NotificationCompat — correct builder pattern\r\n\r\n```kotlin\r\n// ✅ Full notification with actions and deep link\r\nclass NotificationHelper @Inject constructor(\r\n    @ApplicationContext private val context: Context\r\n) {\r\n    private val notificationManager = NotificationManagerCompat.from(context)\r\n\r\n    fun showMessageNotification(\r\n        id: Int,\r\n        senderName: String,\r\n        messagePreview: String,\r\n        conversationId: String\r\n    ) {\r\n        // Deep link PendingIntent\r\n        val intent = Intent(context, MainActivity::class.java).apply {\r\n            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK\r\n            putExtra(\"conversation_id\", conversationId)\r\n        }\r\n        val pendingIntent = PendingIntent.getActivity(\r\n            context, id, intent,\r\n            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE\r\n        )\r\n\r\n        // Reply action\r\n        val replyInput = RemoteInput.Builder(\"reply_text\")\r\n            .setLabel(\"Reply...\")\r\n            .build()\r\n        val replyIntent = Intent(context, NotificationReceiver::class.java).apply {\r\n            action = \"REPLY\"\r\n            putExtra(\"notification_id\", id)\r\n            putExtra(\"conversation_id\", conversationId)\r\n        }\r\n        val replyPendingIntent = PendingIntent.getBroadcast(\r\n            context, id, replyIntent,\r\n            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE  // MUTABLE required for RemoteInput\r\n        )\r\n        val replyAction = NotificationCompat.Action.Builder(\r\n            R.drawable.ic_reply, \"Reply\", replyPendingIntent\r\n        ).addRemoteInput(replyInput).build()\r\n\r\n        val notification = NotificationCompat.Builder(context, NotificationChannels.MESSAGES)\r\n            .setSmallIcon(R.drawable.ic_notification)\r\n            .setContentTitle(senderName)\r\n            .setContentText(messagePreview)\r\n            .setStyle(NotificationCompat.BigTextStyle().bigText(messagePreview))\r\n            .setContentIntent(pendingIntent)\r\n            .addAction(replyAction)\r\n            .setAutoCancel(true)\r\n            .setPriority(NotificationCompat.PRIORITY_HIGH)\r\n            .setCategory(NotificationCompat.CATEGORY_MESSAGE)\r\n            .setVisibility(NotificationCompat.VISIBILITY_PRIVATE)  // hide on lock screen\r\n            .build()\r\n\r\n        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS)\r\n            == PackageManager.PERMISSION_GRANTED) {\r\n            notificationManager.notify(id, notification)\r\n        }\r\n    }\r\n\r\n    fun cancel(id: Int) = notificationManager.cancel(id)\r\n    fun cancelAll() = notificationManager.cancelAll()\r\n}\r\n```\r\n\r\n## Rule 3: Foreground service notification\r\n\r\n```kotlin\r\n// ✅ Required notification for foreground services\r\nclass DownloadService : Service() {\r\n    private val notificationId = 100\r\n\r\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {\r\n        startForeground(notificationId, buildProgressNotification(0))\r\n        return START_STICKY\r\n    }\r\n\r\n    fun updateProgress(progress: Int) {\r\n        NotificationManagerCompat.from(this).notify(notificationId, buildProgressNotification(progress))\r\n    }\r\n\r\n    private fun buildProgressNotification(progress: Int) =\r\n        NotificationCompat.Builder(this, NotificationChannels.BACKGROUND_SYNC)\r\n            .setSmallIcon(R.drawable.ic_download)\r\n            .setContentTitle(\"Downloading...\")\r\n            .setProgress(100, progress, progress == 0)\r\n            .setOngoing(true)  // can't be dismissed by user\r\n            .setSilent(true)\r\n            .build()\r\n}\r\n```\r\n\r\n## Common Mistakes\r\n\r\n❌ Not creating channel before posting — notification silently dropped on Android 8+\r\n❌ `FLAG_MUTABLE` on PendingIntent without RemoteInput — security vulnerability\r\n❌ `FLAG_IMMUTABLE` on PendingIntent with RemoteInput — reply won't work\r\n❌ Not checking POST_NOTIFICATIONS permission on Android 13+\r\n❌ Hardcoded notification IDs — use consistent ID per notification type, not random\r\n❌ No `setAutoCancel(true)` — notification stays after user taps it","tags":["notifications","android","agent","skills","piyushverma0","agent-skills","ai-agent","antigravity","claude-code","codex","cursor","gemini-cli"],"capabilities":["skill","source-piyushverma0","skill-notifications","topic-agent-skills","topic-ai-agent","topic-android","topic-antigravity","topic-claude-code","topic-codex","topic-cursor","topic-gemini-cli","topic-hilt","topic-jetpack-compose","topic-kotlin","topic-material3"],"categories":["android-agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/piyushverma0/android-agent-skills/notifications","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add piyushverma0/android-agent-skills","source_repo":"https://github.com/piyushverma0/android-agent-skills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 8 github stars · SKILL.md body (5,799 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:09:10.284Z","embedding":null,"createdAt":"2026-05-18T13:14:49.889Z","updatedAt":"2026-05-18T19:09:10.284Z","lastSeenAt":"2026-05-18T19:09:10.284Z","tsv":"'0':346,378 '1':38 '100':332,375 '13':428 '18':33 '2':134 '27':2 '3':316 '5':28 '8':45,402 'action':143,208,224 'activ':183,188 'activitycompat.checkselfpermission':297 'addact':278 'addremoteinput':257 'agent':7 'ai':6,30 'alway':39 'android':3,35,44,401,427 'announc':96 'app':93 'appli':71,89,103,180,223 'applic':55 'application.oncreate':52 'applicationcontext':151 'auth':14 'background':105,109,129,131 'bigtext':274 'bill':27 'build':84,97,112,216,259,295,389 'builder':137 'buildprogressnotif':345,358,362 'cancel':307 'cancelal':313 'channel':41,47,65,116,394 'check':422 'class':53,147,326 'class.java':179,222 'claud':8 'clear':189 'code':9 'codex':10 'common':390 'consist':433 'const':119,123,127 'constructor':150 'context':154,155,160,177,198,220,237,263,298 'convers':192,231 'conversationid':169,194,233 'correct':136 'creat':40,393 'createnotificationchannel':60,63 'createnotificationchannelscompat':115 'current':203,242 'cursor':11 'day':34 'deep':145,171 'default':88 'design':17 'direct':75 'dismiss':384 'download':371,373 'downloadservic':327 'drop':399 'error':16 'fitgenz':29 'fix':12 'flag':181,338,403,411 'foreground':317,324 'full':140 'fun':57,62,161,306,312,334,350,361 'grant':302 'hardcod':429 'hide':291 'high':70,284 'hilt':15 'id':163,193,199,228,229,232,238,304,308,311,431,434 'immut':206,412 'inconsist':18 'inject':149 'int':164,309,339,341,342,353,364 'intent':175,176,200,219,336,337 'intent.flag':182,187 'kapt':19 'kotlin':46,139,320 'ksp':20 'link':146,172 'listof':66 'lock':293 'mainact':178 'manifest.permission.post':299 'messag':73,76,121,122,287 'messagepreview':167,271,275 'min':102 'miss':21 'mistak':391 'mutabl':245,246,404 'myapplic':54 'new':184 'notif':1,36,141,227,261,267,300,305,319,322,397,424,430,436,443 'notifi':356 'notificationchannel':118 'notificationchannelcompat.builder':67,85,98 'notificationchannels.background':99,367 'notificationchannels.messages':68,264 'notificationchannels.updates':86 'notificationcompat':135 'notificationcompat.action.builder':252 'notificationcompat.bigtextstyle':273 'notificationcompat.builder':262,365 'notificationcompat.category':286 'notificationcompat.priority':283 'notificationcompat.visibility':289 'notificationhelp':148 'notificationid':331,344,357 'notificationmanag':158 'notificationmanager.cancel':310 'notificationmanager.cancelall':314 'notificationmanager.notify':303 'notificationmanagercompat.from':113,159,354 'notificationmanagercompat.importance':69,87,101 'notificationreceiv':221 'object':117 'oncreat':58 'onstartcommand':335 'overrid':56,333 'packagemanager.permission':301 'pattern':138 'pendingint':173,196,277,406,414 'pendingintent.flag':201,205,240,244 'pendingintent.getactivity':197 'pendingintent.getbroadcast':236 'per':435 'permiss':425 'post':43,396,423 'privat':61,152,156,290,329,360 'progress':352,359,363,376,377 'putextra':191,226,230 'r.drawable.ic':253,266,370 'random':439 'reduc':24 'remoteinput':249,408,416 'remoteinput.builder':211 'repli':207,212,215,225,254,255,417 'replyact':251,279 'replyinput':210,258 'replyint':218,239 'replypendingint':235,256 'requir':247,321 'return':347 'rule':37,133,315 'screen':294 'secur':409 'sendernam':165,269 'servic':318,325,328 'setautocancel':280,441 'setcategori':285 'setcontentint':276 'setcontenttext':270 'setcontenttitl':268,372 'setdescript':74,92,107 'setlabel':214 'setlightsen':82 'setnam':72,90,104 'setongo':379 'setprior':282 'setprogress':374 'setsil':387 'setsmallicon':265,369 'setstyl':272 'setup':48 'setvibrationen':80 'setvis':288 'ship':31 'showmessagenotif':162 'silent':108,398 'skill':4 'skill-notifications' 'source-piyushverma0' 'start':348 'startforeground':343 'startid':340 'state':23 'status':111 'stay':444 'sticki':349 'string':166,168,170 'supabas':13 'super.oncreate':59 'sync':100,106,110,130,132,368 'tap':447 'task':185,190 'text':213 'token':26 'topic-agent-skills' 'topic-ai-agent' 'topic-android' 'topic-antigravity' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-gemini-cli' 'topic-hilt' 'topic-jetpack-compose' 'topic-kotlin' 'topic-material3' 'true':81,83,281,380,388,442 'type':437 'uistat':22 'updat':91,94,125,126,202,241 'updateprogress':351 'use':432 'user':79,386,446 'val':64,120,124,128,153,157,174,195,209,217,234,250,260,330 'vulner':410 'without':407 'won':418 'work':420","prices":[{"id":"162a16c0-249b-4a87-9e6c-dc74097a1cd3","listingId":"d0a334b6-a9ac-4ae8-b8c3-a4a3b8ccfe16","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"piyushverma0","category":"android-agent-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:49.889Z"}],"sources":[{"listingId":"d0a334b6-a9ac-4ae8-b8c3-a4a3b8ccfe16","source":"github","sourceId":"piyushverma0/android-agent-skills/notifications","sourceUrl":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/notifications","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:49.889Z","lastSeenAt":"2026-05-18T19:09:10.284Z"}],"details":{"listingId":"d0a334b6-a9ac-4ae8-b8c3-a4a3b8ccfe16","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"piyushverma0","slug":"notifications","github":{"repo":"piyushverma0/android-agent-skills","stars":8,"topics":["agent-skills","ai-agent","android","antigravity","claude-code","codex","cursor","gemini-cli","hilt","jetpack-compose","kotlin","material3","open-source","skills","supabase"],"license":"mit","html_url":"https://github.com/piyushverma0/android-agent-skills","pushed_at":"2026-04-27T09:15:31Z","description":"27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.","skill_md_sha":"9cac3a1a43748c58eb17c21ba90728ddf5b86ee1","skill_md_path":"skills/notifications/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/notifications"},"layout":"multi","source":"github","category":"android-agent-skills","frontmatter":{},"skills_sh_url":"https://skills.sh/piyushverma0/android-agent-skills/notifications"},"updatedAt":"2026-05-18T19:09:10.284Z"}}