Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
laboratoare:laborator08 [2015/04/27 14:08]
Andrei Roșu-Cojocaru
laboratoare:laborator08 [2015/04/27 23:39] (current)
Andrei Roșu-Cojocaru
Line 72: Line 72:
   * Se identifică **numărul de proiect** care va fi utilizat de dispozitivul mobil pentru a se înregistra la serverul Google Cloud Messaging for Android (''​SENDER_ID''​),​ acesta putând fi preluat din:   * Se identifică **numărul de proiect** care va fi utilizat de dispozitivul mobil pentru a se înregistra la serverul Google Cloud Messaging for Android (''​SENDER_ID''​),​ acesta putând fi preluat din:
     * bara de adrese a aplicației de navigare {{ :​laboratoare:​laborator08:​project_number01.png?​nolink }}     * bara de adrese a aplicației de navigare {{ :​laboratoare:​laborator08:​project_number01.png?​nolink }}
-    * secțiunea //​Overview//​ a consolei Google API {{ :​laboratoare:​laborator08:​project_number02.png?​nolink&​1200 }}+    * secțiunea //​Overview//​ a consolei Google API {{ :​laboratoare:​laborator08:​project_number02.png?​nolink&​1000 }}
   * Se activează API-ul //Cloud Messaging for Android// din secțiunea //Mobile APIs//, ce poate fi accesată din //APIs & auth// → //APIs// a consolei Google API.   * Se activează API-ul //Cloud Messaging for Android// din secțiunea //Mobile APIs//, ce poate fi accesată din //APIs & auth// → //APIs// a consolei Google API.
  
Line 92: Line 92:
 **2.** Se instalează SDK-ul Google Play Services, necesară autentificării / deautentificării la Google Cloud Messaging for Android din cadrul aplicației instalată pe dispozitivul mobil. **2.** Se instalează SDK-ul Google Play Services, necesară autentificării / deautentificării la Google Cloud Messaging for Android din cadrul aplicației instalată pe dispozitivul mobil.
   * În Eclipse Luna, se accesează //Android SDK Manager//   * În Eclipse Luna, se accesează //Android SDK Manager//
-    * din bara de unelte\\ {{ :​laboratoare:​laborator08:​eclipse_toolbar.png?​nolink }}+    * din bara de unelte\\ {{ :​laboratoare:​laborator08:​eclipse_toolbar.png?​nolink&​900 ​}}
     * din meniul principal, //Window// → //Android SDK Manager//     * din meniul principal, //Window// → //Android SDK Manager//
-  * Se selectează următoarele pachete:\\ {{ :​laboratoare:​laborator08:​android_sdk_manager.png?​nolink }}+  * Se selectează următoarele pachete:\\ {{ :​laboratoare:​laborator08:​android_sdk_manager.png?​nolink&​600 ​}}
     * din secțiunea ''​Android 4.1.2 (API 16)'',​ pachetul ''​Google APIs''​     * din secțiunea ''​Android 4.1.2 (API 16)'',​ pachetul ''​Google APIs''​
     * din sețiunea //Extras//, pachetul //Google Play Services//     * din sețiunea //Extras//, pachetul //Google Play Services//
Line 104: Line 104:
  
   * **AVD**   * **AVD**
-    * se creează un dispozitiv virtual pentru care va specifica la secțiunea //Target// valoarea //Google APIs (Google Inc.)//\\ {{ :​laboratoare:​laborator08:​android_virtual_device01.png?​nolink }}+    * se creează un dispozitiv virtual pentru care va specifica la secțiunea //Target// valoarea //Google APIs (Google Inc.)//\\ {{ :​laboratoare:​laborator08:​android_virtual_device01.png?​nolink&​400 ​}}
     * se asociază datele unui cont Google (nume de utilizator, parola) din //​Settings//​ → //​Accounts//,​ selectându-se opțiunea //Add account//\\ {{ :​laboratoare:​laborator08:​android_virtual_device02.png?​nolink }}     * se asociază datele unui cont Google (nume de utilizator, parola) din //​Settings//​ → //​Accounts//,​ selectându-se opțiunea //Add account//\\ {{ :​laboratoare:​laborator08:​android_virtual_device02.png?​nolink }}
   * **Genymotion** - vor trebui instalate manual ''​ARM Transalation''​ și ''​Google Apps for Android''​ întrucât suportul pentru acestea nu mai este asigurat începând cu versiunea 2.0   * **Genymotion** - vor trebui instalate manual ''​ARM Transalation''​ și ''​Google Apps for Android''​ întrucât suportul pentru acestea nu mai este asigurat începând cu versiunea 2.0
