یک اعلان ایجاد کنید

اعلان‌ها اطلاعات کوتاه و به‌موقعی در مورد رویدادهای برنامه شما در زمانی که از آن استفاده نمی‌شود، ارائه می‌دهند. این سند به شما نشان می‌دهد که چگونه یک اعلان با ویژگی‌های مختلف ایجاد کنید. برای آشنایی با نحوه نمایش اعلان‌ها در اندروید، به نمای کلی اعلان‌ها مراجعه کنید. برای نمونه کدی که از اعلان‌ها استفاده می‌کند، به نمونه SociaLite در GitHub مراجعه کنید.

کد موجود در این صفحه از APIهای NotificationCompat از کتابخانه AndroidX استفاده می‌کند. این APIها به شما امکان می‌دهند ویژگی‌هایی را که فقط در نسخه‌های جدیدتر اندروید موجود هستند، اضافه کنید و در عین حال سازگاری با اندروید ۹ (سطح API 28) را نیز حفظ کنید. با این حال، برخی از ویژگی‌ها، مانند عملکرد پاسخ درون‌خطی، منجر به عدم انجام عملیات در نسخه‌های قبلی می‌شوند.

ایجاد یک اعلان پایه

یک اعلان در ابتدایی‌ترین و فشرده‌ترین شکل خود - که به عنوان فرم جمع‌شده نیز شناخته می‌شود - یک آیکون، یک عنوان و مقدار کمی محتوای متنی را نمایش می‌دهد. این بخش نحوه ایجاد اعلانی را نشان می‌دهد که کاربر می‌تواند برای راه‌اندازی یک فعالیت در برنامه شما روی آن ضربه بزند.

شکل ۱. یک اعلان با یک آیکون، یک عنوان و مقداری متن.

برای جزئیات بیشتر در مورد هر بخش از یک اعلان، بخش «آناتومی اعلان» را مطالعه کنید.

اعلام مجوز زمان اجرا

اندروید ۱۳ (سطح API ۳۳) و بالاتر از یک مجوز زمان اجرا برای ارسال اعلان‌های غیرمعاف (از جمله سرویس‌های پیش‌زمینه (FGS)) از یک برنامه پشتیبانی می‌کند.

مجوزی که باید در فایل مانیفست برنامه خود اعلام کنید، در قطعه کد زیر آمده است:

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>

برای جزئیات بیشتر در مورد مجوزهای زمان اجرا، به مجوز زمان اجرای اعلان مراجعه کنید.

تنظیم محتوای اعلان

برای شروع، محتوا و کانال اعلان را با استفاده از شیء NotificationCompat.Builder تنظیم کنید. مثال زیر نحوه ایجاد یک اعلان با موارد زیر را نشان می‌دهد:

  • یک آیکون کوچک، که توسط setSmallIcon() تنظیم شده است. این تنها محتوای قابل مشاهده توسط کاربر است که مورد نیاز است.

  • یک عنوان، که توسط setContentTitle() تنظیم می‌شود.

  • متن بدنه، که توسط setContentText() تنظیم شده است.

  • اولویت اعلان، که توسط setPriority() تنظیم می‌شود. اولویت، میزان مزاحمت اعلان را در اندروید ۷.۱ و قبل از آن تعیین می‌کند. برای اندروید ۸.۰ و بعد از آن، به جای آن، اهمیت کانال را همانطور که در بخش بعدی نشان داده شده است، تنظیم کنید.

val textTitle = "Title"
val textContent = "Content"
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_logo)
    .setContentTitle(textTitle)
    .setContentText(textContent)
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)

سازنده‌ی NotificationCompat.Builder از شما می‌خواهد که یک شناسه‌ی کانال ارائه دهید. این شناسه برای سازگاری با اندروید ۸.۰ (سطح API ۲۶) و بالاتر لازم است، اما توسط نسخه‌های قبلی نادیده گرفته می‌شود.

به طور پیش‌فرض، محتوای متنی اعلان به اندازه یک خط کوتاه می‌شود. می‌توانید با ایجاد یک اعلان قابل بسط، اطلاعات بیشتری را نمایش دهید.

شکل ۲. یک اعلان قابل بسط در حالت‌های جمع‌شده و بازشده.

اگر می‌خواهید اعلان شما طولانی‌تر باشد، می‌توانید با اضافه کردن یک قالب سبک با setStyle() یک اعلان قابل گسترش را فعال کنید. برای مثال، کد زیر یک ناحیه متنی بزرگتر ایجاد می‌کند:

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_logo)
    .setContentTitle("My notification")
    .setContentText("Much longer text that cannot fit one line...")
    .setStyle(NotificationCompat.BigTextStyle()
        .bigText("Much longer text that cannot fit one line..."))
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)

برای اطلاعات بیشتر در مورد سایر سبک‌های اعلان بزرگ، از جمله نحوه اضافه کردن تصویر و کنترل‌های پخش رسانه، به ایجاد یک اعلان قابل گسترش مراجعه کنید.

