3llomi

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

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

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

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

5 Neutral

عن العضو 3llomi

  • الرتبه
    مبدع جديد
  1. بدايةً يجب عليك التوجه الى Facebook Developers والتسجيل كمطور عبر حسابك الشخصي في فيسبوك وذلك بالضغط على Register بعد ذلك نختار Yes ثم Register ثم Create App ID بعد ذلك نضع اسم التطبيق والإيميل الخاص بك سيتم إنشاء تطبيق Facebook خاص بك وسيعطيك APP ID الذي سنحتاجه لاحقاً ثم اضغط على Get Started لنبدأ بتجهيز المشروع ثم نختار Android ستظهر لنا هذه الخطوات نبدأ أولاً بإنشاء مشروع جديد على Android Studio وعلى Firebase ونضيف مكتبةFirebase Auth compile 'com.google.firebase:firebase-auth:10.2.1' ونقوم بربط Firebase ب مشروع Android بعد ذلك نقوم بإضافة Facebook SDK ,ولذلك علينا إضافة mavenCentral قبل dependencies repositories { mavenCentral() } ثم نضيف Facebook SDK في dependencies ثم نعمل Sync ل Gradle compile 'com.facebook.android:facebook-android-sdk:[4,5)' بعد ذلك ننتقل الى الخطوة الثالثة ونضيف اسم package الخاص بمشروع الأندرويد ونضيف اسم الأكتفتي الإفتراضي الذي سيعمل في التطبيق ,في حالتي هو MainActivity ونضيف قبله اسم package ثم . واسم الأكتفتي بعد ذلك ستظهر لك رسالة تحذير بأنه لا يوجد تطبيق بهذا الاسم موجود على متجر Google Play Store,لا عليك اختر Use this package name بعد ذلك سنحتاج الى معرف يدعى Key Hash ولتوليد هذا المعرف يمكنك إما نسخ الأمر الموجود في Facebook Quick Start أو يمكنك إستخدام هذه الأداة التي صنعتها لتسهيل الموضوع ومتوفرة لأنظمة 32 و 64 بت, فقط قم بتشغيل الملف التنفيذي وإختر نوع الKey Hash (هل تريد صنعه ل Debug أم ل Release) عند تشغيل ملف Debug سيطلب منا رمز Keystore قم بإدخال أي رقم أو قم بكتابة android فهي الكلمة الإفتراضية أما عند تشغيل ملف Release فسيطلب منك رمز Alias الذي وضعته عند إنشاء ملف الشهادة الذي ينتهي بصيغة jks (عند عمل Generate Signed APK في الأندرويد ستوديو) ثم سيطلب منك مسار ملف الشهادة (C:User\Desktop\Cert.jks) على سبيل المثال وسيظهر لك ال Key Hash قم بنسخه والصقه في Facebook ثم قم بتفعيل Single Sign On بعد ذلك سنضيف بعد الأمور في Android Studio نقوم بنسخ App ID ونلصقه في ملف strings.xml بعد ذلك نضيف صلاحيات الإنترنت في Android Manifest <uses-permission android:name="android.permission.INTERNET"/> ثم نضيف Meta-Data قبل إغلاق وسم application <meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/> ثم نضيف أكتفتي المسؤولة عن تسجيل الدخول من Facebook (موجودة ضمن Facebook SDK) <activity android:name="com.facebook.FacebookActivity" android:configChanges= "keyboard|keyboardHidden|screenLayout|screenSize|orientation" android:label="@string/app_name" /> ونضيف أكتفتي أخرى في حال كان المستخدم لم يقم بتثبيت تطبيق Facebook في جهازه وهي موجودة ضمن Facebook SDK أيضاً <activity android:name="com.facebook.CustomTabActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="@string/fb_login_protocol_scheme" /> </intent-filter> </activity> ونضيف Protocol Scheme في strings.xml (نقوم بنسخه من Facebook Quick Start Guide) الآن نتجه لى activity_main.xml ونضيف زر تسجيل الدخول ثم نتجه الى MainActivity ونعرف بعض المتغيرات FirebaseAuth LoginButton زر تسجيل الدخول CallbackManager المسؤول عن إعادة نتيجة تسجيل الدخول ثم قمنا بإعطائهم قيم في onCreate ثم جعلنا زر LoginButton يقرأ صلاحيات Email بعد ذلك قم بتسجيل Callback عبر ميثود registerCallback وأعطيناه callbackManager كبارامتر وهي تعيد لنا 3 ميثود onSuccess عند نجاح عملية تسجيل الدخول onCancel عند الغاء عملية التسجيل من قبل المستخدم onError عند حدوث خطأ ما وقمنا بعمل Log في كل حالة ثم قمنا بعمل Override ل onActivityResult وقمنا بتسجيل callbackManager في onActivityResult (بدونه لن تعود أي نتيجة ) أما الآن سنقوم بإنشاء ميثود handleFacebookAccessToken التي تقوم بأخذ Access Token من ميثود onSuccess عبر loginResult ونقوم باستدعاء ميثود mAuth.signInWithCredential لحفظ المستخدم في Firebase وقمنا بعمل Log للنتيجة ثم استدعينا الميثود في onSuccess وقمنا بأخذ AccessToken الآن يجب علينا الحصول على App Secret و App Id قم بالتوجه الى إعدادات تطبيق Facebook الى Dashboard وستجد App Secret و App Id قم بنسخهم ثم توجه الى Firebase Console وقم بتفعيل تسجيل الدخول عبر Facebook والصق App Id و App Secret ثم نقوم بنسخ OAuth Uri ثم Save بعد ذلك نتوجه الى إعدادات Facebook Login ونضع OAuth Uri نجرب تشغيل التطبيق وعند الضغط على زر Login ,اذا كان المستخدم لم يثبت تطبيق Facebook فسيظهر له بهذا الشكل أما اذا قام بتثبيته فسيظهر بهذا الشكل عموماً نجرب التشغيل وعند نجاح العملية ستجد أن المستخدم قد تم تسجيله في Firebase وسيتغير نص الزر الى Log out اذا كنت قد جربت عملية تسجيل الدخول على نفس حساب المطور فستنجح عملية التسجيل,أما اذا قمت بتجريبه على حساب آخر غالباً ستفشل عملية التسجيل وذلك لأنه التطبيق الذي أنشأته ليس Public ولحل هذه المشكلة قم بالتوجه الى إعدادات التطبيق في Facebook في قسم App Review ونقوم بجعله Public رابط المشروع على Github ملاحظة:المشروع على Github للمعاينة فقط ولايمكنك تجربته على Android Studio لعدم وجود google-services.json الخاص بك
  2. ماهو Firebase Auth هي طريقة لربط مستخدمي تطبيقك في Firebase ولها 6 طرق في تسجيل الدخول: Email/Password Google باستخدام حساب Facebook باستخدام حساب Twitter Github Anonymous (تسجيل الدخول بدون إعطاء أي معلومات) تسجيل الدخول باستخدام رقم الهاتف وسنحاول شرح معظم هذه الطرق في الدروس القادمة,أما في هذا الدرس سنشرح أسهل طريقتين لتسجيل الدخول إما باستخدام البريد الإلكتروني أو Anonymous تسجيل الدخول باستخدام Anonymous Login هذه الطريقة لاتطلب من المستخدم إعطاء أي معلومات بل ولن يشعر المستخدم بأنه قد تم تسجيله بالفعل,وتفيد هذه الطريقة في حالة أنك تريد ربط هذا المستخدم وحفظ بيانات اللعبة على سبيل المثال لديك على السيرفر والمستخدم لايريد إعطاء أي معلومات . نبدأ بإنشاء مشروع جديد على Firebase ومشروع جديد في Android Studio ونربط مشروع الAndroid Studio بمشروع الFirebase (كما فعلنا في الدرس السابق) ثم نضيف مكتبة Firebase Auth الى مشروع الأندرويد في (build.gradle:app) ثم نعمل Sync compile 'com.google.firebase:firebase-auth:10.2.1' ثم نذهب الى Firebase Console ونفعل Anonymous بعد ذلك نذهب الى MainActivity ثم نعرف اوبجكت من FirebaseAuth ونسميه mAuth ونعطيه قيمة في onCreate ثم ننشئ ميثود signInAnonymously ونستدعي الميثود في onCreate داخل هذه الميثود استخدمنا ميثود من FirebaseAuth اسمها signInAnonymously وأضفنا لها addOnCompleteListener وهي interface تعيد لنا نتيجة عملية تسجيل الدخول وتعيد لنا task وقمنا بعمل log لنتيجة عملية التسجيل,وإذا كانت العملية غير ناجحة(!Succesful) نقوم بإظهار رسالة Toast نجرب تشغيل التطبيق وإذا نجحت العملية سنجد true في logcat نتوجه الى Firebase Console داخل Authentication ثم Users وسنجد أنه تم تسجيل مستخدم جديد وتظهر لنا User UID وهو ID ينشئ لكل مستخدم موجود على Firebase تسجيل الدخول باستخدام بريد الكتروني Email & Password بدايةً يجب علينا تفعيل تسجيل الدخول باستخدام البريد الإلكتروني من Firebase Console بعد ذلك في activity_main.xml سننشئ 2EditTexts الأول للبريد الإلكتروني والثاني لكلمة المرور بالإضافة الى زر LOGIN لتسجيل الدخول وأخيراً textView للإنتقال الى أكتفتي تسجيل حساب جديد Signup نبدأ أولاً بعملية تسجيل حساب جديد ونقوم بإنشاء أكتفتي جديد لهذا الأمر وسيكون نفس الأكتفتي السابق ولكن بدل زر LOGIN سيكون SIGNUP ونغير عنوان textView للانتقال الى أكتفتي تسجيل الدخول. ثم داخل أكتفتي SignupActivity ننشئ ميثود SignupUser ونستدعيها عند الضغط على زر SignupBtn ونعطيه قيمة الEditTexts. داخل ميثود SignupUser استخدمنا ميثود من Firebase Auth اسمها createUserWithEmailAndPassword وبالطبع تأخذ 2 بارامتر email و password ثم أضفنا ميثود addOnCompleteListener التي تعيد لنا نتيجة العملية وقمنا بالتحقق اذا تمت العملية بنجاح أم لا بنفس فكرة المثال السابق نجرب تشغيل التطبيق ونضع أي إيميل وأي باسوورد ونضغط على Signup وإذا نجحت العملية سنرى رسالة Toast User Created نذهب الى Firebase Console وسنجد أنه تم إنشاء حساب جديد ننتقل الآن الى عملية تسجيل الدخول بحساب حالي(الحساب الذي أنشأناه) في أكتفتي MainActivity وننشئ ميثود SignInUser يأخذ نفس البارامترات ونفس onCompleteListener ولكن هذه المرة signInWithEmailAndPassword إذا نجحت عملية تسجيل الدخول سنرى رسالة Toast معرفة هل تم تسجيل الدخول باستخدام Firebase Auth State Listener في بعض الأحيان عند تشغيل التطبيق نريد معرفة هل تم تسجيل الدخول أم لا (فمثلا إذا تم تسجيل الدخول قم بتشغيل أكتفتي جديد وإلا أظهر أكتفتي تسجيل الدخول..) في MainActivity نعرف اوبجكت من FirebaseAuth.AuthStateListener وننشئ ميثود جديد اسمه initAuthStateListener ونستدعيه داخل onCreate ,هذا الميثود يقوم بتعريف mAuthListener . الmAuthListener تعيد لنا firebaseAuth, الذي يحتوي على ميثود getCurrentUser والتي بدورها تقوم بجلب المستخدم الحالي ,ثم نتحقق اذا كان user لا يساوي null (بالتالي المستخدم قد قام بتسجيل الدخول )عندها نقوم بعمل log ل uid الuser وإلا else المستخدم لم يقم بتسجيل الدخول او قد قام بتسجيل الخروج أخيراً نقوم بعمل override ل onStart و onStop , ونضيف mAuthListener ل mAuth في onStart وفي onStop نقوم بإزالة هذا Listener نجرب تشغيل التطبيق وإذا كان المستخدم قد قام بتسجيل الدخول عندها ستظهر الرسالة في logcat مع uid. تُوفر Firebase أيضاً ميزة تفعيل البريد الإلكتروني(إرسال رسالة تأكيد من Firebase الى البريد الذي تم إدخاله) وإستعادة كلمة المرور في حال نسيانها وتغيير البريد الإلكتروني وإمكانية تخصيص شكل الرسالة ,يمكنك تفقدها في خانة Email Templates في Firebase Console. هذه كانت مقدمة بسيطة عن Firebase Authentication وسنحاول التطرق لها أكثر في الدروس القادمة المشروع كاملاً على Github ملاحظة:المشروع على Github للمعاينة فقط ولايمكنك تجربته على Android Studio لعدم وجود google-services.json الخاص بك
  3. ال Recycler View هو طريقة لعرض البيانات على شكل قائمة سواء كانت أفقية او عمودية او على شكل "شبكي Grid" ويعتبر Recycler View أسرع وأخف على الذاكرة من List View حيث يتم عمل Recycler او إعادة تدوير للعناصر كلما قمنا بعمل Scroll لليست.. بداية يجب علينا وضع Recycler View في المشروع الخاص بنا فهي ظهرت حديثا ولا تتوفر ضمن Android SDK بشكل افتراضي وبالتالي علينا إضافتها بشكل يدوي نذهب الى ملف "build.gradle" (app) ونضيف السطر التالي بالنسبة لي فإني أستخدم Build Tools قديم وبالتالي سأضع هذا السطر compile 'com.android.support:design:24.1.1' أما اذا كنت تستخدم Build Tools حديث (25 على سبيل المثال) فضع هذا السطر compile 'com.android.support:design:25.1.0' ثم اضغط على Sync Now لإضافة المكتبة سنقوم في هذا المشروع إنشاء قائمة بأجهزة "نيكسس Nexus" من الأحدث الى الأقدم وسنضع التالي: اسم الجهاز الشركة المصنعة صورة الجهاز وعند الضغط على عنصر معين سيتم الانتقال الى اكتفتي جديد يحتوي على نفس بيانات العنصر الذي تم الضغط عليه سنبدأ بتصميم Row والذي سيكون الشكل الذي سيظهر عليه كل عنصر نقوم بإنشاء ملف xml على سبيل المثال rv_row ونقوم بوضع به 2 TextView وواحد ImageView ونقوم بوضع الصور في مجلد drawable الآن سنقوم بتجهيز الداتا او البيانات ليتم عرضها ولذلك سنقوم بإنشاء كلاس Model او يسميه البعض ب (POJO (Plain Old Java Objectهذا الكلاس يمكن ان نقول عنه انه عبارة عن علبة فارغة سنضع به بعض المتغيرات ومن ثم نقوم بإنشاء Getters وهي وظيفتها أخذ القيمة و Setters ووظيفتها وضع القيمة وهذا سيسهل علينا بشكل كبير والجميل في الموضوع أنه يمكننا وضع أكثر من نوع متغير String int boolean الخ.. نقوم بإنشاء كلاس جافا عادي نسميه Mobile مثلا سيكون كلاس فارغ بداخله سنقوم بتعريف المتغيرات التالية اسم الموبايل mobileName من نوع String اسم الشركة mobileManufacture من نوع String صورة الموبايل mobile image من نوع int (سيتم استدعاؤه من مجلد drawable) سنقوم بإنشاء ال getters & setters في نفس الكلاس تحت المتغيرات نقوم بالضغط على زر Alt+Insert ستظهر لنا قائمة نحتار Getter and Setter بعد ذلك نختار ال 3 متغيرات ثم OK الآن تم إنشاء Getter and Setter جميل جداً الآن عندما نريد وضع اسم الموبايل على سبيل المثال سنستدعي الميثود setMobileName وهكذا وعندما نريد جلب القيمة نستدعي الميثود getMobileName . إنتهينا من كلاس Model نعود لل MainActivity نقوم بإنشاء List ولكن هذه المرة ليست List<String> بل List<Mobile> ثم نقوم بإنشاء اوبجكت من كلاس Mobile الذي أنشأناه لنستطيع وضع القيم داخله الآن نبدأ بوضع القيم داخل mobile,وعند كتابة mobile. ستبدأ setters و getters بالظهور نبدأ بوضع البيانات ونكرر نفس العملية على باقي العناصر ولكن نغير اسم mobile الى mobile1 mobile2 وهكذا.. ثم نبدأ بإضافة هذه mobile الى الليست الآن نقوم بإنشاء Adapter ليقوم بتحويل هذه البيانات ل Recycler View . نقوم بإنشاء كلاس جديد نسميه MobileAdapter مثلا وفي داخله سنقوم بإنشاء كلاس نسميه MobileHolder وهو Holder الذي سيكون مسؤول عن TextView و ImageView ونجعله extends RecyclerView.ViewHolder سنجد أنه يوجد خطأ نقوم بالضغط على Alt+Enter ثم اختيار Create Constructor matching super وسيقوم بإنشاء Constuctor بشكل اوتوماتيكي. نعود الى كلاس MobileAdapter ونجعله extends RecyclerView.Adapter<MobileAdapter.MobileHolder> ملاحظة: اذا قمت بتسميه الكلاس باسم ثاني سيظهر لك خطأ تأكد من كتابة الاسم الكلاس الذي أنشأته سنجد خطأ نقوم بالضغط على Alt+Enter ونختار Implement methods وسيبدأ بإنشاء ال methods الخاصة ب Recycler View داخل MobileAdapter نقوم بإنشاء List<Mobile> و Context والذي سيتم استدعائهما عند استدعاء Adapter من MainActivity ثم نضغط على Alt+Insert لإنشاء Constructor نختار العنصرين ثم OK نذهب الى ميثود onCreateViewHolder ونقوم بإنشاء View ونسميه row ونجعله يقوم بعمل Inflate لملف rv_row.xml الذي أنشأناه سابقاً ونقوم بإنشاء اوبجكت من MobileHolder ونسميه holder وثم نعطيه row وبدلاً من return null نقوم بعمل return ل holder نذهب الى ميثود getItemCount وبدلاً من return 0 نجعله return mobilelist.size الليست الذي قمنا بإنشاءها وذلك ليصبح طول RecyclerView بنفس حجم list نعود الى كلاس MobileHolder نقوم بربط TextView و ImageView مع ملف xml عبر findViewById الآن داخل onBindViewHolder نقوم بإنشاء mobile ونعطيه list ونعطي position الخاص ب onBindViewHolder وبالتالي نستطيع التحكم بعناصر mobile Mobile mobile = mobileList.get(position); أخيراً نقوم بوضع الداتا ل TextView و ImageView عبر setText و setImageResource وكما نرى استخدمنا getters لنقوم بأخذ الداتا من list انتهينا من Adapter نعود ل activity_main.xml وننشئ recyclerView ونعطيه id على سبيل المثال rv و نقوم بربطها ب MainActivity ثم سنقوم باستدعاء Adapter ونعطيه list و this للكونتيكست نلاحظ أنه يطلب نفس العنصرين الذي قمنا بإنشاءهم في Adapter (عند إنشاء constructor) recyclerView يحتاح LayoutManager وهو الذي سيحدد طريقة عرض ال RecyclerView (بشكل افقي او عمودي او شبكي) نقوم بإعطاءه new LinearLayoutManager(this) بهذا الشكل ستكون طريقة العرض بشكل عمودي بعد ذلك نقوم بإعطاء adapter ل RecyclerView عبر setAdapter أخيراً نقوم بتشغيل التطبيق وسيظهر بهذه النتيجة إظهار بيانات عنصر تم الضغط عليه في أكتفتي ثاني الآن نريد عندما يتم الضغط على أي موبايل أن يتم توجيهنا الى أكتفتي آخر يحتوي على صورة الجهاز واسمه واسم الشركة المصنعة بشكل واضح ومفصل نقوم بإنشاء أكتفتي جديد نسميه DetailsActivity وداخل xml نقوم بإنشاء 2 TextView وواحد ImageView ونقوم بربطهم في ملف الجافا نعود ل rv_row.xml ونعطي id ل RelativeLayout وذلك لنستطيع الضغط على العنصر كاملاً دون ان نضغط على textView او ImageView ونقوم بتعريفه داخل MobileHolder في MobileAdapter الآن عند الضغط على relativeLayout يتم إرسال اسم الموبايل + اسم الشركة + الصورة عبر intent ووضعنا key لكل عنصر ليتم استقبالهم في الأكتفتي الآخر . أخيراً نقوم بتشغيل الأكتفتي نعود الى DetailsActivity لنستقبل البيانات عبر intent نقوم بتخزين النصوص على شكل String ونقوم بتخزين الصورة على شكل int Intent intent = getIntent(); String theName = intent.getStringExtra("name"); String theManufacture = intent.getStringExtra("manufacture"); int theImg = intent.getIntExtra("img", 0); أخيراً نقوم بوضع البيانات في TextView و ImageView mobileName.setText(theName); mobileManufacture.setText(theManufacture); mobileImg.setImageResource(theImg); النتيجة الكود على Github
  4. نبدأ أولاً بإنشاء مشروع جديد على Firebase Console ومشروع جديد على Android Studio ونقوم بربطهم (كما فعلنا في درس Firebase Real-time Database) ثم نضيف مكتبة Firebase Auth في مشروع الأندرويد compile 'com.google.firebase:firebase-auth:10.2.1' ونضيف مكتبة Google compile 'com.google.android.gms:play-services-auth:10.2.1' ونعمل Sync لGradle وبالطبع نُفعل خيار Google من Firebase Console أما الآن فإننا نحتاج لشيئ يُسمى SHA1 Fingerprint لربطه في Firebase مع تطبيقنا,ويمكنك الحصول عليه عبر تطبيق الخطوات في هذا الشرح أو اذا كنت تستخدم الويندوز يمكنك تحميل هذه الأداة الصغيرة التي صنعتها والتي تسهل عليك الأمر بعد تحميل الأداة وفك الضغط عنها سنجد هذه الملفات مايهمنا هو الملفات Genreate Debug Key Google و Genreate Release Key Google الDebug يقوم بتوليد SHA1 اذا كنت ضمن مرحلة التطوير نقوم بفتح الملف ونرى ان الأداة تطلب keystore password والتي عادةً تكون "android" وسنرى أنه تم توليد SHA1 الRelease عندما تقوم بعمل Release للتطبيق(عندما تقوم بتوليد ملف وشهادة بصيغة jks) نقوم بفتح الملف وسنجد انه سيطلب alias الذي قد قمت بوضعه عندما قمت بعمل Generate Signed APK بعد ذلك سيطلب منك مسار ملف key الذي ينتهي بصيغة jks ثم الباسوورد بعد ذلك سيتم توليد الSHA1 قم بنسخه ثم توجه الى Firebase Console ثم اختر Project Settings وانزل للأسفل واختر Add Fingerprint ثم الصق SHA1 الذي نسخته واختر Save بعد ذلك عليك الذهاب لموقع Google Developers Console للحصول على ClientID سنضعه لاحقاً في مشروع الأندرويد (تأكد من أنه نفس اسم المشروع الذي تعمل عليه في Firebase) ستجد في الأسفل Web Client قم بنسخ Client ID بنوع Web Application (سنحتاجه لاحقاً) أما الآن نعود الى Android Studio الى activity_main.xml ونضيف زر من نوع SignInButton وهو زر عادي ولكن بشكل جميل وعليه شعار Google بعد ذلك في MainActivity.java سنقوم بتعريف بعض المتغيرات mAuth شرحناها سابقاً GoogleApiClient مهمته إظهار حسابات Google الموجودة في جهاز الأندرويد SigninButton زر تسجيل الدخول في activity_main.xml RC_SIGN_IN وهو متغير من نوع int عادي سنستخدمه لاحقاً قيمته 0 (يمكنك وضع أي قيمة تريدها) ثم داخل onCreate أعطينا قيمة ل mAuth وعرفنا اوبجكت من GoogleSignInOptions,هذه الميثود مهمتها وضع الإعدادات ليعرف GoogleApiClient ماذا نريد منه, حيث أن Google API Client يحتوي على الكثير من APIs منها Drive API و Google Play Games API الخ.. ثم أعطيناه IDToken (الذي نسخناه سابقاً من Google Developers Console) وطلبنا إظهار Email (RequestEmail) بعد ذلك علينا تعريف GoogleApiClient ونعطيه gso الذي قمنا بتعريفه أخيراً نربط SignInButton وعند الضغط عليه نستدعي ميثود signIn التي سننشأها الآن داخل ميثود SignIn نقوم بتعريف Intent خاص من Auth ونعطيه mGoogleApiClient ,هذا الأكتفتي هو الذي يظهر حسابات Google الموجودة في الجهاز ثم نشغل الأكتفتي ولكن بدلاً من startActivity سنستدعي startActivityForResult ونضع signInIntent و RC_SIGN_IN لماذا وضعنا startActivityForResult؟ ببساطة لأننا نريد أن نعرف نتيجة تشغيل هذا الأكتفتي ,على سبيل المثال هل تم اختيار حساب Google أم أن المستخدم قد رفض هذا الأمر ولمعرفة النتيجة نقوم بعمل Override ل onActivityResult ,هذه الميثود تقوم بإعادة النتيجة وليست فقط ل Google Auth ,فعلى سبيل المثال يمكنك عمل startActivityForResult اذا كنت تريد مثلاً من المستخدم اختيار صورة من الجهاز وبالتالي تريد معرفة هل تم اختيار هذه الصورة أم لا وللتميز بين results نستخدم رقم int ونقوم بعمل if اذا كان request code == RC_SIGN_IN عندها سنقوم بإنشاء GoogleSignInResult ونعطيه data وإذا كانت result.isSuccess ناجحة أي أنه تم اختيار حساب من قبل المتسخدم عندها سنقوم بإنشاء GoogleSignInAccount ونأخذه من result. الى هذه اللحظة لم يتم تسجيل الحساب في Firebase ولذلك سنستدعي ميثود firebaseAuthWithGoogle ونعطيه account أما الآن سنقوم بإنشاء ميثود firebaseAuthWithGoogle وهي تأخذ GoogleSignInAccount ك بارامتر ثم عرفنا AuthCredential وقمنا بإعطاءه الtoken الخاص بالحساب عبر ميثود acct.getIdToekn أخيراً استعملنا mAuth.signInWithCredential وأضفنا CompleteListener لمعرفة نتيجة العملية أما الآن حان وقت التجربة :D نشغل التطبيق ونضغط على زر SignIn سيتم إظهار نافذة تطلب إختيار حساب كما في الصورة وإذا تمت العملية بنجاح سنجد النتيجة في Logcat سنذهب ل Firebase Console لنرى هل تم التسجيل بنجاح بالطبع يمكنك معرفة هل تم تسجيل الدخول من قبل المستخدم أم لا عبر AuthStateListener كما فعلنا في الدرس السابق المشروع على Github ملاحظة:المشروع على Github للمعاينة فقط ولايمكنك تجربته على Android Studio لعدم وجود google-services.json الخاص بك
  5. شكرا لك على الطرح الرائع ،ولكن عندي سؤال يخص المثال الذي ذكرته. اذا قمنا بنسخ نفس الميثود التي تتحقق من الإتصال بالإنترنت وضعناها في Util Class وجعلنا هذه الميثود static بدون ان نطبق Singleton ،في هذه الحالة ما الفرق الذي سيكون مع وجود Singleton وبدون Singleton ؟
  6. ماهو Firebase Auth هي طريقة لربط مستخدمي تطبيقك في Firebase ولها 6 طرق في تسجيل الدخول: Email/Password Google باستخدام حساب Facebook باستخدام حساب Twitter Github Anonymous (تسجيل الدخول بدون إعطاء أي معلومات) تسجيل الدخول باستخدام رقم الهاتف وسنحاول شرح معظم هذه الطرق في الدروس القادمة,أما في هذا الدرس سنشرح أسهل طريقتين لتسجيل الدخول إما باستخدام البريد الإلكتروني أو Anonymous تسجيل الدخول باستخدام Anonymous Login هذه الطريقة لاتطلب من المستخدم إعطاء أي معلومات بل ولن يشعر المستخدم بأنه قد تم تسجيله بالفعل,وتفيد هذه الطريقة في حالة أنك تريد ربط هذا المستخدم وحفظ بيانات اللعبة على سبيل المثال لديك على السيرفر والمستخدم لايريد إعطاء أي معلومات . نبدأ بإنشاء مشروع جديد على Firebase ومشروع جديد في Android Studio ونربط مشروع الAndroid Studio بمشروع الFirebase (كما فعلنا في الدرس السابق) ثم نضيف مكتبة Firebase Auth الى مشروع الأندرويد في (build.gradle:app) ثم نعمل Sync compile 'com.google.firebase:firebase-auth:10.2.1' ثم نذهب الى Firebase Console ونفعل Anonymous بعد ذلك نذهب الى MainActivity ثم نعرف اوبجكت من FirebaseAuth ونسميه mAuth ونعطيه قيمة في onCreate ثم ننشئ ميثود signInAnonymously ونستدعي الميثود في onCreate داخل هذه الميثود استخدمنا ميثود من FirebaseAuth اسمها signInAnonymously وأضفنا لها addOnCompleteListener وهي interface تعيد لنا نتيجة عملية تسجيل الدخول وتعيد لنا task وقمنا بعمل log لنتيجة عملية التسجيل,وإذا كانت العملية غير ناجحة(!Succesful) نقوم بإظهار رسالة Toast نجرب تشغيل التطبيق وإذا نجحت العملية سنجد true في logcat نتوجه الى Firebase Console داخل Authentication ثم Users وسنجد أنه تم تسجيل مستخدم جديد وتظهر لنا User UID وهو ID ينشئ لكل مستخدم موجود على Firebase تسجيل الدخول باستخدام بريد الكتروني Email & Password بدايةً يجب علينا تفعيل تسجيل الدخول باستخدام البريد الإلكتروني من Firebase Console بعد ذلك في activity_main.xml سننشئ 2EditTexts الأول للبريد الإلكتروني والثاني لكلمة المرور بالإضافة الى زر LOGIN لتسجيل الدخول وأخيراً textView للإنتقال الى أكتفتي تسجيل حساب جديد Signup نبدأ أولاً بعملية تسجيل حساب جديد ونقوم بإنشاء أكتفتي جديد لهذا الأمر وسيكون نفس الأكتفتي السابق ولكن بدل زر LOGIN سيكون SIGNUP ونغير عنوان textView للانتقال الى أكتفتي تسجيل الدخول. ثم داخل أكتفتي SignupActivity ننشئ ميثود SignupUser ونستدعيها عند الضغط على زر SignupBtn ونعطيه قيمة الEditTexts. داخل ميثود SignupUser استخدمنا ميثود من Firebase Auth اسمها createUserWithEmailAndPassword وبالطبع تأخذ 2 بارامتر email و password ثم أضفنا ميثود addOnCompleteListener التي تعيد لنا نتيجة العملية وقمنا بالتحقق اذا تمت العملية بنجاح أم لا بنفس فكرة المثال السابق نجرب تشغيل التطبيق ونضع أي إيميل وأي باسوورد ونضغط على Signup وإذا نجحت العملية سنرى رسالة Toast User Created نذهب الى Firebase Console وسنجد أنه تم إنشاء حساب جديد ننتقل الآن الى عملية تسجيل الدخول بحساب حالي(الحساب الذي أنشأناه) في أكتفتي MainActivity وننشئ ميثود SignInUser يأخذ نفس البارامترات ونفس onCompleteListener ولكن هذه المرة signInWithEmailAndPassword إذا نجحت عملية تسجيل الدخول سنرى رسالة Toast معرفة هل تم تسجيل الدخول باستخدام Firebase Auth State Listener في بعض الأحيان عند تشغيل التطبيق نريد معرفة هل تم تسجيل الدخول أم لا (فمثلا إذا تم تسجيل الدخول قم بتشغيل أكتفتي جديد وإلا أظهر أكتفتي تسجيل الدخول..) في MainActivity نعرف اوبجكت من FirebaseAuth.AuthStateListener وننشئ ميثود جديد اسمه initAuthStateListener ونستدعيه داخل onCreate ,هذا الميثود يقوم بتعريف mAuthListener . الmAuthListener تعيد لنا firebaseAuth, الذي يحتوي على ميثود getCurrentUser والتي بدورها تقوم بجلب المستخدم الحالي ,ثم نتحقق اذا كان user لا يساوي null (بالتالي المستخدم قد قام بتسجيل الدخول )عندها نقوم بعمل log ل uid الuser وإلا else المستخدم لم يقم بتسجيل الدخول او قد قام بتسجيل الخروج أخيراً نقوم بعمل override ل onStart و onStop , ونضيف mAuthListener ل mAuth في onStart وفي onStop نقوم بإزالة هذا Listener نجرب تشغيل التطبيق وإذا كان المستخدم قد قام بتسجيل الدخول عندها ستظهر الرسالة في logcat مع uid. تُوفر Firebase أيضاً ميزة تفعيل البريد الإلكتروني(إرسال رسالة تأكيد من Firebase الى البريد الذي تم إدخاله) وإستعادة كلمة المرور في حال نسيانها وتغيير البريد الإلكتروني وإمكانية تخصيص شكل الرسالة ,يمكنك تفقدها في خانة Email Templates في Firebase Console. هذه كانت مقدمة بسيطة عن Firebase Authentication وسنحاول التطرق لها أكثر في الدروس القادمة المشروع كاملاً على Github ملاحظة:المشروع على Github للمعاينة فقط ولايمكنك تجربته على Android Studio لعدم وجود google-services.json الخاص بك
  7. شكراً لدعمك, سأحاول مستقبلاً ان شاء الله
  8. ماهي 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 الخاص بك
  9. ماهي 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 الخاص بك
  10. ماهي 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
  11. ماهي 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

عالم البرمجة

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