-    * în //​Settings//​ → //ADB// → //ADB Connection Settings// se indică locația unde se găsește instalat SDK-ul de Android\\ {{ :​laboratoare:​laborator08:​genymotion_settings.png?​nolink }}+    * în //​Settings//​ → //ADB// → //ADB Connection Settings// se indică locația unde se găsește instalat SDK-ul de Android\\ {{ :​laboratoare:​laborator08:​genymotion_settings.png?​nolink&​400 ​}}
     * se instalează [[http://​pdsd2014.andreirosucojocaru.ro/​Genymotion-ARM-Translation_v1.1.zip|ARM Translation 1.1]] printr-o operație de tip drag-and-drop în ecranul principal al emulatorului     * se instalează [[http://​pdsd2014.andreirosucojocaru.ro/​Genymotion-ARM-Translation_v1.1.zip|ARM Translation 1.1]] printr-o operație de tip drag-and-drop în ecranul principal al emulatorului
       * se afișează o fereastră de dialog ce va conține mesajul ''​File transfer in progress...''​\\ {{ :​laboratoare:​laborator08:​file_transfer_in_progress.png?​nolink }}       * se afișează o fereastră de dialog ce va conține mesajul ''​File transfer in progress...''​\\ {{ :​laboratoare:​laborator08:​file_transfer_in_progress.png?​nolink }}
Line 119: Line 119:
       * [[http://​pdsd2014.andreirosucojocaru.ro/​gapps-jb-20121011-signed.zip|Google Apps for Android 4.1]]       * [[http://​pdsd2014.andreirosucojocaru.ro/​gapps-jb-20121011-signed.zip|Google Apps for Android 4.1]]
     * indiferent de rezultat, se repornește emulatorul prin comanda\\ <​code>​adb -s 192.168.56.101:​5555 reboot</​code>​ (respectiv ''​adb reboot''​)     * indiferent de rezultat, se repornește emulatorul prin comanda\\ <​code>​adb -s 192.168.56.101:​5555 reboot</​code>​ (respectiv ''​adb reboot''​)
-    * se asociază datele unui cont Google (nume de utilizator, parola) din //​Settings//​ → //​Accounts//,​ selectându-se opțiunea //Add account//\\ {{ :​laboratoare:​laborator08:​google_account_setup01.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup02.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup03.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup04.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup05.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup06.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup07.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup08.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​google_account_setup09.png?​nolink }}  +    * se asociază datele unui cont Google (nume de utilizator, parola) din //​Settings//​ → //​Accounts//,​ selectându-se opțiunea //Add account//\\ {{ :​laboratoare:​laborator08:​google_account_setup01.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup02.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup03.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup04.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup05.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup06.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup07.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup08.png?​nolink&​400 ​}}\\ {{ :​laboratoare:​laborator08:​google_account_setup09.png?​nolink&​400 ​}}  
-    * se actualizează //Play Store// prin accesarea opțiunii //​Settings//​ de mai multe ori (procesul de actualizare va fi pornit în fundal, în mod automat)\\ {{ :​laboratoare:​laborator08:​google_account_setup10.png?​nolink }}+    * se actualizează //Play Store// prin accesarea opțiunii //​Settings//​ de mai multe ori (procesul de actualizare va fi pornit în fundal, în mod automat)\\ {{ :​laboratoare:​laborator08:​google_account_setup10.png?​nolink&​400 ​}}
  
 **4.** În situația în care se va folosi un server CCS (XMPP), în prezent, nu mai este necesară o înregistrare suplimentară pentru a se obține acceptul Google de a utiliza serviciul de transmitere a mesajelor de pe dispozitivul mobil. Anterior, o astfel de solicitare trebuia transmisă la [[https://​services.google.com/​fb/​forms/​gcm/​|Upstream Google Cloud Messaging and User Notifications Sign-up]]. **4.** În situația în care se va folosi un server CCS (XMPP), în prezent, nu mai este necesară o înregistrare suplimentară pentru a se obține acceptul Google de a utiliza serviciul de transmitere a mesajelor de pe dispozitivul mobil. Anterior, o astfel de solicitare trebuia transmisă la [[https://​services.google.com/​fb/​forms/​gcm/​|Upstream Google Cloud Messaging and User Notifications Sign-up]].
Line 131: Line 131:
  
 **6**. Se descarcă cea mai recentă versiune stabilă a mediului de dezvoltare Eclipse IDE for Java EE Developers (Windows: [[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-win32.zip|32-bit]]|[[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-win32-x86_64.zip|64-bit]],​ Linux: [[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-linux-gtk.tar.gz|32-bit]]|[[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-linux-gtk-x86_64.tar.gz|64-bit]]) și se integrează cu serverul web Apache Tomcat. **6**. Se descarcă cea mai recentă versiune stabilă a mediului de dezvoltare Eclipse IDE for Java EE Developers (Windows: [[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-win32.zip|32-bit]]|[[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-win32-x86_64.zip|64-bit]],​ Linux: [[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-linux-gtk.tar.gz|32-bit]]|[[http://​www.eclipse.org/​downloads/​download.php?​file=/​technology/​epp/​downloads/​release/​luna/​SR2/​eclipse-jee-luna-SR2-linux-gtk-x86_64.tar.gz|64-bit]]) și se integrează cu serverul web Apache Tomcat.
-  * //Window// → //​Preferences//​ → //Server// → //Runtime Environments//​ și se accesează butonul //​Add...//​\\ {{ :​laboratoare:​laborator08:​server_configuration01.png?​nolink }} +  * //Window// → //​Preferences//​ → //Server// → //Runtime Environments//​ și se accesează butonul //​Add...//​\\ {{ :​laboratoare:​laborator08:​server_configuration01.png?​nolink&​500 ​}} 
-  * se specifică tipul serverului web (''​Apache Tomcat v8.0''​)\\ {{ :​laboratoare:​laborator08:​server_configuration02.png?​nolink }} +  * se specifică tipul serverului web (''​Apache Tomcat v8.0''​)\\ {{ :​laboratoare:​laborator08:​server_configuration02.png?​nolink&​500 ​}} 
-  * se precizează locația la care se găsește serverul web (directorul în care a fost dezarhivat, fără alte configurări) și versiunea de Java Runtime Environment\\ {{ :​laboratoare:​laborator08:​server_configuration03.png?​nolink }}+  * se precizează locația la care se găsește serverul web (directorul în care a fost dezarhivat, fără alte configurări) și versiunea de Java Runtime Environment\\ {{ :​laboratoare:​laborator08:​server_configuration03.png?​nolink&​500 ​}}
 <note tip>Se oferă și posibilitatea descărcării și instalării unei instanțe a serverului web Apache Tomcat, însă de regulă aceasta nu referă cea mai recentă versiune.</​note>​ <note tip>Se oferă și posibilitatea descărcării și instalării unei instanțe a serverului web Apache Tomcat, însă de regulă aceasta nu referă cea mai recentă versiune.</​note>​
  
-{{ :​laboratoare:​laborator08:​server_configuration04.png?​nolink }}+{{ :​laboratoare:​laborator08:​server_configuration04.png?​nolink&​500 ​}}
  
-  * //Window// → //Show View// → //​Other...//​ → //Servers// → //Server// și se accesează legătura //No servers are available. Click this link to create a new server...// sau click pe butonul drept al mouse-ului în spațiul de lucru //Server// → //New// → //​Server//​\\ {{ :​laboratoare:​laborator08:​server_configuration05.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​server_configuration06.png?​nolink }} +  * //Window// → //Show View// → //​Other...//​ → //Servers// → //Server// și se accesează legătura //No servers are available. Click this link to create a new server...// sau click pe butonul drept al mouse-ului în spațiul de lucru //Server// → //New// → //​Server//​\\ {{ :​laboratoare:​laborator08:​server_configuration05.png?​nolink }}\\ {{ :​laboratoare:​laborator08:​server_configuration06.png?​nolink&​800 ​}} 
-  * se selectează serverul web definit anterior și se accesează butonul //​Finish//​\\ {{ :​laboratoare:​laborator08:​server_configuration07.png?​nolink }} +  * se selectează serverul web definit anterior și se accesează butonul //​Finish//​\\ {{ :​laboratoare:​laborator08:​server_configuration07.png?​nolink&​500 ​}} 
-  * se asociază proiectului un server web pe care va rula: click pe butonul drept al mouse-ului pe denumirea proiectului → //​Properties//​ → //Server// și se selectează serverul web definit anterior\\ {{ :​laboratoare:​laborator08:​server_configuration08.png?​nolink }}+  * se asociază proiectului un server web pe care va rula: click pe butonul drept al mouse-ului pe denumirea proiectului → //​Properties//​ → //Server// și se selectează serverul web definit anterior\\ {{ :​laboratoare:​laborator08:​server_configuration08.png?​nolink&​500 ​}}
   * se realizează o referință către biblioteca serverului web, aceasta fiind trecută în classpath: click pe butonul drept al mouse-ului pe denumirea proiectului → //Build Path// → //Add Library//   * se realizează o referință către biblioteca serverului web, aceasta fiind trecută în classpath: click pe butonul drept al mouse-ului pe denumirea proiectului → //Build Path// → //Add Library//
-    * se alege //Server Runtime//\\ {{ :​laboratoare:​laborator08:​server_configuration09.png?​nolink }} +    * se alege //Server Runtime//\\ {{ :​laboratoare:​laborator08:​server_configuration09.png?​nolink&​500 ​}} 
-    * se indică serverul web definit anterior și se accesează butonul ''​Finish''​\\ {{ :​laboratoare:​laborator08:​server_configuration10.png?​nolink }}+    * se indică serverul web definit anterior și se accesează butonul ''​Finish''​\\ {{ :​laboratoare:​laborator08:​server_configuration10.png?​nolink&​500 ​}}
  
 **7.** Se descarcă și se instalează cea mai recentă versiune stabilă a motorului pentru baze de date MySQL. **7.** Se descarcă și se instalează cea mai recentă versiune stabilă a motorului pentru baze de date MySQL.
Line 174: Line 174:
  
 Se realizează o referință către biblioteca pentru accesarea Google Cloud Messaging care este localizată în ''<​android-sdk>/​extras/​google/​google_play_services/​libproject/​google-play-services_lib''​ Se realizează o referință către biblioteca pentru accesarea Google Cloud Messaging care este localizată în ''<​android-sdk>/​extras/​google/​google_play_services/​libproject/​google-play-services_lib''​
-  * se accesează //File// → //Import// → //Android// → //Existing Android Code Into Workspace//​\\ {{ :​laboratoare:​laborator08:​google_cloud_messaging_library_import01.png?​nolink }} +  * se accesează //File// → //Import// → //Android// → //Existing Android Code Into Workspace//​\\ {{ :​laboratoare:​laborator08:​google_cloud_messaging_library_import01.png?​nolink&​500 ​}} 
-  * se indică locația unde se găsește instalată biblioteca pentru accesarea Google Cloud Messaging, creându-se o copie a acestuia în spațiul de lucru (se bifează opțiunea //Copy projects into workspace//​)\\ {{ :​laboratoare:​laborator08:​google_cloud_messaging_library_import02.png?​nolink }} +  * se indică locația unde se găsește instalată biblioteca pentru accesarea Google Cloud Messaging, creându-se o copie a acestuia în spațiul de lucru (se bifează opțiunea //Copy projects into workspace//​)\\ {{ :​laboratoare:​laborator08:​google_cloud_messaging_library_import02.png?​nolink&​800 ​}} 
-  * în fereastra corespunzătoare proiectului aferent aplicației Android (click dreapta pe denumirea proiectului în //Package Explorer// → //​Properties//​),​ se accesează opțiunea //Android// și se specifică referința către biblioteca respectivă în secțiunea //Library// (accesând butonul //Add//)\\ {{ :​laboratoare:​laborator08:​google_cloud_messaging_library_import03.png?​nolink }}+  * în fereastra corespunzătoare proiectului aferent aplicației Android (click dreapta pe denumirea proiectului în //Package Explorer// → //​Properties//​),​ se accesează opțiunea //Android// și se specifică referința către biblioteca respectivă în secțiunea //Library// (accesând butonul //Add//)\\ {{ :​laboratoare:​laborator08:​google_cloud_messaging_library_import03.png?​nolink&​800 ​}}
  
 ==== Structura Proiectului ==== ==== Structura Proiectului ====
Line 183: Line 183:
   - o **activitate** (''​GoogleCloudMessagingActivity''​) care:   - o **activitate** (''​GoogleCloudMessagingActivity''​) care:
     - verifică dacă pe dispozitivul mobil este instalat Google Play Services și dacă această aplicație este actualizată;​     - verifică dacă pe dispozitivul mobil este instalat Google Play Services și dacă această aplicație este actualizată;​
-    - permite înregistrarea dispozitivului mobil la serverul Google Cloud Messaging dacă această operație nu a fost realizată în prealabil (o astfel de informație ar trebui stocată în SharedPreferences astfel încât operația să fie realizată o singură dată); +    - permite înregistrarea dispozitivului mobil la serverul Google Cloud Messaging dacă această operație nu a fost realizată în prealabil (o astfel de informație ar trebui stocată în ''​SharedPreferences'' ​astfel încât operația să fie realizată o singură dată); 
-    - transmite identificatorul de înregistrare obținut către serverul de aplicații (împreună cu alte informații prin care să poată fi recunoscut dispozitivul mobil - de exemplu, un nume de utilizator sau o adresă de posță electronică) +    - transmite identificatorul de înregistrare obținut către serverul de aplicații (împreună cu alte informații prin care să poată fi recunoscut dispozitivul mobil - de exemplu, un nume de utilizator sau o adresă de posță electronică); 
-  - un **ascultător** pentru mesajele cu difuzare (''​GoogleCloudMessagingBroadcastReceiver''​) către care este livrat un mesaj transmis de serverul Google Cloud Messaging, prin intermediul unei intenții+  - un **ascultător** pentru mesajele cu difuzare (''​GoogleCloudMessagingBroadcastReceiver''​) către care este livrat un mesaj transmis de serverul Google Cloud Messaging, prin intermediul unei intenții;
   - (opțional) un **serviciu** (''​GoogleCloudMessagingIntentService''​) lansat în execuție în momentul în care este livrat mesajul (de către ascultător) în cadrul căruia acesta este procesat propriu-zis;​ utilizarea unui serviciu este necesară doar în situația în care prelucrarea mesajului se realizează într-un interval de timp mai mare, fiind necesar să se asigure faptul că dispozitivul mobil nu devine inactiv între timp.   - (opțional) un **serviciu** (''​GoogleCloudMessagingIntentService''​) lansat în execuție în momentul în care este livrat mesajul (de către ascultător) în cadrul căruia acesta este procesat propriu-zis;​ utilizarea unui serviciu este necesară doar în situația în care prelucrarea mesajului se realizează într-un interval de timp mai mare, fiind necesar să se asigure faptul că dispozitivul mobil nu devine inactiv între timp.
  
-{{ :​laboratoare:​lab09-structuraproiectului.jpg?nolink }}+{{ :​laboratoare:​laborator08:​package_explorer.png?nolink&​500 ​}}
  
 === Activitatea GoogleCloudMessagingActivity === === Activitatea GoogleCloudMessagingActivity ===
Line 194: Line 194:
 Pe lângă implementarea interfeței grafice prin care se asigură interacțiunea cu utilizatorul,​ activitatea ''​GoogleCloudMessagingActivity''​ este responsabilă pentru: Pe lângă implementarea interfeței grafice prin care se asigură interacțiunea cu utilizatorul,​ activitatea ''​GoogleCloudMessagingActivity''​ este responsabilă pentru:
  
-**1.** verificarea compatibilității dispozitivului mobil pe care rulează aplicația cu Google Play Services, SDK pe care se bazează Google Cloud Messaging; această operație trebuie să se realizeze ​la crearea activității (metoda ''​onCreate()''​) ​dar și în cazul revenirii la aceasta ​prin alte mijloace (metoda ''​onResume()''​)+**1.** verificarea compatibilității dispozitivului mobil pe care rulează aplicația cu Google Play Services, SDK pe care se bazează Google Cloud Messaging; această operație trebuie să se realizeze ​în momentul în care aplicația devine vizibilă ​(deci pe metoda ''​onResume()''​), astfel încât ​și în situația în care activitatea a fost întreruptă,​ să se identifice situația în care aceasta ​a fost instalată sau actualizată:​
  
 <code java> <code java>
 private boolean checkPlayServices() { private boolean checkPlayServices() {
-    ​int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);​ +  ​int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);​ 
-    if (resultCode != ConnectionResult.SUCCESS) { +  if (resultCode != ConnectionResult.SUCCESS) { 
-        if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { +    if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) { 
-            GooglePlayServicesUtil.getErrorDialog(resultCode,​ this, Configuration.PLAY_SERVICES_RESOLUTION_REQUEST).show();​ +      GooglePlayServicesUtil.getErrorDialog(resultCode,​ this, Configuration.PLAY_SERVICES_RESOLUTION_REQUEST).show();​ 
-        } else { +    } else { 
-            Log.i(TAG, ​"This mobile device does not support Google Play Services."); +      Log.i(Constants.TAG, Constants.GOOGLE_PLAY_SERVICES_ERROR_MESSAGE2); 
-     ​finish(); +      finish();
-        } +
-        return false;+
     }     }
-    return true;+    ​return false; 
 +  } 
 +  ​return true;
 } }
 </​code>​ </​code>​
Line 218: Line 218:
 <code java> <code java>
 private void registerInBackground() { private void registerInBackground() {
-    ​new AsyncTask<​Object,​ Integer, String>​() { +  ​new AsyncTask<​Object,​ Integer, String>​() { 
-        @Override +    @Override 
- protected String doInBackground(Object... params) { +    protected String doInBackground(Object... params) { 
-     ​try { +      try { 
-         ​if (googleCloudMessaging == null) { +        if (googleCloudMessaging == null) { 
-     ​googleCloudMessaging = GoogleCloudMessaging.getInstance(context);​ +          googleCloudMessaging = GoogleCloudMessaging.getInstance(context);​ 
- } +        } 
- registrationId = googleCloudMessaging.register(Configuration.PROJECT_NUMBER);​ +        registrationId = googleCloudMessaging.register(Configuration.PROJECT_NUMBER);​ 
- // store the registration ID into the SharedPreferences +        // store the registration ID into the SharedPreferences 
- // the ID is not yet registered with the application server +        // the ID is not yet registered with the application server 
- setSharedPreferences(context, registrationId, ​Configuration.FAILURE);​ +        ​Utilities.setInformationIntoSharedPreferences(GoogleCloudMessagingActivity.this, registrationId, ​Constants.FAILURE);​ 
-            } catch (IOException ​exception) { +      } catch (IOException ​ioException) { 
-         ​registrationId = "an exception has occurred: " + exception.getMessage();​ + registrationId = "An exception has occurred: " + ioException.getMessage();​ 
-         exception.printStackTrace();​ +        if (Constants.DEBUG) { 
-            } +          ioException.printStackTrace();​ 
-     ​return registrationId;​ +        } 
- } +      } 
- +      ​return registrationId;​ 
- @Override +    } 
- protected void onPostExecute(String registrationId) { +     
-     registrationIdTextViewValue.append(registrationId ​+ "​\n"​); +    @Override 
-     Log.i(TAG, "​registration ID supplied by the Google Cloud Messaging Server is: "+registrationId);​ +    protected void onPostExecute(String registrationId) { 
- } +      Log.i(Constants.TAG,​ Constants.REGISTRATION_ID_INFORMATION_MESSAGE ​+ registrationId); 
-    }.execute(null,​ null, null);+      if (unregisteredFragment != null) { 
 +        unregisteredFragment.setRegistrationIdTextView(registrationId);​ 
 +      } 
 +    ​} 
 +  ​}.execute(null,​ null, null);
 } }
 </​code>​ </​code>​
  
-<note warning>​Întrucât procesul de înregistrare la Google Cloud Messaging implică comunicație prin rețea, este necesar ca acesta să se realizeze asincron, pe un fir de execuție separat, motiv pentru care procesarea a fost realizată în cadrul metodei ''​doInBackground''​ a unui obiect de tip ''​AsyncTask''​.</​note>​+<note warning>​Întrucât procesul de înregistrare la Google Cloud Messaging implică comunicație prin rețea, este necesar ca acesta să se realizeze asincron, pe un fir de execuție separat, motiv pentru care procesarea a fost realizată în cadrul metodei ''​doInBackground()''​ a unui obiect de tip ''​AsyncTask''​.</​note>​
  
 Testarea existenței unei valori corespunzătoare identificatorului de înregistrare în preferințele utilizatorului se face foarte ușor, acesta având o structură de tip asociere între o cheie și o valoare. Testarea existenței unei valori corespunzătoare identificatorului de înregistrare în preferințele utilizatorului se face foarte ușor, acesta având o structură de tip asociere între o cheie și o valoare.
  
 <code java> <code java>
-private ​String ​getRegistrationId(Context context) { +public static ​String ​getRegistrationIdFromSharedPreferences(Context context) { 
-    final SharedPreferences sharedPreferences = getSharedPreferences(context);​ +  final SharedPreferences sharedPreferences = getSharedPreferences(context);​ 
-    String registrationId = sharedPreferences.getString(Configuration.REGISTRATION_ID_PROPERTY,​ ""​);​ +  String registrationId = sharedPreferences.getString(Constants.REGISTRATION_ID_PROPERTY,​ ""​);​ 
-    if (registrationId.isEmpty()) { +  if (registrationId.isEmpty()) { 
-        Log.i(TAG, ​"​Registration ID not found into the SharedPreferences."); +    Log.i(Constants.TAG, Constants.REGISTRATION_ID_ERROR_MESSAGE); 
-        return ​""​; +    return ​Constants.EMPTY_STRING; 
-    } +  } 
-    // check whether the application was updated  +  // check whether the application was updated  
-    // if it is the case, the registration ID previously supplied must be cleared +  // if it is the case, the registration ID previously supplied must be cleared 
-    // as it is not guaranteed to work with the current version +  // as it is not guaranteed to work with the current version 
-    int registeredVersion ​= sharedPreferences.getInt(Configuration.APPLICATION_VERSION_PROPERTY,​ Integer.MIN_VALUE);​ +  int registeredApplicationVersion ​= sharedPreferences.getInt(Constants.APPLICATION_VERSION_PROPERTY,​ Integer.MIN_VALUE);​ 
-    int currentVersion = getApplicationVersion(context);​ +  int currentVersion = getApplicationVersion(context);​ 
-    if (registeredVersion ​!= currentVersion) { +  if (registeredApplicationVersion ​!= currentVersion) { 
-        Log.i(TAG, ​"The application version was changed."); +    Log.i(Constants.TAG, Constants.APPLICATION_VERSION_ERROR_MESSAGE); 
-        return ​""​; +    return ​Constants.EMPTY_STRING; 
-    } +  } 
-    return registrationId;​+  return registrationId;​
 } }
 </​code>​ </​code>​
Line 275: Line 279:
  
 <code java> <code java>
-public static ​class RegisterButtonListener ​implements OnClickListener { +protected ​class SendRegistrationIdButtonClickListener ​implements ​Button.OnClickListener { 
-    @Override +  
-    public void onClick(View view) { +  ​@Override 
-        new AsyncTask<​Object,​ Integer, Integer>​() { +  public void onClick(View view) { 
-            @Override +    new AsyncTask<​Object,​ Integer, Integer>​() { 
-            protected Integer doInBackground(Object... params) { +      @Override 
-                try { +      protected Integer doInBackground(Object... params) { 
-                    HttpClient httpClient = new DefaultHttpClient(); ​        +       
- +        ​try { 
-                    HttpPost httpPost = new HttpPost(Configuration.GOOGLECLOUDMESSAGING_WEBSERVER_URL); +          HttpClient httpClient = new DefaultHttpClient(); ​        
- +           
-                    // send the username, email and registration ID as fields of the HTTP body +          HttpPost httpPost = new HttpPost(Constants.DEVICE_REGISTRATION_SERVICE_ADDRESS); 
-                    // to the application server +           
-                    List<​NameValuePair>​ requestParameters = new ArrayList<​NameValuePair>​(); ​        +          // send the user name, email and registration ID as fields of the HTTP body to the application server 
-                    requestParameters.add(new BasicNameValuePair(Configuration.USER_NAME, usernameEditText.getText().toString()));​ +          List<​NameValuePair>​ requestParameters = new ArrayList<​NameValuePair>​();​ 
-                    requestParameters.add(new BasicNameValuePair(Configuration.EMAIL, emailEditText.getText().toString()));​ +          requestParameters.add(new BasicNameValuePair(Constants.USERNAME, usernameEditText.getText().toString()));​ 
-                    requestParameters.add(new BasicNameValuePair(Configuration.REGISTRATION_ID, ​registrationIdTextViewValue.getText().toString()));​ +          requestParameters.add(new BasicNameValuePair(Constants.EMAIL, emailEditText.getText().toString()));​ 
-                    UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(requestParameters,​ HTTP.UTF_8);​ +          requestParameters.add(new BasicNameValuePair(Constants.REGISTRATION_ID, ​registrationIdTextView.getText().toString()));​ 
-                    httpPost.setEntity(urlEncodedFormEntity);​ +           
- +          ​UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(requestParameters,​ HTTP.UTF_8);​ 
-                    HttpResponse httpPostResponse = httpClient.execute(httpPost);​  +          httpPost.setEntity(urlEncodedFormEntity);​ 
- +           
-                    // transmission of information to the application server was successful +          HttpResponse httpPostResponse = httpClient.execute(httpPost);​  
-                    if (httpPostResponse.getStatusLine().getStatusCode() == 200) +           
-                        return ​Configuration.SUCCESS; +          // transmission of information to the application server was successful 
-                } catch (Exception exception) { +          if (httpPostResponse.getStatusLine().getStatusCode() == 200) { 
-                    Log.e(TAG, exception.getMessage());​ +            return ​Constants.SUCCESS; 
-                    ​exception.printStackTrace();​ +          } 
-                } + } catch (IOException ioException) { 
-                // transmission of information to the application server was not successful +          Log.e(Constants.TAG, "​An ​exception ​has occurred: " + ioException.getMessage());​ 
- return ​Configuration.FAILURE; +          if (Constants.DEBUG) { 
-            }+            ioException.printStackTrace();​ 
 +          } 
 +        ​} 
 + // transmission of information to the application server was not successful 
 + return ​Constants.FAILURE; 
 +      } 
 +       
 +      @Override 
 +      protected void onPostExecute(Integer result) { 
 +        switch(result.intValue()) { 
 +          case Constants.SUCCESS:​ 
 +            // store the registration ID into the SharedPreferences 
 +            // the ID is registered with the application server 
 +            Utilities.setInformationIntoSharedPreferences(getActivity(),​ registrationIdTextView.getText().toString(),​ Constants.SUCCESS);​
             ​             ​
-            ​@Override +            ​// replace the existing fragment in order to forbid the user to register its ID to the registration server 
-            ​protected void onPostExecute(Integer result) { +            ​RegisteredFragment registeredFragment = new RegisteredFragment(); 
-                // ​... +            ​FragmentManager fragmentManager = getActivity().getFragmentManager();​ 
-            } +            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
-        }.execute(null,​ null, null);  +            fragmentTransaction.replace(R.id.container,​ registeredFragment);​ 
-    } +            ​fragmentTransaction.commit();​ 
 +            Log.i(Constants.TAG,​ Constants.REGISTRATION_PROCESS_SUCCESS);​ 
 +            break; 
 +           
 +          case Constants.FAILURE:​ 
 +            Log.i(Constants.TAG,​ Constants.REGISTRATION_PROCESS_FAILURE);​ 
 +            break; 
 + } 
 +      } 
 +    ​}.execute(null,​ null, null); 
 +  }
 } }
 </​code>​ </​code>​
Line 324: Line 351:
  
 Implementarea unui ascultător pentru mesaje cu difuzare poate fi realizată în două variante: Implementarea unui ascultător pentru mesaje cu difuzare poate fi realizată în două variante:
-  - în cazul în care procesarea mesajului implică operații triviale (ce pot fi realizate într-un interval de timp rezonabil), se poate defini un ascultător ''​BroadcastReceiver'',​ prelucrarea propriu-zisă realizându-se în cadrul metodei ''​onReceive''​ (ce primește ca parametri contextul și intenția care a apelat-o, cu informațiile din corpul mesajului atașate într-un obiect de tip ''​Bundle''​),​ apelată în mod automat în momentul în care mesajul este livrat. +  - în cazul în care procesarea mesajului implică operații triviale (ce pot fi realizate într-un interval de timp rezonabil), se poate defini un ascultător ''​BroadcastReceiver'',​ prelucrarea propriu-zisă realizându-se în cadrul metodei ''​onReceive()''​ (ce primește ca parametri contextul și intenția care a apelat-o, cu informațiile din corpul mesajului atașate într-un obiect de tip ''​Bundle''​),​ apelată în mod automat în momentul în care mesajul este livrat. 
-  - în situația în care procesarea mesajului presupune mai multe operații realizate într-un interval de timp semnificativ,​ prelucrarea se va realiza în cadrul unui serviciu dedicat (de tip ''​IntentService''​),​ ce va rula în fundal, în timp ce se obține accesul la un tip de mutex ce împiedică dispozitivul mobil să intre într-o stare de inactivitate care ar suspenda tratarea evenimentului de livrare a unui mesaj, dacă aceasta nu este încă completată:​ <code java>+  - în situația în care procesarea mesajului presupune mai multe operații realizate într-un interval de timp semnificativ,​ prelucrarea se va realiza în cadrul unui serviciu dedicat (de tip ''​IntentService''​),​ ce va rula în fundal, în timp ce se obține accesul la un tip de mutex ce împiedică dispozitivul mobil să intre într-o stare de inactivitate care ar suspenda tratarea evenimentului de livrare a unui mesaj, dacă aceasta nu este încă completată:​ <file java GoogleCloudMessagingBroadcastReceiver.java>
 public class GoogleCloudMessagingBroadcastReceiver extends WakefulBroadcastReceiver { public class GoogleCloudMessagingBroadcastReceiver extends WakefulBroadcastReceiver {
-    ​@Override + 
-    public void onReceive(Context context, Intent intent) { +  ​@Override 
-        // create a component associated to the service that will handle the intent +  public void onReceive(Context context, Intent intent) { 
-        ComponentName component = new ComponentName(context.getPackageName(),​ GoogleCloudMessagingIntentService.class.getName());​ +   
-        // start the service +    ​// create a component associated to the service that will handle the intent 
-        // maintain the mobile device awake +    ComponentName component = new ComponentName( 
-        startWakefulService(context,​ (intent.setComponent(component)));​ +      ​context.getPackageName(),​ 
-        setResultCode(Activity.RESULT_OK);​ +      ​GoogleCloudMessagingIntentService.class.getName() 
-    }+    ​); 
 +     
 +    ​// start the service 
 +    // maintain the mobile device awake 
 +    startWakefulService(context,​ (intent.setComponent(component)));​ 
 +    setResultCode(Activity.RESULT_OK);​ 
 +  }
 } }
-</code>Așa cum se poate observa, a fost utilizat un ascultător de tip ''​WakefulBroadcastReceiver'',​ delegarea procesării mesajului către serviciul dedicat făcându-se prin metoda ''​startWakefulService''​ ce asigură și obținerea accesului la mutexul care împiedică intrarea dispozitivului mobil într-o stare de inactivitate.+</file> 
 + 
 +Așa cum se poate observa, a fost utilizat un ascultător de tip ''​WakefulBroadcastReceiver'',​ delegarea procesării mesajului către serviciul dedicat făcându-se prin metoda ''​startWakefulService''​ ce asigură și obținerea accesului la mutexul care împiedică intrarea dispozitivului mobil într-o stare de inactivitate.
  
 === Serviciul GoogleCloudMessagingIntentService === === Serviciul GoogleCloudMessagingIntentService ===
Line 343: Line 378:
 În cadrul serviciului pentru tratarea evenimentului (de tip ''​IntentService''​) are loc procesarea propriu-zisă a mesajului, aceasta fiind realizată în cadrul metodei ''​onHandleIntent()''​ care primește ca parametru aceeași intenție care a fost transmisă ascultătorului,​ și pentru care a fost obținut mutextul care împiedică intrarea dispozitivului mobil într-o stare de inactivitate. În cadrul serviciului pentru tratarea evenimentului (de tip ''​IntentService''​) are loc procesarea propriu-zisă a mesajului, aceasta fiind realizată în cadrul metodei ''​onHandleIntent()''​ care primește ca parametru aceeași intenție care a fost transmisă ascultătorului,​ și pentru care a fost obținut mutextul care împiedică intrarea dispozitivului mobil într-o stare de inactivitate.
  
-Prelucrarea unui mesaj se face în funcție de tipul acestuia, furnizat de metoda ''​getMessageType''​ a obiectului de tip ''​GoogleCloudMessaging'',​ pe baza informațiilor din intenție. Odată cunoscut tipul mesajului și datele asociate prin ''​Bundle'',​ utilizatorul poate realiza orice acțiune, de la transmiterea unei notificări la modificarea conținutului unor controale grafice din cadrul activității.+Prelucrarea unui mesaj se face în funcție de tipul acestuia, furnizat de metoda ''​getMessageType()''​ a obiectului de tip ''​GoogleCloudMessaging'',​ pe baza informațiilor din intenție. Odată cunoscut tipul mesajului și datele asociate prin ''​Bundle'',​ utilizatorul poate realiza orice acțiune, de la transmiterea unei notificări la modificarea conținutului unor controale grafice din cadrul activității.
  
 <note important>​Ulterior analizei mesajului, trebuie să se asigure și eliberarea mutexului ce împiedică intrarea dispozitivului mobil într-o stare de inactivitate,​ prin apelul metodei ''​completeWakefulIntent()''​ căreia i se transmite ca parametru aceeași intenție pentru care a fost obținută această resursă.</​note>​ <note important>​Ulterior analizei mesajului, trebuie să se asigure și eliberarea mutexului ce împiedică intrarea dispozitivului mobil într-o stare de inactivitate,​ prin apelul metodei ''​completeWakefulIntent()''​ căreia i se transmite ca parametru aceeași intenție pentru care a fost obținută această resursă.</​note>​
  
-<code java>+<file java GoogleCloudMessagingIntentService.java>
 public class GoogleCloudMessagingIntentService extends IntentService { public class GoogleCloudMessagingIntentService extends IntentService {
  
-    private final static String ​TAG = "​GoogleCloudMessagingIntentService"​; +  public GoogleCloudMessagingIntentService() { 
- +    super(Constants.TAG); 
-    ​public GoogleCloudMessagingIntentService() { +  } 
-        ​super(TAG);+   
 +  @Override 
 +  protected void onHandleIntent(Intent intent) { 
 +    // the intent is supplied via the BroadcastReceiver 
 +     
 +    GoogleCloudMessaging googleCloudMessaging ​= GoogleCloudMessaging.getInstance(this); 
 +     
 +    ​String messageType = googleCloudMessaging.getMessageType(intent); 
 +     
 +    Bundle extras = intent.getExtras();​ 
 +    if (!extras.isEmpty()) { 
 +      // filter the message according to its type 
 +      if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { 
 +        ​sendNotification(extras); 
 +      } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) { 
 +        sendNotification(extras);​ 
 +      } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) { 
 +        sendNotification(extras);​ 
 +      }
     }     }
 +    ​
 +    // release the wake lock provided by the WakefulBroadcastReceiver.
 +    GoogleCloudMessagingBroadcastReceiver.completeWakefulIntent(intent);​
 +  }
 +  ​
 +  // create a notification containing the message and post it
 +  private void sendNotification(Bundle bundle) {
 +    Intent notificationHandlerIntent = new Intent(this,​ NotificationHandlerActivity.class);​
 +    notificationHandlerIntent.putExtras(bundle);​
 +    ​
 +    TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);​
 +    taskStackBuilder.addParentStack(NotificationHandlerActivity.class);​
 +    taskStackBuilder.addNextIntent(notificationHandlerIntent);​
 +    ​
 +    PendingIntent notificationHandlerPendingIntent = taskStackBuilder.getPendingIntent(
 +      0,
 +      PendingIntent.FLAG_UPDATE_CURRENT);​
 +      ​
 +    NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
 +      .setSmallIcon(R.drawable.ic_launcher)
 +      .setContentTitle(Constants.TAG)
 +      .setStyle(new NotificationCompat.BigTextStyle().bigText(Constants.NOTIFICATION_MESSAGE))
 +      .setContentText(Constants.NOTIFICATION_MESSAGE);​
 +      ​
 +    builder.setContentIntent(notificationHandlerPendingIntent);​
 +      ​
 +    NotificationManager notificationManager = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);​
 +    notificationManager.notify(Constants.NOTIFICATION_ID,​ builder.build());​
 +  }
 +}
 +</​file>​
  
-    @Override 
-    protected void onHandleIntent(Intent intent) { 
-        // the intent is supplied via the BroadcastReceiver 
- 
-        GoogleCloudMessaging googleCloudMessaging = GoogleCloudMessaging.getInstance(this);​ 
- 
-        Bundle extras = intent.getExtras();​ 
-        if (!extras.isEmpty()) { 
-            String messageType = googleCloudMessaging.getMessageType(intent);​ 
-            // filter the message according to its type 
-            // send a notification containing the data attached to the message (as extras) 
-            if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType)) { 
-                sendNotification("​Send error: " + extras.toString());​ 
-            } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType)) { 
-                sendNotification("​Deleted messages on server: " + extras.toString());​ 
-            } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType)) { 
-                sendNotification("​Received:​ " + extras.toString());​ 
-            } 
-        } 
-        // release the wake lock provided by the WakefulBroadcastReceiver. 
-        GoogleCloudMessagingBroadcastReceiver.completeWakefulIntent(intent);​ 
-    } 
- 
-    // create a notification containing the message and post it 
-    private void sendNotification(String message) { 
-        // ... 
-    } 
-} 
-</​code>​ 
 ==== Permisiuni AndroidManifest.xml ==== ==== Permisiuni AndroidManifest.xml ====
  