یک کانال ایجاد کنید و اهمیت آن را تعیین کنید

قبل از اینکه بتوانید اعلان را در اندروید ۸.۰ و بالاتر ارسال کنید، کانال اعلان برنامه خود را با ارسال نمونه‌ای از NotificationChannel به createNotificationChannel() در سیستم ثبت کنید. کد زیر توسط یک شرط در نسخه SDK_INT مسدود شده است:

fun createNotificationChannel(context: Context) {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is not in the Support Library.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = context.getString(R.string.channel_name)
        val descriptionText = context.getString(R.string.channel_description)
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
            description = descriptionText
        }
        // Register the channel with the system.
        val notificationManager: NotificationManager =
            context.getSystemService(NotificationManager::class.java) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

از آنجا که در اندروید ۸.۰ و بالاتر، قبل از ارسال اعلان‌ها، باید کانال اعلان را ایجاد کنید، این کد را هنگام شروع برنامه اجرا کنید. فراخوانی مکرر آن بی‌خطر است زیرا ایجاد یک کانال موجود هیچ کاری انجام نمی‌دهد.

سازنده‌ی NotificationChannel با استفاده از ثابت NotificationManager به یک سطح اهمیت نیاز دارد. این سطح، نحوه‌ی وقفه در کار کاربر را تعیین می‌کند. برای پشتیبانی از اندروید ۷.۱ و پایین‌تر، اولویت را نیز با setPriority() همانطور که در مثال قبل نشان داده شده است، تنظیم کنید.

اگرچه شما باید اهمیت یا اولویت را تعیین کنید، سیستم رفتار هشدار را تضمین نمی‌کند. سیستم ممکن است آن را بر اساس عوامل دیگر تنظیم کند و کاربر همیشه می‌تواند سطح اهمیت کانال را سفارشی کند.

برای اطلاعات بیشتر در مورد معنای سطوح مختلف، درباره سطوح اهمیت اعلان‌ها مطالعه کنید.

تنظیم عملکرد ضربه زدن به اعلان

هر اعلان باید به یک لمس پاسخ دهد، معمولاً برای باز کردن یک activity در برنامه شما که مربوط به اعلان است. برای انجام این کار، یک content intent تعریف شده با یک شیء PendingIntent را مشخص کنید و آن را به setContentIntent() منتقل کنید.

قطعه کد زیر نحوه ایجاد یک اینتنت ساده برای باز کردن یک اکتیویتی هنگام لمس اعلان توسط کاربر را نشان می‌دهد:

// Create an explicit intent for an Activity in your app.
val intent = Intent(context, AlertDetails::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent =
    PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_logo)
    .setContentTitle("My notification")
    .setContentText("Hello World!")
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
    // Set the intent that fires when the user taps the notification.
    .setContentIntent(pendingIntent)
    .setAutoCancel(true)

این کد تابع setAutoCancel() را فراخوانی می‌کند که به طور خودکار اعلان را هنگام ضربه زدن کاربر حذف می‌کند .

پرچم‌های intent در مثال قبلی، تجربه ناوبری مورد انتظار کاربر را پس از باز شدن برنامه شما با استفاده از اعلان، حفظ می‌کنند. بسته به نوع فعالیتی که شروع می‌کنید، می‌توانید از آن استفاده کنید، که می‌تواند یکی از موارد زیر باشد:

  • یک فعالیت (Activity) که منحصراً برای پاسخ به اعلان وجود دارد. هیچ دلیلی وجود ندارد که کاربر در طول استفاده عادی از برنامه به این فعالیت هدایت شود، بنابراین این فعالیت به جای اضافه شدن به وظیفه موجود برنامه و back Stack ، یک وظیفه جدید را آغاز می‌کند. این نوع intent در نمونه قبلی ایجاد شده است.

  • یک اکتیویتی که در جریان عادی برنامه شما وجود دارد. در این حالت، شروع اکتیویتی یک پشته پشتی ایجاد می‌کند تا انتظارات کاربر برای دکمه‌های Back و Up حفظ شود.

نمایش اعلان

برای نمایش اعلان، تابع NotificationManagerCompat.notify() را فراخوانی کنید و یک شناسه منحصر به فرد برای اعلان و نتیجه‌ی تابع NotificationCompat.Builder.build() به آن ارسال کنید. این موضوع در مثال زیر نشان داده شده است:

with(NotificationManagerCompat.from(context)) {
    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.POST_NOTIFICATIONS
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        // TODO: Consider calling ActivityCompat#requestPermissions here
        // to request the missing permissions, and then overriding
        // public fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>,
        //                                        grantResults: IntArray)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.

        return@with
    }
    // notificationId is a unique int for each notification that you must define.
    notify(notificationId, builder.build())

شناسه اعلانی که به NotificationManagerCompat.notify() ارسال می‌کنید را ذخیره کنید، زیرا وقتی می‌خواهید اعلان را به‌روزرسانی یا حذف کنید، به آن نیاز دارید.

