3llomi

Members
  • عدد منشوراتي

    2
  • تاريخ الإنضمام

  • تاريخ اخر زياره

السمعه بالموقع

2 Neutral

عن العضو 3llomi

  • الرتبه
    مبدع جديد
  1. ماهي Firebase ؟ Firebase هي خدمة قدمتها Google منذ فترة وقد كانت تقتصر فقط على تخزين البيانات وبعض الأشياء البسيطة,ولكن في Google I/O 16 تم الإعلان عن الكثير من المميزات الجديدة والرائعة وأصبحت حديث الكثير من المطورين مميزاتها: Authentication:وهي عملية تسجيل الدخول سواء عن طريق حساب Facebook,Google,Twitter,Email وفي نفس الوقت حماية البيانات الموجودة في Database (بمعنى أنه يمكنك منع أي شخص من استخدام التطبيق دون عملية تسجيل الدخول وهو الوضع الإفتراضي) Realtime Database: وهي تفيد في تخزين البيانات على السيرفر وأكثر شيئ يميزها هي أنها Realtime بمعنى أنه أي تغيير يحصل على الداتابيز سيتغير فوراً في التطبيق كما سنرى في هذا الشرح) Storage:تخزين الملفات والصور Hosting:لإستضافة موقعك على Firebase Notifications: إرسال إشعارات والعديد من المميزات يمكنك تفقدها عند الدخول الى حسابك في Firebase,من الجدير بالذكر أن هذه الخدمات مجانية(ولكن ببعض الحدود,يمكنك رؤية ماهي الحدود عبر هذا الرابط) وهو مايميز Firebase سنبدأ في هذا الدرس شرح عن Firebase Database وسنحاول مستقبلاً بإذن الله شرح باقي الأمور بدايةً يجب أن يكون لديك حساب Google بالطبع بعد ذلك قم بالتوجه الى رابط Firebase Console ثم نختار Add project ثم نضع اسم المشروع الذي نريد ونضع الدولة بعد ذلك سيتم إنشاء المشروع,نضغط على Add Firebase to Your Android App بعد ذلك سيطلب منا اسم الpackage الخاص بتطبيق الأندرويد نقوم بإنشاء تطبيق أندرويد جديد ثم نتوجه الى ملف build.gradle وننسخ applicationId ونلصقه في Firebase ونضغط Register App بعد ذلك نضغط على Download google-services.json وهو ملف إعدادات الذي يربط مشروعنا على Firebase بمشروعنا في Android Studio سيتم تحميل هذا الملف قم بنسخه ثم نتوجه الى Android Studio ونضغط بالزر الأيمن على مجلد app ونختار Show in Explorer سيتم فتح مجلد المشروع في حاسوبك نقوم بلصق هذا الملف في مجلد app الآن يجب علينا إضافة مكتبات Firebase الى مشروعنا في Android Studio نتوجه الى ملف build.gradle(project) ونلصق هذا السطر classpath 'com.google.gms:google-services:3.0.0' ثم نذهب الى build.gradle(app) ونضع سطر plugin apply plugin: 'com.google.gms.google-services' الآن تم ربط مشروع Firebase ,وهذه الخطوات لربط أي مشروع Firebase بشكل عام سواءً كان Auth,Notifications,Storage الخ.. أما الآن سنقوم بإضافة مكتبة Firebase Realtime Database الى ملف build.gradle compile 'com.google.firebase:firebase-database:10.2.1' أخيراً نقوم بالضغط على Sync now ونشغل التطبيق لنتأكد من أن كل شيئ يعمل. إذا واجهتك هذه المشكلة تأكد من وضع سطر plugin في آخر ملف build.gradle. الآن نتوجه الى Firebase Console الى Database ثم Rules ونقوم بتغييره الى { "rules": { ".read": true, ".write": true } } ثم نضغط على Publish لحفظ التغييرات لكي نستطيع الكتابة والقراءة لأي شخص يستخدم التطبيق(في الوضع الإفتراضي فقط المستخدمين الذين قامو بتسجيل الدخول باستخدام Firebase Auth يستطيعون قراءة أو كتابة البيانات ويمكنك تحديد هذه الأمور في Rules) وستجد هذه الرسالة وتفيد بأنه لا يفضل وضع هذه القواعد ويجب عليك فقط جعل المستخدمين الذين قاموا بتسجيل الدخول استخدام التطبيق عموماً سنبدأ بالتوجه الى التطبيق ونبدأ بتخزين البيانات السطر الأول لتعريف database اما السطر الثاني هو لأخذ Reference ال root سنضع قيمة child اسمها “Samsung” وقيمتها “S8” نشغل التطبيق وسنجد أنه تم إضافة هذه البيانات الى Firebase تعتمد الFirebase على مبدأ key و value ملاحظة: عند استدعاء ref.child فإنه يتحقق إذا كان child موجود مسبقاً فسيقوم بإنشاءه وإلا فسيقوم فقط بوضع القيمة ننتقل الآن الى كيفية عرض البيانات من Firebase وسنقوم بعرض كلمة S8 في TextView ونجرب تغييرها من Firebase Console لنرى سرعة التغيير في التطبيق قمنا بوضع ref.child(“Samsung”) وقمنا باستدعاء الميثود addValueEventListener ومهمة هذه الميثود هي الإستماع على أية تغيرات تحصل على child Samsung وبالطبع ال childs الموجودة داخل child Samsung وتقوم بعمل Override ل onDataChange وهي تنادى عند حدوث أي تغيير و onCancelled تنادى عند حدوث أي خطأ في onDataChange تقدم لنا الميثود DataSnapshot وهي البيانات الموجودة في Database ممكن ان تحتوي على String,int,long,double,boolean الخ.. قمنا بوضع setText ل textView بالقيمة الموجودة داخل Firebase ,وبما أنني أعرف نوع المتغير الذي وضعته في Firebase وهو ("S8”) فقمت بعمل cast له ك String.class نشغل التطبيق وسنجد أنه قد قام بوضع كلمة S8 في ال TextView,وعند تغيير القيمة في Firebase Console سيتم تغييرها فوراً في التطبيق نلاحظ بأنه اذا قمت بتغيير القيمة في Firebase Console الى 1 مثلا فسيعتبره متغير من نوع int ويقوم التطبيق بعمل كراش وذلك لأنه قد قمنا بعمل cast له ك String هذه كانت مقدمة بسيطة عن كيفية حفظ وعرض البيانات سنقوم الآن بعمل تطبيق بسيط يخزن أسماء بعض الطلاب مع معدلاتهم وعرضهم في RecyclerView وكالعادة سنبدأ بإنشاء كلاس Model وسنسميه Student ونضع به الاسم String name و المعدل int average الآن سنقوم بإدخال بعض الطلاب على Firebase قمنا بإنشاء اوبجكت من Student ووضعنا الاسم Khalid و average85 ثم قمنا باستدعاء الchild الstudents وقمنا بعمل push وهذه الميثود تقوم بإنشاء key بشكل عشوائي (لكل student) ثم وضعنا setValue لوضع القيمة وهي اوبجكت student الذي أنشأناه سنقوم بإنشاء المزيد من الطلاب للتجربة نأتي الآن الى عرض البيانات في RecyclerView نقوم بإنشاء RecyclerView Adapter نعرف List<Student> و adapter ثم نستدعي ميثود addValueEventListener وداخل onDataChange نقوم بعمل for على DataSnapshot للحصول على جميع المعلومات وأنشأنا Student وقيمته تساوي snapshot وقمنا بعمل cast ك Student.class لأننا نعلم أن نوع المتغيرات الموجودة في Firebase هي نفسها الموجودة في Student Class وهي int و String ثم أضفنا هذا student الى studentList وأخيراً قمنا باستدعاء الميثود adapter.notifyDataSetChanged لتحديث البيانات في Adapter أخيراً نقوم بتعريف RecyclerView و adapter (خارج valueEventListener) نقوم بتشغيل التطبيق وستجد البيانات تم عرضها بهذا الشكل نجرب تغيير قيمة أي عنصر (من Firebase Console) على سبيل المثال نغير معدل Muhammad الى 98 وستجد أنه قد تم تكرار هذه البيانات 😀 وهذا لأن الميثود onDataChange يتم استدعاؤها عند كل حدث تغيير على قاعدة بيانات وبالتالي إعادة إضافة العناصر الList ولحل هذه المشكلة نقوم فقط بتفريغ List في onDataChange أما إذا أردنا عرض العناصر الحديثة من الأعلى (بمعنى أنه أي عنصر جديد يتم إضافته يظهر في الأعلى) فقط نقوم بعكس ال List عبر الميثود Collections.reverse(studentList); وستظهر بهذا الشكل Firebase Query عملية البحث داخل Firebase نبدأ بإنشاء SearchView في MainActivity ومن ثم نقوم باستدعاء الميثود setOnQueryTextListener وداخل onQueryTextSubmit(هذه الميثود تستدعى عندما يتم الضغط على زر البحث في لوحة المفاتيح) نقوم بتعريف Query Query fireQuery = ref.child("students").orderByChild("name").equalTo(query); ثم نبحث داخل الchild الstudents ونقوم بالترتيب على حسب child (orderByChild) في حالتنا هذه نود البحث عن الإسم فوضعنا name (يجب أن يكون نفس الإسم في Firebase) عندما يكون يساوي الQuery الذي كتبه المستخدم في SearchView query (.equalTo) بعد ذلك نقوم باستدعاء addListenerForSingleValueEvent هذه الميثود تشبه تماماً الميثود addValueEventListener ولكن الفرق أن هذه الميثود تستدعى مرة واحدة فقط بدلاً من الإستماع كل مرة. داخل onDataChange قمنا بإنشاء List<Student> جديد وقمنا بعمل for على snapshot وتنفيذ نفس الخطوات التي شرحناها سابقاً ثم عرفنا نفس adapter ولكن أعطيناه searchList الجديدة التي أنشأناها وأخيراً قمنا بعمل setAdapter نشغل التطبيق ونضع اسم Ahmad ونضغط على زر البحث لنرى النتيجة وللتأكد من وجود الاسم Ahmad ام لا في Firebase نقوم بالتحقق اذا كانت قيمة dataSnapshot.getValue == null عندها قم بإظهار رسالة Toast وإلا قم بوضع العناصر داخل list وأظهرها نجرب وضع كلمة ما مثلا test وسنجد أنه تم إظهار رسالة Toast ERROR وللخروج من وضع البحث نقوم باستدعاء الميثود setOnCloseListener وداخل onClose نقوم بتعريف نفس Adapter وإعطاءه studentList التي يوجد داخلها كافة البيانات ثم setAdapter تعديل البيانات على Firebase الآن نذهب الى RecyclerView Row ونضيف 2Buttons واحد للتعديل والآخر للحذف ثم نذهب الى StudentAdapter وعند الضغط على زر editStudent سنقوم بإنشاء Intent الى أكتفتيEditStudentActivity.class الذي سنقوم بإنشاءه بعد قليل وأرسلنا name (اسم الطالب الذي تم الضغط عليه) عبر الintent وأخيراً شغلنا الأكتفتي الآن نقوم بإنشاء EditStudentActivity.class ونضع به 2EditText واحد لتعديل الإسم والآخر للمعدل ونضع 1Button لحفظ القيم الى Firebase بالطبع نقوم بتعريف String name القادم عبر intent ونعيد تعريف FirebaseDatabase و Reference و EditTexts بعدها نقوم بتنفيذ Query على name القادم عبر intent ,وداخل onDataChange نقوم بعمل for كما فعلنا سابقاً وأخيراً قمنا بعمل setText ل EditText (تم استخدام String.ValueOf لأن getAverage تعيد int ويجب تحويلها ل String لوضعها في EditText) وبهذا نكون قد وضعنا اسم الطالب ومعدله فيEditTexts ولحفظ القيم بعد تعديلها ,عند الضغط على زر Save نقوم بإنشاء Student جديد ونضع الإسم name قيمة editText الأول ,ووضعنا المعدل average قيمة EditText الثاني وقمنا بتحويله الى Integer ثم نفذنا Query جديدة وقمنا بعمل for على dataSnapshot ثم snapshot.getRef() هذه الميثود تعيد لنا key الموجود في الداتابيز (على سبيل المثال -Kgzq-ktAGSpAjJRqk2i) وجميع childs الموجودة داخله ثم استدعينا setValue وقمنا بإعطاءه student الذي أنشأناه,وبهذا يكون قد تم التعديل عملية الحذف من Firebase نطبق نفس الخطوات التي طبقناها في عملية التعديل ولكن هذه المرة بدل من setValue نقوم بعملremoveValue() للحذف المشروع على Github ملاحظة:المشروع على Github للمعاينة فقط ولايمكنك تجربته على Android Studio لعدم وجود google-services.json الخاص بك
  2. ماهي Firebase Cloud Functions؟ هي خدمة قدمتها Google منذ بضعة أشهر ومازالت في المرحلة التجريبية BETA ,تساعدك Cloud Functions بتنفيذ أمور معينة عند حدوث أمر معين (عند الكتابة في قاعدة البيانات Firebase Realtime Database) . بعض الأمثلة: إرسال الإشعارات تعديل بعض النصوص على سبيل المثال (تغيير كلمة “Bad” الى “Not Good” بشكل أوتوماتيكي وبدون أي تدخل منك) توليد الصور المصغرة “Thumbnails” بمساعدة Firebase Storage والكثير من الأشياء الأخرى(ألقِ نظرة على الروابط في أسفل المقال) نأتي الى الأمر المهم وهو كيف سنبدأ بتجهيز بيئة العمل و نبدأ بإرسال الإشعارات . الFirebase Cloud Functions مبنية على لغة Javascript وعلى Framework من نوع NodeJS ولهذا يجب علينا أن نقوم بتحميل NodeJS من هذا الرابط ,بعد تثبيته نقوم بفتح CMD او Terminal ونكتب الأمر التالي لتثبيت أدوات Firebase npm install -g firebase-tools عند نجاح التثبيت ستظهر بهذا الشكل ثم نكتب الأمر لتسجيل الدخول بحساب Google واختيار مشروع Firebase firebase login بعد ذلك ستُفتح صفحة ويب وتطلب منك تسجيل الدخول اختر Allow تمت عملية تسجيل الدخول نعود الى CMD ونقوم بإنشاء مجلد جديد وثم غير مسار CMD الى هذا المجلد (او إذا كنت على ويندوز فقم بفتح المجلد واضغط على زر Shift + زر الفأرة الأيمن واختر “Open Command Window Here” ) ثم اكتب الأمر لبدء تجهيز مشروع Cloud Functions firebase init functions الآن سيتم عرض كافة مشاريع Firebase الموجودة في حسابك,قم باختيار المشروع الذي تريد ثم اختر Y تم تجهيز الملفات الآن اذا ذهبنا الى المجلد سنجد به بعض الملفات نتوجه الى مجلد functions وسنجد داخله ملف index.js هذا هو الملف الذي سنقوم بكتابة Cloud Functions بداخله قم بفتحه باستخدام أي محرر أكواد,سأستخدم VSCode يمكنك تحميله من هنا سنجد هذه الأكواد ,نقوم بمسحها سنقوم في مثالنا هذا إرسال إشعار للمستخدم عندما يقوم أحد بمتابعته(بنفس فكرة موقع تويتر) وسنعرض إسم الشخص الذي قام بمتابعته بالإضافة الى صورته في الإشعار ولنفترض أنه لديك مثل هذا الترتيب في قاعدة البيانات Realtime Database جدول users يحتوي على جميع المستخدمين ونسمي كل مستخدم بناء على UID الخاص بFirebase Auth . وداخل كل مستخدم نضع التالي: notificationTokens photo رابط صورة المستخدم userName اسم المستخدم سنقوم بإنشاء جدول نسميه followers والذي سيحتوي على الأشخاص الذين قاموا بمتابعة هذا المستخدم حان وقت العمل , فلنكتب بعض الأكواد ولكن قبل هذا سأذكرك بأن Firebase Cloud Fucntions تعمل على مبدأ تنفيذ أمر معين عند حدوث فعل(كتابة على Firebase Realtime Database مثلاً) //Cloud Functions Modules const functions = require('firebase-functions'); //Firebase Admin SDK Modules (it will send the Notifications to the user) const admin = require('firebase-admin'); //init Admin SDK admin.initializeApp(functions.config().firebase); السطر الثاني نقوم بتعريف او عمل import لمكتبة Firebase Cloud Functions السطر الرابع نقوم بتعريف مكتبة Firebase Admin وهي المسؤولة عن إرسال الإشعارات والكتابة في قاعدة البيانات ثم نقوم بتهيئة مكتبة Admin في السطر الأخير exports.sendNotificationOnNewFollow = functions.database.ref('/followers/{userId}/{followerId}/').onWrite(event => { } بعد ذلك نقوم بتعريف Cloud Function عبر exports.sendNotificationOnNewFollow (sendNotificationOnNewFollow هو اسم الFunction يمكنك تسميته كما تشاء) ونجعلها تستمع الى الأحداث في جدول followers مايكتب بين هذين القوسين{} عند تعريف Cloud Function يسمى Wildcard وببساطة يعني أننا نريد الإستماع الى الأحداث داخل جدول followers داخل userId داخل followerId ,كما أنها أيضاً تعيد لنا قيمة الحدث (كuserId و followerId) وقد يحتوي على أي قيمة كما أنه يمكنك تسميتهم بأي إسم تريد الصورة التالية ستوضح لك الفكرة .onWrite أي أنه عندما يتم الكتابة وهي تعيد لنا حدث event والذي يحتوي على الأمور التى كتبت او تغيرت في Realtime Database const userId = event.params.userId; const followerId = event.params.followerId هنا قمنا بالحصول على userId و followerId عن طريق event.params يجب عليك أن تكتب نفس الأسماء التى في البارامترز سنقوم بكتابة هذا السطر if (!event.data.exists()) { return; } وهذا يعني أنه اذا كانت لاتتوفر بيانات(تم عمل متابعة ثم الغاؤها فوراً) عندها قم بعمل return ولا تنفذ أي شيئ آخر ثم نقوم بتعريف ميثود getDeviceTokensPromise مهمتها جلب الnotificationTokens الخاصة بالمستخدم الذي تمت متابعته(“Ahmad”) const getDeviceTokensPromise = admin.database().ref(`users/${userId}/notificationTokens/`).once('value'); لاحظ أنه تم استخدام userId الذي عرفناه سابقاً ثم بنفس الطريقة نعرف ميثود أخرى تقوم بجلب اسم المستخدم وصورته const getFollowerInfo = admin.database().ref(`users/${followerId}/`).once('value'); هذه المرة قمنا بجلب البيانات الخاصة بناءً على followerId وهو (“Sobhe”) الآن سنستدعي هذه الميثودز في Cloud Functions يجب علينا دائما أن نعود ب Promise وهو الذي سيقول ل Cloud Functions أنه قد انتهى من التنفيذ return Promise.all([getDeviceTokensPromise, getFollowerInfo]).then(results => { const tokensSnapshot = results[0]; const followerSnapshot = results[1]; } داخل Promise نعطيه الميثودز الذي أنشأناها ونضعها داخل مصفوفة Array. .then هذه تعني عندما ينتهي من تنفيذ الميثوذز وهي تعود لنا ب Snapshot أسميناها results وبما أننا قد نفذنا أكثر من ميثود فقمنا بتعريف كل Snapshot على حدى وأعطيناها المكان من Array ثم سنتأكد من أنه يوجد tokens أم لا عبر السطر if (!tokensSnapshot.hasChildren()) { return console.log('There are no notification tokens to send to.'); } واذا لم يوجد سيقوم بطبع رسالة وسيتجاهل ماتبقى من الكود بعد ذلك نقوم بأخذ userName و photo من followerSnapshot ونقوم بطباعتهم في log const followerName = followerSnapshot.val().userName; const followerPhoto = followerSnapshot.val().photo; console.log('Follower Name is: ', followerName); console.log('Follower Photo is: ', followerPhoto); ثم نقوم بتعريف Payload وهو الإشعار الذي سنستقبله في تطبيق Android او IOS او Web (سنشرح بشكل بسيط عن طريق الأندرويد) وهو يحتوي على: title عنوان الإشعار body مانريد كتابته داخل الإشعار(التفاصيل) imgUrl صورة المستخدم (“Sobhe”) // Notification details. const payload = { data: { title: 'you have a New Follower', body: `${followerName} has followed You.`, imgUrl: `${followerPhoto}` } }; ثم نقوم بأخذ notificationTokens من tokensSnapshot وبما أنه قد يحتوي على أكثر من عنصر قمنا بوضعهم في Array const tokens = Object.keys(tokensSnapshot.val()); أخيراً نقوم بإرسال الإشعار عبر messaging وهي تأخذ 2 بارامتر tokens و payload // Send notifications to all tokens. return admin.messaging().sendToDevice(tokens, payload).then(response => { }) وتعود لنا ب response ثم نقوم بتعريف مصفوفة Array فارغة,سنملؤها لاحقا بNotification Tokens الغير صالحة لنقوم بحذفهم const tokensToRemove = []; وأخيراً نقوم بكتابة هذه الأكواد response.results.forEach((result, index) => { const error = result.error; if (error) { console.error('Failure sending notification to', tokens[index], error); // Cleanup the tokens who are not registered anymore. if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') { tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove()); } } }); return Promise.all(tokensToRemove); إذا كان هنالك خطأ سنقوم بطباعته في Log ثم نعود ب Promise لحذف الTokens الغير صالحة في حال وجودهم ليصبح الكود الكامل كالتالي //Cloud Functions Modules const functions = require('firebase-functions'); //Firebase Admin SDK Modules (it will send the Notifications to the user) const admin = require('firebase-admin'); //init Admin SDK admin.initializeApp(functions.config().firebase); exports.sendNotificationOnNewFollow = functions.database.ref('/followers/{userId}/{followerId}/').onWrite(event => { const userId = event.params.userId; const followerId = event.params.followerId // If un-follow we exit the function. if (!event.data.exists()) { return; } // Get the list of device notification tokens. const getDeviceTokensPromise = admin.database().ref(`users/${userId}/notificationTokens/`).once('value'); // Get the follower Info. const getFollowerInfo = admin.database().ref(`users/${followerId}/`).once('value'); //Execute the Functions return Promise.all([getDeviceTokensPromise, getFollowerInfo]).then(results => { const tokensSnapshot = results[0]; const followerSnapshot = results[1]; // Check if there are any device tokens. if (!tokensSnapshot.hasChildren()) { return console.log('There are no notification tokens to send to.'); } const followerName = followerSnapshot.val().userName; const followerPhoto = followerSnapshot.val().photo; console.log('Follower Name is: ', followerName); console.log('Follower Photo is: ', followerPhoto); // Notification details. const payload = { data: { title: 'you have a New Follower', body: `${followerName} has followed You.`, imgUrl: `${followerPhoto}` } }; // Listing all tokens. const tokens = Object.keys(tokensSnapshot.val()); // Send notifications to all tokens. return admin.messaging().sendToDevice(tokens, payload).then(response => { // For each message check if there was an error. const tokensToRemove = []; response.results.forEach((result, index) => { const error = result.error; if (error) { console.error('Failure sending notification to', tokens[index], error); // Cleanup the tokens who are not registered anymore. if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') { tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove()); } } }); return Promise.all(tokensToRemove); }); }); }); نقوم بحفظ الملف Ctrl + S ونعود مرة أخرى الى CMD ونتوجه الى مجلد المشروع ونكتب الأمر firebase deploy --only functions سيتم بدء رفع الملفات الى Firebase وقد تأخذ العملية بعض الوقت عند الإنتهاء نذهب الى Firebase Console الى Functions وستجد ظهور Cloud Function الذي أنشأتها سنتوجه الآن الى Android Studio بشكل سريع لترى كيف يتم تنفيذ الإشعار (يمكنك تحميل السورس كود وتعديله كما تشاء) نقوم بإنشاء كلاس جديد نسميه MyFCMService والذي سيقوم بتلقى الإشعارات من Cloud Functions ونجعله extends FirebaseMessaginService ولاننسَ أن نقوم بتشغيله من MainActivity ثم نقوم بعمل Override لميثود onMessageReceived والتى تعود لنا ب remoteMessage ثم نقوم بتعريف title و body و imgUrl ونلاحظ أنه يجب علينا كتابة نفس الإسم المكتوب في Payload الذي كتبناه في Cloud Functions ثم استدعينا ميثود سنقوم بإنشاءها وهي sendNotification وتأخذ المتغيرات الثلاثة ك بارامترز ثم نقوم بتعريف هذه الميثود,وهي ميثود بسيطة تقوم بأخذ البارامترز وعرضهم في Notification ونلاحظ أنه قد وضعنا صورة الشخص عبر setLargeIcon وقمنا باستدعاء ميثود getProfilePhotoAsBitmap والتي تأخذ imgUrl ك بارامتر private void sendNotification(String title, String messageBody, String imgUrl) { Intent intent = new Intent(this, MyFCMService.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* FRequest code */, intent, PendingIntent.FLAG_ONE_SHOT); Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) .setSmallIcon(android.R.drawable.star_on) .setLargeIcon(getProfilePhotoAsBitmap(imgUrl)) .setContentTitle(title) .setContentText(messageBody) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); } أخيراً نقوم بإنشاء الميثود getProfilePhotoAsBitmap وهي ميثود تقوم بأخذ الرابط وتقوم بتحميل الصورة وإرجاعها ك Bitmap لعرضها في Notification,ولهذا قمنا بالإستعانة بمكتبة Glide المختصة بعرض الصور private Bitmap getProfilePhotoAsBitmap(String url) { Bitmap bitmap = null; try { bitmap = Glide.with(this).load(url).asBitmap().into(168, 168).get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return bitmap; } حان وقت التجربة :] نقوم بتشغيل تطبيق الأندرويد ثم نجعل “Sobhe” يعمل Follow ل “Ahmad” (يمكنك ادخالهم بشكل يدوي من Firebase Console كما فعلت على سبيل التجربة!) رابط المشروع على Github ملاحظة: قد تم إرفاق ملف fcm-cloud-functions-export-Data Structure وهو ملف يحتوي على قاعدة البيانات البسيطة الذي أنشأناها,يمكنك استيرادها من Realtime Database عبر خيار Import Backup (لا تنسَ عمل نسخ احتياطي لقاعدة البيانات لديك قبل تنفيذ عملية الاستيراد) بعض المصادر التي قد تهمك 1,2,3
  3. ماهي Firebase Cloud Functions؟ هي خدمة قدمتها Google منذ بضعة أشهر ومازالت في المرحلة التجريبية BETA ,تساعدك Cloud Functions بتنفيذ أمور معينة عند حدوث أمر معين (عند الكتابة في قاعدة البيانات Firebase Realtime Database) . بعض الأمثلة: إرسال الإشعارات تعديل بعض النصوص على سبيل المثال (تغيير كلمة “Bad” الى “Not Good” بشكل أوتوماتيكي وبدون أي تدخل منك) توليد الصور المصغرة “Thumbnails” بمساعدة Firebase Storage والكثير من الأشياء الأخرى(ألقِ نظرة على الروابط في أسفل المقال) نأتي الى الأمر المهم وهو كيف سنبدأ بتجهيز بيئة العمل و نبدأ بإرسال الإشعارات . الFirebase Cloud Functions مبنية على لغة Javascript وعلى Framework من نوع NodeJS ولهذا يجب علينا أن نقوم بتحميل NodeJS من هذا الرابط ,بعد تثبيته نقوم بفتح CMD او Terminal ونكتب الأمر التالي لتثبيت أدوات Firebase npm install -g firebase-tools عند نجاح التثبيت ستظهر بهذا الشكل ثم نكتب الأمر لتسجيل الدخول بحساب Google واختيار مشروع Firebase firebase login بعد ذلك ستُفتح صفحة ويب وتطلب منك تسجيل الدخول اختر Allow تمت عملية تسجيل الدخول نعود الى CMD ونقوم بإنشاء مجلد جديد وثم غير مسار CMD الى هذا المجلد (او إذا كنت على ويندوز فقم بفتح المجلد واضغط على زر Shift + زر الفأرة الأيمن واختر “Open Command Window Here” ) ثم اكتب الأمر لبدء تجهيز مشروع Cloud Functions firebase init functions الآن سيتم عرض كافة مشاريع Firebase الموجودة في حسابك,قم باختيار المشروع الذي تريد ثم اختر Y تم تجهيز الملفات الآن اذا ذهبنا الى المجلد سنجد به بعض الملفات نتوجه الى مجلد functions وسنجد داخله ملف index.js هذا هو الملف الذي سنقوم بكتابة Cloud Functions بداخله قم بفتحه باستخدام أي محرر أكواد,سأستخدم VSCode يمكنك تحميله من هنا سنجد هذه الأكواد ,نقوم بمسحها سنقوم في مثالنا هذا إرسال إشعار للمستخدم عندما يقوم أحد بمتابعته(بنفس فكرة موقع تويتر) وسنعرض إسم الشخص الذي قام بمتابعته بالإضافة الى صورته في الإشعار ولنفترض أنه لديك مثل هذا الترتيب في قاعدة البيانات Realtime Database جدول users يحتوي على جميع المستخدمين ونسمي كل مستخدم بناء على UID الخاص بFirebase Auth . وداخل كل مستخدم نضع التالي: notificationTokens photo رابط صورة المستخدم userName اسم المستخدم سنقوم بإنشاء جدول نسميه followers والذي سيحتوي على الأشخاص الذين قاموا بمتابعة هذا المستخدم حان وقت العمل , فلنكتب بعض الأكواد ولكن قبل هذا سأذكرك بأن Firebase Cloud Fucntions تعمل على مبدأ تنفيذ أمر معين عند حدوث فعل(كتابة على Firebase Realtime Database مثلاً) //Cloud Functions Modules const functions = require('firebase-functions'); //Firebase Admin SDK Modules (it will send the Notifications to the user) const admin = require('firebase-admin'); //init Admin SDK admin.initializeApp(functions.config().firebase); السطر الثاني نقوم بتعريف او عمل import لمكتبة Firebase Cloud Functions السطر الرابع نقوم بتعريف مكتبة Firebase Admin وهي المسؤولة عن إرسال الإشعارات والكتابة في قاعدة البيانات ثم نقوم بتهيئة مكتبة Admin في السطر الأخير exports.sendNotificationOnNewFollow = functions.database.ref('/followers/{userId}/{followerId}/').onWrite(event => { } بعد ذلك نقوم بتعريف Cloud Function عبر exports.sendNotificationOnNewFollow (sendNotificationOnNewFollow هو اسم الFunction يمكنك تسميته كما تشاء) ونجعلها تستمع الى الأحداث في جدول followers مايكتب بين هذين القوسين{} عند تعريف Cloud Function يسمى Wildcard وببساطة يعني أننا نريد الإستماع الى الأحداث داخل جدول followers داخل userId داخل followerId ,كما أنها أيضاً تعيد لنا قيمة الحدث (كuserId و followerId) وقد يحتوي على أي قيمة كما أنه يمكنك تسميتهم بأي إسم تريد الصورة التالية ستوضح لك الفكرة .onWrite أي أنه عندما يتم الكتابة وهي تعيد لنا حدث event والذي يحتوي على الأمور التى كتبت او تغيرت في Realtime Database const userId = event.params.userId; const followerId = event.params.followerId هنا قمنا بالحصول على userId و followerId عن طريق event.params يجب عليك أن تكتب نفس الأسماء التى في البارامترز سنقوم بكتابة هذا السطر if (!event.data.exists()) { return; } وهذا يعني أنه اذا كانت لاتتوفر بيانات(تم عمل متابعة ثم الغاؤها فوراً) عندها قم بعمل return ولا تنفذ أي شيئ آخر ثم نقوم بتعريف ميثود getDeviceTokensPromise مهمتها جلب الnotificationTokens الخاصة بالمستخدم الذي تمت متابعته(“Ahmad”) const getDeviceTokensPromise = admin.database().ref(`users/${userId}/notificationTokens/`).once('value'); لاحظ أنه تم استخدام userId الذي عرفناه سابقاً ثم بنفس الطريقة نعرف ميثود أخرى تقوم بجلب اسم المستخدم وصورته const getFollowerInfo = admin.database().ref(`users/${followerId}/`).once('value'); هذه المرة قمنا بجلب البيانات الخاصة بناءً على followerId وهو (“Sobhe”) الآن سنستدعي هذه الميثودز في Cloud Functions يجب علينا دائما أن نعود ب Promise وهو الذي سيقول ل Cloud Functions أنه قد انتهى من التنفيذ return Promise.all([getDeviceTokensPromise, getFollowerInfo]).then(results => { const tokensSnapshot = results[0]; const followerSnapshot = results[1]; } داخل Promise نعطيه الميثودز الذي أنشأناها ونضعها داخل مصفوفة Array. .then هذه تعني عندما ينتهي من تنفيذ الميثوذز وهي تعود لنا ب Snapshot أسميناها results وبما أننا قد نفذنا أكثر من ميثود فقمنا بتعريف كل Snapshot على حدى وأعطيناها المكان من Array ثم سنتأكد من أنه يوجد tokens أم لا عبر السطر if (!tokensSnapshot.hasChildren()) { return console.log('There are no notification tokens to send to.'); } واذا لم يوجد سيقوم بطبع رسالة وسيتجاهل ماتبقى من الكود بعد ذلك نقوم بأخذ userName و photo من followerSnapshot ونقوم بطباعتهم في log const followerName = followerSnapshot.val().userName; const followerPhoto = followerSnapshot.val().photo; console.log('Follower Name is: ', followerName); console.log('Follower Photo is: ', followerPhoto); ثم نقوم بتعريف Payload وهو الإشعار الذي سنستقبله في تطبيق Android او IOS او Web (سنشرح بشكل بسيط عن طريق الأندرويد) وهو يحتوي على: title عنوان الإشعار body مانريد كتابته داخل الإشعار(التفاصيل) imgUrl صورة المستخدم (“Sobhe”) // Notification details. const payload = { data: { title: 'you have a New Follower', body: `${followerName} has followed You.`, imgUrl: `${followerPhoto}` } }; ثم نقوم بأخذ notificationTokens من tokensSnapshot وبما أنه قد يحتوي على أكثر من عنصر قمنا بوضعهم في Array const tokens = Object.keys(tokensSnapshot.val()); أخيراً نقوم بإرسال الإشعار عبر messaging وهي تأخذ 2 بارامتر tokens و payload // Send notifications to all tokens. return admin.messaging().sendToDevice(tokens, payload).then(response => { }) وتعود لنا ب response ثم نقوم بتعريف مصفوفة Array فارغة,سنملؤها لاحقا بNotification Tokens الغير صالحة لنقوم بحذفهم const tokensToRemove = []; وأخيراً نقوم بكتابة هذه الأكواد response.results.forEach((result, index) => { const error = result.error; if (error) { console.error('Failure sending notification to', tokens[index], error); // Cleanup the tokens who are not registered anymore. if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') { tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove()); } } }); return Promise.all(tokensToRemove); إذا كان هنالك خطأ سنقوم بطباعته في Log ثم نعود ب Promise لحذف الTokens الغير صالحة في حال وجودهم ليصبح الكود الكامل كالتالي //Cloud Functions Modules const functions = require('firebase-functions'); //Firebase Admin SDK Modules (it will send the Notifications to the user) const admin = require('firebase-admin'); //init Admin SDK admin.initializeApp(functions.config().firebase); exports.sendNotificationOnNewFollow = functions.database.ref('/followers/{userId}/{followerId}/').onWrite(event => { const userId = event.params.userId; const followerId = event.params.followerId // If un-follow we exit the function. if (!event.data.exists()) { return; } // Get the list of device notification tokens. const getDeviceTokensPromise = admin.database().ref(`users/${userId}/notificationTokens/`).once('value'); // Get the follower Info. const getFollowerInfo = admin.database().ref(`users/${followerId}/`).once('value'); //Execute the Functions return Promise.all([getDeviceTokensPromise, getFollowerInfo]).then(results => { const tokensSnapshot = results[0]; const followerSnapshot = results[1]; // Check if there are any device tokens. if (!tokensSnapshot.hasChildren()) { return console.log('There are no notification tokens to send to.'); } const followerName = followerSnapshot.val().userName; const followerPhoto = followerSnapshot.val().photo; console.log('Follower Name is: ', followerName); console.log('Follower Photo is: ', followerPhoto); // Notification details. const payload = { data: { title: 'you have a New Follower', body: `${followerName} has followed You.`, imgUrl: `${followerPhoto}` } }; // Listing all tokens. const tokens = Object.keys(tokensSnapshot.val()); // Send notifications to all tokens. return admin.messaging().sendToDevice(tokens, payload).then(response => { // For each message check if there was an error. const tokensToRemove = []; response.results.forEach((result, index) => { const error = result.error; if (error) { console.error('Failure sending notification to', tokens[index], error); // Cleanup the tokens who are not registered anymore. if (error.code === 'messaging/invalid-registration-token' || error.code === 'messaging/registration-token-not-registered') { tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove()); } } }); return Promise.all(tokensToRemove); }); }); }); نقوم بحفظ الملف Ctrl + S ونعود مرة أخرى الى CMD ونتوجه الى مجلد المشروع ونكتب الأمر firebase deploy --only functions سيتم بدء رفع الملفات الى Firebase وقد تأخذ العملية بعض الوقت عند الإنتهاء نذهب الى Firebase Console الى Functions وستجد ظهور Cloud Function الذي أنشأتها سنتوجه الآن الى Android Studio بشكل سريع لترى كيف يتم تنفيذ الإشعار (يمكنك تحميل السورس كود وتعديله كما تشاء) نقوم بإنشاء كلاس جديد نسميه MyFCMService والذي سيقوم بتلقى الإشعارات من Cloud Functions ونجعله extends FirebaseMessaginService ولاننسَ أن نقوم بتشغيله من MainActivity ثم نقوم بعمل Override لميثود onMessageReceived والتى تعود لنا ب remoteMessage ثم نقوم بتعريف title و body و imgUrl ونلاحظ أنه يجب علينا كتابة نفس الإسم المكتوب في Payload الذي كتبناه في Cloud Functions ثم استدعينا ميثود سنقوم بإنشاءها وهي sendNotification وتأخذ المتغيرات الثلاثة ك بارامترز ثم نقوم بتعريف هذه الميثود,وهي ميثود بسيطة تقوم بأخذ البارامترز وعرضهم في Notification ونلاحظ أنه قد وضعنا صورة الشخص عبر setLargeIcon وقمنا باستدعاء ميثود getProfilePhotoAsBitmap والتي تأخذ imgUrl ك بارامتر private void sendNotification(String title, String messageBody, String imgUrl) { Intent intent = new Intent(this, MyFCMService.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* FRequest code */, intent, PendingIntent.FLAG_ONE_SHOT); Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) .setSmallIcon(android.R.drawable.star_on) .setLargeIcon(getProfilePhotoAsBitmap(imgUrl)) .setContentTitle(title) .setContentText(messageBody) .setAutoCancel(true) .setSound(defaultSoundUri) .setContentIntent(pendingIntent); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); } أخيراً نقوم بإنشاء الميثود getProfilePhotoAsBitmap وهي ميثود تقوم بأخذ الرابط وتقوم بتحميل الصورة وإرجاعها ك Bitmap لعرضها في Notification,ولهذا قمنا بالإستعانة بمكتبة Glide المختصة بعرض الصور private Bitmap getProfilePhotoAsBitmap(String url) { Bitmap bitmap = null; try { bitmap = Glide.with(this).load(url).asBitmap().into(168, 168).get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } return bitmap; } حان وقت التجربة :] نقوم بتشغيل تطبيق الأندرويد ثم نجعل “Sobhe” يعمل Follow ل “Ahmad” (يمكنك ادخالهم بشكل يدوي من Firebase Console كما فعلت على سبيل التجربة!) رابط المشروع على Github ملاحظة: قد تم إرفاق ملف fcm-cloud-functions-export-Data Structure وهو ملف يحتوي على قاعدة البيانات البسيطة الذي أنشأناها,يمكنك استيرادها من Realtime Database عبر خيار Import Backup (لا تنسَ عمل نسخ احتياطي لقاعدة البيانات لديك قبل تنفيذ عملية الاستيراد) بعض المصادر التي قد تهمك 1,2,3

عالم البرمجة

عالم البرمجة مقالات برمجة و دورات مجانية لإحتراف البرمجة هدفنا تبسيط البرمجة ونشرها بيد الكل بشكل ممتع ومتطور ومحدث بإستمرار لمواكبة جديد تطورات البرمجة الحديثة و المتقدمة بدون مقابل