Line 392: Line 447:
 </​code>​ </​code>​
   * în fișierul ''​AndroidManifest.xml''​ se precizează permisiunile necesare pentru ca aplicația să poată utiliza funcționalitățile oferite de Google Cloud Messaging\\ <code xml>   * în fișierul ''​AndroidManifest.xml''​ se precizează permisiunile necesare pentru ca aplicația să poată utiliza funcționalitățile oferite de Google Cloud Messaging\\ <code xml>
-<​manifest package="​ro.pub.systems.pdsd.lab09.GoogleCloudMessagingClient" ...>+<​manifest package="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging" ...>
  
-    ​<​uses-permission android:​name="​android.permission.INTERNET"​ /> +  ​<​uses-permission ​ 
-    <​uses-permission android:​name="​android.permission.GET_ACCOUNTS"​ /> +    ​android:​name="​android.permission.INTERNET"​ /> 
-    <​uses-permission android:​name="​android.permission.WAKE_LOCK"​ /> +  <​uses-permission ​ 
-    <​uses-permission android:​name="​com.google.android.c2dm.permission.RECEIVE"​ />+    ​android:​name="​android.permission.GET_ACCOUNTS"​ /> 
 +  <​uses-permission ​ 
 +    ​android:​name="​android.permission.WAKE_LOCK"​ /> 
 +  <​uses-permission ​ 
 +    ​android:​name="​com.google.android.c2dm.permission.RECEIVE"​ />
  
