التوثيق · التكامل مع 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 أو أحدث، وإلا فقد يفشل الاستدعاء في عرض الإشعار بصمت.