1. الـ Context يعد تقريباً من اكثر الامور المستخدمة في برمجة الاندرويد, وبالتأكيد قد تعاملت معه في تطبيقك ولكن كيف يعمل وماهو بالضبط؟
    مفهوم الـ Context
    افضل طريقة لشرح مفهوم الـ Context هو بالمثال, دعنا نفترض ان لديك مبنى وبداخل المبنى مجموعة من المخازن او المستودعات لتخزين الاغراض, وبداخل كل مستودع بعض الصناديق وكل صندوق بداخله بعض الاغراض, فاذا طلبت منك مثلا ان تحضر لي غرض ما من مستودع ما بداخل المبنى فهنا انا اخبرتك بالوجهة الاولى او المحيط الذي يجب عليك ان تبحث فيه وهو المبنى, ولكن لكي اسهل عملية البحث عليك ساحدد لك المستودع وبشكل اعمق ساحدد لك الصندوق الذي يجب ان تبحث بداخله, فبهذه الطريقة قمت بتضيق المحيط الذي يجب ان تبحث بداخله لكي اسهل عليك مهمة البحث .
    لنطبق نفس المثال السابق على الاندرويد, اعتبر ان جهازك هو المبنى وبداخل جهازك مجموعة من التطبيقات (مستودع) و كل تطبيق معزول تماماً عن بقية البرامج الاخرى, وبداخل كل تطبيق بعض المجلدات وكل مجلد مختص بامر ما, فبعض المجلدات قد تجد بداخله صور وبعضها الاخر قد تجد بداخله نصوص او غيرها.
    الان ان اردت مثلاً ان اصل الى صورة موجودة في مجلد الصور الخاصة بـ تطبيق X, فيجب علي ان احدد المحيط (Context)الذي يجب ان ابحث بداخله, فالمحيط  هنا هو التطبيق X, ويسمى هذا المحيط في الاندرويد بالـContext.
    من احد النقاط المهمة ايضا التي يجب ان نوضحها هي ان محيط الغرفة بداخله مجموعة من الصناديق, وهذه الصناديق تنتمي للغرفة وهذا يعني انه من محيط الغرفة استطيع الوصول الى اي صندوق اريده ولكن من محيط الصندوق لا استطيع الوصول الى بقية الصناديق الا عن طريق الغرفة.

    ماهو الـ Context
    ان اردنا تعريفه من منظور برمجي فالـ Context يعد Abstract Class تستطيع من خلاله الوصول الى ملفات وموارد التطبيق او تستطيع من خلاله استدعاء اوامر موجودة في النظام مثل  launching activities, broadcasting and receiving intents في نطاق المحيط الخاص بالتطبيق.
    انواع الـ Context

    الـ Context له العديد من الانواع وليست جميعها متشابهه اثناء انشائها, فنوعها يعتمد على الـ Component الذي قام بانشائها:
    Application
    الـ Context من هذا النوع يعد singleton وهذا يعني ان تطبيقك لديه Context واحد فقط من نوع application ومن خلال هذا الـ context تستطيع الوصول الى جميع المصادر (resuorese) والملفات الخاصة بالتطبيق سواء كانت صور او واجهات او غيرها, تستطيع استدعاء هذا الـ Context طريق الميثود  getApplication() من اي Activity او Service, وتستطيع استدعائه عن طريق getApplicationContext() من اي Object يرث من Context, من ناحية اخرى لايهم من اي Class قمت باستدعائهلانه في جميع الاحوال سيرجع لك نفس الـ Context.
    Activity/Service
    هذا النوع يرث من ContextWrapper وهو مايعرف بـ base context, فعند انشائك لـ Activity or Service جديدة, سيقوم النظام بانشاء Context خاص بها, وهذا يعني اذا قمت بانشاء two acttivities سيقوم النظام بانشاء Context لكل Acticity, و مايجب عليك الانتباه له هو ان كل context قام النظام بانشائه هو معزول تماماً عن الاخر اي ان الـ context الخاص بالـ Activity الاول ليس له الصلاحية للوصول الـ Context الاكتفتي الثانيه الا عن طريق Application Context وهو الذي يجمعهم سويه.
    Broadcast Receiver
    لايحتوي على Context خاص به ولايقوم النظام بانشاء واحد خاص به مثل الـ Activity, ولكن مايفعله النظام هو انه يمرر له Context عند استدعائه, والـ Context الذي سيقوم النظام بتمريره له هو الـ Context الذي قام باستدعائه, فمثلا لو قام Service بالتعامل مع الـ Broadcast Receiver سيقوم النظام بتمرير الـ Context الخاص بالسيرفس عن طريق ميثود  onReceive() في كل مرة يتم استدعائه فيها.
    Content Provider
    ايضا لاتحتوي على Context خاص بها, ولكن يعطى لها Context عند انشائها والتي يمكن الوصول لها عن طريق getContext().
    صلاحيات الـ Context
    كما قلنا سابقاً ان هناك انواع عديدة من الـ Context وبعضها معزول تماماً عن الاخر “baseContext” وبعضها لديه الصلاحية للوصول الى Context اصغر منه “ApplicationContext”, فبعضها تستطيع من خلاله الوصول الى بعض الصلاحيات وبعضها لاتستطيع

    تنبيهات للتعامل مع الـ Context
    لإستدعاء الـ Context الخاص بالـ Application , قم باستدعائه مباشرة عن طريق getApplicationContext() لاستدعاء الـ Context الخاص بالـ Activity, قم باستدعائه عن طريق getBaseContext() لاتقم ابداً بحفظ الـContext الخاص بالـ Application بداخل اوبجكت من Context وانما قم باستدعائه دائما عن طريق getApplicationContext() اخيراً
    الـ Context من احد اهم المفاهيم في برمجة الاندرويد والتي يجب عليك معرفتها ومعرفة انواعها وطريقة التعامل معها, فكل نوع لديه صلاحياته الخاصة وكل نوع يجب عليك استخدامه في مكانه المناسب والوقت المناسب.
    مستوى المقال: متوسط
  2. السلام عليكم و رحمة الله وبركاته.
     
    في هذه المقالة سنتعلم كيفية التعامل مع ListView في Android Studio و كيفية إنشائه وبنائه داخل تطبيقك.
     
    ملاحظة: يفضل أن تكون قد قرأت مقالتي السابقة عن كيفية التعامل مع RSS Feed في Android Studio من هنا.  
    في البداية، ما هو الـListView ؟
    الـListView هو  أداة من أدوات واجهة المستخدم (View) و التي تستخدم لعرض مجموعة من البيانات من خلالها و التي بدورها تتيح ميزة الـScroll Bar. و تحديدا تستخدم في عرض البيانات التي تكون على شكل مجموعة عناصر مثل قائمة لمجموعة من الصور أو أسماء فريق كرة القدم ..الخ. علما أنه لا يكاد يوجد تطبيق في Play Store إلا ويوجد فيه الـListView بما في تلك التطبيقات الشهيرة كتطبيق Facebook فكل البوستات الموجودة في الـFacebook و التي تشاهدها في الصفحة الرئيسية عند فتح التطبيق ما هي إلا شرائح من البيانات المعروضة باستخدام الـListView و كذلك في Twitter وInstagram و غيرها.. .
     
    و لذلك يعتبر الـListView من أهم الأدوات الموجودة في برنامج الـAndroid Studio  و من أكثر الأدوات استخداما في بناء التطبيقات إن لم يكن أكثرها لذلك من المهم لمبرمج التطبيقات أن يتعلم طريقة إنشائه و التعامل معه في التطبيق و ما سنقوم بعمله في هذه المقالة هو عرض قائمة للبيانات المحملة و المعالجة في المقالة السابقة و هي Top10Appsباستخدام الـListView.
     
    ما هي آلية عمل الـListView؟

    لإنشاء View لكل شريحة من البيانات يتطلب هذا الأمر الكثير من الوقت و المساحة من الـComputer حيث أن كل View تحتاج تقريبا اللى 1 Kb من RAM فتخيل لو تم انشاء الألاف من Views.
    فهنا تكمن فائدة الـListView، حيث أنها تعمل على إعادة استخدام الـView المنشأة سابقا عندما يقوم المستخدم بالنزول أو الطلوع بالقائمة (Scrolling).
     
     تتلقى الـListView بياناتها من Adapter و الذي بدوره يقوم بتوفير البيانات و تنسيق الـView المراد عرض البيانات عليها كما هو موضح أدناه.


     
    و الآن ننتقل للتطبيق في الـAndroid Studio:
     
    في البداية نقوم بفتح Android Studio ثم نقوم بالضغط على File ثم Open و ننتقل الى الـProject الذي يحتوي على الملفات و الكلاسات التي تم انشاؤها في المقالة السابقة.


    و الآن حتى يتم إنشاء ListView يجب علينا ما يلي:
     
    الانتقال الى ملف activity_main.xml. من قائمة Pallete ثم container نبحث عن الـListView و نقوم بسحبها الى Layout. نعدل الـID و لتكن على سبيل المثال xmlListView. نعدل الـMargins و التي هي: 16,16,16,16 كما هو موضح ادناه.
     
    و الآن نقوم بانشاء layout حيث ان هذا الـlayout يحتوي على الـViews المراد تنسيقها ووضع البيانات بداخلها عن طريق الـAdapter:
    من قائمة File ثم new ثم نقوم بالضغط على Layout resource file. نقوم بتسميته على سبيل المثال list_record.xml. نضيف اليه ثلاثة Text View من قائمة Pallete. نعدل الـID لهذه الـText Views وليكن على سبيل المثال: tvName, tvArtist, tvSummary. نعدل الـMargins على عحسب الرغبة. نعدل الـConstraints  كما هو موضح أدناه.  

     
    و الآن حتى نقوم بانشاء الـAdapter يجب علينا ما يلي:
     
    انشاء كلاس و نسميه على سبيل المثال FeedAdapter.  حيث أن هذا الكلاس Extends الـArrayAdapter. و يحتوي أيضا على Inner Class ويسمى بالـViewHolder. package com.example.rami.top10apps; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.TextView; import java.util.List; /** * Created by Rami on 2/19/2017. */ public class FeedAdapter extends ArrayAdapter { private static final String TAG = "FeedAdapter"; private final int layoutResource; private final LayoutInflater layoutInflater; private List<FeedEntry> applications; public FeedAdapter(Context context, int resource, List<FeedEntry> applications) { super(context, resource); this.layoutResource = resource; this.layoutInflater = LayoutInflater.from(context); this.applications = applications; } @Override public int getCount() { return applications.size(); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { convertView = layoutInflater.inflate(layoutResource, parent, false); viewHolder = new ViewHolder(convertView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } FeedEntry currentApp = applications.get(position); viewHolder.tvName.setText(currentApp.getName()); viewHolder.tvArtist.setText(currentApp.getArtist()); viewHolder.tvSummary.setText(currentApp.getSummary()); return convertView; } private class ViewHolder { final TextView tvName; final TextView tvArtist; final TextView tvSummary; ViewHolder(View v) { this.tvArtist = (TextView) v.findViewById(R.id.tvArtist); this.tvName = (TextView) v.findViewById(R.id.tvName); this.tvSummary = (TextView) v.findViewById(R.id.tvSummary); } } }
    و لقد قمنا بعمل التالي:
    إنشاء layoutResource variable  من نوع int و الذي يحتوي على Layout الذي أنشأناه و أسميناه بالـList_record.xml. إنشاء layoutInflator variable من نوع LayoutInflator و الذي يقوم بتحويل الـLayout الى اوبجكت يمكن التعديل عليه. إنشاء applications variable  من نوع <List<FeedEntry و الذي يحتوي على البيانات المراد عرضها. إنشاء Constructor يتلقى الباراميترز الخاصة بالـinstance variables. عمل override للـmethods التالية:() getCount(), getView. الميثود getCount تقوم بإرجاع عدد العناصر الموجودة بالـList. الميثود getView يتم استدعاؤها تلقائيا من ListView عند الحاجة الى بيانات جديدة و يتم تمرير الـParameters التالية اليها: position, convertView, parent. و تقوم هذه الميثود بعمل التالي:
    إنشاء اوبجكت من الـViewHolder بحيث يتم تخزين الـTextViews بهذا الاوجكت لمرة واحدة. حفظ هذا الاوبجكت في الـTag الخاص بالـconvertView.  يتم تعديل الـTextViews وفقا للبيانات المستخرجة من الاوبجكت currentApp.  
    بالنهاية، يتم إنشاء Inner class و تسميته بالـViewHolder.  
    من الممكن أن تتساءل ما فائدة الـViewHolder؟
    كما تلاحظ يتم استدعاء الميثود findViewById لمرة واحدة في بداية تشغيل التطبيق بينما من دون هذا الكلاس سوف يتم استدعاء هذه الميثود كل ما احتاج الـListView الى بيانات جديدة.
     
    و الآن نذهب الى الـInner class في الـكلاس MainActivity و الذي يسمى DownloadData، و نقوم بوضع هذا الكود ادناه في الميثود onPostExcute.
    @Override protected void onPostExecute(String s) { super.onPostExecute(s); Log.d(TAG, "onPostExecute: parameter is " + s); ParseApplications parseApplications = new ParseApplications(); parseApplications.parse(s); FeedAdapter feedAdapter = new FeedAdapter(MainActivity.this, R.layout.list_record, parseApplications.getApplications()); appList.setAdapter(feedAdapter); } كم نلاحظ أعلاه:
    تم إنشاء اوبجكت من الكلاس ParseApplications و الذي بدوره يقوم بمعالجة البيانات المحملة. إنشاء اوبجكت من الكلاس FeedAdapter و تمرير الـarguments اللازمة للـAdapter و أخيرا يتم استدعاء الميثود setAdapter على الاوبجكت appList و الذي يمثل الـxmlListView.  
    ملاحظة: الكلاس DownloadData تم إنشاؤه في المقالة السابقة.

    و أخيرا نقوم بتشغيل البرنامج وهذه هي النتيجة النهائية.


     
    و هنا وصلنا الى نهاية هذا الدرس.😄
    في الدروس القادمة سوف أتناول كيفية التعامل مع ملفات الـJson في الـAndroid.
    مستوى المقال: متوسط
  3. بسم الله الرحمن الرحيم
    سنتحدث اليوم بإذن الله عن الـ Geofence, وهو من المواضيع الشيقة في التطوير لأندرويد, جميل جدا, لنبدأ.
    لنفترض جدلا أن لدينا محل تجاري يقدم عروض خاصة عن طريق التطبيق الخاص به, فقط لزبائنه الذين هم جلوس داخل المحل أو قريبين منه بمسافة 50 م, عن طريق إظهار notification مكتوب فيها خصم 10%, وعند الضغط على notification تتجه لصحفة لا يمكن الوصول إليها إلا عن طريق هذا notification, سيكون هذا المثال الذي نطبق عليه.
    بداية ما هو الـ Geofence, هو عبارة عن API يتيح لك معرفة ما إذا كان المستخدم قريب من أماكن تود أنت كمبرمج أن تقدم له تنبيه أو Action معين.
    قبل البدأ سنقوم بتجهير ملفي gradle و manifest.
    بالنسبة لملف gradle سنحتاج لأن نضيف google play services لتطبيق, عن طريق إضافة  هذا السطر في dependencies - هذه المكتبة بأحدث إصدار وقت كتابة هذه المقالة -
    compile 'com.google.android.gms:play-services:10.0.1' أما في ملف manifest سنضيف رقم النسخة كـ meta-data, ورقم النسخة يضاف مباشرة في string.xml بعد عمل sync إذا تم تحديث gradle
     
    <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> ولا ننسى أن نطلب صلاحية الوصول لـ Location
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> الاّن التطبيق جاهز من ناحية المكتبات لنبدأ في كتابة الكود
    سيكون لدينا intent service تقوم بإضافة المواقع التي نرغب أن ننبه المستخدم إذا كان بمقربة منها, و service أخرى لإظهار notification
    سنبدأ بـ intent service ونسميها AddLocationService, أولا نقوم بتعريفها في ملف manifest
    <service android:name=".AddLocationService" /> نبدأ بعمل implements لثلاث Interfaces وهي
    ConnectionCallbacks  وهي تطلب منك عمل override لـ onConnected
    OnConnectionFailedListener وتطلب من عمل override لـ onConnectionSuspended و onConnectionFailed
    ResultCallback<T> وتطلب من عمل override لـ onResult والتي تخبرك عن حالة الطلب الخاص بك من حيث النجاح أو الفشل
    ثم نحتاج لـ object من نوع GoogleApiClient حيث سيكون الاتصال عن طريقه ويقوم بشكل ذاتي بإدارة الإتصال, ونسند له API الذي نريد استخدامه ألا وهو LocationServices
    لنلقي نظرة على شكل الكود حتى هذه اللحظة
    public class AddLocationService extends IntentService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status>{ GoogleApiClient mGoogleApiClient; /** * Creates an IntentService. Invoked by your subclass's constructor. * * String AddLocationService important only for debugging. */ public AddLocationService() { super("AddLocationService"); } @Override protected void onHandleIntent(@Nullable Intent intent) { mGoogleApiClient = new GoogleApiClient.Builder(this) .addOnConnectionFailedListener(this) .addConnectionCallbacks(this) .addApi(LocationServices.API) .build(); mGoogleApiClient.connect(); } @Override public void onConnected(@Nullable Bundle bundle) {} @Override public void onConnectionSuspended(int i) {} @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {} @Override public void onResult(@NonNull Status status) {} جميل جدا, في حالة كان هناك مشكلة في الاتصال أو تعطل الاتصال لفترة بسيطة سنضع log في onConnectionSuspended, onConnectionFailed كنوع من debug لمعرفة نوع الخطأ .
    أما في حالة onConnected سنبدأ في إنشاء geofence الخاص بنا كالتالي
    @Override public void onConnected(@Nullable Bundle bundle) { try { LocationServices.GeofencingApi.addGeofences( mGoogleApiClient, // The GeofenceRequest object. getGeofencingRequest(), getGeofencePendingIntent() ).setResultCallback(this); // Result processed in onResult(). } catch (SecurityException securityException) { // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission. Log.i(getClass().getSimpleName(),securityException.getMessage()); } } كما تلاحظ استدعينا دالة addGeofences التي نبني فيها Geofence الخاص بنا, وتستقبل ثلاث متغيرات وهي google api client وهو الذي قمنا ببناءه سلفاً, وقلنا أنه مسؤول عن إدارة الاتصال, و geofencingRequest سنتحدث عنها بعد قليل, وPendingIntent سنتحدث عنها لاحقا في هذه المقالة هي الأخرى أيضا.
    فيكون شكل الدالة كالتالي
    PendingResult<Status> addGeofences(GoogleApiClient var1, GeofencingRequest var2, PendingIntent var3)
    وسيرجع لنا status التي ستبين لنا حالة الـ request من حيث النجاح أو الفشل.
    نلاحظ أن لدينا two function وهي getGeofencingRequest و getGeofencePendingIntent
    لنبدأ بطريقة عمل GeofencingRequest وهي المسؤلة عن إضافة geofence list التي نريد متابعتها وإظهار التنبيه بشكل مناسب, وهي كالتالي
    private GeofencingRequest getGeofencingRequest() { GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_DWELL); builder.addGeofences(getGeofecne()); return builder.build(); } طبعا هو يستخدم builder pattern ونلاحظ أنه يطلب setInitialTrigger و addGeofences
    لدينا ثلاث أنوع من Triggers
    INITIAL_TRIGGER_DWELL : نستخدمه عندما يكون المستخدم داخل المنطقة التي حددناها لبعض الوقت, ويكون لدينا GEOFENCE_TRANSITION_DWELL.
    INITIAL_TRIGGER_ENTER : نستخدمه عندما يكون المستخدم داخل المنطقة التي حددناها, ويكون لدينا GEOFENCE_TRANSITION_ENTER
    INITIAL_TRIGGER_EXIT : نستخدمه عندما يكون المستخدم خارج المنطقة التي حددناها, ويكون لدينا GEOFENCE_TRANSITION_EXIT
    جميل جداً تعرفنا على أنواع Triggers التي قد نضعها في setInitialTrigger, وفي حالتنا سنستخدم INITIAL_TRIGGER_DWELL و INITIAL_TRIGGER_ENTER
    في الحقيقة نحن نريد GEOFENCE_TRANSITION_DWELL فقط لكن لاستخدامه تكون دائما مضطر لاستخدام INITIAL_TRIGGER_ENTER معه, لانه يمكننا من معرفه لحظة دخول الشخص إلى المنطقة المحدده والبدء بحساب الوقت الذي نقدره, قبل إظهار التنبيه.
    بالنسبة لـ addGeofences فهي list of geofence وهو من أهم الجزئيات في geofencing
    getGeofecne تبنى كالتالي
    private List<Geofence> getGeofecne(){ List<Geofence> mGeofenceList = new ArrayList<>(); //add one object mGeofenceList.add(new Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId("key") // Set the circular region of this geofence. .setCircularRegion( 25.768466, //lat 47.567625, //long 50) // radios // Set the expiration duration of the geofence. This geofence gets automatically // removed after this period of time. //1000 millis * 60 sec * 5 min .setExpirationDuration(1000 * 60 * 5) // Set the transition types of interest. Alerts are only generated for these // transition. We track entry and exit transitions in this sample. .setTransitionTypes( Geofence.GEOFENCE_TRANSITION_DWELL) //Time before fire notification .setLoiteringDelay(3000) // Create the geofence. .build()); return mGeofenceList; } قمنا بإنشاء list of geofence وأضفنا Object واحد ولإنشاء object ستحتاج إلى عدة أمور
    Id نعرف به geofence ويكون من نوع string
    تحديد المحيط الخاص بالمنطقة عن طريق ثلاث أمور setCircularRegion( LATITUDE, LONGITUDE, RADIUS)
     تحديد الوقت الذي بعده يتم حذف geofence من list والوقت يحدد بالجزء من الثانية  في setExpirationDuration(1000 * 60 * 5) حالة وضعنا NEVER_EXPIRE سيتم إشعار اليوزر في كل مرة حسب نوع الحالة التي نضعها, عند كل دخول, أو عند بقائه فترة محدد, أو عند خروجه.
    تحديد متى يتم تنبيه المستخدم, حال الدخول, أو الخروج أو عندما يكون داخل المنطقة لمدة من الزمن عن طريق setTransitionTypes() وتستخدم أحد هذه الثوابت أو جميعها
    GEOFENCE_TRANSITION_ENTER تحدثنا عنها في getGeofencingRequest أنها تستخدم مع INITIAL_TRIGGER_ENTER
    GEOFENCE_TRANSITION_DWELL فتستخدم مع INITIAL_TRIGGER_ENTER و INITIAL_TRIGGER_DWELL
    GEOFENCE_TRANSITION_EXIT تستخدم مع INITIAL_TRIGGER_EXIT
    لذلك يجب التنبه لوضع trigger في request مع transition الصحيح في geofecne.
    أضفت هنا object واحد لتبسيط المثال لكن بإمكانك إضافة الكثير وبطريقة dynmic أيضا
    الاّن أكلمنا GeofenceRequest
    لننتقل لـ PendingIntent
    private PendingIntent getGeofencePendingIntent() { // Reuse the PendingIntent if we already have it. if (mGeofencePendingIntent != null) { return mGeofencePendingIntent; } Intent intent = new Intent(this, GeofenceTransitionsIntentService.class); return PendingIntent.getService(this, 0, intent, PendingIntent. FLAG_UPDATE_CURRENT); } قمنا ببناء pending intent بسيطة جدا الغرض منها إخراج notification للمستخدم, عن طريق استدعاء GeofenceTransitionsIntentService class وهو class الثاني الذي تحدثنا عنه في بداية المقالة
    جميل جدا
    public class GeofenceTransitionsIntentService extends IntentService { protected static final String TAG = "GeofenceTransitionsIS"; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public GeofenceTransitionsIntentService() { // Use the TAG to name the worker thread. super(TAG); } /** * Handles incoming intents. * @param intent sent by Location Services. This Intent is provided to Location * Services (inside a PendingIntent) when addGeofences() is called. */ @Override protected void onHandleIntent(Intent intent) { } الشكل الأولي لـ service, سنضيف logic الخاص بنا في onHandleIntnet, وهو قراءة البيانات القادمة في intent عن طريق دالة GeofencingEvent.fromIntent(intent) وسنحفظها في geofencingEvent
    ثم نتأكد من أنها سليمة عن طريق code التالي
    if (geofencingEvent.hasError()) { Log.e(TAG, getErrorString(geofencingEvent.getErrorCode())); return; } public String getErrorString(int errorCode) { switch (errorCode) { case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE: return "not Available"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES: return "Too many Geofences"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS: return "Too many Pending Intents"; default: return "unknown geofence error"; } } لكل خطأ في geofence رقم معين وفي حال حدوثه سيظهر هنا في هذا log, فأحد الأخطاء مثلا, هو أن تضيف أكثر من 100 geofecne في geofence list, فسيظهر لك خطأ GEOFENCE_TOO_MANY_GEOFENCES, وبقية الأخطاء على نفس النسق
    بعد ذلك نتعرف على نوع Transition القادم والذي تم اسناده سابقا عن طريق دالة setTransitionTypes
    وعلى ضوءه نحدد notification التي نخرجها فيكون شكل class النهائي, كالتالي
    public class GeofenceTransitionsIntentService extends IntentService { protected static final String TAG = "GeofenceTransitionsIS"; /** * This constructor is required, and calls the super IntentService(String) * constructor with the name for a worker thread. */ public GeofenceTransitionsIntentService() { // Use the TAG to name the worker thread. super(TAG); } /** * Handles incoming intents. * @param intent sent by Location Services. This Intent is provided to Location * Services (inside a PendingIntent) when addGeofences() is called. */ @Override protected void onHandleIntent(Intent intent) { GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); if (geofencingEvent.hasError()) { Log.e(TAG, getErrorString(geofencingEvent.getErrorCode())); return; } // Get the transition type. int geofenceTransition = geofencingEvent.getGeofenceTransition(); // Test that the reported transition was of interest. if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) { // Get the transition details as a String. String geofenceTransitionDetails = "Discount 10% for you"; // Send notification and log the transition details. sendNotification(geofenceTransitionDetails); Log.i(TAG, geofenceTransitionDetails); } else { // Log the error. Log.e(TAG, getString(R.string.geofence_transition_invalid_type + geofenceTransition)); } } public String getErrorString(int errorCode) { switch (errorCode) { case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE: return "not Available"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES: return "Too many Geofences"; case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS: return "Too many Pending Intents"; default: return "unknown geofence error"; } } /** * Posts a notification in the notification bar when a transition is detected. * If the user clicks the notification, control goes to the MainActivity. */ private void sendNotification(String notificationDetails) { // Create an explicit content Intent that starts the main Activity. Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); // Construct a task stack. TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); // Add the main Activity to the task stack as the parent. stackBuilder.addParentStack(MainActivity.class); // Push the content Intent onto the stack. stackBuilder.addNextIntent(notificationIntent); // Get a PendingIntent containing the entire back stack. PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Get a notification builder that's compatible with platform versions >= 4 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); // Define the notification settings. builder.setSmallIcon(R.drawable.common_google_signin_btn_icon_dark_normal) // In a real app, you may want to use a library like Volley // to decode the Bitmap. .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.cast_abc_scrubber_primary_mtrl_alpha)) .setColor(Color.RED) .setContentTitle(notificationDetails) .setContentText(getString(R.string.geofence_transition_notification_text)) .setContentIntent(notificationPendingIntent); // Dismiss notification once the user touches it. builder.setAutoCancel(true); // Get an instance of the Notification manager NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // Issue the notification mNotificationManager.notify(0, builder.build()); } } وقد أضفت فيه جزئية إظهار notifiction ولن أتطرق لها بالشرح في هذه المقالة لكيلا تطول فيمل القارئ, في الـ Main Activity أضفت سطر واحد يشغل service
    @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startService(new Intent(this,AddLocationService.class)); } طيب بكذا يكون تطبيقنا أكتمل وقابل لتجربة لكن بعطيك معلومة سريعة بما إننا استخدمنا IntentService, فهي راح تستقبل الأوامر عن طريق onHandleIntent حتى تنهيها وبعد كذا راح تسوي stop وبعدها destroy, طبعا قريب حلول كثيرة أشهرها استخدام broadcast receiver, اختبر نفسك وحاول تطبق geofence معاه, ورفعت هنا على github الكود الخاص بالمثال هذا مع  broadcast receiver.
     
    تأكد من أن تكون قد قمت بإعطاء التطبيق الصلاحيات المطلوبة قبل عمل اختبار لتطبيق, عن طريق الإعدادات -> التطبيقات -> الصلاحيات
     
    هذا وصلى الله وسلم وبارك
    المراجع:
    https://developer.android.com/training/location/geofencing.html
    https://code.tutsplus.com/tutorials/how-to-work-with-geofences-on-android--cms-26639
      Android programming big nerd ranch guide 
    Android Application Development Cookbook
    واستفدت كثيرا من الأسئلة المطروحة في stack overflow
    مستوى المقال: مبتدئ
  4. السلام عليكم و رحمة الله و بركاته.

    في هذه المقالة سنتعلم كيفية التعامل مع RSS Feed عن طريق تحميلها و قرائتها بالإضافة الى استخراجها في الـAndroid  Studio.
    في البداية، ما هي RSS ؟

    خدمة RSS هي خدمة لمتابعة آخر الأخبار بشكل مباشر وبدون الحاجة إلى زيارة الموقع ، ستقدم لك خدمة RSS  الكثير من المعلومات منها: عنوان الخبر ، ومختصر لنص الخبر ، ووصلة أو رابط لنص الخبر الكامل على الموقع ، و صورة عن الخبر، بالإضافة إلى عدد التعليقات الموجودة ... الخ.
    و الآن ننتقل الى التطبيق في الـ Android Studio...

    في البداية نقوم بفتح Android Studio ثم نقوم بالضغط على Start a new Android Studio project و نبقي الخيارات الافتراضية على حالها و نقوم بتسميته و حفظه.
     
    و الآن حتى يتم تحميل XML file  يجب علينا:
    انشاء thread منفصل و لحسن الحظ يتوفر على الـ Android Studio كلاس يسمى بالـ AsyncTask يقوم باغلب المهام. نقوم بانشاء Inner class  في MainActivity.java و الذي بدوره يقوم بـ AsyncTask class" extends"، ثم نضيف الmethods وهي onPostExecute ،doInBackground كما هو موضح ادناه:  
    public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } private class DownloadData extends AsyncTask<String, Void, String> { private static final String TAG = "DownloadData"; @Override protected void onPostExecute(String s) { super.onPostExecute(s); } @Override protected String doInBackground(String... params) { return null; }  
    الآن نقوم بانشاء method في داخل DownloadData كلاس ونسميها على سبيل المثال downloadXML و التي بدورها سوف تقوم بتحميل ملف XML  كاملا حيث سيتم استدعاء هذه الـmethod في doInBackround
    @Override protected String doInBackground(String... params) { String rssFeed = downloadXML(params[0]); if (rssFeed == null) { Log.e(TAG, "doInBackground: Error downloading"); } return rssFeed; } private String downloadXML(String urlPath) { StringBuilder XMLResult = new StringBuilder(); try { URL url = new URL(urlPath); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); int response = connection.getResponseCode(); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); int charRead; char[] inputBuffer = new char[500]; while (true) { charRead = reader.read(inputBuffer); if (charRead < 0) { break; } if (charRead > 0) { XMLResult.append(String.copyValueOf(inputBuffer, 0, charRead)); } } reader.close(); return XMLResult.toString(); } catch (MalformedURLException e) { Log.e(TAG, "downloadXML: Invalid URL " + e.getMessage()); } catch (IOException e) { Log.e(TAG, "downloadXML: IO Exception reading Data: " + e.getMessage()); } catch (SecurityException e) { Log.e(TAG, "downloadXML: Permesion denied. " + e.getMessage()); } return null; }  
    كم هو موضح أعلاه الـ downloadXML تتلقى parameter و الذي هو عبارة عن URL لملف XML. 
    ملاحظة: سوف يتم استخدام RSS Feed  الموجودة في apple.com  و المتعلقة Top10FreeApps. و لقد قمنا بعمل التالي:
    انشاء StringBuilder object حتى يتم الحاق كامل ملف XML بهذا object. انشاء الإتصال بالإنترنت. قراءة كامل ملف XML  عن طريق BufferReader و يمكن أن تتساءل ما الفائدة من المتغير response؟ عن طريقه يمكن معرفة نجاح الاتصال بالانترنت او فشله. حيث انه اذا ظهر لك المتغير response  بقيمة 200 هذا يعني نجاح الاتصال، غير ذلك يعني فشله. قراءة عدد الأحرف الموجودة في ملف XML كما هو موضح أعلاه و إلحاقها بالمتغير XMLResult  حيث أن الـloop سوف تنتهى عند انتهاء ملف XML. أحطنا الـ code بـ try and catch حتى نتعامل مع الأخطاء المحتملة منها: فقدان الاتصال بالانترنت، او خطأ في عنوان الرابط.. الخ. بالنهاية اذا حصل خطأ نقوم بـreturn null.  
    قبل أن نبدأ باستخراج المعلومات أود أن نلقي نظرة عن ماهية ملف XML المتعلق ب Top10FreeApp. باستخدام موقع code beautify يمكننا أن نرى التكوين الداخلي لملف XML.

    كما نلاحظ أعلاه، ال Entry TAG يحتوي على  name TAG ,summary TAG , artist TAG و التي تمثل بعضا من المعلومات الممكن استخراجها.
    ننتقل الآن الى كيفية استخراج المعلومات المطلوبة من ملف XML.
    في البداية يجب علينا عمل التالي:
    انشاء كلاس يحتوي على fields و التي سوف تخزن بها المعلومات المطلوبة من ملف XML. ننشئ كلاس ونسميه FeedEntry و الذي يحتوي على متغيرات: name, Artist, Summary. إضافة getters and setters  لهذه المتغيرات.  
    public class FeedEntry { private String name; private String artist; private String summary; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getArtist() { return artist; } public void setArtist(String artist) { this.artist = artist; } public String getSummary() { return summary; } public void setSummary(String summary) { this.summary = summary; } }  
    ثم ننشئ كلاس آخر و نسميه على سبيل المثال PareseApplication و الذي بدوره سوف يحتوي على method التي تقوم بقراءة و استخراج المعلومات المطلوبة من XML file  و تخزينها في array  تحتوي على objects  من الكلاس FeedEntry.
    public class ParseApplications { private ArrayList<FeedEntry> applications; public ParseApplications() { this.applications = new ArrayList<>(); } public ArrayList<FeedEntry> getApplications() { return applications; } public boolean parse(String xmlData) { boolean status = true; FeedEntry currentRecord = null; boolean inEntry = false; String textValue = ""; try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); XmlPullParser xpp = factory.newPullParser(); xpp.setInput(new StringReader(xmlData)); int eventType = xpp.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String tagName = xpp.getName(); switch (eventType) { case XmlPullParser.START_TAG: if ("entry".equalsIgnoreCase(tagName)) { inEntry = true; currentRecord = new FeedEntry(); } break; case XmlPullParser.TEXT: textValue = xpp.getText(); break; case XmlPullParser.END_TAG: if (inEntry) { if ("entry".equalsIgnoreCase(tagName)) { applications.add(currentRecord); inEntry = false; } else if ("name".equalsIgnoreCase(tagName)) { currentRecord.setName(textValue); } else if ("artist".equalsIgnoreCase(tagName)) { currentRecord.setArtist(textValue); }else if ("summary".equalsIgnoreCase(tagName)) { currentRecord.setSummary(textValue); } } break; default: //Nothing else to do. } eventType = xpp.next(); } } catch (Exception e) { status = false; e.printStackTrace(); } return status; } }  
    لقد قمنا بعمل التالي: 
    اضافة متغير  applications من نوع ArrayList و الذي سوف يتم تخزين فيه objects من نوع FeedEntry. اضافة Constructor  لانشاء object من هذا الكلاس و احتوائه على Getter للوصول الى المتغير applications. انشاء method  و نسميها parse  و التي بدورها سوف تستخرج المعلومات المطلوبة من ملف XML المحمل كما هو موضح أعلاه. هذه الـmethod  تحتوي على اربع متغيرات:
    متغير Status من نوع boolean و الذي يدل على نجاح الـmethod  او فشلها. و متغير currentRecord  من نوع FeedEntry و الذي سوف يتم تخزين المعلومات المطلوبة فيه. و متغير InEntry  من نوع boolean الذي سوف يعطينا إشارة بوصولنا الى Entry TAG. بالإضافة الى متغير textValue من نوع String الذي سوف يقوم بتخزين النص الموجود في الـTAG.  
    ملاحظة: لحسن الحظ  Android Studio توفر لنا كلاس اسمه XmlPullParserFactory  و الذي بدوره سوف يقوم بكثير من مهام معالجة ملف XML. هذا الكلاس يعتمد على قيم ثوابت(Constatns) معرفة في الكلاس و التي تسمى بالـevents. بعض الأمثلة عليها: END_DOCUMENT, START_TAG, TEXT , END_TAG.  
    انشاء اوبجكت من XmlPullParserFactory ثم نمرر إليه محتويات الـXML  كـparameter. نقوم باستخراج المعلومات المطلوبة من ملف XML و تخزينها في currentRecord ثم اضافة هذا object الى applications. احاطة الكود بـ try and catch لتلافي الأخطاء التي من الممكن وقوعها أثناء استخراج المعلومات. و بالنهاية لا ننسى الخطوة المهمة و التي لا يكتمل التطبيق إلا بها و هي أخذ الإذن للإتصال بالإنترنت. حيث تتم هذه العملية بإضافة السطر البرمجي الموضح أدناه في ملف AndroidManifest.xml المتواجد في مجلد Manifests.  
    <uses-permission android:name="android.permission.INTERNET"/>  
    و هنا وصلنا الى نهاية هذا الدرس.😄
    في الدروس القادمة سوف أتناول كيفية عرض المعلومات المستخرجة باستخدام ListViews.
    مستوى المقال: مبتدئ
  5. قامت شركة قوقل باطلاق انسخة ٧.١ من نظام نوقا ولم يكن التحديث لاصلاح المشاكل وانما جلب معه بعض المميزات الجديدة ولعل من ابرزها هو اختصارات التطبيق  App Shortcuts وسنقوم باذن الله في هذا الدرس بشرح هذه الميزه وطريقة استخدامها.
    ماهي App Shortcuts ومتى نستخدمها ؟!
    ببساطة تقوم باضافة قائمة على التطبيق تسمح للمستخدم بعرضها دون الدخول الى التطبيق وهي مشابهه لما يوجد في نظام ios  . ويمكن استخدامها لعرض مهمات او خصائص للمستخدم تسهل الوصول لها بسرعه وسلاسة !،
    تحتوي على نوعين :
    static : تضاف في ملفات الريسورس للتطبيق وتكون ثابته ولايمكن تغييرها الا بنشر التطبيق مره اخرى
    dynamic : نقوم باضافة في الوقت الفعلي ويمكن تحديثها دون الحاجة الى نشر التطبيق مره اخرى
    اضافة App Shortcuts :
    اضافة اختصارات التطبيق تكون بشكل سهل وسنبدأ باضافة القائمة الثابته والتي سنعرفها من ملفات resources .

     
    STATIC SHORTCUTS
    ساقوم بافتراض ان لديك مشروع في تطبيق اندرويد ستديو واحتاج منك الذهاب الى ملف AndroidManifest.xml وستقوم باضافة meta-data الموجود في الكود في الاسفل :
    <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" /> تلاحظ في تاق meta-data يوجد android:resource وهذا المفتاح من يتعامل مع resource في التطبيق والذي عرف في res/xml/shourtscuts.xml هنا يمكنك تعريف اختصاراتك الثابته للتطبيق.
    الان سنقوم باضافة اختصار يفتح لنا مثلاً StaticShourtcutActivity كما سنلاحظ في الكود التالي :
    <?xml version="1.0" encoding="utf-8"?> <shortcuts xmlns:android="http://schemas.android.com/apk/res/android"> <shortcut android:enabled="true" android:icon="@drawable/ic_home_black_24dp" android:shortcutDisabledMessage="@string/static_shortcut_disabled_message" android:shortcutId="static" android:shortcutLongLabel="@string/static_shortcut_long_label" android:shortcutShortLabel="@string/static_shortcut_short_label"> <intent android:action="android.intent.action.VIEW" android:targetClass="com.example.ahmed.appshoutcut.StaticActivity" android:targetPackage="com.example.ahmed.appshoutcut" /> </shortcut> </shortcuts>  
    تلاحظ ان root للكود هو shortcuts والذي سيقوم بالتعامل مع اكثر من shourtcut في التطبيق :
    enabled هنا تقوم بتفعيل او الغاء تفعيل الاختصار
    icon الايقونة الخاصة بالاختصار
    shortcutDisabledMessage 
    تظهر رسالة في حال قام المستخدم بالضغط على اختصار غير مفعل
    shortcutLongLabel عنوان طويل يظهر في حال كان الانشر يستطيع عرضها “التابلت مثلاً”
    shortcutShortLabel هنا هو النص الذي سيظهر للمستخدم كعنوان للاختصار
    intent هنا المهمه او الامر الذي سيظهر في حال المستخدم اختار الاختصار

    DYNAMIC SHORTCUTS
    الان سنقوم بالتعامل مع النوع الثاني من الاختصارات وهي “المتغيره” والتي تتحدث مباشرة دون الحاةه الى نشر التطبيق مره اخرى وعلى عكس الاختصارات الثابته لن نحتاج الى اضافتها في xml او resource التطبيق ولكن سنقوم بكتابتها في كود Java .
    الان سنقوم باضافة اول اختصار متغير وسنتعامل مع ShortcutManager و ShortcutInfo.Builder وسنقوم ببناء اول اختصار متغير في الشاشة الرئيسية MainActivity.onCreate#
    ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); ShortcutInfo webShortcut = new ShortcutInfo.Builder(this, "shortcut_web") .setShortLabel("a7med.name") .setLongLabel("Open a7med.name web site") .setIcon(Icon.createWithResource(this, R.drawable.ic_web_black_24dp)) .setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://a7med.name"))) .build(); shortcutManager.setDynamicShortcuts(Collections.singletonList(webShortcut)); الان عند تشغيل التطبيق سنلاحظ اضافة الاختصار الثاني الخاص بنا .
    هناك الكثير عن App Shortcuts سأتكلم عنه في تدوينات لاحقة باذن الله
    شكرا لكم جميعاً وعذراً على الانقطاع
    مستوى المقال: متوسط
  6. بسم الله الرحمن الرحيم
    نكمل ما بدأناه بالحديث عن أكثر  Design pattern استخداما في بيئة Android واليوم نتم الحديث عن Creational patterns بعد تكلمنا عن Singleton و dependency injection
    اليوم سكون حديثنا عن Builder Design patterns
    نقتبس التعريف من كتاب GoF
    جميل جداً كما ذكر في التعريف إذ أننا سنقوم بتقسيم بناء Object إلى عدة method نبدء بالأساسية التي لا يمكن التعامل مع Object بدونها, ثم ننتقل إلى الاختيارية حتى نتم بناء Object على الوجه الذي نريد.
    لنبدأ بمثال يوضح المشكلة .
    لنفترض أننا نريد إنشاء class لفريق معين كالتالي :
    public class Player { private String name ; private String team ; private double height ; private int salary ; private String phone ; private String twitterAccount ; public Player(String name, String team, double height, int salary, String phone, String twitterAccount) { this.name = name; this.team = team; this.height = height; this.salary = salary; this.phone = phone; this.twitterAccount = twitterAccount; } } نحن هنا نتعامل مع six class data member فقط فلو فرضنا أن لدينا 10 فسيكون شكل constructor رهيب جدا وفي هذا عدة مشاكل منها
    أنك لن تستطيع تذكر أماكن data member في constructor مما يسبب في وضع البيانات في غير مكانها وستحصل على نتائج غريبة قد لا يصنفها compiler ك Error ولتلافي هذه المشكلة
    سنستعرض أحد الحلول وهو Telescopin Design Pattern حيث سنقوم بكتابة شكل constructor فقط
    public Player(String name, String team) { this(name, team,0.0); } public Player(String name, String team, double height) { this(name, team,height,0); } public Player(String name, String team, double height, int salary) { this(name, team,height,salary,""); } public Player(String name, String team, double height, int salary, String phone) { this(name, team,height,salary,phone,""); } public Player(String name, String team, double height, int salary, String phone, String twitterAccount) { this.name = name; this.team = team; this.height = height; this.salary = salary; this.phone = phone; this.twitterAccount = twitterAccount; } كما تلاحظ نبدأ بالمعلومات الأساسية التي لا يمكن الاستمرار بدونها ثم بعد ذلك نبدأ بتوفير البيانات في حال كانت لدينا أو نسند قيم افتراضية.
    ولهذه الطريقة عيوبها أولا صعوبة قراءة code وأيضا صعوبة في كتابة client code (إنشاء object ), بالإضافة إلى أنك قد تجبر على كتابة parameters التي لا تريد كتابتها.
    لهذه قد يكون استخدام Telescopin ليس الحل المناسب, لكن يوجد حل أخر لنأخذ فرصة في تجربته ألا وهو javaBeans Pattern , لنطبقه على نفس المثال السابق.
    public Player() {} public void setName(String name) { this.name = name; } public void setTeam(String team) { this.team = team; } public void setHeight(double height) { this.height = height; } public void setSalary(int salary) { this.salary = salary; } public void setPhone(String phone) { this.phone = phone; } public void setTwitterAccount(String twitterAccount) { this.twitterAccount = twitterAccount; } فكما ترى نبدأ ب constructor وبعد ذلك setters بعدد data members , وهذا الحل يحل بعض الإشكالات السابقة ويخلق إشكالات جديدة, فأولا سيكون إنشاء Object مرتبط بعدد كبير من method التي يجب استدعاؤها وتذكرها 
    بالإضافة إلى أنه لا يمكنك جعل class immutable, وليس thread safe, فتجازونا بعض إشكالات Telescopin ووقعنا في إشكالات أخرى ,فسنجمع بينهم في Patterm وهو Builder Design Pattern.
    لنعيد كتابة constructor بإستخدام نفس المثال
    public class Player { private String name ; private String team ; private double height ; private int salary ; private String phone ; private String twitterAccount ; public static class Builder { private final String name ; private final String team ; private double height = 0; private int salary = 0; private String phone = ""; private String twitterAccount = ""; public Builder(String name, String team) { this.name = name; this.team = team; } public Builder height(double height) { this.height = height; return this ; } public Builder salary(int salary) { this.salary = salary; return this ; } public Builder phone(String phone) { this.phone = phone; return this ; } public Builder twitterAccount(String twitterAccount) { this.twitterAccount = twitterAccount; return this ; } public Player build(){ return new Player(this); } } public Player(Builder builder) { name = builder.name; team = builder.team; height = builder.height; salary = builder.salary;; phone = builder.phone;; twitterAccount = builder.twitterAccount; } } في البداية قمنا ببناء inner class  سميناه Builder حيث سنقوم بداخله ببناء Object الخاص بنا وبعد ذلك نرجعه عن طريق دالة build التي تقوم بعمل return ل object الذي قمنا ببناءه, نبدأ بالمتغيرات الأساسية في Object وهي التي كانت final ولا تأخذ قيم افتراضية, ثم الاختيارية التي قد نستخدمها فنستدعي method الخاصة بها, أو لا نستخدمها فنسند فيه القيم الافتراضية, فيكون شكل client كالتالي
    Player player = new Player.Builder("Aziz", "Android") .height(10.2) .phone("0555555555") .salary(100) .build(); كما نلاحظ قمنا بكتابة client سهل القراءة وحتى الكتابة, ومرن وبسيط جدا.
    اذا ف Builder Pattern خيار جيد جداً حينما يكون عدد Parameters ليس بالقليل ويكون فيه عدد من Parameters اختيارية
     
    المراجع ومصادر :
    Effective Java (2nd Edition)
    https://sourcemaking.com/design_patterns/builder
    http://www.blackwasp.co.uk/gofpatterns.aspx
    مستوى المقال: محترف
  7. بسم الله الرحمن الرحيم
    نكمل ما بدأنا به في شرح design pattern الأكثر استخداما في بيئة Android
    اليوم بإذن الله سيكون حديثنا عن مفهوم Dependency Injection.
    تعريف wikipedia
    فما هو Inversion of Control ؟ التعريف من wikipedia  أيضا
    قد تكون عملية الترجمة غير مجدية أو أن التعريفات ليست واضحة عموما بإذن الله سنفصل الموضوع تفصيلا بسيطاً يجعله واضحا بإذن الله.
    ببساطة مفهوما Dependency Injection و Inversion of Control (IoC) بدون الخوض في التفاصيل تعنى بحذف dependencies من code
    ماذا نعني ب dependencies (الاعتمادية) ؟
    خذ هذا المثال
    public class MSWord { private ErrorCorrection corrector; public MSWord() { this.corrector = new ErrorCorrection(); } } نلاحظ أنه لكي أقوم بإنشاء Object من class MSWrod فأنا مضطر لإنشاء Object من  class ErrorCorrection ونقطة الاعتمادية هنا هي إنشاء Object
    لكي تقوم بتنفيذ Dependency Injection لابد أن تعمل Inject (حقن) object بحيث يأتي إلى class ك parameter  مبني, فتتم عملية الإسناد فقط
    بالمثال يتضح المقال, فهذه مثال ننفذ فيه Dependency Injection على نفس code السابق
    public class MSWord { private ErrorCorrection corrector; public MSWord(ErrorCorrection corrector) { this.corrector = corrector; } } ففي Main أو مكان تنفيذ code ستقوم بالتالي
    public static void main(String[] args) { ErrorCorrection corrector = new ErrorCorrection() MSWord word = new MSWord(corrector); } جميل جدا, الأمر نفسه بتمرير dependencies ك parameter سواء كان في constructor أو method, يأتي السؤال الأهم لماذا أستخدم هذا pattern ؟
    لنفترض أن لدينا class A ولديه subclass B ويحتاج object من class C
    public class C { String s ; C(String s){ this.s = s ; } } public class A { private C c ; A(String s){ this.c = new C(s); } } public class B extends A{ B(String s){ super(s); } } جميل, في حال تم تغيير constructor في class C فأنت مضطر لتغيير constructor في class A وجميع من يرث منه, فلو قلنا فرضاً أننا قررنا إضافة Strimg أخر لدى C
    public class C { String s ,s1; C(String s ,String s1 ){ this.s = s ; this.s1 = s1 ; } } public class A { private C c ; A(String s,String s1){ this.c = new C(s,s1); } } public class B extends A{ B(String s,String s1){ super(s,s1); } } فكما ترى اضطررنا لتغيير جميع classes نظرا لأننا غيرنا في class يعتمدون عليه, لكن ماذا لو طبقنا Dependency Injection على هؤلاء classes
    public class C { String s ; C(String s){ this.s = s ; } } public class A { private C c ; A(C c){ this.c = c ; } } public class B extends A{ B(C c){ super(c); } } لنغير في C و constructor الخاص بها ونرى ماذا يحدث.
    public class C { String s ,s1; C(String s,String s1){ this.s = s ; this.s1 = s1 ; } } public class A { private C c ; A(C c){ this.c = c ; } } public class B extends A{ B(C c){ super(c); } } نلاحظ الفائدة Dependency Injection حيث أن التغيير تم في مكان واحد فقط.
    من فوائد Dependency Injection
    له فوائد جدا كثيرة ستلاحظها أثناء الكتابة أو حين تقرأ عنه ومنها
    - قابلية التعديل - maintainability
    - قابلية الاختبار -  testability
    - قابلية الاستفادة من نفس code عوضاً عن إعادة كتابته - reusability
    - بالإضافة إلى أنه يقلل من coupling في code
     
    للاستزادة هنا بعض المراجع التي قرأت منها وفيها الكثير من المعلومات التي فضلت عدم إيرادها لجعل الموضوع أكثر بساطة
    https://en.wikipedia.org/wiki/Dependency_injection
    http://frogermcs.github.io/dependency-injection-with-dagger-2-introdution-to-di/
    http://stackoverflow.com/questions/3058/what-is-inversion-of-control
    مستوى المقال: محترف
  8. بسم الله الرحمن الرحيم
    بإذن الله ستكون هذه المقالة بداية لسلسلة من المقالات المتعلقة ب Design Patterns الأكثر أستخداما في بيئة Android.
    ونستهل هذه السلسلة ب Observer Design Pattern أو ما يسمى ب Publish-Subscribe (Pub-Sub) Pattern.
    في تعريف هذا Pattern في كتاب GoF :
    ولتبسيط التعريف نقول :
    عندما تريد تنبيه أو تحديث مجموعة objects عن حدوث تغيير في object أخر
     
    جميل جداً, دعنا نضرب مثالاً قبل أن نبدأ.
    - لدينا مدونة تنشر مقالات.
    - قمت أنت بالأشتراك في قائمة تقوم بتنبيهك في حال صدرت أي مقالة.
    - لم تعد مهتما بالمجال الذي تكتب هذه المدونة عنه, قمت بإلغاء الإشتراك منها.
    - قام مجموعة من أصدقائك بالإشتراك وإلغاء الإشتراك.
     
    المثال السابق هو ملخص لما للكود الذي سنقوم بكتابته في حال فهمك إياه فأنت قطعت شوط كبير في فهم هذا Design Pattern
    .
    بداية هذا هو UML الخاص بالكود الذي سنكتبه الان, سيتضح أكثر بعد كتابه الكود.

    جميل الان لنبدأ في كتابة الكود
    في البداية لدينا two interfaces الأول هو subjuct أو يمكنك أن تسميه puplisher والثاني observer أو يمكنك أن تسميه subscriber - إذا لم تكن تستخدم intrtfaces أثناء كتابتك لبرامجك فقد وضعت مرجع قد يفيدك في نهاية المقالة لمعرفة أهمية وأليه استخدام interfaces-
    public interface Subject { void addObserver(Observer o); void removeObserver(Observer o); void notifyObservers(); public void setUpdate(String article); public String getUpdate(); } public interface Observer { void Update (); } ليس هناك الكثير من التفاصيل في إنشاء هذين interfaces لكن سنبدأ في التعليق عند مرحلة  implementation
    دعنا نبدأ ب subjuct ونسوي له implementation في class Publisher
    public class Publisher implements Subject{ private List <Observer> observers ; String article; public Publisher() { observers = new ArrayList<>(); } @Override public void addObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(o); } @Override public void notifyObservers() { for(Observer ob : observers) ob.Update(); } @Override public String getUpdate() { return article; } @Override public void setUpdate(String article) { this.article = article ; notifyObservers(); } } في البداية قمنا بإنشاء List نستطيع أن نسميها قائمة المشتركين في هذا class الذي أفترضنا أنه مدونة لنشر المقالات, ومتغير String والذي هو عبارة عن المقالة التي نريد تنبيه متابعينا حال صدورها.
    لدينا ثلاث functions رئيسية لا يكاد يخلو Observer Design Pattern منها وهي :
         addObserver : ﻷضافة متابع جديد لهذا class أو كما يحلو لنا أن نسميه المدون
        removeObserver : لحذف متابع
         notifyObservers : لتنبيه المتابعين ونشر المقالة لهم ,وقد تختلف تصرفاتهم عندما يأتيهم التنبيه منهم يبدأ بقر ائتها  أو إهمالهما أو حفظها لديه لغرض معين أو غير ذلك.
    لننتقل الان ل classes التي قامت بعمل implementation ل Observer وهم لا يختلفون كثيرا في implementation
    public class Reader implements Observer{ private String article ; private Subject sub ; public Reader(Subject blog) { article = "" ; sub = blog ; sub.addObserver(this); } @Override public void Update() { article = sub.getUpdate(); System.out.println("You :"+article); } } public class Reader implements Observer{ private String article ; private Subject sub ; public Reader(Subject blog) { article = "" ; sub = blog ; sub.addObserver(this); } @Override public void Update() { article = sub.getUpdate(); System.out.println("You :"+article); } } class reader والذي افترضت حين بدأت بكتابة المقالة أنه أنت , وclass الأخر هو Friend والذي افترضت أنه صديقك
    كما تلاحظون في كلا classes  قمنا بعمل Override ل update  تحضر المقالة و تقرأها, وفي داخل Constructor قمنا بإنشاء المتغيرات الخاصة بنا والانضمام إلى List المتابعين الخاصين بالمدونة
    public class PupSub { public static void main(String[] args) { // TODO code application logic here Publisher pup = new Publisher(); new Reader(pup); new Friend(pup); pup.setUpdate("Art1"); pup.setUpdate("Art2"); pup.setUpdate("Art3"); } } في البداية قمنا بإنشاء قائمة المتابعين الخاصة بالمدونة وكانت فارغة قم أنشأنا متابع من Reader class والذي سبق وافترضنا أنه أنت, ثم Friend class والذي هو صديقك.
    كلاكما متابع لهذه المدونة والمدون قد وضعكما في قائمته ويقوم بإخباركم عن كل جديد في هذه المدونة فور وصوله وأنتما تقومان بقراءة ما يأتيكما فور ما يأتي.
    ثم نشر المدون ثلاث تدوينات عبر setUpdate التي تستدعي notifyObservers ثم تقوم هذه function باستدعاء Update الخاصة بكل class من المتابعين ويقومون بدورهم بتحديث
    التدوينات التي لديهم عن طريق استداعاء getUpdate الخاصة بالمدون.
     
    قد تجد صعوبة في فهم هذا Pattern وغيره في المرة الأولى وهذا طبيعي. عليك تطبيقة وفهم أجزاءه وبعد ذلك سيتضح لك.
    وأخيرا, لماذا أستخدم Observer Design Pattern ؟
    Observer Design Pattern يقلل من التداخل في الكود-Low Coupling- وهذا سيسهل عليك في حال رغبت في إضافة classes أو إزالتها, فجميع classes تعمل بشكل مستقل عن بعضها .
     
    المراجع:
    - http://informatic-ar.com/ال-coupling-وال-cohesion-في-تصميم-البرمجيات/
    - https://sourcemaking.com/design_patterns/observer
    - Head First Design Patterns
    - البرمجة باستخدام Interfaces
    مستوى المقال: محترف
  9. بسم الله الرحمن الرحيم
    نكمل ما بدأناه في الحديث عن design pattern التي يكثر استخدامها في بيئة android
    اليوم سيكون حديثنا عن Singleton
    هذا pattern يصنف ضمن Creational Patterns والتي تهتم بطرق إنشاء objct أو مجموعة objects والعلاقات التي بينها في حال كان لدينا أكثر من object.
    ونبدأ بتعريف هذا Pattern من كتاب GoF :
    إذا في Singleton  يجعلك متأكدا من أن class الذي بنيته باستخدام هذا pattern انشأ منه object اوحد فقط .
    وأول ما يخطر في بالك في حال كنت جديدا على هذا النمط, لماذا object اوحد فقط ؟ سيكون الجواب في نهاية المقالة بإذن الله
    بداية سنبدأ بكتابة مثال بسيط على  Singleton pattern بلغة java
    public class Singleton { private static Singleton singleton = null ; private Singleton() {} public static Singleton getSingleton() { if (singleton == null) singleton = new Singleton(); return singleton; } } يبدو واضحا من الوهلة الأولى أن class انف الذكر لا يمكن إنشاء أكثر من object منه.
    في private Constructor لا تقوم بإنشاء أي object كما أن data member التي لديك static لكنها private بمعنى لديك static data member لا تستطيع الوصول إليه إلا عن طريق method داخل class
    وهذه method تقوم بالتأكد فيما إذا كان لديك object تم إنشاؤه, والذي لن ينشأ غيره.
    لكن في الحقيقة لو استخدمنا نفس هذا implementation في multithreading فقد ينشأ أكثر من object, فلمنع هذا حدوث هذه المشكلة لدينا أكثر من حل,فمثلا نستخدم synchronized keyword  والتي
    ستعمل فقط مع thread واحده في نفس الوقت, لتفادي إنشاء أكثر من instance - وقد لا يكون أفضل حل-
    قدد تتعدد طرق implementation ولكن تشترك في شيء واحد وهو أنه لا يمكن أنشاء أكثر من object من هذا class
    طيب جميل جداً لان لنطبق هذا pattern على Android
    سنقوم بالتالي بناء دالة تتأكد من حالة network وترجع true في حال كان هناك اتصال وError في حال لم يكن.
    ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); if(info != null && info.isConnectedOrConnecting() && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting()){ //your code } ستكرر هذه code في كل مكان تنوي فيه التحقق من حالة الشبكة,, وعندما تكرر code فيعتبر إشكال في تصميمه.
    إذا ماذا سنفعل ؟
    سنقوم بالتالي بناء App class , وهو class على مستوى التطبيق بمعنى يمكن لأي جزء من تطبيق أن يصل إليه, كيف ؟
    يكون لدينا class فيه static method لكل object لا نحتاج لتكراره في code ومن ثم نستدعيه عن طريق class .
    مثال
    public static boolean NetworkState(Context context){ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); return info != null && info.isConnectedOrConnecting() && cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnectedOrConnecting(); } في هذه الحالة يمكننا التحقق من حالة network بهذه الطريقة
    if(Application.NetworkState(Context context)){ //your code } وهذا يمنحنا ميزة سهولة التعديل maintainable  في حال أردنا تغيير طريقة عمل function كإظهار تنبيه معين.
    وقس على ذلك من المزايا , كما أن هذا المثال يمكن استخدامه مع عدد كبير من الحالات كإنشاء object من مكتبة retrofit والوصول له من أي مكان على مسوى التطبيق.
     
    لا ينبغي عمل subclass ل Singleton  class حيث سيتوجب عليك جعل Constructor public وبعد هذا التغيير لم يعد class يسمى Singleton حيث يمكن إنشاء instance منه.
    بالإضافة إلى أنك قد تكون جعلت static data member مما يعني أن subclass سيكون مشترك مع superclass في نفس data member وقد يؤدي إلى نتائج غير مرغوب فيها غالبا.
     
    نجيب على التساؤل الذي طرحناه في بداية المقالة لماذا object اوحد فقط ؟
    أتضح ولو بشكل جزئي من الأمثلة التي ذكرناها في بداية المقالة بعض فوائد Singleton, وهو أنه ستمر بك بعض الحالات لا تريد فيها أكثر من object وحيد تتعامل معه ومن هذه الحالات
    Toast كما ذكرنا ,و thread pools و caches و logging والقائمة تطول, فمتى ما رأيت أن class فيه static class or data member هي التي تحتاجها فقط فسيكون Singleton حل مناسب لك.
     
    المراجع:
    Head First Design Patterns
    http://www.javaworld.com/article/2073352/core-java/simply-singleton.html
    http://www.blackwasp.co.uk/gofpatterns.aspx
    http://www.journaldev.com/1377/java-singleton-design-pattern-best-practices-examples
    مستوى المقال: محترف
  10. يوجد العديد من التطبيقات التي تحتوي على ميزة مشاركة المواقع القريبة من المستخدم ولعل اكبر مثال على ذلك هو تطبيق واتس اب فهو يستخدم تقريباً نفس الفكرة المقدمة من قوقل وهي Place Picke وتتيح لك هذه الميزة السرعة في تحديد الاماكن القريبة من المستخدم بدون استخدام تقنيات مثل NearBy والتعقيدات واستهدام الخرايط .
    في هذا الدرس ستتعرف على طريقة مشاركة الاماكن بشكل سريع واسطر قليله جداً.
    بداية كل بداية في استخدام خدمات قوقل هو الحصول على API KEY  .
    الحصول على API KEY :
    ادخل الى هذه الرابط https://console.developers.google.com ثم قم باتباع الشرح الموجود اسفل الفيديو :
    الحصول على SHA1
    اضافة خدمات قوقل للتطبيق :
    في ملف gradel.bulide قم بكتابة هذا السطر المضلل :
    dependencies { compile 'com.google.android.gms:play-services-location:8.4.0' } الان الانتقال الى Activity :
    قم باضافة Button او اي شئ تريد من المستخدم اذا قام بالضغط عليه ان يقوم بنقله الى شاشة اختيار مكان قريب، وفي امر الضغط لهذا Button نقوم بكتابة هذا السطرين :
    PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder(); try { startActivityForResult(builder.build(MainActivity.this), PLACE_PICKER_REQUEST); } catch (GooglePlayServicesRepairableException | GooglePlayServicesNotAvailableException e) { e.printStackTrace(); } الان اذا قام المستخدم بالضغط على Button سيتم نقله الى شاشة اختيار مكان قريب .
    لكن الان كيف اقوم باعادة النتائج اذا قام المستخدم بالاختيار  .
    اعادة النتائج :
    نحتاج الان اضافة دالة onActivityresult() لاعادة البيانات التي قام المستخدم باختيارها .
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == PLACE_PICKER_REQUEST) { if (resultCode == RESULT_OK) { Place place = PlacePicker.getPlace(data, this); String place_name = String.format(getString(R.string.place_name), place.getName()); String place_addrress = String.format(getString(R.string.place_adress), place.getAddress()); name.setText(place_name); address.setText(place_addrress); } } } عند اعادة النتائج سنلاحظ استخدامنا لـPlace حيث سيحتوي على جميع بيانات الموقع:
    Place place = PlacePicker.getPlace(data, this); اهم البيانات التي يحتوي عليها Opject Place :
    لم اتطرق لبعض الدوال ،، فقط المهم منها الدالة وظيفتها getId اعادة رقم الموقع وهو رقم فريد getLatLng اعادة خطوط الطول والعرض للموقع الحالي getName اعادة اسم الموقع الحالي getPhoneNumber اعادة رقم الهاتف للموقع الحالي اذا كام متوفر getPlaceTypes اعادة نوع الموقع "مطعم او صراف .. الخ" النتيجة النهائية :
     

    مستوى المقال: متوسط
  11. كثير من مطوري الاندرويد يقومون باستخدام AsyncTask وخاصة عندما يريدون تنفيذ بعض الاكواد في thread منفصل, ولكن كثير منهم يتعامل مع هذا الكلاس بطريقة خاطئة ودون فهم لدورة حياة الاندرويد وما الذي يحدث بالضبط في الخلفية, في هذه المقالة سنوضح الخطأ الشائع وكيف تقوم بمعالجة المشكلة.
    الاستخدام الخاطئ
    الطريقة الشائعة والتي تجدها في معظم المواقع والتي ستجدها بالتاكيد في معظم مشاريع مطوري الاندرويد هي كالتالي:
    public class MainActivity extends Activity {     @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);   }     // Somewhere the AsyncTask is started     public class MyAsyncTask extends AsyncTask<Void, Void, String> {       @Override protected String doInBackground(Void... params) {       // Do work       return result;     }       @Override protected void onPostExecute(String result) {       Log.d("MyAsyncTask", "Received result: " + result);     }   } } المشكلة بالكود هي ان كلاس  AsyncTask مضمن بداخل الـ Activity وبالتالي اي تغيير في حالة الـ Activity والتي قامت باستدعاء الـ AsyncTask الموجود بداخلها قد يقتل عمل الـ AsyncTask, ولكن لن يتم ازالة الـ Activity من الذاكرة الا في حالة انتهاء الـ AsyncTask.
    الان لتتخيل معي المثال التالي: لدي اكتفتي واحدة وبداخلها AsyncTask وفي كل مرة اقوم باستدعاء الاكتفتي فانا استدعي AsyncTask جديد واي تغيير في حالة الاكتفتي ربما استدعي AsyncTask اخر, وكل واحد من الـ AsyncTask لديه عملية يجب عليه ان ينفذها قبل ان يُقتل وقبل ان يسمح للـ Activity بان تزال من الذاكرة.
    ماسيحدث بالمثال بالاعلى هو مشكلتين, الاولى هي مشكلة في الذاكرة memory issues والثانية هي انك ستفقد العمليات التي قام الـ AsyncTask بتنفيذها وستقوم بتكرار العملية اكثر من مرة.
    معالجة المشكلة
    لمعالجة المشكلة بالاعلى فالافضل ان تقوم باستخدام مبدأ الـ event bus وطريقة تنفيذه كالتالي:
    نقل الـ AsyncTask الى كلاس منفصل عن الـ  Activity. انشاء كلاس جديد يقوم بتخزين ناتج  AsyncTask عند اكتماله. ربط الـ الـAsyncTask بالـ Activity الخاصة بك عن طريق الـ bus. لذلك سنقوم بانشاء كلاس جديد تحت مبدأ الـ Singleton ويكون كالتالي:
     public class MyBus { private static final Bus BUS = new Bus();  public static Bus getInstance() { return BUS;    } } ولنفترض ان الـ AsyncTask سيقوم بارجاع String فقط عن اكتمال عمله, لذلك سنقوم بانشاء  كلاس جديد وليكن اسمه AsyncTaskResultEvent :
    public class AsyncTaskResultEvent { private String result; public AsyncTaskResultEvent(String result) { this.result = result; } public String getResult() { return result; } } نقوم بانشاء كلاس الـ AsyncTask والذي سيقوم بتنفيذ اي كود تريده:
    public class MyAsyncTask extends AsyncTask<Void, Void, String> { @Override protected String doInBackground(Void... params) { Random random = newRandom(); final long sleep = random.nextInt(10); try{ Thread.sleep(sleep * 1000); } catch(InterruptedException e) { e.printStackTrace(); } return"Slept for "+ sleep + " seconds"; } @Override protected void onPostExecute(String result) { MyBus.getInstance().post(newAsyncTaskResultEvent(result)); } } اخيرا سنقوم بربط الـ AsyncTask  مع الـ Activity ولكن يجب عليك بالغاء الربط بينهم في ميثود onDestroy حتى لانقع في نفس المشكلة من جديد:
    public class MainActivity extends Activity {   @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     findViewById(R.id.button).setOnClickListener(newView.OnClickListener() {       @OverridepublicvoidonClick(View v) {         newMyAsyncTask().execute();       }     });     MyBus.getInstance().register(this);   }   @Override protected void onDestroy() {     MyBus.getInstance().unregister(this);     super.onDestroy();   }   @Subscribe public void onAsyncTaskResult(AsyncTaskResultEvent event) {     Toast.makeText(this, event.getResult(), Toast.LENGTH_LONG).show();   } } اخيراً
    هذه الطريقة ليست محصورة مع الـ AsyncTask بل تستطيع استخدامها مع اي امر اخر, فعلى سبيل المثال الـ services لديه نفس المشكلة اذا استخدمتها بطريقة خاطئة وحلها هو باستخدام الـ event bus.
    مستوى المقال: متوسط
  12. لا يخفى علينا جميعا أن التاريخ الهجري هو التاريخ المعتمد والرسمي في تعاملاتنا الحكومية وهو بداية هجرة النبي صلى الله عليه وسلم وقد نحتاج لاضافته في احد مشاريعنا لتحديد المواعيد أو شي اخر.
    ولكن لايوجد دعم رسمي للتاريخ الهجري في جافا القديمة ولكن تم ذلك في الإصدار الثامن الذي لم يعتمد في الاندرويد الا لبعض الapi .
    لذلك سنقوم باستخدام مكتبة JodaTime وتعتبر من أقوى المكتبات واسهلها في التعامل مع التواريخ والأوقات بجميع الصيغ.
    سنقوم بإذن الله في هذا الدرس بإنشاء تطبيق بسيط كما يظهر في الفيديو بالأسفل لتحويل التاريخ من ميلادي إلى هجري والعكس وأيضا حساب الايام المتبقية على رمضان .
    المتطلبات :
    ١ - كوب قهوة 
    ٢ - اندرويد ستديو ويفضل ان تقوم بالتحديث الي الاصدار 2 لتتمتع بالمحاكي السريع  انشئ مشروع جديد: بداية كل بداية وثورة تقنية هو انشاء مشروع جديد قم بتسمية المشروع كما تشاء وبعد ذلك قم باختيار Empty Activity .
    قم باضافة هذا السطر في gradle.builde وهو خاص بمكتبة JodaTime.
    dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.3.0' compile 'joda-time:joda-time:2.9.3' } تصميم واجهة التطبيق :
    سنقوم بتصميم واجهة بسيطة باستخدام Material Desing لعرض :
    - عرض تاريخ اليوم بالهجري.
    - المتبقي على شهر رمضان ،بعض المناسبات .
    - تحويل التاريخ من الميلادي للهجري.
    الشفرة البرمجية الخاصة بالواجهه :
    <?xml version="1.0" encoding="utf-8"?> <FrameLayout 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" tools:context="com.example.ahmed.hijri.MainActivity"> <android.support.v7.widget.GridLayout android:id="@+id/choice_grid" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:padding="4dp" app:alignmentMode="alignBounds" app:columnCount="2" app:rowOrderPreserved="false" app:useDefaultMargins="true"> <android.support.v7.widget.CardView android:layout_width="0dp" android:layout_height="150dp" android:gravity="center" app:layout_columnWeight="1" app:layout_gravity="fill_horizontal"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" android:orientation="vertical" android:padding="10dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="ميلادي" android:textColor="@color/md_white_1000" android:textSize="18sp" android:textStyle="bold" /> </LinearLayout> <TextView android:id="@+id/gey_date" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textColor="@color/md_grey_500" android:textSize="24sp" android:textStyle="bold" tools:text="April 20 2016" /> </LinearLayout> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:layout_width="0dp" android:layout_height="150dp" android:gravity="center" app:layout_columnWeight="1" app:layout_gravity="fill_horizontal"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" android:orientation="vertical" android:padding="10dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="هجري" android:textColor="@color/md_white_1000" android:textSize="18sp" android:textStyle="bold" /> </LinearLayout> <TextView android:id="@+id/hijri_date" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textColor="@color/md_grey_500" android:textSize="24sp" android:textStyle="bold" tools:text="April 20 2016" /> </LinearLayout> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:layout_width="0dp" android:layout_height="wrap_content" android:gravity="center" android:text="Tile3" app:layout_columnSpan="2" app:layout_columnWeight="1" app:layout_gravity="fill_horizontal" app:layout_rowWeight="1"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorAccent" android:orientation="vertical" android:padding="10dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="متبقي على رمضان" android:textColor="@color/md_white_1000" android:textSize="18sp" android:textStyle="bold" /> </LinearLayout> <TextView android:id="@+id/ramadan_date" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/tv_heading" android:layout_marginTop="20dp" android:gravity="center" android:text="345" android:textColor="@color/md_grey_500" android:textSize="67sp" android:textStyle="bold" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/tv_heading" android:layout_marginBottom="20dp" android:gravity="center" android:text="يوم" android:textColor="@color/md_grey_500" android:textSize="17sp" android:textStyle="bold" /> </LinearLayout> </android.support.v7.widget.CardView> </android.support.v7.widget.GridLayout> <android.support.design.widget.FloatingActionButton android:id="@+id/action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="15dp" android:src="@drawable/ic_date_range_black_24dp" /> </FrameLayout> كتابة الشفرة البرمجية للـActivity :
    قمنا سابقاً بانشاء Activity فارغ والان سنقوم بكتابة الاوامر الخاصة بمكتبة Joda وشرحها بشكل ميسر وبسيط . DateTime.now() هنا لعرض التاريخ الميلادي بصيغه كاملة مع الوقت.
    لكن لو فرضاً اردنا تخصيص الوقت بصيغه معينه تناسب التطبيق الخاص بنا . مثلا اريد التاريخ بهذه الصيغه :
    "الجمعة ٢٢ ابريل ٢٠١٦ ميلادي"
    ركز معي في الجدول التالي وقم بتحديد ماذا تريد ان تعرض :
    الرمز   شرح القيمة المستردة مثال G التقويم الحالي "هجري ، ميلادي ، الخ" نص AH,Ad C القرن الحالي رقم ٢٠ Y السنة الحالية في القرن سنة 1437 x نحتاجه اذا صادف ان السنة بدأت من نصف الاسبوع سنة 1437 w رقم الاسبوع في weekyear "المشروحة في الاعلى" رقم ٢٧ e رقم اليوم في الاسبوع رقم ٢ E اسم اليوم في الاسبوع نص الاثنين y  سنة  رقم  1437 D  رقم اليوم في السنة  رقم  ١١٤ M  رقم الشهر في السنة رقم   ٩ d  رقم اليوم في الشهر  رقم  ١١ a لمعرفة الوقت    نص  ص او م K  الساعة في اليوم (٠-١١)  رقم  ٠ h  رقم الساعة في نصف اليوم (١ - ١٢ )  رقم  ١٢ H  الساعة في اليوم ( ٠ - ٢٣ )  رقم  ١٣ k  الساعة صحيحة في اليوم ( ١ - ٢٤ )  رقم  ٢٤ m  الدقيقة في الساعة  رقم  ٣٠ s  الثانية في الدقيقة  رقم  ٥٥ S جزء من الثانية    رقم  ٤٥٦ z المنطقة الزمنية  نص  PST Z  المنطقة الزمنية بحسب ID  منطقة زمنية  SA/Ryadth
    الان ومن خلال اطلاعك على الجدول ستقوم بتحديد الصيغة وستكون بهذ الشكل :
    DateTime.now().toString("E dd MMMM YYYY G") حددت اليوم من  الاسبوع ثم اليوم من الشهر واسم الشهر وكذلك السنة وبعد ذلك اضفت نوع التقويم
    عرض التاريخ الهجري الحالي :
    الان درسنا يقوم على هذه النقطة وهو عرض التاريخ الهجري وسيكون بالاعتماد على التاريخ الميلادي الحالي :  DateTime dtIslamic = DateTime.now().withChronology(IslamicChronology.getInstance()); بهذا السطر فقط نستطيع عرض التاريخ الهجري وستقوم انت اخي المتابع معي بكتابة الصيغة التي تعجبك  . تحويل التواريخ :
    - من ميلادي الى هجري : DateTime date = new DateTime(year, monthOfYear + 1, dayOfMonth, , , ); DateTime dtIslamic = date.withChronology(IslamicChronology.getInstance()); result.setText(dtIslamic.toString("E dd MMMM yyyy G")); - من هجري الى ميلادي : بعض التلميحات لاستخدام JodaTime :
    - عرض عدد الايام المتبقية بين تاريخين : Days.daysBetween(new LocalDate(DateTime.now()), new LocalDate(dtIso)).getDays(); في الكود السابق كتبت اولاً التاريخ الحالي الذي ساقوم بالحساب ابتداءً منه وبعد ذلك كتبت تاريخ النهاية وستقوم المكتبة بحساب عدد الايام.
    ستجد في المثال المرفق طريقة عرض عدد الايام المتبقية على الشهر الفضيل عسى ان يبلغنا الله لافاقدين ولا مفقودين.
    قيس على المثال السابق "الشهور والاسابيع والايام والدقائق والثواني "
    - تحديد تاريخ معين والمنطقة الزمنية :
    تستطيع تحديد التاريخ والوقت والمنطقة الزمنية باستخدام المكتبة وتخصيصها. DateTime cestTime = new DateTime(2013, 6, 10, 2, , DateTimeZone.forID("Europe/Berlin")); - التحقق من هل التاريخ قبل او بعد التاريخ الحالي :
    توجد دالة تدعى isBefore وهي للتاكد هل التاريخ هذا قبل ام بعد وتستطيع مثلا استخدامها اذا اردت من المستخدم تحديد تاريخ مستقبلي مثلاً. التطبيق :
    https://github.com/ahmedoid/Hijri ختاماً :
    اتمنى اني وفقت في ايصال المعلومة بشكل بسيط وسلس وان شاء الله القادم افضل
    مستوى المقال: متوسط

عالم البرمجة

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