-    ​<​permission android:​name="​ro.pub.systems.pdsd.lab09.GoogleCloudMessagingClient.permission.C2D_MESSAGE"​ +  ​<​permission ​ 
-        android:​protectionLevel="​signature"​ /> +    ​android:​name="​ro.pub.systems.pdsd.lab08.googlecloudmessaging.permission.C2D_MESSAGE"​ 
-    <​uses-permission android:​name="​ro.pub.systems.pdsd.lab09.GoogleCloudMessagingClient.permission.C2D_MESSAGE"​ /> +    android:​protectionLevel="​signature"​ /> 
- +  <​uses-permission ​ 
-    <​application ...> +    ​android:​name="​ro.pub.systems.pdsd.lab08.googlecloudmessaging.permission.C2D_MESSAGE"​ />
-        <​receiver +
-            android:​name="​.GoogleCloudMessagingBroadcastReceiver"​ +
-            android:​permission="​com.google.android.c2dm.permission.SEND"​ > +
-            <​intent-filter>​ +
-                <action android:​name="​com.google.android.c2dm.intent.RECEIVE"​ /> +
-                <​category android:​name="​ro.pub.systems.pdsd.lab09.GoogleCloudMessagingClient"​ /> +
-            </​intent-filter>​ +
-        </​receiver>​ +
-        <service android:​name="​.GoogleCloudMessagingIntentService"​ /> +
-    </​application>+
  
 +  <​application ...>
 +    <​meta-data
 +      android:​name="​com.google.android.gms.version"​
 +      android:​value="​@integer/​google_play_services_version"​ />
 +    <​activity
 +      android:​name="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.view.GoogleCloudMessagingActivity"​
 +      android:​label="​@string/​app_name"​ >
 +      <​intent-filter>​
 +        <action android:​name="​android.intent.action.MAIN"​ />
 +        <​category android:​name="​android.intent.category.LAUNCHER"​ />
 +      </​intent-filter>​
 +    </​activity>​
 +    <​activity
 +      android:​name="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.view.NotificationHandlerActivity"​
 +      android:​parentActivityName="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.view.GoogleCloudMessagingActivity">​
 +      <​meta-data
 +        android:​name="​android.support.PARENT_ACTIVITY"​
 +        android:​value="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.view.GoogleCloudMessagingActivity"​ />
 +    </​activity>​
 +    <​receiver
 +      android:​name="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.controller.GoogleCloudMessagingBroadcastReceiver"​
 +      android:​permission="​com.google.android.c2dm.permission.SEND"​ >
 +      <​intent-filter>​
 +        <action android:​name="​com.google.android.c2dm.intent.RECEIVE"​ />
 +        <​category android:​name="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.controller.GoogleCloudMessagingBroadcastReceiver"​ />
 +      </​intent-filter>​
 +    </​receiver>​
 +    <service android:​name="​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.controller.GoogleCloudMessagingIntentService"​ />
 +  </​application>​
 </​manifest>​ </​manifest>​
 </​code>​ </​code>​
 +
 <note important>​Pentru a se asigura faptul că aplicația Android va rula corespunzător,​ ținând cont de faptul că Google Cloud Messaging nu poate fi folosit pe dispozitive mobile rulând un sistem de operare Android anterior versiunii 2.3, proprietatea ''​android:​minSdkVersion''​ va avea o valoare egală sau mai mare cu 8.</​note>​ <note important>​Pentru a se asigura faptul că aplicația Android va rula corespunzător,​ ținând cont de faptul că Google Cloud Messaging nu poate fi folosit pe dispozitive mobile rulând un sistem de operare Android anterior versiunii 2.3, proprietatea ''​android:​minSdkVersion''​ va avea o valoare egală sau mai mare cu 8.</​note>​
