جميع الأنشطة

يتم تحديث محتوى هذا السجل تلقائيا   

  1. أمس
  2. السلام عليكم عملت بال HTML <html> <body> <form action = "http://localhost:27017/login"method = "POST"> <p>Name:</p> <p><input type = "text" name = "username" placeholder="Username"/></p> <p>Phone No.:</p> <p><input type="tel" Phone= "Phone" placeholder="Phone"/></p> <p><input type = "submit" value = "submit"/></p> </form> </body> </html> و بال PyMongo هنا حطيت الداتا from pymongo import MongoClient if __name__ == '__main__': client = MongoClient('localhost', 27017) db = client['mongoDatabaseDemo'] collection_user = db['login'] new_login = collection_user.insert_many( [ { 'name':'Ali', "Phone No": '0551873725' }, { "name":"Ahmad", "Phone No": '0566138249' }, { 'name':'Mohammed', "Phone No": '0505274618' }, { "name":"Hadeel", "Phone No": '0504726381' } ] ) والمشكله هنا في ال Python Code from flask import Flask, redirect, url_for, request app = Flask(__name__) @app.route('/success/<name>') def success(name,Phone): return 'welcome %s' %name @app.route('/login',methods = ['POST', 'GET']) def views(): if request.method == 'POST': name = request.form['name'] Phone = request.form['Phone'] return redirect(url_for('success',name = name, Phone = Phone)) else: name = request.args.get('name') Phone = request.args.get('Phone') return redirect(url_for('success',name = name, Phone = Phone)) if __name__ == '__main__': app.run(debug = True) اللي ابغا اسويهه انو ادخل الاسم ورقم الجوال المشكله اني جديده على البايثون ف ماعرفت ايش الغلط
  3. طلبك موجود برجاء التواصل معى او ترك رقمك للتواصل للتعرف على كافة الخدمات والبرامج الجاهزة برجاء التواصل معنا على الارقام التالية : أ / نايف النفيعى 0551199722 أ/ محمد محروس 0545605600 # جنا_لنظم_المعلومات # تصميم_البرامج_الادارية_والمحاسبية # تصميم_مواقع_الانترنت # تصميم_تطبيقات_الجوال # تصميم_اي_برنامج_يتم_طلبه_خصيصا
  4. الاسبوع الماضي
  5. السلام عليكم ورحمة الله وبركاته، سنلقي اليوم نظرة سريعة لأهم أدوات المطورين المرفقة مع متصفحي Chrome و Firefox، وينطبق هذا الشرح على أغلب المتصفحات التي تستخدم Webkit أو Gecko. يمكنك فتحها في كروم وفايرفوكس بالضغط على F12 أو Ctrl+Shift+I أولا: Inspect elements. هذه الخاصية تُظهر لك عناصر HTML في قائمة بحيث يمكنك توسيع العنصر الأب لرؤية العناصر الأبناء، وبمجرد الضغط على أي عنصر، ستظهر لك جميع خصائص CSS المطبقة على ذلك العنصر سواء كانت inline-style أو في ملف css منفصل، أو بداخل <style> إضافة إلى الخصائص الموروثة عن العناصر ذات المستوى الأعلى، ومن أهم ما يميز هذه الخاصية إمكانية تعديل وإضافة وحذف أي عنصر من عناصر HTML أو أي خاصية CSS مطبقة على ذلك العنصر بحيث تظهر التعديلات مباشرة على المتصفح وتختفي بمجرد تحديث الصفحة، وتفيد هذه الخاصية بشكل كبير أثناء التصميم، فبإمكانك تجربة عدة أشكال أو أنماط للعنصر دون الحاجة لتعديل الكود الأصلي أو تحديث الصفحة كلما عدلت شيئا، وبمجرد وصولك للشكل الذي تريد كل ما عليك فعله هو نسخ الخصائص التي أضفتها ولصقها في مشروعك. ويمكنك بالضغط على زر computed (الموجود فوق خصائص css في المتصفحين) مشاهدة مقياس العوامل المؤثرة في حجم العنصر (margin/padding/border/size) بشكل منظم كما في الصورة: ولتسهيل الأمر وتخفيف العناء في البحث عن عنصر HTML الذي تريد اختياره وفرت لك أدوات المطورين هذه الأداة : وبمجرد الضغط عليها يمكنك اختيار العنصر من الصفحة مباشرة دون الحاجة للبحث في كود HTML. ثانيا: اختبار توافقية التصميم مع مختلف أحجام الشاشات. يمكنك الوصول إلى هذه الخاصية بالضغط على ctrl+shift+m في المتصفحين أو من خلال الضغط على الأيقونة التي على شكل هاتف محمول. ولعمل تصميم متجاوب مع جميع الأجهزة بسرعة وسهولة يمكنك استخدام أطر عمل جاهزة، تعرف على أفضل أطر العمل : ثالثا: Console. هي أداة مفيدة لمبرمجي جافاسكربت، حيث أنها مساحة تُظهر الأخطاء البرمجية في جافاسكربت، أو بعض المعلومات المهمة التي يتم طباعتها باستخدام جافاسكربت، إضافة إلى إمكانية كتابة أكواد جافاسكربت وتنفيذها مباشرة على الصفحة (في الــ console الخاص بفايرفوكس توجد خصائص أكثر حيث يمكنك فلترة النتائج لعرض نتيجة أي XHR request أو عرض المشاكل الموجودة في css) مقدمة في جافاسكربت : رابعا: Networking. هذه الأداة من أهم الأدوات أثناء اختبار الموقع حيث ستظهر لك جميع الاتصالات التي تقوم بها الصفحة، فالصفحة تتصل بعدد من ملفات CSS و JS إضافة إلى طلبات XHR ، ويمكنك عرض وفلترة جميع هذه الطلبات فيمكنك إظهار Requests Headers و Response Headers إضافة إلى معرفة السرعة التي تم بها معالجة الطلب مما سيمكنك من معرفة الملفات أو الطلبات التي تأخذ وقتا أطول ومحاولة معالجة الأمر. هنا شرح لأدوات Networking في متصفح كروم: وهذه هي أدوات Networking في فايرفوكس: نلاحظ وجود خاصية باسم Throttling في كروم، هذه الخاصية مفيدة جدا، من خلالها بإمكانك اختيار سرعة اتصال الإنترنت لديك، تفيد هذه الخاصية في محاكاة عمل الموقع لدى أصحاب الاتصال الضعيف، إضافة إلى إمكانية اختبار الأمور التي تتم بشكل سريع عندك، مثل عرض كلمة Loading مثلا أو شيء متحرك يدل على تحميل مكونات الصفحة، غالبا ستتم بشكل سريع جدا لديك ولن تلاحظها ولن تعلم إذا كانت تعمل بشكل جيد لأنك تشغل الموقع بشكل محلي فسيكون تحميل الصفحات سريعا جدا، فتساعد هذه الخاصية على اختبار هذه الأمور التي قد لا تلاحظها بالعادة إلا بعد رفع الموقع إلى السيرفر. نلاحظ أن الخاصية غير موجودة في قسم Networking في فايرفوكس، لكنهم أدرجوها ضمن أداة اختبار التوافقية مع أحجام الشاشات كما في الصورة : بالطبع هذا ليس شرحا كاملا لأدوات المطورين في المتصفحات، لكنني أحببت أن ألقي الضوء على أهم الأدوات المستخدمة بكثرة، يمكنك الاطلاع أكثر على الأدوات الخاصة بكروم من هذه الروابط: https://developer.chrome.com/devtools https://developers.google.com/web/tools/chrome-devtools/ وهنا مراجع لأدوات المطورين في فايرفوكس: https://developer.mozilla.org/son/docs/Tools https://developer.mozilla.org/en-US/docs/Tools/Tools_Toolbox
  6. أهلا وسهلاً بكم في لمحة مبسطة عن الــ BroadcastReceiver ، سأتكلم في هذي المقالة عن تعريف الـ BroadcastReceiver و طريقة اضافتها لبرنامجك .. تنويه : اللغة المستخدمة في الشرح هي kotlin ماهو BroadcastReceiver ؟ هو عبارة عن أرسال و استقبال بين البرنامج و النظام ، عند حدوث event معين يكون معرف مسبقا في النظام او يتم تعريفه من قبل المبرمج . في البداية دعنا نلقي نظرة على المعرفة مسبقا في النظام - بعض منها : Battery Low WI-Fi connected BATTERY_OKAY Incomming SMS AIRPLANE_MODE BATTERY_CHANGED ACTION_POWER_CONNECTED الأن عندما تريد إنشاء حدث معين أنت أمام خيارين هما : statically BroadcastReceiver * هو اضافة حدث في ملف الـــ AndroidMainfest.xml أو Dynamic BroadcastReceiver * تسجيل الحدث بإستخدام ال جافا / الكوتلين داخل ال activity . ملاحظة : ال BroadcastReceiver لا يقبل عمليات تاخذ وقت في تنفيذها مثل استخرج بيانات أو ارسال بيانات او عمل مؤقت .... الخ ، لانه يعمل في ال main thread . لنأخذ مثال بسيطا لعمل برنامج يظهر نص عند الضغط على الزر بإستخدام الــ statically BroadcastReceiver : في البداية دعنا ننشي كلاس يظهر لي رسالة عند ضعط المستخدم على الزر وليكن اسم هذا الكلاس : MybroadcastReceiver class MybroadcastReceiver:BroadcastReceiver() { override fun onReceive(p0: Context?, p1: Intent?) { Toast.makeText(p0!!," Hello form First receiver ",Toast.LENGTH_LONG).show() } } الان ننتقل الى كلاس ال MainActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // send receiver when clicked button btn_sendReceiver.setOnClickListener({ // declared intent and pass MybroadcastReceiver ... var intent = Intent(this,MybroadcastReceiver::class.java) sendBroadcast(intent) }) } الأن في ملف ال AndroidMainfest.xml نقوم بتسجل هذا الحدث : // After activity tag .. <receiver android:name=".MybroadcastReceiver"> </receiver> النتيجة : Pesudo Code 1 - craete subClass extends BroadcastReceiver . 2 -override the onReceiver method . 3- add receiver on AndroidMainfest.xml 4 - create event to send data . 5 - declaerd intent . 6 - sendBroadcastReceiver(intent) . #### عمل ( InnerClass BroadcastReceiver ) بنفس عمل الآلية السابقة ، نحتاج الى كلاس يراث من broadcastReceiver و أكشن يشير الى هذا الكلاس ... هذي المرة سأقوم بتعريف كلاس داخلي يشير الى الأكشن .. MainActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // send receiver when clicked button btn_sendReceiver.setOnClickListener({ // declared intent and put action ... var intent = Intent("send.msg.receiver") sendBroadcast(intent) }) } /***** Start InnerClass **************/ public class MybroadcastInner:BroadcastReceiver(){ override fun onReceive(p0: Context?, p1: Intent?) { Toast.makeText(p0!!," Hello form InnerReceiver receiver ",Toast.LENGTH_LONG).show() } } /***** End InnerClass **************/ } لاحظ انه تم تمرير اكشن - action - في ال intent ، هذا الاكشن سيتم تعريفه في ملف الــ AndroidMainfest.xml ملاحظة : عند كتابة الكود بالجافا تحتاج الى اضافة كلمة static قبل اسم الكلاس الداخلي مثلا : public static class MybroadcastInner extends BroadcastReceiver{} ملف ال AndroidMainfest.xml : // After activity Tag . <receiver android:name=".MainActivity$MybroadcastInner"> <intent-filter> <action android:name="send.msg.receiver"/> </intent-filter> </receiver> لاحظ انه تم تعريف الكلاس الاساسي ثم تم وضع علامة ( $) قبل اسم الكلاس الداخلي لكي يتمكن ملف الاندرويد من التعرف على الكلاس الداخلي ثم بعد ذلك تم إنشاء intent-filter بداخله اكشن - action - لاحظ ان اسم الاكشن هو نفس الاسم الذي تم تمريره لل intent . فعند التنفيذ ستظهر نفس النتيجة . في هذا الجزء تم التعرف على كتابة كلاس داخلي ، و الوصول له من خلال ملف AndroidMainfest.xml تنويه : يمكنك تمرير أكشن الى الــ Intent حتى لو كان ال BroadcastReceiver في كلاس منفصل ، مثل ما عملنا في الجزء السابق . في المقالة القادمة ، سأتحدث عن Dynamic BroadcastReceiver . دمتم بخير .
  7. بسم الله الرحمن الرحيم في إستمرارنا في الحديث عن أهم وأحدث مكتبات منصة الأندرويد سنتحدث اليوم عن واحدة من أشهر المكاتب المستخدمة مؤخراً في بعض التطبيقات المشهورة ومن ضمنها تطبيق Jodel الشهير. هذه المكتبة هي مكتبة Crouton. لنتعرف معاً على هذه المكتبة وما أهميتها وكيف تعمل. مكتبة Crouton هي مكتبة تتيح لك تنبيه المستخدم وإظهار بعض الإشعارات. تشبه في عملها مكتبة Toast الشهيرة ولكنها تختلف عنها بأنها تحل بعض المشكلات المتعلقة بـ Toast. واحدة من أهم مشاكل الـ Toast هي مشكلة out of context وهي بأن Toast تعمل وتظهر بغض النظر عن الـ context أو المضمون. قد تظهر في سياق مختلف تماماً عن المتوقع أي أنه عند الإنتقال لـ Activity أخرى سيستمر إشعار الـ Toast بالظهور كما أنها غير قابلة للتعديل وموحدة الشكل. كل هذه المشاكل من الممكن حلها بمكاتب مختلفة ولكن من أسهل هذه المكاتب هي Crouton. ولكن السؤال .. كيف تعمل ؟ مكتبة Crouton تتيح لك التحكم الكامل بشكل الإشعارات ولونها وخصائصها بما تتناسب مع تطبيقك. تعطيك كبداية 3 أشكال ثابتة إذا أردت الإبقاء على أشكال Crouton دون أي تغيير. هذه الأشكال هي : 1- Alert Notification : باللون الأحمر والخط الأبيض ولمدة 3 ثواني تقريباً 2- Info Notification : باللون الأزرق والخط الأبيض ولمدة 3 ثواني تقريباً 3- Confirm Notification : باللون الأخضر والخط الأبيض ولمدة 3 ثواني تقريباً لنبدأ التطبيق ونرى كيفية إظهار هذه الأشكال. قبل بداية التطبيق وكما جرت العادة سنحتاج إلى إضافة Dependency لملف build.gradle ولكن هذه المرة سنحتاج لتعديل ملفي الـ gradle. بالنسبة لملف build.gradle "project" سنضيف mavenCentral() داخل Block الـ repositories الموجود في buildscript بالنسبة لملف build.gradle "module" سنضيف compile 'de.keyboardsurfer.android.widget:crouton:[email protected]' نقوم بعمل Sync من الأعلى حتى تضاف المكتبة ونستطيع بعدها البدء في العمل. نقوم بإنشاء Layout بسيطة توضح عمل هذه المكتبة. تحتوي هذه الـ Layout على 3 أزرار خاصة بكل style وأيضاً على زر Toast وزر آخر لتوضيح الـ Custom Notification. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingTop="50dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="INFO" /> <Button android:id="@+id/alert" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Alert" /> <Button android:id="@+id/succ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Success" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/toast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Toast" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Custom" /> </LinearLayout> </LinearLayout> صورة توضح الشكل النهائي للـ Layout نذهب الآن للجزء الأهم وهو ملف الـ Java. ملاحظة : سنقوم بتطبيق مفاهيم الـ ButterKnife. لمزيد من المعلومات نرجو زيارة الموضوع التالي: دليلك لأفضل مكاتب الأندرويد - الجزء الثاني - ButterKnife أولاً : سنقوم في البداية بتعريف المتغيرات والـ Buttons @BindView(R.id.alert) Button alert; @BindView(R.id.info) Button info; @BindView(R.id.succ) Button succ; @BindView(R.id.toast) Button toast; @BindView(R.id.custom) Button custom; ثانياً : سنقوم بعمل Bind للمتغيرات داخل onCreate method ButterKnife.bind(this); ثالثاً : سنقوم بإختيار كل زر للقيام بوظيفة معينة. نلاحظ عندما نريد إظهار Crouton أو إشعار لا نحتاج لتعريف أي متغيرات فهي تعمل بنفس طريقة عمل Toast. لإظهار الإشعارات بالأشكال السابقة التي سبق وتحدثنا عنها سنحتاج لتمرير 3 params فقط وهي الـ Context والنص والشكل سواء كان alert – info – confirm والطريقة تبدو مشابهة تماماً لطريقة الـ Toast. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO); } @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", Style.ALERT); } @OnClick(R.id.succ) void clicked3() { Crouton.showText(this, "SUCCESS 3alamPro", Style.CONFIRM); } @OnClick(R.id.toast) void clicked4() { Toast.makeText(getApplicationContext(), "3alamPro", Toast.LENGTH_LONG).show(); } رابعاً : نقوم بإختبار التطبيق ونرى الفرق بين Crouton وبين Toast. وكيف أن Toast تستمر بالظهور حتى عند الخروج من التطبيق ولكن Crouton تختفي وهو المطلوب. ماذا لو أردنا البقاء على هذه الأشكال ولكن نريد تغيير الوقت المحدد مسبقاً. بكل بساطة سنحتاج لعمل Object من كلاس Configuration الموجود مسبقاً داخل مكتبة Crouton. Configuration croutonConfiguration; ومن ثم نعرف الـ Object داخل onCreate method ونعطيه الوقت المطلوب ( الوقت المدخل يقاس بالـ milliseconds والثانية الواحدة تساوي 1000 ميلي ثانية ). croutonConfiguration = new Configuration.Builder().setDuration(1000).build(); // 1 sec الآن سنقوم بتغيير الوقت لواحدة من الإشعارات المعرفة مسبقاً ولكننا سنحتاج لتمرير باراميتر إضافي وهو getTaskId() سنتكلم عليه فيما بعد ولكن في الوقت الراهن قد لا يهمنا كثيراً. بهذا التعديل سيتغير الوقت ليصبح ثانية واحدة فقط بدلاً من 3 ثواني. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO,getTaskId(),croutonConfiguration); } نقوم بالتجربة والمقارنة بين INFO وبين ALERT ونستطيع رؤية الفرق في التوقيت. بعد الإنتهاء من أول قسم سنبدأ في تصميم الـ Notification الخاصة بنا. وطبعاً هذا الأمر متاح بكل سهولة مع Crouton. سنقوم في البداية بتغيير لون الخط ولون خلفية الإشعار فقط. نلاحظ بأننا كنا نمرر باراميتر من نوع Style داخل crouton. الباراميتر Style.INFO عبارة عن ستايل جاهز ولتغييره سنحتاج لعمل Style خاص بنا. لذلك سنحتاج إلى عمل Object من كلاس style وإضافة جميع الخصائص بشكل يدوي. Style style; ونقوم بتعريف الـ Object داخل onCreate method. style = new Style.Builder() .setBackgroundColorValue(Color.parseColor("#000000")) // black .setGravity(Gravity.CENTER_HORIZONTAL) .setConfiguration(croutonConfiguration) .setHeight(100) .setTextColorValue(Color.parseColor("#ffffff")).build(); // white للشرح بشكل أعمق للخصائص : setBackgroundColorValue(Color.parseColor()) نستخدمها لإختيار لون خلفية الإشعار. setGravity نستخدمها لإختيار مكان النص سواء كان في المنتصف أو على اليمين أو على اليسار. setConfiguration نستخدمها لإختيار المدة الزمنية لإظهار الإشعار. (يجب إنشاء Object منفصل كالذي تم إنشاؤه في الخطوات السابقة ) setHeight نحدد من خلالها إرتفاع الإشعار. setTextColorValue نحدد من خلالها لون خط النص داخل الإشعار. ومن ثم ننهي التعريف بـ .build نقوم الآن بتعديل واحدة من الإشعارات المعرفة سابقاً وإختيار الـ style الخاص بنا بدلاً من المعرف مسبقاً. @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", style); } ونرى النتيجة. تعمل بشكل مثالي. سنقوم الان بعمل إشعار مختلف تماماً يتضمن صور ونص وليس نص فقط. لتطبيق هذا الأمر سنحتاج إلى إنشاء Layout جديدة. سنقوم داخل هذه الـ Layout بتصميم شكل الإشعار. نقوم بإنشاء Layout جديدة ونسميها crouton_custom على سبيل المثال. نقوم بتصميم Layout بسيطة تتضمن شعار الموقع فقط. نلاحظ بأن إرتفاع الـ Layout يمثل إرتفاع الإشعار نفسه لذلك إختيار الإرتفاع مهم جداً. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#000000"> <ImageView android:layout_width="100dp" android:layout_height="30dp" android:layout_centerInParent="true" android:src="@drawable/pro"/> </RelativeLayout> </RelativeLayout> الشكل النهائي للـ Layout. للاستمرار بذلك يجب علينا تعريف Object جديد من نوع View وعمل Inflate للـ Layout الخاصة بالإشعار. ومن ثم عمل Crouton جديد. @OnClick(R.id.custom) void clicked5() { View customView = getLayoutInflater().inflate(R.layout.crouton_custom, null); Crouton.show(this,customView); } والنتيجة .. إلى هنا نكون قد وصلنا إلى نهاية الشرح الخاص بالمكتبة. وسنقوم في المواضيع القادمة بإستعراض المزيد من المكتبات الخاصة بمنصة الأندرويد بإذن الله.
  8. ماهي 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
  9. السلام عليكم ورحمة الله وبركاته. سنشرح في هذه المقالة طريقة الاستفادة من منصة Transifex لترجمة مشاريعك ترجمة "بشرية" دقيقة بعيدا عن استخدام المترجمات الآلية. نبذة عن موقع Transifex: هي منصة متكاملة لترجمة المشاريع البرمجية، حيث يطرح المبرمج أو المسؤول عن ترجمة البرنامج ملف الترجمة الخاص بالبرنامج، ويمكنه تعيين فرق أو إدارتها لتسهيل عملية الترجمة إلى مختلف اللغات. هل الموقع مجاني ؟ في حال أردت التسجيل في الموقع كمترجم متطوع فتسجيل الحساب مجاني ويمكنك طلب الانضمام إلى الفرق للبدء بالمساعدة بالترجمة، أما في حال أردت نشر مشروعك لطلب المساعدة على الترجمة من المتطوعين في الموقع أو لتسهيل عملية الترجمة على فريق الترجمة لديك باستخدام هذه المنصة فسيكون نشر المشروع مجانيًا في حال كان مشروعك مفتوح المصدر. من يستخدم هذه المنصة؟ العديد من المواقع والبرامج المعروفة تستخدم Transifex لترجمة مشاريعها مثل Trello, Soundcloud, VLC وغيرها. كيف أنشر مشروعي (المفتوح المصدر) على المنصة؟ بعد تسجيل حساب جديد في الموقع إضغط على القائمة المجاورة لصورة المستخدم، هذه القائمة هي قائمة المنظمات التي اشتركت بها وتساعد في ترجمة مشاريعها، إضغط على Create Organization لإنشاء منظمة جديدة: ثم املأ النموذج بالمعلومات الصحيحة لمنظمتك أو فريقك، وضع علامة ☑️ على خيار Yes, my content has Open Source license ويجب أن يكون مشروعك في هذه الحالة مفتوح المصدر ومرخصا بإحدى الرخص المتعارف عليها للمشاريع مفتوحة المصدر (مثل GPL, MIT, Apache وغيرها) وسيُطلب منك إدخال رابط المستودع الخاص بمشروعك لاحقا. تم الآن إنشاء المنظمة الخاصة بك، عليك أن تقوم بإنشاء مشروع جديد بالضغط على سيظهر لك نموذج إنشاء المشروع، وسنشرح الخيارات واحدة واحدة: عليك أن تحدد في هذه الخانة نوع المشروع، private للمشاريع الخاصة التي لا تريد أن يطلع عليها إلا فريقك الخاص، public للمشاريع العامة، اختر public واختر My project is a non-commercial Open Source project في حال كان مشروعك مفتوح المصدر وغير ربحي (في حال لم يكن كذلك فلن يكون نشر مشروعك على المنصة مجانيا) ثم عليك تزويد Transifex برابط المستودع الخاص بمشروعك (على GitHub مثلا) حتى يتحققوا من الرخصة الخاصة بالمشروع: في هذه الخانة ستحدد نوع المشروع على Transifex من ناحية طريقة الترجمة، الطريقة الأولى هي File-based يجب عليك فيها أن ترفع ملف اللغات إلى الموقع ويقوم المترجمون بتصفح قائمة الكلمات وترجمتها، أما الطريقة الثانية (وهي طريقة جديدة في تاريخ كتابة هذه الكلمات) فهي تعتمد على Javascript حيث يقوم الموقع باستخراج جميع الكلمات من موقعك، ويعرض موقعك داخل نافذة مصغرة بحيث يمكنك الضغط على الكلمة التي تريد وترجمتها. وهذا مثال من موقع عالم البرمجة على طريقة عرض وترجمة الكلمات (لكن بالطبع لا يمكنني القيام بالترجمة فيجب إضافة مكتبة Javascript تزودني بها Transifex حتى تتمكن من تخزين الكلمات لديها مع الترجمات) : الخيار الأخير تحدد منه اللغة الأصلية لمشروعك واللغات التي تريد الترجمة إليها : كل ما بقي عليك فعله الآن هو دعوة المترجمين إلى المشروع وانتظار الترجمات الدقيقة 🙃
  10. يسعى المُبرمج دائمًا لاختبار تطبيقاته بصورة مُكثّفة وبأكثر من حالة قبل إطلاقها بشكل رسمي. إلا أن هذا لا يعني أن المشاكل لن تحدث، وهنا يأتي دور فريق العمل الذي يحتاج لمراقبة كل شيء عن كثب لتقديم تجربة استخدام مُميّزة، وإلا سيُغادر المستخدم لو وجد الخطأ يتكرّر على فترة زمنية طويلة. في إطار عمل ExpressJS هناك أكثر من طريقة لمراقبة الطلبات والأخطاء الحاصلة في التطبيق، منها مكتبة Morgan التي تقوم بكتابة كل طلب وارد في الكونسول Console، وبالتالي يُمكن معرفة الطلبات الناجحة وتلك التي عادت بـ 404، دلالة على أن المستخدم طلب صفحة غير موجودة. في هذه الحالة يُمكن للقائمين على التطبيق تحليل الطلب ومعرفة فيما إذا كان هناك خطأ بالفعل للتخلّص منه. استخدام المكتبة بسيط جدًا يبدأ بتثبيتها npm install morgan --save بعدها وفي الملف الرئيسي للتطبيق يتم استخدامها بالطريقة الآتية let morgan = require("morgan") app.use(morgan('combined')) وبعد إتمام تلك الخطوات يُمكن مراقبة الكونسول Console وستظهر الطلبات الواردة بهذا الشكل ويُمكن بالمناسبة تغيير combined إلى dev أو tiny لتتغير البيانات التي يتم تدوينها في الكونسول. ماذا عن الأخطاء الأُخرى الحاصلة نتيجة لـ Throw؟ هل سيقضي المُبرمج وقته مُراقبًا ملفات الـ Log أو الكونسول طوال الوقت؟ بكل تأكيد لا لأن أداة AirBrake تأتي لمعالجة هذه المشكلة. تقوم هذه الأداة برصد الأخطاء ومن ثم إرسال تنبيه للمستخدم عبر بريده بشكل فوري، أو عبر حسابات فريق العمل في تطبيق سلاك مثلًا، وبالتالي يضمن المُبرمج أن يطّلع أولًا بأول على المشاكل الحاصلة. طريقة الاستخدام سهلة أيضًا ومُباشرة، تبدأ بالتوجه لموقع الأداة AirBrake.io ثم تسجيل حساب مجاني وإنشاء تطبيق جديد للحصول على مفتاح خاص. ثم يتم تثبيت مكتبة الأداة بالشكل التالي: npm install airbrake-js --save في الملف الرئيسي للتطبيق نقوم بالآتي let airBrake = require('airbrake-js') let ErrorHandler = require('./node_modules/airbrake-js/dist/instrumentation/express') // إنشاء كائن جديد let airBrakeClient = new airBrake({ projectId: 00000, //يتم استبدال الأصفار بالمعرف الخاص الذي ستحصل عليه بعد التسجيل في الموقع projectKey: 'MYKEY' // يتم استبدال المفتاح هذا بالمفتاح الذي يقوم الموقع بتوليده لك }) أخيرًا وقبل الـ Listen يتم إضافة السطر الآتي app.use(ErrorHandler(airBrakeClient)); في هذه الحالة وعند حدوث أي خطأ سيتم التبليغ عبر لوحة تحكّم الموقع أولًا كما هو موضّح في الصورة. مع إمكانية ربط الحساب مع حسابات أُخرى مثلما ذكرت سابقًا لتسهيل التنبيه على الأخطاء.
  11. يسعى المُبرمج دائمًا لاختبار تطبيقاته بصورة مُكثّفة وبأكثر من حالة قبل إطلاقها بشكل رسمي. إلا أن هذا لا يعني أن المشاكل لن تحدث، وهنا يأتي دور فريق العمل الذي يحتاج لمراقبة كل شيء عن كثب لتقديم تجربة استخدام مُميّزة، وإلا سيُغادر المستخدم لو وجد الخطأ يتكرّر على فترة زمنية طويلة. في إطار عمل ExpressJS هناك أكثر من طريقة لمراقبة الطلبات والأخطاء الحاصلة في التطبيق، منها مكتبة Morgan التي تقوم بكتابة كل طلب وارد في الكونسول Console، وبالتالي يُمكن معرفة الطلبات الناجحة وتلك التي عادت بـ 404، دلالة على أن المستخدم طلب صفحة غير موجودة. في هذه الحالة يُمكن للقائمين على التطبيق تحليل الطلب ومعرفة فيما إذا كان هناك خطأ بالفعل للتخلّص منه. استخدام المكتبة بسيط جدًا يبدأ بتثبيتها npm install morgan --save بعدها وفي الملف الرئيسي للتطبيق يتم استخدامها بالطريقة الآتية let morgan = require("morgan") app.use(morgan('combined')) وبعد إتمام تلك الخطوات يُمكن مراقبة الكونسول Console وستظهر الطلبات الواردة بهذا الشكل ويُمكن بالمناسبة تغيير combined إلى dev أو tiny لتتغير البيانات التي يتم تدوينها في الكونسول. ماذا عن الأخطاء الأُخرى الحاصلة نتيجة لـ Throw؟ هل سيقضي المُبرمج وقته مُراقبًا ملفات الـ Log أو الكونسول طوال الوقت؟ بكل تأكيد لا لأن أداة AirBrake تأتي لمعالجة هذه المشكلة. تقوم هذه الأداة برصد الأخطاء ومن ثم إرسال تنبيه للمستخدم عبر بريده بشكل فوري، أو عبر حسابات فريق العمل في تطبيق سلاك مثلًا، وبالتالي يضمن المُبرمج أن يطّلع أولًا بأول على المشاكل الحاصلة. طريقة الاستخدام سهلة أيضًا ومُباشرة، تبدأ بالتوجه لموقع الأداة AirBrake.io ثم تسجيل حساب مجاني وإنشاء تطبيق جديد للحصول على مفتاح خاص. ثم يتم تثبيت مكتبة الأداة بالشكل التالي: npm install airbrake-js --save في الملف الرئيسي للتطبيق نقوم بالآتي let airBrake = require('airbrake-js') let ErrorHandler = require('./node_modules/airbrake-js/dist/instrumentation/express') // إنشاء كائن جديد let airBrakeClient = new airBrake({ projectId: 00000, //يتم استبدال الأصفار بالمعرف الخاص الذي ستحصل عليه بعد التسجيل في الموقع projectKey: 'MYKEY' // يتم استبدال المفتاح هذا بالمفتاح الذي يقوم الموقع بتوليده لك }) أخيرًا وقبل الـ Listen يتم إضافة السطر الآتي app.use(ErrorHandler(airBrakeClient)); في هذه الحالة وعند حدوث أي خطأ سيتم التبليغ عبر لوحة تحكّم الموقع أولًا كما هو موضّح في الصورة. مع إمكانية ربط الحساب مع حسابات أُخرى مثلما ذكرت سابقًا لتسهيل التنبيه على الأخطاء.
  12. Earlier
  13. السلام عليكم ورحمة الله وبركاته. سنشرح في هذه المقالة طريقة الاستفادة من منصة Transifex لترجمة مشاريعك ترجمة "بشرية" دقيقة بعيدا عن استخدام المترجمات الآلية. نبذة عن موقع Transifex: هي منصة متكاملة لترجمة المشاريع البرمجية، حيث يطرح المبرمج أو المسؤول عن ترجمة البرنامج ملف الترجمة الخاص بالبرنامج، ويمكنه تعيين فرق أو إدارتها لتسهيل عملية الترجمة إلى مختلف اللغات. هل الموقع مجاني ؟ في حال أردت التسجيل في الموقع كمترجم متطوع فتسجيل الحساب مجاني ويمكنك طلب الانضمام إلى الفرق للبدء بالمساعدة بالترجمة، أما في حال أردت نشر مشروعك لطلب المساعدة على الترجمة من المتطوعين في الموقع أو لتسهيل عملية الترجمة على فريق الترجمة لديك باستخدام هذه المنصة فسيكون نشر المشروع مجانيًا في حال كان مشروعك مفتوح المصدر. من يستخدم هذه المنصة؟ العديد من المواقع والبرامج المعروفة تستخدم Transifex لترجمة مشاريعها مثل Trello, Soundcloud, VLC وغيرها. كيف أنشر مشروعي (المفتوح المصدر) على المنصة؟ بعد تسجيل حساب جديد في الموقع إضغط على القائمة المجاورة لصورة المستخدم، هذه القائمة هي قائمة المنظمات التي اشتركت بها وتساعد في ترجمة مشاريعها، إضغط على Create Organization لإنشاء منظمة جديدة: ثم املأ النموذج بالمعلومات الصحيحة لمنظمتك أو فريقك، وضع علامة ☑️ على خيار Yes, my content has Open Source license ويجب أن يكون مشروعك في هذه الحالة مفتوح المصدر ومرخصا بإحدى الرخص المتعارف عليها للمشاريع مفتوحة المصدر (مثل GPL, MIT, Apache وغيرها) وسيُطلب منك إدخال رابط المستودع الخاص بمشروعك لاحقا. تم الآن إنشاء المنظمة الخاصة بك، عليك أن تقوم بإنشاء مشروع جديد بالضغط على سيظهر لك نموذج إنشاء المشروع، وسنشرح الخيارات واحدة واحدة: عليك أن تحدد في هذه الخانة نوع المشروع، private للمشاريع الخاصة التي لا تريد أن يطلع عليها إلا فريقك الخاص، public للمشاريع العامة، اختر public واختر My project is a non-commercial Open Source project في حال كان مشروعك مفتوح المصدر وغير ربحي (في حال لم يكن كذلك فلن يكون نشر مشروعك على المنصة مجانيا) ثم عليك تزويد Transifex برابط المستودع الخاص بمشروعك (على GitHub مثلا) حتى يتحققوا من الرخصة الخاصة بالمشروع: في هذه الخانة ستحدد نوع المشروع على Transifex من ناحية طريقة الترجمة، الطريقة الأولى هي File-based يجب عليك فيها أن ترفع ملف اللغات إلى الموقع ويقوم المترجمون بتصفح قائمة الكلمات وترجمتها، أما الطريقة الثانية (وهي طريقة جديدة في تاريخ كتابة هذه الكلمات) فهي تعتمد على Javascript حيث يقوم الموقع باستخراج جميع الكلمات من موقعك، ويعرض موقعك داخل نافذة مصغرة بحيث يمكنك الضغط على الكلمة التي تريد وترجمتها. وهذا مثال من موقع عالم البرمجة على طريقة عرض وترجمة الكلمات (لكن بالطبع لا يمكنني القيام بالترجمة فيجب إضافة مكتبة Javascript تزودني بها Transifex حتى تتمكن من تخزين الكلمات لديها مع الترجمات) : الخيار الأخير تحدد منه اللغة الأصلية لمشروعك واللغات التي تريد الترجمة إليها : كل ما بقي عليك فعله الآن هو دعوة المترجمين إلى المشروع وانتظار الترجمات الدقيقة 🙃
  14. السلام عليكم ورحمة الله وبركاته في هذه المقالة سنتعلم طريقة تسجيل وتشغيل المقاطع الصوتية باستخدام Swift 4 تسجيل وتشغيل المقاطع سهل ولكن يحتاج الى الوقت لكتابة الاكواد، لذا لنبدأ أولا، قم باستدعاء الفريموورك AVFoundation المختصة بمعالجة كل المقاطع المرئية والصوتية على منصات أبل import AVFoundation ثم قم بتبني البروتوكولات AVAudioRecorderDelegate و AVAudioPlayerDelegate لاستخدامهما لاحقا class ViewController: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate { ثم قم باضافة: زرين لبدء/ايقاف التسجيل/التشغيل recordingSession ليعالج جلسة تسجيل الصوت audioRecorder ليتعامل مع التسجيل نفسه ويحفظه audioPlayer لتشغيل المقاطع الصوتية بعد تسجيلها وpath لحفظ مسار ملف الصوت المسجل وتشغيله لاحقا @IBOutlet weak var recordButton: UIButton! @IBOutlet weak var playButton: UIButton! var recordingSession: AVAudioSession! var audioRecorder: AVAudioRecorder! var audioPlayer: AVAudioPlayer? var path: URL! (يتم استخدام الrecordingSession من قبل نظام iOS لتنظيم تسجيل وتشغيل المقاطع الصوتية منعا لتداخل الأصوات من أكثر من تطبيق -مثلا لكي لا يستطيع تطبيق تسجيل او تشغيل صوت عند اجراء مكالمة-) تسجيل الصوت يتطلب اذن المستخدم ليتم، ان تم اعطاء الاذن في هذا الكود سيتم تغيير نص زر التسجيل، اضف هذا الكود في ()viewDidLoad playButton.isEnabled = false recordingSession = AVAudioSession.sharedInstance() do { try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord) try recordingSession.setActive(true) recordingSession.requestRecordPermission() { [unowned self] allowed in DispatchQueue.main.async { if allowed { self.recordButton.setTitle("تسجيل", for: .normal) } else { // failed to record! } } } } catch { // failed to record! } قبل اضافة الIBAction الخاص بزر التسجيل، سنضيف ميثود startRecording التي (1) ستحدد مكان حفظ ملف الصوت، (2) اعدادات مسجل الصوت ثم (3) بدء التسجيل func startRecording() { let audioFilename = getDocumentsDirectory().appendingPathComponent("recording.m4a") //1 path = audioFilename playButton.isEnabled = false let settings = [ //2 AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 12000, AVNumberOfChannelsKey: 1, AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue ] do { audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings) //3 audioRecorder.delegate = self audioRecorder.record() //3 recordButton.setTitle("ايقاف التسجيل", for: .normal) } catch { finishRecording(success: false) } } سنحتاج لاضافة الميثود getDocumentsDirectory لتعمل الميثود السابقة في هذه الميثود سنعيد المسار المتاح من النظام للتطبيق لحفظ الملفات فيه، سنقوم بحفظ الملف الصوتي في المسار الرئيسي للتطبيق func getDocumentsDirectory() -> URL { let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) let documentsDirectory = paths[0] return documentsDirectory } انتهينا من الفنكشن الخاصة ببدء التسجيل، والآن نحتاج لفنكشن تقوم بانهاء التسجيل عند طلب المستخدم func finishRecording(success: Bool) { audioRecorder.stop() audioRecorder = nil playButton.isEnabled = true if success { recordButton.setTitle("اعادة التسجيل", for: .normal) } else { recordButton.setTitle("تسجيل", for: .normal) // recording failed :( } } بانهاء الفنكشنين السابقة، نستطيع اخيرا كتابة الIBAction الخاص بزر التسجيل @IBAction func recordTapped(_ sender: Any) { if audioRecorder == nil { startRecording() } else { finishRecording(success: true) } } وهكذا نكون قد انتهينا من تسجيل الصوت لتشغيل الصوت سنضيف الIBAction التالي الخاص بزر التشغيل @IBAction func playTapped(_ sender: Any) { do { audioPlayer = try AVAudioPlayer(contentsOf: path) audioPlayer?.delegate = self audioPlayer?.play() recordButton.isEnabled = false } catch { // couldn't load file :( } } نهاية سنضيف الفنكشين التالية من البروتوكولات التي قمنا بتبنيها في بداية المقالة الأولى audioRecorderDidFinishRecording يتم استدعاؤها عن توقف التسجيل في بعض الاحيان، قد يقوم النظام بايقاف التسجيل عنوة (عندما ترد مكالمة على سبيل المثال)، في هذه الفنكشن سنتأكد عند عدم نجاح التسجيل لنضبط نص زر التسجيل func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { if !flag { finishRecording(success: false) } } والفنكشن الثانية audioPlayerDidFinishPlaying يتم استدعاؤها عند انتهاء تشغيل المقطع الصوتي func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { recordButton.isEnabled = true } (بالامكان استخدام ()audioPlayer.stop لايقاف التشغيل) (تأكد من ربط الOutlets والActions قبل التشغيل) النتيجة النهائية
  15. السلام عليكم ورحمة الله وبركاته المعذرة اولاً لطرح الموضوع في مكان غير مكانه ولكني لم اجد قسم طلبات البرمجة مطلوب برمجة تطبيق Ios واندرويد لتطبيق مثل السوق المفتوح بالضبط علماً بان التطبيقات سيتم ربطها api مع الموقع والموقع مبرمج بـ Yii2 اذا كان موضوعي في قسم مخالف ارجو نقله الى القسم المناسب وشكراً جزيلاً لكم
  16. بسم الله الرحمن الرحيم في إستمرارنا في الحديث عن أهم وأحدث مكتبات منصة الأندرويد سنتحدث اليوم عن واحدة من أشهر المكاتب المستخدمة مؤخراً في بعض التطبيقات المشهورة ومن ضمنها تطبيق Jodel الشهير. هذه المكتبة هي مكتبة Crouton. لنتعرف معاً على هذه المكتبة وما أهميتها وكيف تعمل. مكتبة Crouton هي مكتبة تتيح لك تنبيه المستخدم وإظهار بعض الإشعارات. تشبه في عملها مكتبة Toast الشهيرة ولكنها تختلف عنها بأنها تحل بعض المشكلات المتعلقة بـ Toast. واحدة من أهم مشاكل الـ Toast هي مشكلة out of context وهي بأن Toast تعمل وتظهر بغض النظر عن الـ context أو المضمون. قد تظهر في سياق مختلف تماماً عن المتوقع أي أنه عند الإنتقال لـ Activity أخرى سيستمر إشعار الـ Toast بالظهور كما أنها غير قابلة للتعديل وموحدة الشكل. كل هذه المشاكل من الممكن حلها بمكاتب مختلفة ولكن من أسهل هذه المكاتب هي Crouton. ولكن السؤال .. كيف تعمل ؟ مكتبة Crouton تتيح لك التحكم الكامل بشكل الإشعارات ولونها وخصائصها بما تتناسب مع تطبيقك. تعطيك كبداية 3 أشكال ثابتة إذا أردت الإبقاء على أشكال Crouton دون أي تغيير. هذه الأشكال هي : 1- Alert Notification : باللون الأحمر والخط الأبيض ولمدة 3 ثواني تقريباً 2- Info Notification : باللون الأزرق والخط الأبيض ولمدة 3 ثواني تقريباً 3- Confirm Notification : باللون الأخضر والخط الأبيض ولمدة 3 ثواني تقريباً لنبدأ التطبيق ونرى كيفية إظهار هذه الأشكال. قبل بداية التطبيق وكما جرت العادة سنحتاج إلى إضافة Dependency لملف build.gradle ولكن هذه المرة سنحتاج لتعديل ملفي الـ gradle. بالنسبة لملف build.gradle "project" سنضيف mavenCentral() داخل Block الـ repositories الموجود في buildscript بالنسبة لملف build.gradle "module" سنضيف compile 'de.keyboardsurfer.android.widget:crouton:[email protected]' نقوم بعمل Sync من الأعلى حتى تضاف المكتبة ونستطيع بعدها البدء في العمل. نقوم بإنشاء Layout بسيطة توضح عمل هذه المكتبة. تحتوي هذه الـ Layout على 3 أزرار خاصة بكل style وأيضاً على زر Toast وزر آخر لتوضيح الـ Custom Notification. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingTop="50dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="INFO" /> <Button android:id="@+id/alert" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Alert" /> <Button android:id="@+id/succ" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Success" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/toast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Toast" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/custom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Custom" /> </LinearLayout> </LinearLayout> صورة توضح الشكل النهائي للـ Layout نذهب الآن للجزء الأهم وهو ملف الـ Java. ملاحظة : سنقوم بتطبيق مفاهيم الـ ButterKnife. لمزيد من المعلومات نرجو زيارة الموضوع التالي: دليلك لأفضل مكاتب الأندرويد - الجزء الثاني - ButterKnife أولاً : سنقوم في البداية بتعريف المتغيرات والـ Buttons @BindView(R.id.alert) Button alert; @BindView(R.id.info) Button info; @BindView(R.id.succ) Button succ; @BindView(R.id.toast) Button toast; @BindView(R.id.custom) Button custom; ثانياً : سنقوم بعمل Bind للمتغيرات داخل onCreate method ButterKnife.bind(this); ثالثاً : سنقوم بإختيار كل زر للقيام بوظيفة معينة. نلاحظ عندما نريد إظهار Crouton أو إشعار لا نحتاج لتعريف أي متغيرات فهي تعمل بنفس طريقة عمل Toast. لإظهار الإشعارات بالأشكال السابقة التي سبق وتحدثنا عنها سنحتاج لتمرير 3 params فقط وهي الـ Context والنص والشكل سواء كان alert – info – confirm والطريقة تبدو مشابهة تماماً لطريقة الـ Toast. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO); } @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", Style.ALERT); } @OnClick(R.id.succ) void clicked3() { Crouton.showText(this, "SUCCESS 3alamPro", Style.CONFIRM); } @OnClick(R.id.toast) void clicked4() { Toast.makeText(getApplicationContext(), "3alamPro", Toast.LENGTH_LONG).show(); } رابعاً : نقوم بإختبار التطبيق ونرى الفرق بين Crouton وبين Toast. وكيف أن Toast تستمر بالظهور حتى عند الخروج من التطبيق ولكن Crouton تختفي وهو المطلوب. ماذا لو أردنا البقاء على هذه الأشكال ولكن نريد تغيير الوقت المحدد مسبقاً. بكل بساطة سنحتاج لعمل Object من كلاس Configuration الموجود مسبقاً داخل مكتبة Crouton. Configuration croutonConfiguration; ومن ثم نعرف الـ Object داخل onCreate method ونعطيه الوقت المطلوب ( الوقت المدخل يقاس بالـ milliseconds والثانية الواحدة تساوي 1000 ميلي ثانية ). croutonConfiguration = new Configuration.Builder().setDuration(1000).build(); // 1 sec الآن سنقوم بتغيير الوقت لواحدة من الإشعارات المعرفة مسبقاً ولكننا سنحتاج لتمرير باراميتر إضافي وهو getTaskId() سنتكلم عليه فيما بعد ولكن في الوقت الراهن قد لا يهمنا كثيراً. بهذا التعديل سيتغير الوقت ليصبح ثانية واحدة فقط بدلاً من 3 ثواني. @OnClick(R.id.info) void clicked1() { Crouton.showText(this, "INFO 3alamPro", Style.INFO,getTaskId(),croutonConfiguration); } نقوم بالتجربة والمقارنة بين INFO وبين ALERT ونستطيع رؤية الفرق في التوقيت. بعد الإنتهاء من أول قسم سنبدأ في تصميم الـ Notification الخاصة بنا. وطبعاً هذا الأمر متاح بكل سهولة مع Crouton. سنقوم في البداية بتغيير لون الخط ولون خلفية الإشعار فقط. نلاحظ بأننا كنا نمرر باراميتر من نوع Style داخل crouton. الباراميتر Style.INFO عبارة عن ستايل جاهز ولتغييره سنحتاج لعمل Style خاص بنا. لذلك سنحتاج إلى عمل Object من كلاس style وإضافة جميع الخصائص بشكل يدوي. Style style; ونقوم بتعريف الـ Object داخل onCreate method. style = new Style.Builder() .setBackgroundColorValue(Color.parseColor("#000000")) // black .setGravity(Gravity.CENTER_HORIZONTAL) .setConfiguration(croutonConfiguration) .setHeight(100) .setTextColorValue(Color.parseColor("#ffffff")).build(); // white للشرح بشكل أعمق للخصائص : setBackgroundColorValue(Color.parseColor()) نستخدمها لإختيار لون خلفية الإشعار. setGravity نستخدمها لإختيار مكان النص سواء كان في المنتصف أو على اليمين أو على اليسار. setConfiguration نستخدمها لإختيار المدة الزمنية لإظهار الإشعار. (يجب إنشاء Object منفصل كالذي تم إنشاؤه في الخطوات السابقة ) setHeight نحدد من خلالها إرتفاع الإشعار. setTextColorValue نحدد من خلالها لون خط النص داخل الإشعار. ومن ثم ننهي التعريف بـ .build نقوم الآن بتعديل واحدة من الإشعارات المعرفة سابقاً وإختيار الـ style الخاص بنا بدلاً من المعرف مسبقاً. @OnClick(R.id.alert) void clicked2() { Crouton.showText(this, "ALERT 3alamPro", style); } ونرى النتيجة. تعمل بشكل مثالي. سنقوم الان بعمل إشعار مختلف تماماً يتضمن صور ونص وليس نص فقط. لتطبيق هذا الأمر سنحتاج إلى إنشاء Layout جديدة. سنقوم داخل هذه الـ Layout بتصميم شكل الإشعار. نقوم بإنشاء Layout جديدة ونسميها crouton_custom على سبيل المثال. نقوم بتصميم Layout بسيطة تتضمن شعار الموقع فقط. نلاحظ بأن إرتفاع الـ Layout يمثل إرتفاع الإشعار نفسه لذلك إختيار الإرتفاع مهم جداً. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="#000000"> <ImageView android:layout_width="100dp" android:layout_height="30dp" android:layout_centerInParent="true" android:src="@drawable/pro"/> </RelativeLayout> </RelativeLayout> الشكل النهائي للـ Layout. للاستمرار بذلك يجب علينا تعريف Object جديد من نوع View وعمل Inflate للـ Layout الخاصة بالإشعار. ومن ثم عمل Crouton جديد. @OnClick(R.id.custom) void clicked5() { View customView = getLayoutInflater().inflate(R.layout.crouton_custom, null); Crouton.show(this,customView); } والنتيجة .. إلى هنا نكون قد وصلنا إلى نهاية الشرح الخاص بالمكتبة. وسنقوم في المواضيع القادمة بإستعراض المزيد من المكتبات الخاصة بمنصة الأندرويد بإذن الله.
  17. السلام عليكم ورحمة الله وبركاته كما في العنوان ماطريقة حفظ صورة الى مكتبة الصور في نظام iOS برمجيًا وهل سوف احتاج الى طلب صلاحية لحفظ الصورة ام لا؟ اريد كود Objective C ان كان ممكن
  18. وعليكم السلام، أولا: إذا كان المستخدم سيدخل قيمة كبيرة يفضل أن تستخدم long كنوع بيانات للمدخلات. ثانيا، أنت بحاجة إلى تحديد عدد السنين والأيام باستخدام الدقائق، بالتالي ستقوم بتحويل الدقائق إلى سنين عن طريق قسمتها على كل من (60 للتحويل إلى ساعات ثم 24 للتحويل إلى أيام ثم 365 للتحويل إلى سنين) ثم ستقوم ، بإيجاد باقي القسمة لنفس العملية الحسابية الآتية حتى تعرف كم دقيقة ستبقى معك وتقسم هذا الرقم على كل من ( 60 للتحويل إلى ساعات ثم 24 للتحويل إلى أيام) وتطبع الناتج للمستخدم. لنفترض أن المستخدم أدخل الرقم في متغير باسم min int min = (user input); int numberOfMinutesInYear = 365 * 24 * 60; // عدد الدقائق في السنة الواحدة int numberOfMinutesInDay = 24 * 60; // عدد الدقائق في اليوم الواحد int years = min/numberOfMinutesInYear; // نقسم عدد الدقائق الموجودة لدينا على عدد الدقائق في السنة للتحويل int days = (min%numberOfMinutesInYear)/numberOfMinutesInDay; // في العملية الحسابية الاولى بين الاقواس سيتم ايجاد عدد الدقائق الباقية من القسمة على الرقم المستخدم في التحويل إلى سنين System.out.print("Y: " + years); System.out.print("M: " + days); لاحظ أننا لم نضطر أن نستخدم Math.floor أو أي ميثود أخرى لإزالة الأعشار الناتجة مع السنين، لأننا نستخدم أصلا متغيرات من نوع int ، أو long إذا كنت تتوقع أرقام طويلة، فلن تكون هناك أعشار ناتجة من العملية الحسابية.
  19. السلام عليكم فيه سؤال جلست احاول احله يومين ولا عرفت و جاني شعور اني غبي مع العلم ان السؤال سهل بس مدري ليش ما عرفت احله وهو (Write a program that prompts the user to enter the minutes (e.g., 1 billion), and displays the number of years and days for the minutes. For simplicity, assume a year has 365 days) انا قالي واحد استخدم % بس ما عرفت استخدمها
  20. هل جربت استخدام unique في ملف الـ Migrations و في الـ Validator الخاص بالفورم ؟
  21. بسم الله الرحمن الرحيم السلام عليكم ورحمة الله وبركاته،، موضوعنا اليوم سيكون عن المصفوفات التي هي من أهم تراكيب البيانات التي نرتب بها البيانات. وهي طريقة مستخدمة في أغلب لغات البرمجة العالية المستوى (high level programming languages). ماهي المصفوفة؟ المصفوفة عبارة عن كائن (Object) يتم فيه تخزين عدد محدد من بيانات من نفس النوع. قد يبدو الكلام النظري هنا صعب الفهم فدعنا نطرح مثالا للتوضيح؛ افترض أن لديك درجة لطالب في مادة الرياضيات وتود تخزينها في البرنامج، سيكون هذا سهلا للغاية كل ما عليك هو إنشاء متغير واحد فقط وتعطيه القيمة (درجة الطالب)، لكن ماذا لو أردت تخزين درجات جميع الطلاب في شعبة الرياضيات (100 طالب)!! من الصعب جدا أن نعرف 100 متغير، غير أن ذلك سيستهلك مساحة كبيرة من الذاكرة. فالحل هو إنشاء مصفوفة! فهنا تكمن فائدة المصفوفات إذ أنك من خلال إنشاء مصفوفة واحدة حجمها 100 ستتمكن من تخزين ال 100 درجة في مكان واحد وباسم متغير واحد. إذن، كيف تنشئ مصفوفة؟ هناك عدة أمور يجب تذكرها جيدًا عند إنشاء المصفوفات: 1- يجب أن تكون جميع البيانات في المصفوفة من نفس النوع (data type). مثال: أن تكون جميع درجات الطلاب من نوع int. 2- يجب أن يكون حجم المصفوفة ثابت لا يتغير (fixed size). مثال: أن يكون حجم مصفوفة درجات الطلاب 100 فلا يمكن إضافة درجة أي طالب بعد ال100. تعريف المصفوفة (Array declaration) يكون كالآتي: مثال: فبعد هذا السطر نكون قد عرفنا المصفوفة فقط ولم ننشئ المصفوفة بعد، بمعنى أنه تم إنشاء اسم متغير يشير إلى null (لا شيء) في الذاكرة. إنشاء المصفوفة (Array creation) يكون كالآتي: مثال: وبعد هذا السطر نكون قد أنشأنا المصفوفة، وأصبح اسم المتغير في الذاكرة يشير إلى المصفوفة التي أنشأناها، والبيانات بداخلها ستكون القيم الابتدائية الخاصة بنوع البيانات. وبإمكاننا أيضا عمل تعريف وإنشاء للمصفوفة في نفس السطر كالآتي: مثال: حسنا، للتو تعلمنا كيف نعرف وننشئ مصفوفة، لكنها ستكون بقيم ابتدائية! ما معنى ذلك؟ أي أن المصفوفة ستكون معبأة بقيم ابتدائية خاصة بنوع البيانات فكما في مثالنا السابق ستكون القيم الابتدائية 0 لأن نوع البيانات هو int بينما لو كان نوع البيانات Boolean ستكون القيم الابتدائية false وهكذا على حسب نوع البيانات. لكننا لا نريد ذلك! نريد أن نعبأ المصفوفة بالقيم التي نريد! ولكن كيف نصل إلى عناصر المصفوفة؟ كل المصفوفات عناصرها مرقمة من 0 إلى (حجم المصفوفة -1) ويسمى هذا الترقيم بال index فهو بمثابة عنوان للعنصر وبه نستطيع الوصول إليه. مثلا لو كان عندي مصفوفة اسمها numbers حجمها 4 لتخزين أربعة أرقام فسيكون ال index لها من 0 إلى 3، إذ أن العنصر الأول سيكون متواجد عند الindex رقم 0 والعنصر الثاني عند الindex رقم 1، وهكذا إلى نهاية المصفوفة. مثلا لو أردنا وضع رقم 5 في العنصر الأول سنقوم بعمل الآتي: ; القيمة المراد وضعها = [index] اسم المتغير ;numbers[0]=5 ولو أردنا وضع رقم 90 في العنصر الأخير سنقوم بعمل الآتي: ; القيمة المراد وضعها = [حجم المصفوفة-1] اسم المتغير ;numbers[numbers.length-1]=90 استخدمنا هنا خاصية خاصة بالمصفوفات تسمى length تعطينا حجم المصفوفة، وقمنا بإنقاص 1 من حجم المصفوفة لأننا سنتعامل مع index وآخر حد للindex هو حجم المصفوفة-1. وكيفية استدعاء هذه الخاصية كالآتي: length.اسم المصفوفة هذه الطريقة مفيدة في حالة أردنا إجراء تعديلات على عناصر محددة، لكن لو أردنا تعبئة المصفوفة كاملة فهذه الطريقة ستكون مضيعة للوقت! لذلك نحتاج أن نسنخدم الloop وهي أن نمر على كل عنصر بالترتيب ونضع فيها القيمة التي نريدها. مثلا لو أردنا إنشاء مصفوفة حجمها 5 فيها الأرقام من 1 إلى 5 سيكون كالآتي: *هنا i+1 لأن index يبدأ من الصفر ونحن نريد المصفوفة تبدأ من رقم 1. ولو أردنا طباعة عناصر المصفوفة سنحتاج إلى loop أيضا كالتالي: Output: وهكذا في جميع العمليات التي تجرى على المصفوفات سنحتاج إلى loop وننتبه إلى أن الloop يجب أن تنتهي عند آخر index في المصفوفة ولا تزيد لكيلا يظهر الخطأ ArraysOutOfBoundException. *المصادر: كتاب Introduction to Java programming, Tenth Edition, Y. Daniel Liang.
  22. بسم الله الرحمن الرحيم إستكمالاً لسلسلة مكتبات الأندرويد سنستعرض اليوم مكتبة من أهم المكتبات التي يستخدمها المطورون بشكل أساسي في تطوير التطبيقات على منصة الأندرويد. هذه المكتبة هي مكتبة Butter Knife. ولكن السؤال الأهم هو ما فائدة هذه المكتبة ؟ من المعروف لدى المطورين بأن طريقة ربط الـ View component مع الأوبجكت داخل كود الجافا هو باستخدام الأمر find view by id. ومن ثم ربط الأوبجكت عن طريق R.id.component. هذه الطريقة يوماً بعد يوم تثبت عدم فعاليتها وخصوصاً عند وجود الكثير من الـ components داخل الـ layout التي ستضطر لربطها بشكل يدوي. كما أن الربط العادي ستضطر في البداية إلى إنشاء المتغير في بداية الكلاس أما تعريفه فيجب أن يتم داخل onCreate method. هذه الطريقة باتت تثبت عدم فعاليتها يوماً بعد يوم. ButterKnife هي مكتبة تستطيع من خلالها ربط الأوبجكت مع الـ view component بشكل سريع وسلس وبشكل مباشر. كما تمكنك المكتبة من ربط الـ drawables والـ strings وغيرها وليست مقتصرة فقط على الـ views. كيف تعمل هذه المكتبة ؟ لنبدأ ... أولاً : وكما جرت العادة يجب إضافة هذه المكتبة داخل المشروع كـ dependency إضافية داخل ملف الـ gradle.build. من المهم أيضاً إضافة الـ annotationProcessor كي تعمل المكتبة بشكل صحيح. ومن ثم عمل sync لكي تتم الإضافة. compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' ثانياً: بعد عمل sync يمكننا الآن البدء في تصميم الـ layout .. طبعاً سأقوم بتصميم layout بسيطة فقط لتوضيح عمل هذه المكتبة. من الممكن ملاحظة بأن ملف الـ xml لم يتغير بتاتاً ولم نقم بإضافة أي كود إضافي وهذه ميزة إضافية أيضاً لأن هناك الكثير من المكتبات الأخرى التي توفر ميزة الربط ستضطر حين إستخدامها إلى إضافة تعديل بسيط داخل ملف الـ xml. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="15dp"> <TextView android:id="@+id/key" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="WEBSITE" /> <TextView android:id="@+id/value" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="3alamPro" android:layout_alignParentEnd="true"/> </RelativeLayout> صورة توضح الشكل النهائي للـ layout ثالثاً: سنبدأ الآن العمل على ملف الـ Java حيث سنتعلم بشكل أكبر طريقة عمل المكتبة. في البداية سنقوم كما جرت العادة بتعريف المتغيرات ولكن هذه المرة وقبل تعريفها سنقوم بكتابة @BindView(R.id.key) ليصبح الشكل النهائي للمتغيرات كالتالي public class MainActivity extends AppCompatActivity { @BindView(R.id.key) TextView key; @BindView(R.id.value) TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } بهذه الأسطر البسيطة سنكون قد أنتهينا تماماً من الربط .. طبعاً للمقارنة هذا ما سنقوم بكتابته بدون butterknife .. قد يبدو الفرق بسيط .. ولكن مع وجود الكثير من الـ components التي تحتاج للربط سيكون الفرق واضح. public class MainActivity extends AppCompatActivity { TextView key; TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); key = (TextView) findviewbyid(R.id.key); value = (TextView) findviewbyid(R.id.value); } رابعاً: لا يجب أن ننسى إضافة بعد تعريف onCreate method. ButterKnife.bind(this); لتصبح onCreate method في شكلها النهائي @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } هذا كان توضيح بسيط للميزة الأساسية التي تقدمها المكتبة .. ولكنها أيضاً تقدم المزيد من الإختصارات منها مثلاً [email protected] التي توفر لكن عناء ربط الـ strings الموجودة داخل ملف الـ values بدلاً من getResources().getStrings(R.strings.whatever) أيضاً توفر [email protected] وسنستعرض كيفية عملها. سنقوم الآن بإنشاء ملف Drawable عبارة عن إطار سنقوم بوضعه على key textview. نقوم بإنشاء ملف Drawable جديد ونسميه background على سبيل المثال بعد ذلك نقوم بإضافة الكود المرفق ( خلفية صفراء وحواف سوداء ) <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#eee1"></solid> <stroke android:color="#000" android:width="10dp"></stroke> </shape> بالعودة لملف الجافا سنقوم بإنشاء أوبجكت جديد لملف الـ Drawable بطريقة الـ Bind. @BindDrawable(R.drawable.background) Drawable drawable; ومن ثم وضعه كخلفية للـ textview داخل onCreate method بعد ButterKnife.bind طبعاً key.setBackground(drawable); سنقوم أيضاً بتغيير الـ Text الموجودة داخل الـ textviews للتأكد بأن الربط يعمل بشكل صحيح key.setText("Website Bind"); value.setText("Pro3alam"); نقوم بتشغيل البرنامج والتأكد من النتيجة .. كل شي على ما يرام 👍 هل إنتهينا ؟ ليس بعد .. مازال هناك ميزة قوية تقدمها هذه المكتبة .. وهي الإستغناء التام عن الـ inner-classes الناتجة عن الـ Listener عند عمل setOnClickListener . عن طريق ButterKnife يمكنك بكل بساطة كتابة [email protected] ومن ثم الـ id الخاص بالـ view . على سبيل المثال .. سنقوم بإظهار Toast بسيط عند الضغط على أحد الـ textviews من خلال [email protected] @OnClick(R.id.key) void clicked1() { Toast.makeText(getApplicationContext(), "key", Toast.LENGTH_LONG).show(); } @OnClick(R.id.value) void clicked2() { Toast.makeText(getApplicationContext(), "value", Toast.LENGTH_LONG).show(); } عند تشغيل البرنامج وعند الضغط على الـ textviews سنرى بأن كل شي يعمل بشكل صحيح بهذا نكون قد إنتهينا من موضوعنا بإستعراض واحدة من أهم وأقوى المكاتب وسنقوم بإستعراض المزيد من المكتبات في قادم المواضيع بإذن الله
  23. السلام عليكم ورحمة الله وبركاته هذه المقالة ستتحدث عن الCoredata، كدليل لمن لم يستخدم ال Coredata ابدا من قبل. سننشئ تطبيق to-do بسيط لشرح كيفية استخدام الCoredata لماذا الCoredata؟ يتم استخدام ال Coredata لحفظ بيانات الObjects على الجهاز لاستعادتها لاحقا بعد اغلاق التطبيق/الجهاز وهو ما يعرف بـ Persistence Data هناك مزايا للCoredata كامكانية التعديل على الrecords والفلترة والPartial Loading عكس الUserDefaults وصحيح أن هناك frameworks أخرى تقدم ميزة حفظ البيانات locally (مثل مكتبة Realm على سبيل المثال) لكننا لن نتطرق للفروقات بينهما و سنتحدث فقط عن الCoredata ماذا ستتعلم هنا؟ في دليل المبتدئين لن نغوص عميقا في الCoredata، لكنك ستتعلم كيفية حفظ، استعادة، وحذف البيانات. سأفترض بأنك على معرفة بالdelegates, optionals وكيفية انشاء UITableView بسيط ان لم تكن على علم بها أو ان اردت مراجعة هذه المفاهيم ستجدها في المواضيع التالية: الoptional في swift3 مقدمة في الtableview في برنامج الxcode الtable view في الxcode ( الجزء الثاني ) واجهة المستخدم في هذا الدليل سنقوم بانشاء to-do-app بسيط يتكون من واجهتين، الأولى تتكون من UITableView فقط لعرض المهام المضافة والثانية ستسخدم لاضافة المهام تحتوي على UITextField لادخال نص المهمة وUIButton لحفظ المهمة والعودة للواجهة السابقة بداية عندما تقوم بانشاء المشروع لأول مرة، تأكد من اختيار Use Core Data عند انشاء المشروع ستلاحظ وجود ملف جديد xcdatamodeld. هذا الملف كال Spreadsheet سيستعمل لانشاء مودل للبيانات التي نرغب بتخزينها اضغط على الملف الجديد واضف Task كEntity وname كAttribute من نوع String سنبدأ بحفظ البيانات في الواجهة الثانية AddTask للقيام بذلك يجب أن نحصل على access للCoredata Stack هناك كلمتين مخيفتين عليك تذكرهما جيدا، NSPersistentContainer و NSManagedObjectContext يمكنك التفكير بال Container كالصندوق الذي سنحتفظ فيه بالبيانات، والContext كفتحة الصندوق التي سنستطيع من خلالها اضافة، استرجاع وحذف البيانات بالمناسبة، في الAppDelegate.swift ستجد العديد من الميثودز والخصائص المتعلقة بالCoredata، كل ماعلينا عمله هو استخدامها في الواجهة الثانية سنضيف الكود التالي 1/ في هذا السطر قمنا بانشاء reference للContext (الصندوق) لنقوم بالتخزين فيه 2/ قمنا بتحديد نوع البيانات التي نريد تخزينها (من نوع Task Entity) وقمنا بتحديد الname المراد تخزينه 3/ طلبنا من الContext حفظ البيانات التي تم ادخالها. (بما أن الTask Entity تم ربطها بالContext فسيقوم الContext بحفظها) 4/ سنعود للواجهة الأولى لعرض المهمات الآن لعرض المهمات سنضيف الكود التالي للواجهة الأولى class TasksTable: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext //1 var tasks: [Task] = [] //2 override func viewDidLoad() { //3 super.viewDidLoad() tableView.delegate = self tableView.dataSource = self } override func viewWillAppear(_ animated: Bool) { //4 getData() tableView.reloadData() } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { //5 return tasks.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { //5 let cell = UITableViewCell() let task = tasks[indexPath.row] if let myName = task.name { cell.textLabel?.text = myName } return cell } func getData() { //6 do { tasks = try context.fetch(Task.fetchRequest()) } catch { print("Fetching Failed") } } func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { //7 if editingStyle == .delete { let task = tasks[indexPath.row] context.delete(task) (UIApplication.shared.delegate as! AppDelegate).saveContext() do { tasks = try context.fetch(Task.fetchRequest()) } catch { print("Fetching Failed") } } tableView.reloadData() } } سنقوم بشرح الكود السابق بالتفصيل 1/ قمنا بانشاء reference لل Context في أعلى الكلاس لنستخدمه عند الحاجة في الميثودز المختلفة 2/ قمنا بانشاء Array مؤقتة لتخزين البيانات المجلوبة من الContext وعرضها في الجدول 3/ في الViewDidLoad قمنا بتحديد الواجهة الحالية ك Delegate و DataSource للجدول 4/ في الViewWillAppear قمنا باستدعاء الميثود ()getData لجلب البيانات باستخدام الContext وتحديث الجدول لعرضها 5/ في الميثودين numberOfRowsInSection و cellForRowAt indexPath قمنا بانشاء الجدول وعرض الخلايا بالبيانات المطلوبة 6/ في الميثود getData قمنا بطلب fetch من الContext وقمنا بتحديد الTask Entity للحصول على آخر نسخة من بياناتها هذا الاستدعاء يجب أن يكون متضمن داخل do-catch block 7/ في الميثود editingStyle الخاصة بالجدول قمنا باضافة ميزة السحب للحذف (كالتالي) واستخدمنا الميثود ()context.delete لحذف الtask التي تم اختيارها ثم قمنا باعادة استدعاء البيانات من جديد وتحديث الجدول لعرضها النتيجة النهائية للتطبيق ونصل هنا الى ختام مقالتنا أستودعكم الله الذي لا تضيع ودائعه
  24. عندى مشروع فكرته توظيف بيتم التسجيل فة الوظائف وملاء الاستمارة وهو معمول laravel framwork ومحتاج اعدل فية بعض الاشياء مثل 1- عدم تكرار التسجيل البيانات فى قاعدة البيانات فهل ممكن حد يساعدنى
  25. بسم الله الرحمن الرحيم إستكمالاً لسلسلة مكتبات الأندرويد سنستعرض اليوم مكتبة من أهم المكتبات التي يستخدمها المطورون بشكل أساسي في تطوير التطبيقات على منصة الأندرويد. هذه المكتبة هي مكتبة Butter Knife. ولكن السؤال الأهم هو ما فائدة هذه المكتبة ؟ من المعروف لدى المطورين بأن طريقة ربط الـ View component مع الأوبجكت داخل كود الجافا هو باستخدام الأمر find view by id. ومن ثم ربط الأوبجكت عن طريق R.id.component. هذه الطريقة يوماً بعد يوم تثبت عدم فعاليتها وخصوصاً عند وجود الكثير من الـ components داخل الـ layout التي ستضطر لربطها بشكل يدوي. كما أن الربط العادي ستضطر في البداية إلى إنشاء المتغير في بداية الكلاس أما تعريفه فيجب أن يتم داخل onCreate method. هذه الطريقة باتت تثبت عدم فعاليتها يوماً بعد يوم. ButterKnife هي مكتبة تستطيع من خلالها ربط الأوبجكت مع الـ view component بشكل سريع وسلس وبشكل مباشر. كما تمكنك المكتبة من ربط الـ drawables والـ strings وغيرها وليست مقتصرة فقط على الـ views. كيف تعمل هذه المكتبة ؟ لنبدأ ... أولاً : وكما جرت العادة يجب إضافة هذه المكتبة داخل المشروع كـ dependency إضافية داخل ملف الـ gradle.build. من المهم أيضاً إضافة الـ annotationProcessor كي تعمل المكتبة بشكل صحيح. ومن ثم عمل sync لكي تتم الإضافة. compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' ثانياً: بعد عمل sync يمكننا الآن البدء في تصميم الـ layout .. طبعاً سأقوم بتصميم layout بسيطة فقط لتوضيح عمل هذه المكتبة. من الممكن ملاحظة بأن ملف الـ xml لم يتغير بتاتاً ولم نقم بإضافة أي كود إضافي وهذه ميزة إضافية أيضاً لأن هناك الكثير من المكتبات الأخرى التي توفر ميزة الربط ستضطر حين إستخدامها إلى إضافة تعديل بسيط داخل ملف الـ xml. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="15dp"> <TextView android:id="@+id/key" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="WEBSITE" /> <TextView android:id="@+id/value" android:layout_width="100dp" android:layout_height="50dp" android:gravity="center" android:text="3alamPro" android:layout_alignParentEnd="true"/> </RelativeLayout> صورة توضح الشكل النهائي للـ layout ثالثاً: سنبدأ الآن العمل على ملف الـ Java حيث سنتعلم بشكل أكبر طريقة عمل المكتبة. في البداية سنقوم كما جرت العادة بتعريف المتغيرات ولكن هذه المرة وقبل تعريفها سنقوم بكتابة @BindView(R.id.key) ليصبح الشكل النهائي للمتغيرات كالتالي public class MainActivity extends AppCompatActivity { @BindView(R.id.key) TextView key; @BindView(R.id.value) TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } بهذه الأسطر البسيطة سنكون قد أنتهينا تماماً من الربط .. طبعاً للمقارنة هذا ما سنقوم بكتابته بدون butterknife .. قد يبدو الفرق بسيط .. ولكن مع وجود الكثير من الـ components التي تحتاج للربط سيكون الفرق واضح. public class MainActivity extends AppCompatActivity { TextView key; TextView value; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); key = (TextView) findviewbyid(R.id.key); value = (TextView) findviewbyid(R.id.value); } رابعاً: لا يجب أن ننسى إضافة بعد تعريف onCreate method. ButterKnife.bind(this); لتصبح onCreate method في شكلها النهائي @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } هذا كان توضيح بسيط للميزة الأساسية التي تقدمها المكتبة .. ولكنها أيضاً تقدم المزيد من الإختصارات منها مثلاً [email protected] التي توفر لكن عناء ربط الـ strings الموجودة داخل ملف الـ values بدلاً من getResources().getStrings(R.strings.whatever) أيضاً توفر [email protected] وسنستعرض كيفية عملها. سنقوم الآن بإنشاء ملف Drawable عبارة عن إطار سنقوم بوضعه على key textview. نقوم بإنشاء ملف Drawable جديد ونسميه background على سبيل المثال بعد ذلك نقوم بإضافة الكود المرفق ( خلفية صفراء وحواف سوداء ) <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="#eee1"></solid> <stroke android:color="#000" android:width="10dp"></stroke> </shape> بالعودة لملف الجافا سنقوم بإنشاء أوبجكت جديد لملف الـ Drawable بطريقة الـ Bind. @BindDrawable(R.drawable.background) Drawable drawable; ومن ثم وضعه كخلفية للـ textview داخل onCreate method بعد ButterKnife.bind طبعاً key.setBackground(drawable); سنقوم أيضاً بتغيير الـ Text الموجودة داخل الـ textviews للتأكد بأن الربط يعمل بشكل صحيح key.setText("Website Bind"); value.setText("Pro3alam"); نقوم بتشغيل البرنامج والتأكد من النتيجة .. كل شي على ما يرام 👍 هل إنتهينا ؟ ليس بعد .. مازال هناك ميزة قوية تقدمها هذه المكتبة .. وهي الإستغناء التام عن الـ inner-classes الناتجة عن الـ Listener عند عمل setOnClickListener . عن طريق ButterKnife يمكنك بكل بساطة كتابة [email protected] ومن ثم الـ id الخاص بالـ view . على سبيل المثال .. سنقوم بإظهار Toast بسيط عند الضغط على أحد الـ textviews من خلال [email protected] @OnClick(R.id.key) void clicked1() { Toast.makeText(getApplicationContext(), "key", Toast.LENGTH_LONG).show(); } @OnClick(R.id.value) void clicked2() { Toast.makeText(getApplicationContext(), "value", Toast.LENGTH_LONG).show(); } عند تشغيل البرنامج وعند الضغط على الـ textviews سنرى بأن كل شي يعمل بشكل صحيح بهذا نكون قد إنتهينا من موضوعنا بإستعراض واحدة من أهم وأقوى المكاتب وسنقوم بإستعراض المزيد من المكتبات في قادم المواضيع بإذن الله
  26. الحمد الله وفقني الله واجتزت اختبار A.A.D والمقابلة بنجاح اولا ماهي شهادة A.A.D او Google Certified Android Associate Developer هي شهادة من قوقل لمطوري تطبيقات نظام الاندرويد , وهي بمثابة اعتراف من قوقل ان الحاصل عليها لديه المهارات اللازمة لتطوير وبناء وتصحيح تطبيقات الاندرويد من قوقل تجربتي مع الاختبار :- في البداية دخلت الصفحة الرسمية للاختبار وراجعت المتطلبات والمهارات التي يجب علي اتقانها للنجاح في الاختبار واستلام الشهادة , وجدت نفسي ان بعض المهارات ما اعرفها ولكن في نفس صفحة الاختبار وضعو كورس مجاني تابع لموقع udacity التعليمي فيها معظم متطلبات الاختبار وقالو انك اذا اكملت هذا الكورس بتكون جاهز في الاختبار , وفعلا عملت enroll لها واكملت الكورس في خلال شهرين تقريبا علما ان بعض محتويات الكورس كنت اعرفها وتخطيتها , ايضا اعتمدت على مراجع تعليمية اخرى لل Testing لان الكورس ما غطى جميع ال Testing skills المطلوبة في الشهادة مثل Unit test غير موجودة كانت وكانت احدى متطلبات الاختبار الي يجب علي اتقانها وانا كنت ضعيف فيه لذلك اعتمدت على مراجع اخرى ( بذكر جميعها في الاسفل) , وبعدين توكلت على الله وسجلت في الاختبار , والاختبار كان عبارة عن بروجكت ناقص فيه عيوب واخطاء برمجية وفيها متطلبات يجب عليي اكمالها في غضون 24 ساعة. سجلت في الاختبار الساعة 7:30 مساء واكملت جميع المتطلبات ساعة 12:30 صباحا يعني اخذ عني 5 ساعات تقريبا وبعدها نمت , وفي صباح اليوم التالي قمت بمراجعة المتطلبات مرة اخرى وتأكدت من الاكواد والطريقة الي اكملت فيها المتطلبات اكثر من مرة وبعدها توكلت على الله وعملت zip للبروجكت ورفعتها في صفحة الاختبار اخذ عني المراجعة ساعة ونصف تقريبا. وبعدها بيومين وصلتني رسالة مفادها اني اكملت جميع متطلبات البروجكت ونجحت في القسم الاول من الاختبار 😃 في الرسالة الي وصلتني فيها رابط ومطلوب مني اعمل موعد للمقابلة تسمى exit interview مع هوية صالحة او رخصة قيادة للتأكد من هويتي , وبناء على على المعلومات الموجودة في موقع الاختبار ان المقابلة عبارة عن اسالة تخص الاختبار نفسه واسالة عامة في تطوير تطبيقات الاندرويد ولكن ك احتياط قمت مرة اخرى بمراجعة البروجكت الي اكملته وفهمت طريقة عمل معظم الأكواد وتوكلت على الله وعملت موعد , والموعد عبارة عن مكالمة فيديو عن طريق تطبيق اسمه zoom . المقابلة كانت سلسلة للغاية , والمقابل في البداية سأل اسالة عامة في تطوير تطبيقات الاندرويد , اي مطور اندرويد لازم يعرفها وهي من اساسياته مثلا يسألك اذكر طرق عمل خاصية x او ماهي الطرق المثلى لعمل الخاصية y وايضا راح يسال اسالة تخص الاختبار نفسه , وبما اني اكملت القسم الاول في الاختبار كانت الاسألة متوقعة جدا وبعدها ب 3 ايام وصلتني رسالة في الايميل مفادها اني اكملت جميع متطلبات الاختبار وقريبا راح يرسلون لي الشهادة ك budget فيديو تعريفي للشهادة من قناة تطوير تطبيقات الاندرويد الرسمي من قوقل المهارات الي يجب ان تتقنها عشان تنجج باذن الله في الاختبار :- هذا مرجع في موقع github فيها مواقع تعليمية فيها دروس للمهارات الي لازم تعرفها ك مطور اندرويد وعشان تنجح في الاختبار https://github.com/Amejia481/Associate-Android-Developer-Certification كورسين ( متوسط - متقدم) تطوير تطبيقات اندرويد من يوداسيتي - مجاني https://www.udacity.com/course/new-android-fundamentals--ud851 هذه بعض المقالات لاشخاص حصلو على الشهادة وعرضو تجربتهم والي استفدت منها قبل الاختبار , انصح بقراتها http://www.3zcs.me/blog/2017/03/اختبار-associate-android-developer/ https://medium.com/default-to-open/taking-the-associate-android-developer-exam-c400d8e74363 https://medium.com/@multidots/how-to-clear-associate-android-developer-exam-ab3ae5d783b7 https://medium.com/@samesidem/my-journey-to-becoming-an-associate-android-developer-certified-by-google-10569896be0d سعر الاختبار 149 دولار امريكي . واخيرا هذا رابط التسجيل في الإختبار https://developers.google.com/training/certification/associate-android-developer/ اذا لديك اي استفسار انا حاظر و تقدر تمنشني في تويتر او تترك لي تعليق هنا @AlHadrami07 بالتوفيق للجميع . اخوكم عبد الله.
  27. أهلا وسهلاً بكم في لمحة مبسطة عن الــ BroadcastReceiver ، سأتكلم في هذي المقالة عن تعريف الـ BroadcastReceiver و طريقة اضافتها لبرنامجك .. تنويه : اللغة المستخدمة في الشرح هي kotlin ماهو BroadcastReceiver ؟ هو عبارة عن أرسال و استقبال بين البرنامج و النظام ، عند حدوث event معين يكون معرف مسبقا في النظام او يتم تعريفه من قبل المبرمج . في البداية دعنا نلقي نظرة على المعرفة مسبقا في النظام - بعض منها : Battery Low WI-Fi connected BATTERY_OKAY Incomming SMS AIRPLANE_MODE BATTERY_CHANGED ACTION_POWER_CONNECTED الأن عندما تريد إنشاء حدث معين أنت أمام خيارين هما : statically BroadcastReceiver * هو اضافة حدث في ملف الـــ AndroidMainfest.xml أو Dynamic BroadcastReceiver * تسجيل الحدث بإستخدام ال جافا / الكوتلين داخل ال activity . ملاحظة : ال BroadcastReceiver لا يقبل عمليات تاخذ وقت في تنفيذها مثل استخرج بيانات أو ارسال بيانات او عمل مؤقت .... الخ ، لانه يعمل في ال main thread . لنأخذ مثال بسيطا لعمل برنامج يظهر نص عند الضغط على الزر بإستخدام الــ statically BroadcastReceiver : في البداية دعنا ننشي كلاس يظهر لي رسالة عند ضعط المستخدم على الزر وليكن اسم هذا الكلاس : MybroadcastReceiver class MybroadcastReceiver:BroadcastReceiver() { override fun onReceive(p0: Context?, p1: Intent?) { Toast.makeText(p0!!," Hello form First receiver ",Toast.LENGTH_LONG).show() } } الان ننتقل الى كلاس ال MainActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // send receiver when clicked button btn_sendReceiver.setOnClickListener({ // declared intent and pass MybroadcastReceiver ... var intent = Intent(this,MybroadcastReceiver::class.java) sendBroadcast(intent) }) } الأن في ملف ال AndroidMainfest.xml نقوم بتسجل هذا الحدث : // After activity tag .. <receiver android:name=".MybroadcastReceiver"> </receiver> النتيجة : Pesudo Code 1 - craete subClass extends BroadcastReceiver . 2 -override the onReceiver method . 3- add receiver on AndroidMainfest.xml 4 - create event to send data . 5 - declaerd intent . 6 - sendBroadcastReceiver(intent) . #### عمل ( InnerClass BroadcastReceiver ) بنفس عمل الآلية السابقة ، نحتاج الى كلاس يراث من broadcastReceiver و أكشن يشير الى هذا الكلاس ... هذي المرة سأقوم بتعريف كلاس داخلي يشير الى الأكشن .. MainActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // send receiver when clicked button btn_sendReceiver.setOnClickListener({ // declared intent and put action ... var intent = Intent("send.msg.receiver") sendBroadcast(intent) }) } /***** Start InnerClass **************/ public class MybroadcastInner:BroadcastReceiver(){ override fun onReceive(p0: Context?, p1: Intent?) { Toast.makeText(p0!!," Hello form InnerReceiver receiver ",Toast.LENGTH_LONG).show() } } /***** End InnerClass **************/ } لاحظ انه تم تمرير اكشن - action - في ال intent ، هذا الاكشن سيتم تعريفه في ملف الــ AndroidMainfest.xml ملاحظة : عند كتابة الكود بالجافا تحتاج الى اضافة كلمة static قبل اسم الكلاس الداخلي مثلا : public static class MybroadcastInner extends BroadcastReceiver{} ملف ال AndroidMainfest.xml : // After activity Tag . <receiver android:name=".MainActivity$MybroadcastInner"> <intent-filter> <action android:name="send.msg.receiver"/> </intent-filter> </receiver> لاحظ انه تم تعريف الكلاس الاساسي ثم تم وضع علامة ( $) قبل اسم الكلاس الداخلي لكي يتمكن ملف الاندرويد من التعرف على الكلاس الداخلي ثم بعد ذلك تم إنشاء intent-filter بداخله اكشن - action - لاحظ ان اسم الاكشن هو نفس الاسم الذي تم تمريره لل intent . فعند التنفيذ ستظهر نفس النتيجة . في هذا الجزء تم التعرف على كتابة كلاس داخلي ، و الوصول له من خلال ملف AndroidMainfest.xml تنويه : يمكنك تمرير أكشن الى الــ Intent حتى لو كان ال BroadcastReceiver في كلاس منفصل ، مثل ما عملنا في الجزء السابق . في المقالة القادمة ، سأتحدث عن Dynamic BroadcastReceiver . دمتم بخير .
  28. السلام عليكم ورحمة الله وبركاته طرحنا مسابقة عالم البرمجة للنقاش الهادف ، و هدفنا إثراء المحتوى العربي في مجال البرمجة ، فاز معنا الطرح المتميز و المفيد حيث قام بتقييم المقالات وترقيتها فريق عالم البرمجة سنذكر بهذه المقالة الفائزين معنا ، و مقالاتهم ، و نشكرهم على ماقدموا من فائدة للجميع ، و إثراء المحتوى العربي في البرمجة. الفائزون في شهر September-2017 المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: المقال الفائز: في الختام: هدفنا في موقع عالم البرمجة إثراء المحتوى العربي في مجال البرمجة ، و تعزيز حب المساعدة بين المبرمجين تستطيع كتابة ماتحب في ساحات النقاش ، و يمكن تكون احد الفائزين معنا بمسابقة عالم البرمجة للنقاش الهادف فهي مازالت مستمره ايضا تستطيع مساعدة المبرمجين بالإجابة عن أسئلتهم ، و حل المشاكل التي تواجههم بقسم سؤال وجواب ؛ لتكون مرجع لبقية المبرمجين شعارنا في عالم البرمجة "إن في قضاء حوائج الناس لذة لا يَعرفها إلا من جربها، فافعل الخير مهما استصغرته فإنك لا تدري أي حسنة تدخلك الجنة." -ابن القيم- وممكن تستفيد من حبيبي القارئ اعلم ان فريق عالم البرمجة يصب كل جهودة لمساعدة المبرمجين الذين يخصصون من وقتهم لنشر العلم المفيد ويساعدون الغير ويارب يقدرنا نوقف معكم ونساعدكم قد مانقدر.
  1. عرض المزيد من النشاطات

عالم البرمجة

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