علاوه بر این، برای آزمایش اعلان‌های اولیه در دستگاه‌های دارای اندروید ۱۳ و بالاتر، اعلان‌ها را به صورت دستی روشن کنید یا یک پنجره محاوره‌ای برای درخواست اعلان‌ها ایجاد کنید.

دکمه‌های عملیاتی اضافه کنید

یک اعلان می‌تواند تا سه دکمه‌ی عملیاتی ارائه دهد که به کاربر اجازه می‌دهد به سرعت پاسخ دهد، مانند به تعویق انداختن یادآوری یا پاسخ دادن به یک پیام متنی. اما این دکمه‌های عملیاتی نباید عملکردی را که هنگام ضربه زدن کاربر به اعلان انجام می‌شود، تکرار کنند.

شکل ۳. یک اعلان با یک دکمه‌ی عملیاتی.

برای افزودن یک دکمه‌ی عملیاتی، یک PendingIntent به متد addAction() ارسال کنید. این کار مانند تنظیم اکشن پیش‌فرض ضربه زدن برای اعلان است، با این تفاوت که به جای راه‌اندازی یک اکتیویتی، می‌توانید کارهای دیگری مانند راه‌اندازی یک BroadcastReceiver که کاری را در پس‌زمینه انجام می‌دهد، انجام دهید تا اکشن، برنامه‌ای را که از قبل باز است، مختل نکند.

برای مثال، کد زیر نحوه ارسال یک broadcast به یک گیرنده خاص را نشان می‌دهد:

val ACTION_SNOOZE = "snooze"
val snoozeIntent = Intent(context, MyBroadcastReceiver::class.java).apply {
    action = ACTION_SNOOZE
    putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val snoozePendingIntent: PendingIntent =
    PendingIntent.getBroadcast(context, 0, snoozeIntent, PendingIntent.FLAG_IMMUTABLE)
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_logo)
    .setContentTitle("My notification")
    .setContentText("Hello World!")
    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
    .setContentIntent(pendingIntent)
    .addAction(R.drawable.snooze, context.getString(R.string.snooze),
        snoozePendingIntent)

برای اطلاعات بیشتر در مورد ساخت یک BroadcastReceiver برای اجرای کارهای پس‌زمینه، به نمای کلی Broadcasts مراجعه کنید.

اگر در عوض سعی دارید یک اعلان با دکمه‌های پخش رسانه، مانند مکث و رد کردن آهنگ‌ها، ایجاد کنید، نحوه ایجاد یک اعلان با کنترل‌های رسانه را ببینید.

افزودن قابلیت پاسخ مستقیم

قابلیت پاسخ مستقیم (direct reply) که در اندروید ۷.۰ (API level 24) معرفی شد، به کاربران اجازه می‌دهد متن را مستقیماً در اعلان وارد کنند. سپس متن بدون باز کردن هیچ فعالیتی به برنامه شما ارسال می‌شود. به عنوان مثال، می‌توانید از یک قابلیت پاسخ مستقیم استفاده کنید تا به کاربران اجازه دهید به پیام‌های متنی پاسخ دهند یا لیست کارها را از داخل اعلان به‌روزرسانی کنند.

شکل ۴. ضربه زدن روی دکمه «پاسخ» ورودی متن را باز می‌کند.

عمل پاسخ مستقیم به عنوان یک دکمه اضافی در اعلان ظاهر می‌شود که یک ورودی متنی را باز می‌کند. هنگامی که کاربر تایپ کردن را تمام می‌کند، سیستم پاسخ متنی را به هدفی که برای عمل اعلان مشخص کرده‌اید، پیوست می‌کند و هدف را به برنامه شما ارسال می‌کند.

دکمه پاسخ را اضافه کنید

برای ایجاد یک اکشن اعلان که از پاسخ مستقیم پشتیبانی می‌کند، این مراحل را دنبال کنید:

یک نمونه از RemoteInput.Builder ایجاد کنید که بتوانید آن را به اکشن اعلان خود اضافه کنید. سازنده‌ی این کلاس، رشته‌ای را می‌پذیرد که سیستم از آن به عنوان کلید ورودی متن استفاده می‌کند. برنامه‌ی شما بعداً از آن کلید برای بازیابی متن ورودی استفاده می‌کند.

// Key for the string that's delivered in the action's intent.
val replyLabel: String = context.resources.getString(R.string.reply_label)
val remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
    setLabel(replyLabel)
    build()
}

یک PendingIntent برای اکشن پاسخ ایجاد کنید.

// Build a PendingIntent for the reply action to trigger.
val replyPendingIntent: PendingIntent =
    PendingIntent.getBroadcast(context,
        conversationId,
        getMessageReplyIntent(conversationId),
        PendingIntent.FLAG_MUTABLE)

شیء RemoteInput را با استفاده از addRemoteInput() به یک اکشن متصل کنید.

// Create the reply action and add the remote input.
val action: NotificationCompat.Action =
    NotificationCompat.Action.Builder(R.drawable.reply,
        context.getString(R.string.reply_label), replyPendingIntent