التوثيق · AppsKitSDK (AKS) — دليل التكامل مع Android

9. الإشعارات المحلية (AppsKitSDKLocalNotificationManager)

يوفّر AKS مجموعة أدوات صغيرة للإشعارات المحلية، تتضمن طابورًا ومنشئ إشعارات وفئة BroadcastReceiver مجردة، ويمكنك استخدامها لعرض إشعاراتك المحلية. لا يسجّل AKS مستقبِل البث في ملف البيان الخاص به، ولا يجدول أي شيء نيابةً عنك؛ فكلا الأمرين يقع على عاتقك.

9.1 ملف البيان

أنشئ فئة فرعية من AppsKitSDKLocalNotificationBroadCastReceiver (كما هو موضّح في §9.2)، وعرّفها كعنصر <receiver> يُدمج ضمن عنصر <application> نفسه الوارد في §2:

xml
<application
    android:name=".YourApplication">

    <receiver
        android:name=".YourNotificationReceiver"
        android:exported="false" />

</application>

إذا كنت تستهدف Android 13 أو أحدث (API 33)، فأضف أيضًا إذن الإشعارات الذي يُطلب وقت التشغيل. لا يعرّف ملف البيان الخاص بـ AKS هذا الإذن، لذا لن يُدمج تلقائيًا في ملفك:

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

يُعدّ POST_NOTIFICATIONS إذنًا يُطلب وقت التشغيل، لذا يجب عليك طلبه من المستخدم (مثلًا عبر ActivityResultContracts.RequestPermission()) قبل أن تتمكّن من عرض الإشعارات فعليًا.

9.2 إضافة إشعار إلى الطابور

أنشئ فئة فرعية من AppsKitSDKLocalNotificationBroadCastReceiver ونفّذ checkNotifications()، وهي نقطة التخصيص التي تحدّد فيها ما إذا كان موعد عرض إشعار قد حان، وتُستدعى في كل مرة يُفعَّل فيها مستقبِل البث:

kotlin
class YourNotificationReceiver : AppsKitSDKLocalNotificationBroadCastReceiver() {

    override fun checkNotifications() {
        addNotificationInQueue(
            NotificationModel(
                id = "daily_reminder",                      // dedup key — a second add with the same id is ignored
                title = "Come back!",
                description = "You have unfinished items waiting.",
                icon = R.drawable.ic_notification,
                notificationId = 1001,                       // the actual Android notification ID passed to notify()
                targetScreen = "com.yourapp.MainActivity"    // fully-qualified Activity class opened on tap
            )
        )
    }
}

فور انتهاء checkNotifications()، تستدعي onReceive تلقائيًا AppsKitSDKLocalNotificationManager.showNotification(context)، التي تسحب أقدم كائن NotificationModel من الطابور وتعرضه. لا تحتاج إلى استدعاء showNotification بنفسك في هذا المسار؛ فإضافة الإشعار إلى الطابور داخل checkNotifications() كافية.

يتحقّق مسار العرض عبر الطابور بنفسه من إذن POST_NOTIFICATIONS على Android 13 أو أحدث، ويتخطّى العرض بصمت (مع كتابة سطر في السجل) إذا لم يُمنح الإذن، لذا تأكّد من طلبه أولًا وفقًا لما ورد في §9.1.

9.3 الجدولة

لا يسجّل AKS مستقبِل البث هذا ولا يشغّله وفق أي جدول زمني؛ أنت من يحدّد وقت استدعاء onReceive، عادةً باستخدام AlarmManager:

kotlin
val intent = Intent(context, YourNotificationReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
    context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)

val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setRepeating(
    AlarmManager.RTC_WAKEUP,
    System.currentTimeMillis() + intervalMs,
    intervalMs,
    pendingIntent
)

بالنسبة إلى intervalMs، يمكنك تعيين فاصل زمني ثابت في الشفرة، أو استخدام القيمة المحدّدة في الإعدادات عن بُعد لبوابة AKS (HOURS_TO_MAKE_NOTIFICATION، وقيمتها الافتراضية ساعتان إذا لم تُضبط). تتوفّر هذه القيمة عبر getTimeIntervalInMilliSeconds() في الفئة الأساسية لمستقبِل البث، لكن مستوى الوصول إليها هو protected، لذا استدعِها من داخل فئتك الفرعية بدلًا من موضع الاستدعاء أعلاه:

kotlin
class YourNotificationReceiver : AppsKitSDKLocalNotificationBroadCastReceiver() {
    fun intervalMillis(): Long = getTimeIntervalInMilliSeconds()
    override fun checkNotifications() { /* ... */ }
}

لا تضمن AlarmManager.setRepeating توقيتًا دقيقًا عند تفعيل وضع Doze أو تحسينات البطارية. إذا كنت بحاجة إلى تشغيل موثوق أثناء الخمول، فأعِد الجدولة باستخدام setExactAndAllowWhileIdle عند كل تشغيل بدلًا منها، أو انتقل إلى PeriodicWorkRequest في WorkManager (بفاصل زمني لا يقل عن 15 دقيقة). لا يفرض AKS أيًا من الخيارين؛ اختر ما يناسب تطبيقك. لاحظ أيضًا أن المنبّهات لا تبقى مسجّلة بعد إعادة تشغيل الجهاز إلا إذا أعدت تسجيلها بنفسك (مثلًا من مستقبِل بث BOOT_COMPLETED).

9.4 عرض إشعار مباشرةً (دون طابور)

لعرض إشعار فورًا، دون المرور بمسار الطابور ومستقبِل البث الموضّح أعلاه:

kotlin
// Tapping opens the Activity at classPath
AppsKitSDKLocalNotificationManager.showNotification(
    context = this,
    id = 2001,
    classPath = "com.yourapp.MainActivity",
    channelId = "general",
    title = "New message",
    message = "You've got a new message waiting.",
    notificationIcon = R.drawable.ic_notification
)

// Or pass a fully-built Intent instead of a class path
AppsKitSDKLocalNotificationManager.showNotification(
    context = this,
    id = 2002,
    intent = Intent(this, MainActivity::class.java).putExtra("from", "notification"),
    channelId = "general",
    title = "New message",
    message = "Tap to view details.",
    notificationIcon = R.drawable.ic_notification
)

تنشئ كلتا الصيغتين المحمّلتين تحميلًا زائدًا قناة الإشعارات نيابةً عنك إذا لم تكن موجودة بالفعل. وعلى خلاف مسار الطابور في §9.2، لا تتحقّق أيٌّ منهما بنفسها من إذن POST_NOTIFICATIONS. اطلب إذن وقت التشغيل بنفسك على Android 13 أو أحدث، وإلا فقد يفشل الاستدعاء في عرض الإشعار بصمت.