-    * permisiunea ''​android.permission.INTERNET''​ este necesară pentru ca aplicația să poate transmite identificatorul său serverului de aplicații, prin intermediul căruia se realizează înregistrarea sa +    * permisiunea ''​android.permission.INTERNET''​ este necesară pentru ca aplicația să poate transmite identificatorul său serverului de aplicații, prin intermediul căruia se realizează înregistrarea sa; 
-    * permisiunea ''​android.permission.GET_ACCOUNTS''​ este necesară întrucât Google Cloud Messaging solicită (pentru sisteme de operare Android anterioare versiunii 4.0.4) un cont Google +    * permisiunea ''​android.permission.GET_ACCOUNTS''​ este necesară întrucât Google Cloud Messaging solicită (pentru sisteme de operare Android anterioare versiunii 4.0.4) un cont Google; 
-    * permisiunea ''​android.permission.WAKE_LOCK''​ (opțională) este necesară pentru a se preveni întreruperea activității în momentul în care este primit un mesaj +    * permisiunea ''​android.permission.WAKE_LOCK''​ (opțională) este necesară pentru a se preveni întreruperea activității în momentul în care este primit un mesaj; 
-    * permisiunea ''​android.permission.RECEIVE''​ este necesară pentru ca aplicația să se poată înregistra (în cadrul serverului de aplicații) și pentru a putea primi mesaje +    * permisiunea ''​android.permission.RECEIVE''​ este necesară pentru ca aplicația să se poată înregistra (în cadrul serverului de aplicații) și pentru a putea primi mesaje; 
-    * permisiunea de tip ''​permission.C2D_MESSAGE''​ (prefixată de denumirea pachetului care identifică în mod unic aplicația) pentru a preveni altă aplicație Android să se înregistreze (în cadrul serverului de aplicații) și să primească mesajele care îi sunt destinate+    * permisiunea de tip ''​permission.C2D_MESSAGE''​ (prefixată de denumirea pachetului care identifică în mod unic aplicația) pentru a preveni altă aplicație Android să se înregistreze (în cadrul serverului de aplicații) și să primească mesajele care îi sunt destinate;
 <note warning>​Este necesar ca acest tip de permisiune să utilizeze această sintaxă, în caz contrar mesajele nefiind transmise aplicației Android.</​note>​ <note warning>​Este necesar ca acest tip de permisiune să utilizeze această sintaxă, în caz contrar mesajele nefiind transmise aplicației Android.</​note>​
     * un **ascultător** (de tipul ''​com.google.android.c2dm.intent.RECEIVE''​) având permisiunea ''​com.google.android.c2dm.SEND''​ astfel încât doar motorul Google Cloud Messaging să îi poată transmite mesaje     * un **ascultător** (de tipul ''​com.google.android.c2dm.intent.RECEIVE''​) având permisiunea ''​com.google.android.c2dm.SEND''​ astfel încât doar motorul Google Cloud Messaging să îi poată transmite mesaje
Line 434: Line 513:
 Se precizează regulile pentru obfuscatorul Proguard (în fișierul ''​proguard-project.txt''​ din rădăcina proiectului),​ astfel încât acesta să nu elimine clasele necesare:\\ <code java> Se precizează regulile pentru obfuscatorul Proguard (în fișierul ''​proguard-project.txt''​ din rădăcina proiectului),​ astfel încât acesta să nu elimine clasele necesare:\\ <code java>
 -keep class * extends java.util.ListResourceBundle { -keep class * extends java.util.ListResourceBundle {
-    ​protected Object[][] getContents();​+  ​protected Object[][] getContents();​
 } }
  
 -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable { -keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
-    ​public static final *** NULL;+  ​public static final *** NULL;
 } }
  
 -keepnames @com.google.android.gms.common.annotation.KeepName class * -keepnames @com.google.android.gms.common.annotation.KeepName class *
 -keepclassmembernames class * { -keepclassmembernames class * {
-    ​@com.google.android.gms.common.annotation.KeepName *;+  ​@com.google.android.gms.common.annotation.KeepName *;
 } }
  
 -keepnames class * implements android.os.Parcelable { -keepnames class * implements android.os.Parcelable {
-    ​public static final ** CREATOR;+  ​public static final ** CREATOR;
 } }
 </​code>​ </​code>​
  
-===== Proiectarea și dezvoltarea serverului =====+===== Proiectarea și dezvoltarea serverului ​(opțional) ​=====
  
 Arhitectura Google Cloud Messaging implică existența a două servere: Arhitectura Google Cloud Messaging implică existența a două servere:
Line 496: Line 575:
 // to which the application server sends a POST request in order to be further transmitted to the mobile device // to which the application server sends a POST request in order to be further transmitted to the mobile device
   
-final public static String ​REQUEST_SERVER_URL ​= "​https://​android.googleapis.com/​gcm/​send";​+final public static String ​GCM_SERVER_ADDRESS ​= "​https://​android.googleapis.com/​gcm/​send";​
  
 // ... // ...
  
-HttpPost httpRequest = new HttpPost(REQUEST_SERVER_URL);+HttpPost httpRequest = new HttpPost(GCM_SERVER_ADDRESS);
 </​code>​ </​code>​
  
Line 557: Line 636:
  * (for each mobile device to which the message is to be transmitted the message must specify  * (for each mobile device to which the message is to be transmitted the message must specify
  * the id provided by the GCM server during the registration process)  * the id provided by the GCM server during the registration process)
- ​* ​+ *
  * -> data contains the payload to be transmitted;​ the developer may define its own keys  * -> data contains the payload to be transmitted;​ the developer may define its own keys
  ​* ​  ​* ​
Line 569: Line 648:
 JSONArray registrationIds = new JSONArray();​ JSONArray registrationIds = new JSONArray();​
 registrationIds.add(registrationId);​ registrationIds.add(registrationId);​
-requestBody.put("​registration_ids"​, registrationIds);​+requestBody.put(Constants.REGISTRATION_IDS, registrationIds);​
 JSONObject payload = new JSONObject();​ JSONObject payload = new JSONObject();​
-payload.put("​message"​, message); +payload.put(Constants.MESSAGE, message); 
-requestBody.put("​data"​, payload);+requestBody.put(Constants.DATA, payload);
 </​code>​ </​code>​
  
Line 585: Line 664:
  "​registration_ids":​ ["​..."​],​  "​registration_ids":​ ["​..."​],​
  "​data":​ {  "​data":​ {
-     "​message":​ "​..."​+   "​message":​ "​..."​
  }  }
 } }
