الحلقة 3: قنوات الإشعارات — قلب نظام Android
تُعد قنوات الإشعارات (Notification Channels) الجهاز العصبي المركزي لنظام إشعارات Android. منذ إصدار Android 8.0 (Oreo)، فرضت Google أن ينتمي كل إشعار إلى فئة أو "قناة" محددة، مما يمنح المستخدمين تحكماً دقيقاً في تجربتهم.
في هذا الدرس، سنستكشف:
- فلسفة القنوات: لماذا ابتعدت Google عن نهج "مقاس واحد يناسب الجميع".
- التنفيذ في إكسبو: إنشاء وإدارة قنوات متعددة برمجياً.
- مستويات الأهمية: فهم كيف تؤثر الأولويات المختلفة على سلوك المستخدم.
- أفضل الممارسات: تصميم استراتيجية قنوات تحترم خصوصية المستخدم.
لماذا قنوات الإشعارات؟
قبل إصدار Android 8.0، كان المستخدمون يواجهون خياراً محبطاً: إما قبول جميع الإشعارات من التطبيق أو حظرها بالكامل. لم يكن هناك حل وسط.
كان حل Google هو القنوات. يسمح هذا التحول المعماري للمطورين بتصنيف الإشعارات بناءً على غرضها (مثل "الرسائل المباشرة"، "العروض الترويجية"، "تحديثات التواصل"). يمكن للمستخدم بعد ذلك الدخول إلى إعدادات النظام وكتم قناة "العروض الترويجية" مع الحفاظ على قناة "الرسائل المباشرة" بأقصى مستوى للصوت.
“[!IMPORTANT] في نظام Android 8.0 فما فوق، إذا حاولت إرسال إشعار دون تحديد معرف قناة (Channel ID) صالح، فسيستخدم النظام خياراً افتراضياً أو قد يفشل في عرض الإشعار تماماً.
إنشاء القنوات في إكسبو
في إكسبو، نستخدم مكتبة expo-notifications لتعريف هذه القنوات. من الممارسات القياسية تهيئة قنواتك بمجرد تشغيل التطبيق.
index.tstypescriptimport * as Notifications from 'expo-notifications'; import { Platform } from 'react-native'; const configureChannels = async () => { if (Platform.OS !== 'android') return; // 1. التحديثات العامة (افتراضية) await Notifications.setNotificationChannelAsync('default', { name: 'إشعارات عامة', importance: Notifications.AndroidImportance.DEFAULT, sound: 'default', }); // 2. تنبيهات عاجلة (أولوية عالية) await Notifications.setNotificationChannelAsync('urgent', { name: 'تنبيهات عاجلة', importance: Notifications.AndroidImportance.HIGH, vibrationPattern: [0, 500, 500, 500], bypassDnd: true, // يتجاوز وضع "عدم الإزعاج" }); // 3. تحديثات صامتة (أولوية منخفضة) await Notifications.setNotificationChannelAsync('silent', { name: 'تحديثات صامتة', importance: Notifications.AndroidImportance.LOW, sound: false, }); };
المعايير الأساسية:
name: هو النص الذي يراه المستخدم فعلياً في إعدادات نظام Android. اجعله وصفياً ومعرباً بشكل صحيح.importance: يحدد هذا المعيار السلوك البصري والصوتي للإشعار.bypassDnd: محجوز للتنبيهات الحرجة على مستوى النظام. استخدمه بحذر شديد.
فهم مستويات الأهمية
تُعد مستويات الأهمية (importance) الجزء الأكثر أهمية في تكوين القناة. فهي تملي مدى "قوة" ظهور الإشعار للمستخدم.
| المستوى | الصوت/الاهتزاز | ظهور مفاجئ (Heads-Up) | شريط الحالة | حالة الاستخدام |
|---|---|---|---|---|
| URGENT | نعم | نعم | نعم | المكالمات الواردة، التنبيهات الحرجة |
| HIGH | نعم | نعم | نعم | رسائل الدردشة الحية، التذكيرات |
| DEFAULT | نعم | لا | نعم | التحديثات العامة، التفاعلات الاجتماعية |
| LOW | لا | لا | نعم (أدنى) | المهام الخلفية، العروض الترويجية |
تخصيص التجربة
بالإضافة إلى الأهمية، تسمح لك القنوات بتحديد "شخصية" فريدة لأنواع الإشعارات المختلفة من خلال الصوت وأنماط الاهتزاز.
أنماط الاهتزاز المخصصة
نط الاهتزاز (vibrationPattern) هو مصفوفة من الأرقام تمثل المللي ثانية:
[انتظار، اهتزاز، انتظار، اهتزاز، ...]
مثال: [0, 250, 250, 250] ينشئ اهتزازاً قصيراً مزدوجاً.
الصوت المخصص
لاستخدام صوت مخصص، ضع ملف الصوت (مثل alert.wav) في مجلد الأصول (assets) وقم بالإشارة إليه بالاسم.
index.tstypescriptawait Notifications.setNotificationChannelAsync('custom-alert', { name: 'قناة بصوت مخصص', importance: Notifications.AndroidImportance.HIGH, sound: 'alert.wav', });
أفضل ممارسات التصميم الاستراتيجي
- لا تبالغ في التصنيف: استهدف إنشاء 3-5 قنوات رئيسية فقط. كثرة القنوات تربك المستخدم.
- تسمية واضحة: استخدم لغة مفهومة (مثل "رسائل الدردشة") بدلاً من المعرفات التقنية.
- احترم المستخدم: لا تجعل قناة العروض الترويجية بأهمية "عالية" (High). سيتسبب ذلك في حذف تطبيقك.
- خيارات احتياطية: قم دائماً بتعريف قناة "افتراضية" (default) لالتقاط أي إشعارات قد لا يتم تعيينها برمجياً.
التنفيذ في مشروعك
لضمان جاهزية قنواتك دائماً، استدعِ دالة التهيئة داخل useEffect في جذر تطبيقك (عادةً App.tsx أو _layout.tsx).
index.tstypescriptuseEffect(() => { async function setup() { await configureChannels(); console.log("تمت تهيئة قنوات الإشعارات بنجاح."); } setup(); }, []);
الآن، عند إرسال إشعار، ما عليك سوى تمرير channelId ليتناسب مع السلوك الذي تريده:
index.tstypescriptawait Notifications.scheduleNotificationAsync({ content: { title: "اجتماع عاجل", body: "سيبدأ اجتماعك خلال 5 دقائق.", channelId: "urgent", // يتطابق مع المعرف المحدد أعلاه }, trigger: null, });