Line 593: Line 672:
  
 <code java> <code java>
-HttpClient client ​= HttpClientBuilder.create().build();+CloseableHttpClient httpClient ​= HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
  
 // ... // ...
Line 600: Line 679:
  
 // send the request // send the request
-HttpResponse httpResponse = client.execute(httpRequest);​+HttpResponse httpResponse = httpClient.execute(httpRequest);​
 </​code>​ </​code>​
  
Line 636: Line 715:
 <​code>​ <​code>​
 if (firstLine.startsWith("​id"​)) { if (firstLine.startsWith("​id"​)) {
-    ​if (secondLine.equals("​registration_id"​)) { +  ​if (secondLine.equals("​registration_id"​)) { 
-        old_registration_id = get registration_id from the request field +    old_registration_id = get registration_id from the request field 
-        replace old_registration_id with new registration_id in the request / application server database +    replace old_registration_id with new registration_id in the request / application server database 
-    }+  }
 } else { } else {
-    ​switch(Error) { +  ​switch(Error) { 
-        case "​NotRegistered":​ +    case "​NotRegistered":​ 
-            // the application was uninstalled from the device +      // the application was uninstalled from the device 
-            // or it does not have a broadcast receiver in order to process com.google.android.c2dm.intent.RECEIVE intents +      // or it does not have a broadcast receiver in order to process com.google.android.c2dm.intent.RECEIVE intents 
-            remove the registration ID from the application server database +      remove the registration ID from the application server database 
-            break; +      break; 
-        default: +    default: 
-            // non-recoverable error regarding the registration ID +      // non-recoverable error regarding the registration ID 
-            remove the registration ID from the application server database +      remove the registration ID from the application server database 
-            break; +      break; 
-    }+  }
 } }
 </​code>​ </​code>​
Line 668: Line 747:
 <​code>​ <​code>​
 if (failure != 0 && canonical_ids != 0) { if (failure != 0 && canonical_ids != 0) {
-    ​for-each (result ∈ results) { +  ​for-each (result ∈ results) { 
-        if (result.registration_id) { +    if (result.registration_id) { 
-            // the old registration ID is not included within the response +      // the old registration ID is not included within the response 
-            old_registration_id = get registration_id from the request field registration_ids,​ having the same index+      old_registration_id = get registration_id from the request field registration_ids,​ having the same index
             ​             ​
-            ​replace old_registration_id with new result.registration_id in the request +      ​replace old_registration_id with new result.registration_id in the request 
-        } else { +    } else { 
-            switch(error) { +      switch(error) { 
-                case "​Unavailable":​ +        case "​Unavailable":​ 
-                    retry sending the request +          retry sending the request 
-                    break; +          break; 
-                case "​NotRegistered":​ +        case "​NotRegistered":​ 
-                    // the application was uninstalled from the device +          // the application was uninstalled from the device 
-                    // or it does not have a broadcast receiver in order to process com.google.android.c2dm.intent.RECEIVE intents +          // or it does not have a broadcast receiver in order to process com.google.android.c2dm.intent.RECEIVE intents 
-                    remove the registration ID from the application server database +          remove the registration ID from the application server database 
-                    break; +          break; 
-                default: +        default: 
-                    // non-recoverable error regarding the registration ID +          // non-recoverable error regarding the registration ID 
-                    remove the registration ID from the application server database +          remove the registration ID from the application server database 
-                    break; +          break; 
-            } +      }
-        ​}+
     }     }
 +  }
 } }
 </​code>​ </​code>​
Line 735: Line 814:
 @Override @Override
 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,​ IOException { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,​ IOException {
-    ​Enumeration<​String>​ parameters = request.getParameterNames();​ +  ​Enumeration<​String>​ parameters = request.getParameterNames();​ 
-    ​boolean found = false; +   
-    while(parameters.hasMoreElements()) { +  String registrationId ​= null; 
-        String parameter = (String)parameters.nextElement();​ +  ​String username = null; 
-        if (parameter.equals(Constants.REGISTRATION_ID)) { +  String email = null; 
-            found = true; +   
-            ​registrationId = request.getParameter(parameter);​ +  ​while (parameters.hasMoreElements()) { 
-        } +    String parameter = (String)parameters.nextElement();​ 
-        if (parameter.equals(Constants.USER_NAME)) { +    if (parameter.equals(Constants.REGISTRATION_ID)) { 
-            ​userName ​= request.getParameter(parameter);​ +      registrationId = request.getParameter(parameter);​ 
-        } +     ​} 
-        if (parameter.equals(Constants.EMAIL)) { +     ​if (parameter.equals(Constants.USERNAME)) { 
-            email = request.getParameter(parameter);​ +       username ​= request.getParameter(parameter);​ 
-        } +     ​} 
-    } +     ​if (parameter.equals(Constants.EMAIL)) { 
-    if (found) { +       ​email = request.getParameter(parameter);​ 
-        ArrayList<​String> ​values ​= new ArrayList<​String>​();​ +     ​} 
-        ​values.add(registrationId); +   ​} 
-        ​values.add(userName);​ +         
-        values.add(email); +   if (registrationId != null && !registrationId.isEmpty() &&​ 
-        ​values.add(general.Constants.SYSTEM_FUNCTION[0]); +       ​username != null && !username.isEmpty() &&​ 
-        try { +       email != null && !email.isEmpty()) { 
-       ​  ​   DataBaseConnection.insertValuesIntoTable(Constants.TABLE_NAME,​ null, values, true); +     ​ArrayList<​String> ​columnNames ​= new ArrayList<​String>​();​ 
-        } catch (Exception exception) { +     columnNames.add(Constants.REGISTRATION_ID); 
-            System.out.println("​exceptie:​ "​+exception.getMessage()); +     columnNames.add(Constants.USERNAME); 
-            if (Constants.DEBUG) +     columnNames.add(Constants.EMAIL); 
-                exception.printStackTrace(); +     ArrayList<​String>​ values = new ArrayList<​String>​(); 
-        } +     values.add(registrationId); 
-    } +     values.add(username); 
-    response.setContentType("​text/​html"​); +     values.add(email); 
-    try { +      
-        ​PrintWriter printWriter = new PrintWriter(response.getWriter());​ +     try { 
-        ArrayList<​ArrayList<​Object>>​ records = DataBaseConnection.getTableContent(Constants.TABLE_NAME, ​null, null, null, null); +       dataBaseWrapper.insertValuesIntoTable(Constants.TABLE_NAME, ​columnNames, values, false); 
-        ​GoogleCloudMessagingGraphicUserInterface.displayGoogleCloudMessagingGraphicUserInterface(records,​ printWriter);​ +     ​} catch (SQLException | DataBaseException ​exception) { 
-    ​} catch (Exception ​exception) { +       ​System.out.println("​An exception has occurred: "​+exception.getMessage());​ 
-        System.out.println("​exceptie: "​+exception.getMessage());​ +       ​if (Constants.DEBUG) ​{ 
-        if (Constants.DEBUG) +         ​exception.printStackTrace();​ 
-            exception.printStackTrace();​ +       } 
-        response.setStatus(HttpServletResponse.SC_BAD_REQUEST); +     }  
-    } +     response.setStatus(HttpServletResponse.SC_OK);  
-    response.setStatus(HttpServletResponse.SC_OK);+   ​} else { 
 +     ​response.setStatus(HttpServletResponse.SC_BAD_REQUEST); 
 +   }
 } }
 </​code>​ </​code>​
Line 781: Line 862:
 Se observă faptul că se prelucrează cererea de tip POST, iterându-se pe toți parametrii incluși în scopul identificării valorilor asociate atributelor denumite nume de utilizator, adresă de poștă electronică și identificator de înregistrare (conform protocolului de comunicație definit de utilizator),​ aceștia fiind stocați în baza de date locală pentru a fi utilizați în transmiterea ulterioară a unui mesaj. Se observă faptul că se prelucrează cererea de tip POST, iterându-se pe toți parametrii incluși în scopul identificării valorilor asociate atributelor denumite nume de utilizator, adresă de poștă electronică și identificator de înregistrare (conform protocolului de comunicație definit de utilizator),​ aceștia fiind stocați în baza de date locală pentru a fi utilizați în transmiterea ulterioară a unui mesaj.
  
-<note important>​Este foarte important să fie specificată o stare în cadrul răspunsului (prin apelul metodei ''​setStatus''​ a obiectului ''​HttpServletResponse''​) desemnând rezultatul procesării mesajului, în caz contrar clientul Android blocându-se până la expirarea perioadei de timp pentru așteptare (//eng.// timeout).</​note>​+<note important>​Este foarte important să fie specificată o stare în cadrul răspunsului (prin apelul metodei ''​setStatus()''​ a obiectului ''​HttpServletResponse''​) desemnând rezultatul procesării mesajului, în caz contrar clientul Android blocându-se până la expirarea perioadei de timp pentru așteptare (//eng.// timeout).</​note>​ 
 ===== Activitate de Laborator ===== ===== Activitate de Laborator =====
  
Line 789: Line 871:
   * se generează o cheie publică pentru transmiterea de mesaje provenind de la un server, indiferent de adresa Internet la care se găsește acesta.   * se generează o cheie publică pentru transmiterea de mesaje provenind de la un server, indiferent de adresa Internet la care se găsește acesta.
  
-Mai multe detalii pot fi obținute în secțiunea [[:​laboratoare:​laborator09#​configurare|Configurare]].+Mai multe detalii pot fi obținute în secțiunea [[:​laboratoare:​laborator08#​configurare|Configurare]].
  
 <​note>​În cazul în care nu dispuneți de un cont Google, acesta va trebui creat înainte de a implementa aceste cerințe.</​note>​ <​note>​În cazul în care nu dispuneți de un cont Google, acesta va trebui creat înainte de a implementa aceste cerințe.</​note>​
 +
 +<note important>​Aceste valori nu vor fi utilizate decât împreună, altfel se vor genera erori de tipul ''​MismatchSenderId''​.</​note>​
  
 ==== Clientul Android ==== ==== Clientul Android ====
  
-**2.** ​Să se configureze clientul pentru a putea rula o aplicație Android folosind Google Cloud Messaging:​ +**2.** ​În contul Github personal, să se creeze un depozit denumit '​Laborator08-Client'​. Inițial, acesta ​trebuie ​să fie gol (nu trebuie să bifați nici adăugarea unui fișier ''​README.md''​, nici a fișierului ''​.gitignore''​ sau a a fișierului ''​LICENSE''​).
-  * se instalează SDK-ul Google Cloud Messaging +
-  * se configurează mașina pe care va rula aplicația +
-    * în cazul în care se utilizează un emulator, se configurează astfel încât ​acesta să aibă instalat Google Play Services +
-    * în cazul în care se utilizează un dispozitiv fizic, acesta ​trebuie să ruleze un sistem de operare Android cu o versiune ulterioară 2.2, având asociat un cont Google+
  
-Mai multe detalii pot fi obținute ​în secțiunea [[:laboratoare:​laborator09#​configurare_client|Configurare client]]+**3.** Să se cloneze ​în directorul de pe discul local conținutul depozitului la distanță de la [[https://​www.github.com/​pdsd2015/​Laborator08-Client|]]. 
  
-**3.** ​În mediul de dezvoltare să se deschidă proiectele ''​google-play-services_lib''​ (acesta ar trebui să se găsească și la ''​<​android-sdk>/​extras/​google/​google_play_services/​libproject/​google-play-services_lib'' ​după ce a fost instalat SDK-ul Google Cloud Messaging) ​și ''​GoogleCloudMessagingClient''​.+În urma acestei operații, directorul Laborator08-Client va trebui să se conțină directoarele ​''​labtasks''​ și ''​solutions''​. ​
  
-**4.** În interfața ''​ro.pub.systems.pdsd.lab09.googlecloudmessagingclient.Configuration'',​ trebuie modificate următoarele valori:+<​code>​ 
 +student@pdsd2015:​~$ git clone https://​www.github.com/​pdsd2015/​Laborator08-Client 
 +</​code>​ 
 + 
 +**4.** Să se încarce conținutul descărcat în cadrul depozitului '​Laborator08-Client'​ de pe contul Github personal. <​code>​ 
 +student@pdsd2015:​~$ cd Laborator08-Client 
 +student@pdsd2015:​~/​Laborator08-Client$ git remote add Laborator08-Client_perfectstudent https://​github.com/​perfectstudent/​Laborator08-Client 
 +student@pdsd2015:​~/​Laborator08-Client$ git push Laborator08-Client_perfectstudent master 
 +</​code>​ 
 + 
 +**5.** Să se importe în mediul integrat de dezvoltare Eclipse Luna SR1a (4.4.1) proiectele ''​google-play-service_lib''​ și ''​GoogleCloudMessagingClient''​ din directorul ''​labtasks''​ (în această ordine). 
 + 
 +Să se configureaze mașina pe care va rula aplicația:​ 
 +  * în cazul în care se utilizează un emulator, se configurează astfel încât acesta să aibă instalat Google Play Services; 
 +  * în cazul în care se utilizează un dispozitiv fizic, acesta trebuie să ruleze un sistem de operare Android cu o versiune ulterioară 2.2, având asociat un cont Google. 
 + 
 +<​note>​În situația în care nu se reușește configurarea unui dispozitiv virtual, se poate folosi [[http://​pdsd2015.andreirosucojocaru.ro/​resources/​Genymobile.tar.gz|o imagine Nexus S (API 16)]] care trebuie plasată în directorul ''​$HOME''​.</​note>​ 
 + 
 +Mai multe detalii pot fi obținute în secțiunea [[:​laboratoare:​laborator08#​configurare_client|Configurare client]] 
 + 
 +**6.** În interfața ''​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.general.Constants'',​ trebuie modificate următoarele valori, __**numai în situația în care se folosește un server web Apache Tomcat**__:
   * ''​PROJECT_NUMBER''​ - cu numărul de proiect generat de consola Google API;   * ''​PROJECT_NUMBER''​ - cu numărul de proiect generat de consola Google API;
-  * ''​GOOGLECLOUDMESSAGING_WEBSERVER_URL''​ - cu adresa ​Internet ​la care rulează serverul de aplicații +  * adresele ​Internet ​pentru serviciile ​care expun funcționalități către dispozitivul mobil: 
-    * dispozitiv mobil - adresa Internet indicată ca default gateway a rețelei de calculatoare din care fac parte dispozitivul mobil și mașina pe care se găsește serverul de aplicații +    * dispozitiv mobil - adresa Internet indicată ca default gateway a rețelei de calculatoare din care fac parte dispozitivul mobil și mașina pe care se găsește serverul de aplicații; 
-    * AVD - 10.0.2.1 +    * AVD - 10.0.2.1; 
-    * Genymotion - 192.168.56.1+    * Genymotion - 192.168.56.1.
  
-<note tip>​Portul pe care rulează serverul web Apache Tomcat este de regulă 8080 (dacă nu s-au realizat alte modificări în fișierul de configurare ''​server.xml''​).</​note>​+<note tip>​Portul pe care rulează serverul web Apache Tomcat este de regulă 8080 (dacă nu s-au realizat alte modificări în fișierul de configurare ''​server.xml''​). În situația în care, în momentul în care se încearcă să se realizeze conexiunea la serverul de aplicații, se obține o excepție de tip //Timeout// / //​Connection Refused//, investigați dacă este permisă comunicația pe portul respectiv.</​note>​
  
-**5.** Să se ruleze aplicația Android și să se genereze identificatorul de înregistrare,​ care va fi transmis prin accesarea butonului ''​Send'' ​de îndată ce serverul de aplicații devine disponibil.+**7.** Să se ruleze aplicația Android și să se genereze identificatorul de înregistrare,​ care va fi transmis ​către serverul de aplicații ​prin accesarea butonului ''​Send ​Registration ID''​.
  
-==== Serverul de Aplicații ====+{{ :​laboratoare:​laborator08:​mobile_device_application01.png?​nolink&​400 }}
  
-<note tip>​Informații referitoare la configurarea serverului de aplicații pot fi obținute la [[:​laboratoare:​laborator09#​configurare_server|Configurare Server]].</​note>​+{{ :​laboratoare:​laborator08:​mobile_device_application02.png?​nolink&​400 }}
  
-**6.** Să se descarce cea mai recentă versiune stabilă a serverului web Apache Tomcat și să se dezarhiveze.+În fragmentul ''​UnregisteredFragment''​ din pachetul ''​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.view'',​ pe clasa ascultător asociată butonului, ​să se transmită o cerere HTTP de tip POST către componenta din serverul de aplicații, responsbilă cu înregistrarea dispozitivelor mobile (disponibilă la ''​Constants.DEVICE_REGISTRATION_SERVICE_ADDRESS''​).
  
-**7.** Să se descarce cea mai recentă versiune stabilă a mediului de dezvoltare Eclipse IDE for Java EE Developers, integrându-se cu serverul web Apache Tomcat.+Aceasta va conține următorii parametrii:​ 
 +  ​* ''​Constants.USERNAME''​ - numele de utilizator din câmpul text; 
 +  ​* ''​Constants.EMAIL''​ - adresa de poștă electronică din câmpul text; 
 +  * ''​Constants.REGISTRATION_ID'' ​- identificatorul de înregistrare furnizat de serviciul Google Cloud Messaging.
  
-**8.** Să se instaleze motorul ​pentru ​gestiunea bazelor de date MySQL și să se ruleze script-ul ''​Laborator09.sql''​ din cadrul proiectului ''​GoogleCloudMessagingWebServer''​ care creează o bază de date cu o tabelă în care vor fi stocate informațiile referitoare la identificatorii de înregistrare transmiși de clienții Android.+Codificarea utilizată pentru ​această comunicație este UTF-8.
  
-<code sql>+În situația în care codul întors pe linia de stare în răspuns este 200, se va întoarce rezultatul ''​Constants.SUCCESS''​. 
 + 
 +**8.** În situația în care înregistrarea a fost realizată cu succes, interfața grafică a aplicației este modificată în sensul în care se dorește afișarea unei liste cu utilizatorii care au fost înregistrați deja, către care se dorește transmiterea unui mesaj de notificare. 
 + 
 +{{ :​laboratoare:​laborator08:​mobile_device_application03.png?​nolink&​400 }} 
 + 
 +**a)** Pe firul de execuție ''​RegisteredDevicesFetcher''​ din fragmentul ''​RegisteredFragment'',​ să se transmită o cerere HTTP de tip GET către către componenta din serverul de aplicații, responsbilă cu furnizarea listei dispozitivelor mobile înregistrate,​ în format JSON (disponibilă la ''​Constants.REGISTERED_DEVICES_SERVICE_ADDRESS''​). 
 + 
 +Să se prelucreze documentul JSON obținut, astfel încât prin intermediul acestuia să se populeze un tablou de obiecte ''​RegisteredDevice'',​ transmis ca parametru adaptorului obiectului de tip ''​Spinner''​ în care pot fi vizualizate astfel de informații. 
 + 
 +<​code ​java> 
 +final ArrayList<​RegisteredDevice>​ registeredDevices = new ArrayList<​RegisteredDevice>​();​ 
 +// ... 
 +registeredDevicesSpinner.post(new Runnable() { 
 +  @Override 
 +  public void run() { 
 +    RegisteredDevicesAdapter registeredDevicesAdapter = new RegisteredDevicesAdapter(getActivity(),​ registeredDevices);​ 
 +    registeredDevicesSpinner.setAdapter(registeredDevicesAdapter);​ 
 +  } 
 +}); 
 +</​code>​ 
 + 
 +Documentul JSON conține informații despre identificatorul din baza de date, identificatorul de înregistrare furnizat de serverul GoogleCloudMessaging,​ numele de utilizator, adresa de poștă electronică și momentul de timp la care a fost realizată înregistratrea. 
 + 
 +<code json> 
 +[ 
 +  { 
 +    "​id":​ ..., 
 +    "​registration_id":​ "​...",​ 
 +    "​username":​ "​...",​ 
 +    "​email":​ "​...",​ 
 +    "​timestamp":​ "​...",​ 
 +  }, 
 +  ... 
 +] 
 +</​code>​ 
 + 
 +^  atribut JSON  ^  tip de date  ^  detaliu ​ ^ 
 +| ''​Constants.ID''​ | ''​int''​ | identificator în baza de date | 
 +| ''​Constants.REGISTRATION_ID''​ | ''​String''​ | identificator de înregistrare furnizat de serverul Google Cloud Messaging | 
 +| ''​Constants.USERNAME''​ | ''​String''​ | numele de utilizator | 
 +| ''​Constants.EMAIL''​ | ''​String''​ | adresa de poștă electonică | 
 +| ''​Constants.TIMESTAMP''​ | ''​String''​ | momentul de timp la care a fost realizată înregistrarea | 
 + 
 +**b)** Pe firul de execuție ''​MessagePushExecutor''​ din fragmentul ''​RegisteredFragment'',​ să se transmită o cerere HTTP de tip POST către către componenta din serverul de aplicații, responsbilă cu transmiterea unui mesaj sub forma unei notificări (disponibilă la ''​Constants.MESSAGE_PUSH_SERVICE_ADDRESS''​). 
 + 
 +Aceasta va conține următorii parametrii (instanțiați ca membrii ai clasei): 
 +  * ''​Constants.REGISTRATION_ID''​ - identificatorul de înregistrare furnizat de serverul Google Cloud Messaging;​ 
 +  * ''​Constants.MESSAGE''​ - mesajul propriu-zis care se dorește a fi transmis. 
 + 
 +Codificarea utilizată pentru această comunicație este UTF-8. 
 + 
 +În această situație, nu este necesară prelucrarea rezultatului obținut. 
 + 
 +**9.** În momentul în care este transmis un mesaj de către expeditor, să se observe notificarea destinatarului și activitatea în care pot fi vizualizați parametrii pachetului furnizat de serverul Google Cloud Messaging. 
 + 
 +{{ :​laboratoare:​laborator08:​mobile_device_application04.png?​nolink&​400 }} 
 + 
 +**10.** ​ Să se încarce modificările realizate în cadrul depozitului '​Laborator08-Client'​ de pe contul Github personal, folosind un mesaj sugestiv. <​code>​ 
 +student@pdsd2015:​~/​Laborator08-Client$ git add * 
 +student@pdsd2015:​~/​Laborator08-Client$ git commit -m "​implemented taks for laboratory 08-Client"​ 
 +student@pdsd2015:​~/​Laborator08-Client$ git push Laborator08-Client_perfectstudent master 
 +</​code>​ 
 + 
 +==== Serverul de Aplicații (Opțional) ==== 
 + 
 +<note tip>​Informații referitoare la configurarea serverului de aplicații pot fi obținute la [[:​laboratoare:​laborator08#​configurare_server_optional|Configurare Server]].</​note>​ 
 + 
 +Funcționalitatea corespunzătoare serverului de aplicații este disponibilă în două variante: 
 +  * PHP & MySQL, găzduită la o adresă Internet publică; 
 +  * JavaServlets / JSP & MySQL (folosind un server web Apache Tomcat 8.0.21 - integrat în structura proiectului,​ fără a necesita configurare),​ care poate fi rulat pe mașina fizică (local), comunicând cu dispozitivul mobil numai în situația în care acestea se găsesc în aceeași rețea și portul 8080 nu este blocat - de aceea, toate operațiile de configurare ce urmează se referă la această aplicație. 
 + 
 +Pe serverul de aplicații sunt disponibile următoarele funcționalități,​ în cadrul unor componente diferite: 
 +  * înregistrarea unui dispozitiv mobil în cadrul unei baze de date locale, pe baza unui identificator de înregistrare furnizat de serverul Google Cloud Messaging;​ 
 +  * listarea tuturor dispozitivelor mobile înregistrate sub forma unui document JSON; 
 +  * transmiterea unui mesaj sub forma unui notificări către un dispozitiv mobil, prin intermediul serverului Google Cloud Messaging. 
 + 
 +**11.** În contul Github personal, să se creeze un depozit denumit '​Laborator08-Server'​. Inițial, acesta trebuie să fie gol (nu trebuie să bifați nici adăugarea unui fișier ''​README.md'',​ nici a fișierului ''​.gitignore''​ sau a a fișierului ''​LICENSE''​). 
 + 
 +**12.** Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://​www.github.com/​pdsd2015/​Laborator08-Server|]].  
 + 
 +În urma acestei operații, directorul Laborator08-Server va trebui să se conțină directoarele ''​PHP''​ și ''​ServletsJSP''​.  
 + 
 +<​code>​ 
 +student@pdsd2015:​~$ git clone https://​www.github.com/​pdsd2015/​Laborator08-Server 
 +</​code>​ 
 + 
 +**13.** Să se încarce conținutul descărcat în cadrul depozitului '​Laborator08-Server'​ de pe contul Github personal. <​code>​ 
 +student@pdsd2015:​~$ cd Laborator08-Server 
 +student@pdsd2015:​~/​Laborator08-Server$ git remote add Laborator08-Server_perfectstudent https://​github.com/​perfectstudent/​Laborator08-Server 
 +student@pdsd2015:​~/​Laborator08-Server$ git push Laborator08-Server_perfectstudent master 
 +</​code>​ 
 + 
 +**14.** Să se descarce cea mai recentă versiune stabilă a mediului de dezvoltare Eclipse IDE for Java EE Developers, integrându-se cu serverul web Apache Tomcat. 
 + 
 +**15.** Să se instaleze motorul pentru gestiunea bazelor de date MySQL și să se ruleze script-ul ''​googlecloudmessaging.sql''​ din cadrul directorului ''​ServletsJSP''​ care creează o bază de date cu o tabelă în care vor fi stocate informațiile referitoare la identificatorii de înregistrare transmiși de clienții Android. 
 + 
 +<code>
 CREATE DATABASE googlecloudmessaging;​ CREATE DATABASE googlecloudmessaging;​
  
Line 832: Line 1032:
  
 CREATE TABLE registered_devices ( CREATE TABLE registered_devices (
- id              INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, +  ​id              INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL, 
- registration_id TEXT NOT NULL, +  registration_id TEXT NOT NULL, 
- username ​       VARCHAR(255),​  +  username ​       VARCHAR(255),​  
- email         ​VARCHAR(255),​ +  email           ​VARCHAR(255),​ 
- creation_time ​  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP+  ​timestamp ​      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
 ); );
 </​code>​ </​code>​
Line 842: Line 1042:
 Rularea acestuia se poate realiza folosind una dintre următoarele variante, presupunând că vă aflați în directorul în care se găsește acest fișier: Rularea acestuia se poate realiza folosind una dintre următoarele variante, presupunând că vă aflați în directorul în care se găsește acest fișier:
   - <​code>​   - <​code>​
-mysql -u root -p <Laborator09.sql+student@pdsd2015:​~/​Laborator08-Server/​JSPServlets$ ​mysql -u root -p <googlecloudmessaging.sql
 </​code>​ </​code>​
   - <​code>​   - <​code>​
-mysql -u root -p+student@pdsd2015:​~/​Laborator08-Server/​JSPServlets$ ​mysql -u root -p
 Enter password: Enter password:
 </​code>​ </​code>​
     - <​code>​     - <​code>​
-mysql> source ​Laborator09.sql;+mysql> source ​googlecloudmessaging.sql;
 </​code>​ sau </​code>​ sau
     - <​code>​     - <​code>​
-mysql> \. Laborator09.sql;+mysql> \. googlecloudmessaging.sql;
 </​code>​ </​code>​
  
-**9.** În mediul de dezvoltare Eclipse IDE for Java EE Developers ​să se deschidă proiectul ''​GoogleCloudMessaging''​.+**16.** Să se importe proiectul ''​GoogleCloudMessagingServer''​ în mediul de dezvoltare Eclipse IDE for Java EE Developers.
  
-**10.** În interfața ''​general.Constants'',​ trebuie modificate următoarele valori: ​+**17.** În interfața ''​Constants''​ din pachetul ''​ro.pub.cs.systems.pdsd.lab08.googlecloudmessaging.general'',​ trebuie modificate următoarele valori: ​
   * ''​DATABASE_PASSWORD''​ - parola de administrare a motorului pentru gestiunea bazei de date MySQL, care a fost introdusă la instalarea acestui produs;   * ''​DATABASE_PASSWORD''​ - parola de administrare a motorului pentru gestiunea bazei de date MySQL, care a fost introdusă la instalarea acestui produs;
   * ''​API_KEY''​ - cheia publică de acces a serverului Google Cloud Messaging de către serverul de aplicații, așa cum a fost generată de Consola Google API.   * ''​API_KEY''​ - cheia publică de acces a serverului Google Cloud Messaging de către serverul de aplicații, așa cum a fost generată de Consola Google API.
  
-=====  ===== +**18.** În acest moment, se poate realiza comunicația client Android ↔ server de aplicații prin intermediul serviciului Google Cloud Messaging:​ 
- +  * clientul Android trimite identificatorul de înregistrare către serverul de aplicații;​ 
-**11.** În acest moment, se poate realiza comunicația client Android ↔ server de aplicații prin intermediul serviciului Google Cloud Messaging:​ +  * se accesează ​componenta pentru transmiterea unui mesaj prin intermediul unei notificări în aplicația curentă ​de navigare (//eng.// browser)\\ {{ pdsd:​laboratoare:​laborator08:​message_push.png?nolink&​800 ​}} 
-  * clientul Android trimite identificatorul de înregistrare către serverul de aplicații;\\ {{ :​laboratoare:​lab09-mobiledeviceunregistered.jpg?​nolink }}\\ {{ :​laboratoare:​lab09-mobiledeviceregistered.jpg?​nolink }} +    * PHP: http://​pdsd2015.andreirosucojocaru.ro/​exemple/​laboratoare/​laborator08/​googlecloudmessaging/​message_push.php;​ 
-  * se accesează ​servletul ''​GoogleCloudMessagingRegisterServlet'' ​prin intermediul unei aplicații de navigare (//eng.// browser) ​pentru a se transmite un mesaj către dispozitivul mobil, acesta fiind afișat sub forma unei notificări.\\ {{ :​laboratoare:​lab09-GoogleCloudMessagingWebServer.jpg?nolink }}+    * JavaServlets / JSP (local): http://​192.168.56.1:​8080/​GoogleCloudMessagingServer/​MessagePushServlet.
  
 ===== Resurse Utile ===== ===== Resurse Utile =====
-{{:​laboratoare:​client.zip|Surse Client}}\\ ​ 
-{{:​laboratoare:​server.zip|Surse Server}} 
- 
 [[http://​tomcat.apache.org/​download-70.cgi|Apache Tomcat 7 Downloads]]\\ ​ [[http://​tomcat.apache.org/​download-70.cgi|Apache Tomcat 7 Downloads]]\\ ​
 [[http://​www.eclipse.org/​downloads/​packages/​eclipse-ide-java-ee-developers/​keplersr2|Eclipse IDE for Java EE Developers]]\\ [[http://​www.eclipse.org/​downloads/​packages/​eclipse-ide-java-ee-developers/​keplersr2|Eclipse IDE for Java EE Developers]]\\
Line 879: Line 1076:
 [[http://​developer.android.com/​reference/​com/​google/​android/​gms/​gcm/​GoogleCloudMessaging.html|Google Cloud Messaging API]]\\ ​ [[http://​developer.android.com/​reference/​com/​google/​android/​gms/​gcm/​GoogleCloudMessaging.html|Google Cloud Messaging API]]\\ ​
 [[http://​stackoverflow.com/​questions/​20121883/​how-to-install-google-play-services-in-a-genymotion-vm-with-no-drag-and-drop-su|How to install Google Play Services in a Genymotion VM (with no drag and drop support)?​]]\\ ​ [[http://​stackoverflow.com/​questions/​20121883/​how-to-install-google-play-services-in-a-genymotion-vm-with-no-drag-and-drop-su|How to install Google Play Services in a Genymotion VM (with no drag and drop support)?​]]\\ ​
-[[http://​wiki.cyanogenmod.org/​w/​Google_Apps|Google Apps]]\\  +[[http://​forum.xda-developers.com/​showthread.php?​t=2528952|Installing ARM Translation and GApps]]\\  
-[[http://​forum.xda-developers.com/​showthread.php?​t=2528952|Installing ARM Translation and GApps]]+[[http://​developer.android.com/​guide/​topics/​ui/​notifiers/​notifications.html|Android Notifications]]
laboratoare/laborator08.1430132915.txt.gz · Last modified: 2015/04/27 14:08 by Andrei Roșu-Cojocaru
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0