Differences

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

Link to this comparison view

eim:colocvii:colocviu02 [2016/05/16 02:50]
tmp.andrei.cojocaru removed
eim:colocvii:colocviu02 [2021/05/30 21:52] (current)
dan_valentin.bina [Observații Generale]
Line 7: Line 7:
 </​HTML>​ </​HTML>​
  
-{{url>http://​ocw.cs.pub.ro/​courses/​_media/​eim/​colocvii/​colocviu02/​eim2016-tp02-varsim.pdf 90%,800px}}+{{url>https://​ocw.cs.pub.ro/​courses/​_media/​eim/​colocvii/​colocviu02/​eim-colocviu2-exemplu.pdf 90%,800px}}
  
 <​HTML>​ <​HTML>​
Line 13: Line 13:
 </​HTML>​ </​HTML>​
  
-{{ :​eim:​colocvii:​colocviu02:​eim2016-tp02-varsim.pdf }}+{{ :​eim:​colocvii:​colocviu02:​eim-colocviu2-exemplu.pdf }}
  
 ===== Observații Generale ===== ===== Observații Generale =====
Line 25: Line 25:
     * virtual: Genymotion, AVD.     * virtual: Genymotion, AVD.
  
-<​hidden>​ +Accesul la servicii web prin intermediul protocolului HTTP poate fi realizată prin intermediul bibliotecii [[https://repo1.maven.org/maven2/cz/​msebera/​android/​httpclient/​4.4.1.2/​httpclient-4.4.1.2.jar|Apache HTTP Components]]. ​
-<note important>​În situația în care lucrați pe calculatoarele din laboratorul Ixia (EG106) este recomandat să folosiți: +
-  * fluxbox ca utilitar pentru gestiunea ferestrelor (se alege din interfața de autentificare);​  +
-  * epiphany pentru navigare Internet. +
-Este de preferat să nu se depășească memoria disponibilă,​ întrucât comportamentul acestor mașini este imprevizibil în momentul în care se folosește swap-ul (se blochează, fără posibilitatea de recuperare din eroare).</note> +
-</hidden>+
  
-În situația ​în care este necesară procesarea codului sursă (în format HTML) al paginii Internet furnizat ca răspuns la cererea transmisă către un serviciu web, se poate folosi [[http://​jsoup.org/​packages/​jsoup-1.9.1.jar|biblioteca Jsoup]].+Integrarea sa în mediul integrat pentru dezvoltare **Android Studio 2.2.3** se face prin referirea acestei biblioteci ca modul al aplicației Android.
  
-Integrarea sa în mediul integrat pentru dezvoltare Eclipse Mars 1 (4.5.1) se face prin plasarea fișierului corespunzător în directorul //libs// al proiectului. Acesta devine vizibil în momentul în care se accesează operația //Refresh// din meniul contextual, disponibilă și prin tasta //F5//. Ulterior, pentru a fi inclus în classpath, este necesar ca din meniul contextual asociat fișierului ''​jsoup-1.9.1.jar''​ (disponibil pe butonul drept al mouse-ului) să se acceseze //Build Path// → //Add to Build Path//.+Atașarea unui modul nou la proiectul Android Studio se face accesând //File// → //New// → //New Module//. 
 + 
 +{{ :​eim:​colocvii:​colocviu02:​01newmodule.png?​nolink }} 
 + 
 +În fereastra //Create New Module// se indică tipul de modul //Import .JAR/.AAR Package//​. 
 + 
 +{{ :​eim:​colocvii:​colocviu02:​02importjaraarpackage.png?​nolink&​600 }} 
 + 
 +Se precizează:​ 
 +  * locația la care se găsește biblioteca ce va fi importată sub formă de modul; 
 +  * denumirea subproiectului din care va face parte modulul; 
 + 
 +{{ :​eim:​colocvii:​colocviu02:​03createnewmodulehttpclient.png?​nolink&​600 }} 
 + 
 +După ce biblioteca a fost referită în cadrul mediului integrat pentru dezvoltare Android Studio 2.2.3, aceasta va putea fi vizualizată în vizualizarea //Android// a proiectului. 
 + 
 +{{ :​eim:​colocvii:​colocviu02:​04projectstructurehttpclient.png?​nolink }} 
 + 
 +De asemenea, în fișierul ''​build.gradle''​ trebuie specificată explicit dependența de această bibliotecă:​ 
 + 
 +<file gradle build.gradle>​ 
 +... 
 +dependencies { 
 +  ... 
 +  implementation group: '​cz.msebera.android',​ name: '​httpclient',​ version: '​4.4.1.2'​ 
 +
 +</​file>​ 
 + 
 +Alternativ, în situația în care se folosește un SDK corespunzător unui nivel de API mai mare decât 23, se poate preciza folosirea bibliotecii ''​org.apache.http.legacy'':​ 
 + 
 +<file gradle build.gradle>​ 
 +android { 
 +  compileSdkVersion 25 
 +  buildToolsVersion "​25.0.2"​ 
 +  ... 
 +
 +dependencies { 
 +  ... 
 +  implementation '​org.apache.http.legacy'​ 
 +
 +</​file>​ 
 + 
 +În situația în care este necesară procesarea codului sursă (în format HTML) al paginii Internet furnizat ca răspuns la cererea transmisă către un serviciu web, se va folosi [[https://​jsoup.org/​packages/​jsoup-1.10.2.jar|biblioteca Jsoup]]. Referirea acestei biblioteci se face tot sub formă de modul al proiectului Android Studio. 
 + 
 +Pentru a putea folosi funcționalitatea pusă la dispoziție de această bibliotecă,​ este necesar să se definească dependența de aceasta în fișierul ''​build.gradle'':​ 
 + 
 +<file gradle build.gradle>​ 
 +... 
 +dependencies { 
 +  ... 
 +  implementation project (':​jsoup-1.10.2'​) 
 +
 +</​file>​ 
 + 
 +<​hidden>​ 
 +Integrarea sa în mediul integrat pentru dezvoltare ​**Eclipse Mars 1 (4.5.1)** se face prin plasarea fișierului corespunzător în directorul //libs// al proiectului. Acesta devine vizibil în momentul în care se accesează operația //Refresh// din meniul contextual, disponibilă și prin tasta //F5//. Ulterior, pentru a fi inclus în classpath, este necesar ca din meniul contextual asociat fișierului ''​jsoup-1.9.1.jar''​ (disponibil pe butonul drept al mouse-ului) să se acceseze //Build Path// → //Add to Build Path//.
  
 {{ :​eim:​colocvii:​colocviu02:​eclipse_library01.png?​nolink }} {{ :​eim:​colocvii:​colocviu02:​eclipse_library01.png?​nolink }}
  
-După ce biblioteca a fost integrată în cadrul mediului integrat pentru dezvoltare Eclipse ​Luna SR1a (4.4.1), aceasta va putea fi vizualizată în secțiunea //​Referenced Libraries// din proiectul corespunzător.+După ce biblioteca a fost integrată în cadrul mediului integrat pentru dezvoltare Eclipse ​Mars 1 (4.5.1), aceasta va putea fi vizualizată în secțiunea //​Referenced Libraries// din proiectul corespunzător.
  
 {{ :​eim:​colocvii:​colocviu02:​eclipse_library02.png?​nolink }} {{ :​eim:​colocvii:​colocviu02:​eclipse_library02.png?​nolink }}
 +</​hidden>​
  
-// TODOAndroid Studio ​//+În situația în care este necesară procesarea unui document JSON furnizat ca răspuns la cererea transmisă către un serviciu web, se vor utiliza clasele [[http://developer.android.com/​reference/​org/​json/​JSONObject.html|JSONObject]],​ respectiv [[http://developer.android.com/​reference/​org/​json/​JSONArray.html|JSONArray]],​ din cadrul SDK-ului Android.
  
-În situația în care este necesară procesarea unui document JSON furnizat ca răspuns la cererea transmisă către un serviciu web, se vor utiliza clasele [[http://​developer.android.com/​reference/​org/​json/​JSONObject.html|JSONObject]],​ respectiv [[http://​developer.android.com/​reference/​org/​json/​JSONArray.html|JSONArray]],​ din cadrul SDK-ului Android. ​Pentru vizualizare,​ se poate folosi utilitarul [[http://​jsonformatter.curiousconcept.com/​|JSON Formatter & Validator]],​ prin care structura documentului JSON poate fi inspectată cu ușurință.+Pentru vizualizare,​ se poate folosi utilitarul [[http://​jsonformatter.curiousconcept.com/​|JSON Formatter & Validator]],​ prin care structura documentului JSON poate fi inspectată cu ușurință.
  
 ===== Rezolvări ===== ===== Rezolvări =====
  
-Proiectul ​Eclipse ​corespunzător aplicației Android ce conține rezolvările complete ale cerințelor colocviului ​este disponibil ​pe [[https://​github.com/​pdsd2015/​PracticalTest02|contul de Github al disciplinei]].+Proiectul ​Android Studio ​corespunzător aplicației Android ce conține rezolvările complete ale cerințelor colocviului ​sunt disponibile ​pe [[https://​github.com/​eim-lab/​PracticalTest02|contul de Github al disciplinei]].
  
-**1.** Se accesează [[https://​www.github.com|Github]] și se realizează autentificarea în contul personal, prin intermediul butonului //Sign in//.+**1.** **a)** Se accesează [[https://​www.github.com|Github]] și se realizează autentificarea în contul personal, prin intermediul butonului //Sign in//.
  
-{{ :​eim:​colocvii:​colocviu01:github_authentication.png?​nolink&​700 }}+{{ :​eim:​colocvii:​colocviu02:05github_authentication.png?​nolink&​700 }}
  
 Se creează o zonă de lucru corespunzătoare unui proiect prin intermediului butonului //New Repository//​. Se creează o zonă de lucru corespunzătoare unui proiect prin intermediului butonului //New Repository//​.
  
-{{ :​eim:​colocvii:​colocviu02:​github_new_repository.png?nolink }}+{{ :​eim:​colocvii:​colocviu02:​06github_newrepository.png?nolink }}
  
 Configurarea depozitului la distanță presupune specificarea:​ Configurarea depozitului la distanță presupune specificarea:​
Line 69: Line 120:
   * tipului de licență sub care este publicat codul sursă.   * tipului de licență sub care este publicat codul sursă.
  
-{{ :​eim:​colocvii:​colocviu02:​github_repository_configuration.png?nolink }}+{{ :​eim:​colocvii:​colocviu02:​07github_repository_configuration.png?nolink }}
  
-**2.** Prin intermediul comenzii ''​git clone''​ se poate descărca întregul conținut în directorul curent (de pe discul local), inclusiv istoricul complet al versiunilor anterioare (care poate fi ulterior reconstituit după această copie, în cazul coruperii informațiilor stocate pe serverul la distanță).+**b)** Prin intermediul comenzii ''​git clone''​ se poate descărca întregul conținut în directorul curent (de pe discul local), inclusiv istoricul complet al versiunilor anterioare (care poate fi ulterior reconstituit după această copie, în cazul coruperii informațiilor stocate pe serverul la distanță).
  
 <​note>​În situația în care se dorește clonarea conținutului din depozitul la distanță în alt director decât cel curent, acesta poate fi transmis ca parametru al comenzii, după URL-ul la care poate fi accesat proiectul în Github.</​note>​ <​note>​În situația în care se dorește clonarea conținutului din depozitul la distanță în alt director decât cel curent, acesta poate fi transmis ca parametru al comenzii, după URL-ul la care poate fi accesat proiectul în Github.</​note>​
  
 <​code>​ <​code>​
-student@eim2016:~$ git clone https://​www.github.com/​perfectstudent/​PracticalTest02+student@eim:~$ git clone https://​www.github.com/​perfectstudent/​PracticalTest02
 </​code>​ </​code>​
  
-**3.** Se urmăresc indicațiile disponibile în secțiunea ​[[:​eim:​laboratoare:​laborator01#​crearea_unei_aplicatii_android_in_eclipse_mars_1_451_-_obligatoriu|Crearea unei aplicații Android în Eclipse Mars 1 (4.5.1)]], respecti ​[[eim:​laboratoare:​laborator01#​crearea_unei_aplicatii_android_in_android_studio_151_-_obligatoriu|Crearea unei aplicații Android în Android Studio ​1.5.1]].+**c)** Se urmăresc indicațiile disponibile în secțiunea [[eim:​laboratoare:​laborator01#​crearea_unei_aplicatii_android_in_android_studio_-_obligatoriu|Crearea unei aplicații Android în Android Studio]].
  
-**4.** Pentru implementarea interfeței grafice, se vor defini controalele care asigură interacțiunea cu utilizatorul pentru fiecare dintre componentele aplicației Android:+**2.** Pentru implementarea interfeței grafice, se vor defini controalele care asigură interacțiunea cu utilizatorul pentru fiecare dintre componentele aplicației Android:
   * serverul   * serverul
     * un câmp text pentru specificarea portului pe care va accepta conexiuni de la clienți;     * un câmp text pentru specificarea portului pe care va accepta conexiuni de la clienți;
Line 96: Line 147:
 <​note>​Nu este necesar ca în dezvoltarea interfeței grafice să se utilizeze elemente grafice complexe (de tipul fragmentelor sau listelor). Nu este punctat nici aspectul estetic al acesteia. Tot ce contează este ca interfața grafică să poată fi utilizată pentru implementarea cerințelor funcționale.</​note>​ <​note>​Nu este necesar ca în dezvoltarea interfeței grafice să se utilizeze elemente grafice complexe (de tipul fragmentelor sau listelor). Nu este punctat nici aspectul estetic al acesteia. Tot ce contează este ca interfața grafică să poată fi utilizată pentru implementarea cerințelor funcționale.</​note>​
  
-{{ :​eim:​colocvii:​colocviu02:​graphic_interface01.png?​nolink&​400 }}+{{ :​eim:​colocvii:​colocviu02:​08graphicuserinterface.png?​nolink&​400 }}
  
 <file xml activity_practical_test02_main.xml>​ <file xml activity_practical_test02_main.xml>​
 +<?xml version="​1.0"​ encoding="​utf-8"?>​
 <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​ <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​
 +  xmlns:​app="​http://​schemas.android.com/​apk/​res-auto"​
   xmlns:​tools="​http://​schemas.android.com/​tools"​   xmlns:​tools="​http://​schemas.android.com/​tools"​
   android:​layout_width="​match_parent"​   android:​layout_width="​match_parent"​
   android:​layout_height="​match_parent"​   android:​layout_height="​match_parent"​
   android:​orientation="​vertical"​   android:​orientation="​vertical"​
-  tools:​context="​ro.pub.cs.systems.eim.practicaltest02.graphicuserinterface.PracticalTest02MainActivity"​ >+  tools:​context="​ro.pub.cs.systems.eim.practicaltest02.view.PracticalTest02MainActivity">​
  
   <​TextView   <​TextView
Line 112: Line 165:
     android:​textSize="​25sp"​     android:​textSize="​25sp"​
     android:​textStyle="​bold"​     android:​textStyle="​bold"​
-    android:​text="​@string/​server"​ /> +    android:​text="​@string/​server"/>​ 
-    +
   <​LinearLayout   <​LinearLayout
     android:​layout_width="​match_parent"​     android:​layout_width="​match_parent"​
     android:​layout_height="​wrap_content"​     android:​layout_height="​wrap_content"​
     android:​baselineAligned="​false">​     android:​baselineAligned="​false">​
-        ​+
     <​ScrollView     <​ScrollView
       android:​layout_width="​0dp"​       android:​layout_width="​0dp"​
       android:​layout_height="​wrap_content"​       android:​layout_height="​wrap_content"​
       android:​layout_weight="​1">​       android:​layout_weight="​1">​
-      ​+
       <​EditText       <​EditText
         android:​id="​@+id/​server_port_edit_text"​         android:​id="​@+id/​server_port_edit_text"​
- android:​layout_width="​match_parent"​ +        ​android:​layout_width="​match_parent"​ 
- android:​layout_height="​wrap_content"​ +        android:​layout_height="​wrap_content"​ 
- android:​hint="​@string/​server_port"​ /> +        android:​hint="​@string/​server_port"/>​ 
-        +
     </​ScrollView>​     </​ScrollView>​
-        ​+
     <​ScrollView     <​ScrollView
       android:​layout_width="​0dp"​       android:​layout_width="​0dp"​
       android:​layout_height="​wrap_content"​       android:​layout_height="​wrap_content"​
       android:​layout_weight="​1">​       android:​layout_weight="​1">​
-      ​+
       <Button       <Button
         android:​id="​@+id/​connect_button"​         android:​id="​@+id/​connect_button"​
- android:​layout_width="​wrap_content"​ +        ​android:​layout_width="​wrap_content"​ 
- android:​layout_height="​wrap_content"​ +        android:​layout_height="​wrap_content"​ 
- android:​layout_gravity="​center"​ +        android:​layout_gravity="​center"​ 
- android:​text="​@string/​connect"​ /> +        android:​text="​@string/​connect"/>​ 
-        +
     </​ScrollView>​     </​ScrollView>​
-        ​+
   </​LinearLayout>​   </​LinearLayout>​
-    ​+
   <Space   <Space
     android:​layout_width="​wrap_content"​     android:​layout_width="​wrap_content"​
     android:​layout_height="​10dp"​ />     android:​layout_height="​10dp"​ />
-    ​+
   <​TextView   <​TextView
     android:​layout_width="​wrap_content"​     android:​layout_width="​wrap_content"​
Line 158: Line 211:
     android:​textSize="​25sp"​     android:​textSize="​25sp"​
     android:​textStyle="​bold"​     android:​textStyle="​bold"​
-    android:​text="​@string/​client"​ /> +    android:​text="​@string/​client"/>​ 
-    +
   <​LinearLayout   <​LinearLayout
     android:​layout_width="​match_parent"​     android:​layout_width="​match_parent"​
     android:​layout_height="​wrap_content"​     android:​layout_height="​wrap_content"​
     android:​baselineAligned="​false">​     android:​baselineAligned="​false">​
-        ​+
     <​ScrollView     <​ScrollView
       android:​layout_width="​0dp"​       android:​layout_width="​0dp"​
       android:​layout_height="​wrap_content"​       android:​layout_height="​wrap_content"​
       android:​layout_weight="​1">​       android:​layout_weight="​1">​
-        ​+
       <​EditText       <​EditText
         android:​id="​@+id/​client_address_edit_text"​         android:​id="​@+id/​client_address_edit_text"​
         android:​layout_width="​match_parent"​         android:​layout_width="​match_parent"​
         android:​layout_height="​wrap_content"​         android:​layout_height="​wrap_content"​
-        android:​hint="​@string/​client_address"​ /> +        android:​hint="​@string/​client_address"/>​ 
-        +
     </​ScrollView>​     </​ScrollView>​
-        ​+
     <​ScrollView     <​ScrollView
       android:​layout_width="​0dp"​       android:​layout_width="​0dp"​
       android:​layout_height="​wrap_content"​       android:​layout_height="​wrap_content"​
       android:​layout_weight="​1">​       android:​layout_weight="​1">​
-        ​+
       <​EditText       <​EditText
         android:​id="​@+id/​client_port_edit_text"​         android:​id="​@+id/​client_port_edit_text"​
         android:​layout_width="​match_parent"​         android:​layout_width="​match_parent"​
         android:​layout_height="​wrap_content"​         android:​layout_height="​wrap_content"​
-        android:​hint="​@string/​client_port"​ /> +        android:​hint="​@string/​client_port"/>​ 
-        +
     </​ScrollView>​     </​ScrollView>​
-        ​ + 
-  </​LinearLayout> ​    +  </​LinearLayout>​ 
-    +
   <​GridLayout   <​GridLayout
     android:​layout_width="​match_parent"​     android:​layout_width="​match_parent"​
Line 198: Line 251:
     android:​rowCount="​2"​     android:​rowCount="​2"​
     android:​columnCount="​2">​     android:​columnCount="​2">​
-        ​+
     <​EditText     <​EditText
       android:​id="​@+id/​city_edit_text"​       android:​id="​@+id/​city_edit_text"​
Line 206: Line 259:
       android:​hint="​@string/​city"​       android:​hint="​@string/​city"​
       android:​layout_row="​0"​       android:​layout_row="​0"​
-      android:​layout_column="​0"​ /> +      android:​layout_column="​0"/>​ 
-        +
     <Spinner     <Spinner
       android:​id="​@+id/​information_type_spinner"​       android:​id="​@+id/​information_type_spinner"​
Line 214: Line 267:
       android:​entries="​@array/​information_types"​       android:​entries="​@array/​information_types"​
       android:​layout_row="​1"​       android:​layout_row="​1"​
-      android:​layout_column="​0"​ /> +      android:​layout_column="​0"/>​ 
-        +
     <Button     <Button
       android:​id="​@+id/​get_weather_forecast_button"​       android:​id="​@+id/​get_weather_forecast_button"​
-      android:​layout_width="​100dp"+      android:​layout_width="​wrap_content"
       android:​layout_height="​wrap_content"​       android:​layout_height="​wrap_content"​
       android:​layout_gravity="​center"​       android:​layout_gravity="​center"​
Line 224: Line 277:
       android:​layout_row="​0"​       android:​layout_row="​0"​
       android:​layout_rowSpan="​2"​       android:​layout_rowSpan="​2"​
-      android:​layout_column="​1"​ /> +      android:​layout_column="​1"/>​ 
-        +
   </​GridLayout>​   </​GridLayout>​
  
Line 231: Line 284:
     android:​layout_width="​match_parent"​     android:​layout_width="​match_parent"​
     android:​layout_height="​match_parent">​     android:​layout_height="​match_parent">​
-    ​ + 
-    <​TextView +  <​TextView 
-      android:​id="​@+id/​weather_forecast_text_view"​ +    android:​id="​@+id/​weather_forecast_text_view"​ 
-      android:​layout_width="​match_parent"​ +    android:​layout_width="​match_parent"​ 
-      android:​layout_height="​wrap_content"​ +    android:​layout_height="​wrap_content"​ 
-      ​android:​singleLine="​false"​ +    android:​maxLines="​10"/>​ 
-      ​android:​maxLines="​10"​ /> +
-    +
   </​ScrollView>​   </​ScrollView>​
  
Line 265: Line 317:
 </​note>​ </​note>​
  
-**5.** Implementarea serverului presupune:+**3.** Implementarea serverului presupune:
  
 **a)** un fir de execuție care gestionează solicitările de conexiune de la clienți: **a)** un fir de execuție care gestionează solicitările de conexiune de la clienți:
Line 286: Line 338:
   try {   try {
     while (!Thread.currentThread().isInterrupted()) {     while (!Thread.currentThread().isInterrupted()) {
-      Log.i(Constants.TAG,​ "​[SERVER] Waiting for a connection...");+      Log.i(Constants.TAG,​ "​[SERVER ​THREAD] Waiting for a client invocation...");
       Socket socket = serverSocket.accept();​       Socket socket = serverSocket.accept();​
-      Log.i(Constants.TAG,​ "​[SERVER] A connection request was received from " + socket.getInetAddress() + ":"​ + socket.getLocalPort());​+      Log.i(Constants.TAG,​ "​[SERVER ​THREAD] A connection request was received from " + socket.getInetAddress() + ":"​ + socket.getLocalPort());​
       CommunicationThread communicationThread = new CommunicationThread(this,​ socket);       CommunicationThread communicationThread = new CommunicationThread(this,​ socket);
       communicationThread.start();​       communicationThread.start();​
     }     }
   } catch (ClientProtocolException clientProtocolException) {   } catch (ClientProtocolException clientProtocolException) {
-    Log.e(Constants.TAG,​ "An exception has occurred: " + clientProtocolException.getMessage());​+    Log.e(Constants.TAG,​ "[SERVER THREAD] ​An exception has occurred: " + clientProtocolException.getMessage());​
     if (Constants.DEBUG) {     if (Constants.DEBUG) {
       clientProtocolException.printStackTrace();​       clientProtocolException.printStackTrace();​
     }     }
   } catch (IOException ioException) {   } catch (IOException ioException) {
-    Log.e(Constants.TAG,​ "An exception has occurred: " + ioException.getMessage());​+    Log.e(Constants.TAG,​ "[SERVER THREAD] ​An exception has occurred: " + ioException.getMessage());​
     if (Constants.DEBUG) {     if (Constants.DEBUG) {
       ioException.printStackTrace();​       ioException.printStackTrace();​
Line 316: Line 368:
 <code java> <code java>
 private class ConnectButtonClickListener implements Button.OnClickListener { private class ConnectButtonClickListener implements Button.OnClickListener {
 +
   @Override   @Override
   public void onClick(View view) {   public void onClick(View view) {
     String serverPort = serverPortEditText.getText().toString();​     String serverPort = serverPortEditText.getText().toString();​
     if (serverPort == null || serverPort.isEmpty()) {     if (serverPort == null || serverPort.isEmpty()) {
-      Toast.makeText( +      Toast.makeText(getApplicationContext(),​ "[MAIN ACTIVITY] ​Server port should be filled!",​ Toast.LENGTH_SHORT).show();​
-        ​getApplicationContext(),​ +
-        ​"​Server port should be filled!",​ +
-        ​Toast.LENGTH_SHORT +
-      ​).show();+
       return;       return;
     }     }
     serverThread = new ServerThread(Integer.parseInt(serverPort));​     serverThread = new ServerThread(Integer.parseInt(serverPort));​
-    if (serverThread.getServerSocket() ​!= null) +    if (serverThread.getServerSocket() ​== null) { 
-      serverThread.start();​ +      Log.e(Constants.TAG,​ "[MAIN ACTIVITY] Could not create ​server thread!"​)
-    } else +      return;
-      Log.e(Constants.TAG,​ "[MAIN ACTIVITY] Could not creat server thread!"​);​+
     }     }
 +    serverThread.start();​
   }   }
 +
 } }
 </​code>​ </​code>​
Line 343: Line 393:
 @Override @Override
 protected void onDestroy() { protected void onDestroy() {
 +  Log.i(Constants.TAG,​ "[MAIN ACTIVITY] onDestroy() callback method has been invoked"​);​
   if (serverThread != null) {   if (serverThread != null) {
     serverThread.stopThread();​     serverThread.stopThread();​
Line 352: Line 403:
 <code java> <code java>
 public void stopThread() { public void stopThread() {
 +  interrupt();​
   if (serverSocket != null) {   if (serverSocket != null) {
-    interrupt();​ 
     try {     try {
-      ​if (serverSocket != null) { +      serverSocket.close();​
-        ​serverSocket.close();​ +
-      }+
     } catch (IOException ioException) {     } catch (IOException ioException) {
-      Log.e(Constants.TAG,​ "An exception has occurred: " + ioException.getMessage());​+      Log.e(Constants.TAG,​ "[SERVER THREAD] ​An exception has occurred: " + ioException.getMessage());​
       if (Constants.DEBUG) {       if (Constants.DEBUG) {
         ioException.printStackTrace();​         ioException.printStackTrace();​
Line 375: Line 424:
   * în situația în care informațiile meteorologice se găsesc în obiectul gestionat de server, sunt preluate local;   * în situația în care informațiile meteorologice se găsesc în obiectul gestionat de server, sunt preluate local;
   * în situația în care informațiile meteorologice nu se găsesc în obiectul gestionat de server, sunt preluate prin interogarea serviciului Internet, la distanță:   * în situația în care informațiile meteorologice nu se găsesc în obiectul gestionat de server, sunt preluate prin interogarea serviciului Internet, la distanță:
-    * se realizează o cerere de tip ''​POST''​ la adresa Internet [[http://​www.wunderground.com/​cgi-bin/​findweather/​getForecast]];​ parametrul care trebuie precizat este ''​query''​ și are valoarea orașului pentru care se dorește să se obțină informațiile;​ acesta se atașează cererii de tip ''​POST'',​ folosind codificarea ''​UTF-8'';​ +    * o cerere de tip ''​GET''​ în care valoarea parametrului este inclusă în cadrul adresei Internet: [[http://api.openweathermap.org/data/2.5/weather?q=Bucharest&​appid=...]]);
-    * alternativ, se poate realiza ​o cerere de tip ''​GET''​ în care valoarea parametrului este inclusă în cadrul adresei Internet: [[http://www.wunderground.com/cgi-bin/findweather/getForecast?query=...]]);+
     * se obține răspunsul sub forma unui șir de caractere, reprezentând codul sursă al paginii Internet; ​     * se obține răspunsul sub forma unui șir de caractere, reprezentând codul sursă al paginii Internet; ​
     * folosind biblioteca Jsoup, se inspectează documentul în format HTML furnizat pentru a determina locația la care sunt disponibile informațiile necesare <code html>     * folosind biblioteca Jsoup, se inspectează documentul în format HTML furnizat pentru a determina locația la care sunt disponibile informațiile necesare <code html>
-<​script>​ +{ 
-  ​wui.asyncCityPage = true; +  "coord"
-  wui.bootstrapped.API = ""​+    "lon": ​26.11, 
-  wui.api_data = +    "lat": 44.43 
-  ​+  }
-    "response": ​+  "weather": ​[ 
-      "​version":​ "2.0", +    { 
-      "​units":​ "​metric",​ +      "id": ​804
-      "​location":​ { +      "main": "Clouds", 
-        "​name":​ "​Bucuresti",​ +      "description": "overcast clouds", 
-        "​neighborhood":​null,​ +      "icon": "04n"
-        "​city":​ "​Bucuresti",​ +
-        "​state":​ null, +
-        "​state_name":"​Romania",​ +
-        "​country":​ "​RO",​ +
-        "​country_iso3166":​null,​ +
-        "​country_name":"​Romania",​ +
-        "​zip":"​00000",​ +
-        "​magic":"​11"+
-        "wmo":"​15420",​ +
-        "​latitude":44.50000000, +
-        "​longitude":​26.12999916+
-        "elevation":91.00000000,​ +
-        "​l":​ "/​q/​zmw:​00000.11.15420"​ +
-      }, +
-      "date": ​+
- ... +
-      }+
-      "current_observation": ​+
-        ​"source":​ "PWS", +
-        "station": ​+
-          ...  +
-        }, +
-        ​"estimated": null+
-        "date": ​+
-          ... +
-        }, +
-        ​"metar": "AAXX 16161 15420 22997 80502 10201 20109 30072 40180 55008 8807/ 333 55300 0//// 20454",​ +
-        "​condition":"​Overcast",​ +
-        "​temperature":​ 21.5, +
-        "​humidity":​48,​ +
-        "​wind_speed":​6.9,​ +
-        ..., +
-        "​pressure":​ 1018, +
-        ..., +
-      }+
     }     }
-  ​}; +  ​], 
-</​script>​ +  "​base":​ "​stations",​ 
-</​code>​ Se observă faptul că informațiile necesare se regăsesc în cadrul unei etichete de tip ''<​script> ​... </​script>''​ care conține un obiect denumit ''​wui.api_dat''​ exprimat în format JSONÎn acest sensse obține lista tuturor etichetelor de tip ''​script''​ (se folosește metoda ''​getElementsByTag()''​)se preia conținutul acestora (prin intermediul metodei ''​data()''​ din cadrul clasei ''​Element''​) și se verifică dacă se regăsește șirul de caractere ''​wui.api_dat'';​+  "​main":​ { 
 +    "​temp":​ 284.65, 
 +    "​feels_like":​ 283.92, 
 +    "​temp_min":​ 283.15, 
 +    "​temp_max":​ 285.93, 
 +    "​pressure":​ 1016, 
 +    "​humidity":​ 81 
 +  }, 
 +  "​visibility":​ 10000, 
 +  "​wind":​ { 
 +    "​speed":​ 0.5, 
 +    "​deg":​ 310 
 +  }, 
 +  "​clouds":​ { 
 +    "​all":​ 88 
 +  }, 
 +  "​dt":​ 1588887122,​ 
 +  "​sys":​ { 
 +    "​type":​ 1, 
 +    "​id":​ 6911, 
 +    "​country":​ "​RO",​ 
 +    "​sunrise":​ 1588906577,​ 
 +    "​sunset":​ 1588958868 
 +  }, 
 +  "​timezone":​ 10800, 
 +  "​id":​ 683506, 
 +  "​name":​ "​Bucharest",​ 
 +  "​cod":​ 200 
 +
 +</​code> ​
     * se inspectează documentul în format JSON pentru a obține informațiile necesare: se obțin, succesiv, obiectele atașate ca valori pentru cheile ''​response''​ → ''​current_observation''​ și ulterior datele meteorologice,​ regăsite ca valori sub cheile ''​temperature'',​ ''​wind_speed'',​ ''​condition'',​ ''​pressure'',​ ''​humidity'';​     * se inspectează documentul în format JSON pentru a obține informațiile necesare: se obțin, succesiv, obiectele atașate ca valori pentru cheile ''​response''​ → ''​current_observation''​ și ulterior datele meteorologice,​ regăsite ca valori sub cheile ''​temperature'',​ ''​wind_speed'',​ ''​condition'',​ ''​pressure'',​ ''​humidity'';​
     * se construiește un obiect de tipul ''​WeatherForecastInformation''​ folosind informațiile furnizate și se transmite către server pentru ca acesta să fie utilizat ulterior pentru cereri provenite de la alți clienți, vizând același oraș.     * se construiește un obiect de tipul ''​WeatherForecastInformation''​ folosind informațiile furnizate și se transmite către server pentru ca acesta să fie utilizat ulterior pentru cereri provenite de la alți clienți, vizând același oraș.
Line 449: Line 491:
  
 <code java> <code java>
-@Override 
 public void run() { public void run() {
-  if (socket ​!= null) { +  if (socket ​== null) { 
-    try { +    ​Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Socket is null!"​);​ 
-      BufferedReader bufferedReader = Utilities.getReader(socket);​ +    return; 
-      PrintWriter ​   printWriter ​   = Utilities.getWriter(socket);​ +  } 
-      if (bufferedReader ​!= null && ​printWriter ​!= null) { +  ​try { 
-        Log.i(Constants.TAG,​ "​[COMMUNICATION THREAD] Waiting for parameters from client (city / information type)!"); +    BufferedReader bufferedReader = Utilities.getReader(socket);​ 
-        String city            = bufferedReader.readLine();​ +    PrintWriter printWriter = Utilities.getWriter(socket);​ 
-        String informationType = bufferedReader.readLine();​ +    if (bufferedReader ​== null || printWriter ​== null) { 
-        HashMap<​String,​ WeatherForecastInformation>​ data = serverThread.getData();​ +      ​Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Buffered Reader / Print Writer are null!"​);​ 
-        WeatherForecastInformation weatherForecastInformation = null; +      return; 
-        if (city != null && !city.isEmpty() && informationType != null && !informationType.isEmpty()) { +    } 
-          ​if (data.containsKey(city)) { +    ​Log.i(Constants.TAG,​ "​[COMMUNICATION THREAD] Waiting for parameters from client (city / information type!"​);​ 
-            Log.i(Constants.TAG,​ "​[COMMUNICATION THREAD] Getting the information from the cache..."​);​ +    String city = bufferedReader.readLine();​ 
-            weatherForecastInformation = data.get(city);​ +    String informationType = bufferedReader.readLine();​ 
-          } else { +    if (city == null || city.isEmpty() || informationType == null || informationType.isEmpty()) { 
-            Log.i(Constants.TAG,​ "​[COMMUNICATION THREAD] Getting the information from the webservice..."​);​ +      Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Error receiving parameters from client (city / information type!"​);​ 
-            HttpClient httpClient = new DefaultHttpClient();​ +      return; 
-            HttpPost httpPost = new HttpPost(Constants.WEB_SERVICE_ADDRESS);​ +    } 
-            List<​NameValuePair>​ params = new ArrayList<​NameValuePair>(); +    ​HashMap<​String,​ WeatherForecastInformation>​ data = serverThread.getData();​ 
-            params.add(new BasicNameValuePair(Constants.QUERY_ATTRIBUTE,​ city)); +    WeatherForecastInformation weatherForecastInformation = null; 
-            UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params,​ HTTP.UTF_8);​ +    if (data.containsKey(city)) { 
-            httpPost.setEntity(urlEncodedFormEntity);​ +      Log.i(Constants.TAG,​ "​[COMMUNICATION THREAD] Getting the information from the cache..."​);​ 
-            ResponseHandler<​String>​ responseHandler = new BasicResponseHandler();​ +      weatherForecastInformation = data.get(city);​ 
-            String pageSourceCode = httpClient.execute(httpPost,​ responseHandler);​ +    } else { 
-            if (pageSourceCode ​!= null) { +      Log.i(Constants.TAG,​ "​[COMMUNICATION THREAD] Getting the information from the webservice..."​);​ 
-              Document document = Jsoup.parse(pageSourceCode);​ +      HttpClient httpClient = new DefaultHttpClient();​ 
-              Element element = document.child(0);​ +      HttpPost httpPost = new HttpPost(Constants.WEB_SERVICE_ADDRESS);​ 
-              Elements ​scripts ​= element.getElementsByTag(Constants.SCRIPT_TAG);​ +      List<​NameValuePair>​ params = new ArrayList<>​();​ 
-              for (Element script: ​scripts) { +      params.add(new BasicNameValuePair(Constants.QUERY_ATTRIBUTE,​ city)); 
-                String scriptData = script.data();​ +      UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params,​ HTTP.UTF_8);​ 
-                if (scriptData.contains(Constants.SEARCH_KEY)) { +      httpPost.setEntity(urlEncodedFormEntity);​ 
-                  int position = scriptData.indexOf(Constants.SEARCH_KEY) + Constants.SEARCH_KEY.length();​ +      ResponseHandler<​String>​ responseHandler = new BasicResponseHandler();​ 
-                  scriptData = scriptData.substring(position);​ +      String pageSourceCode = httpClient.execute(httpPost,​ responseHandler);​ 
-                  JSONObject content = new JSONObject(scriptData);​ +      if (pageSourceCode ​== null) { 
-   ​JSONObject currentObservation = content.getJSONObject(Constants.CURRENT_OBSERVATION);​ +        ​Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Error getting the information from the webservice!"​);​ 
-                  String temperature = currentObservation.getString(Constants.TEMPERATURE);​ +        return; 
-                  String windSpeed = currentObservation.getString(Constants.WIND_SPEED);​ +      } 
-                  String condition = currentObservation.getString(Constants.CONDITION);​ +      ​Document document = Jsoup.parse(pageSourceCode);​ 
-                  String pressure = currentObservation.getString(Constants.PRESSURE);​ +      Element element = document.child(0);​ 
-                  String humidity = currentObservation.getString(Constants.HUMIDITY);​ +      Elements ​elements ​= element.getElementsByTag(Constants.SCRIPT_TAG);​ 
-                  weatherForecastInformation = new WeatherForecastInformation( +      for (Element script: ​elements) { 
-                    temperature,​ +        String scriptData = script.data();​ 
-                    ​windSpeed, +        if (scriptData.contains(Constants.SEARCH_KEY)) { 
-                    ​condition, +          int position = scriptData.indexOf(Constants.SEARCH_KEY) + Constants.SEARCH_KEY.length();​ 
-                    ​pressure, +          scriptData = scriptData.substring(position);​ 
-                    ​humidity +          JSONObject content = new JSONObject(scriptData);​ 
-                  ); +          JSONObject currentObservation = content.getJSONObject(Constants.CURRENT_OBSERVATION);​ 
-                  serverThread.setData(city,​ weatherForecastInformation);​ +          String temperature = currentObservation.getString(Constants.TEMPERATURE);​ 
-                  break+          String windSpeed = currentObservation.getString(Constants.WIND_SPEED);​ 
-                } +          String condition = currentObservation.getString(Constants.CONDITION);​ 
-              } +          String pressure = currentObservation.getString(Constants.PRESSURE);​ 
-            } else { +          String humidity = currentObservation.getString(Constants.HUMIDITY);​ 
-              Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Error getting the information from the webservice!"​);​ +          weatherForecastInformation = new WeatherForecastInformation( 
-            } +            temperature,​ windSpeed, condition, pressure, humidity 
-          } +          ); 
-          if (weatherForecastInformation != null) { +          serverThread.setData(city,​ weatherForecastInformation);​ 
-            String result = null; +          break;
-            if (Constants.ALL.equals(informationType)) { +
-              result = weatherForecastInformation.toString();​ +
-            } else if (Constants.TEMPERATURE.equals(informationType)) { +
-              result = weatherForecastInformation.getTemperature();​ +
-            } else if (Constants.WIND_SPEED.equals(informationType)) { +
-              result = weatherForecastInformation.getWindSpeed();​ +
-            } else if (Constants.CONDITION.equals(informationType)) { +
-              result = weatherForecastInformation.getCondition();​ +
-            } else if (Constants.HUMIDITY.equals(informationType)) { +
-              result = weatherForecastInformation.getHumidity();​ +
-            } else if (Constants.PRESSURE.equals(informationType)) { +
-              result = weatherForecastInformation.getPressure();​ +
-            } else { +
-              result = "Wrong information type (all / temperature / wind_speed / condition / humidity / pressure)!";​ +
-            } +
-            printWriter.println(result);​ +
-            printWriter.flush();​ +
-          } else { +
-            Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Weather Forecast information is null!"​);​ +
-          } +
-        } else { +
-          Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Error receiving parameters from client (city / information type)!"​);+
         }         }
-      } else { 
-        Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] BufferedReader / PrintWriter are null!"​);​ 
       }       }
-      socket.close(); +    } 
-    } catch (IOException ioException) { +    if (weatherForecastInformation == null) { 
-      Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] An exception has occurred: " + ioException.getMessage());​ +      Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Weather Forecast Information is null!")
-      if (Constants.DEBUG) { +      return
-        ioException.printStackTrace();​ +    ​
-      +    String result = null; 
-    } catch (JSONException jsonException) { +    switch(informationType) { 
-      Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] An exception has occurred: " + jsonException.getMessage());​ +      case Constants.ALL:​ 
-      if (Constants.DEBUG) { +        result = weatherForecastInformation.toString();​ 
-        jsonException.printStackTrace();​+        break; 
 +      case Constants.TEMPERATURE:​ 
 +        result = weatherForecastInformation.getTemperature();​ 
 +        break; 
 +      case Constants.WIND_SPEED:​ 
 +        result = weatherForecastInformation.getWindSpeed();​ 
 +        break; 
 +      case Constants.CONDITION:​ 
 +        result = weatherForecastInformation.getCondition();​ 
 +        break; 
 +      case Constants.HUMIDITY:​ 
 +        result = weatherForecastInformation.getHumidity();​ 
 +        break; 
 +      case Constants.PRESSURE:​ 
 +        result = weatherForecastInformation.getPressure();​ 
 +        break; 
 +      default: 
 +        result = "​[COMMUNICATION THREAD] Wrong information type (all / temperature / wind_speed / condition / humidity / pressure)!";​ 
 +    } 
 +    printWriter.println(result);​ 
 +    printWriter.flush();​ 
 +  ​} catch (IOException ioException) { 
 +    Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] An exception has occurred: " + ioException.getMessage());​ 
 +    if (Constants.DEBUG) { 
 +      ioException.printStackTrace();​ 
 +    
 +  } catch (JSONException jsonException) { 
 +    Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] An exception has occurred: " + jsonException.getMessage());​ 
 +    if (Constants.DEBUG) { 
 +      jsonException.printStackTrace();​ 
 +    } 
 +  } finally { 
 +    if (socket != null) { 
 +      try { 
 +        socket.close();​ 
 +      } catch (IOException ioException) { 
 +        Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] An exception has occurred: " + ioException.getMessage());​ 
 +        if (Constants.DEBUG) { 
 +          ioException.printStackTrace();​ 
 +        }
       }       }
     }     }
-  } else { 
-    Log.e(Constants.TAG,​ "​[COMMUNICATION THREAD] Socket is null!"​);​ 
   }   }
 } }
 </​code>​ </​code>​
  
-**6.** Implementarea clientului presupune un fir de execuție pe care sunt realizate următoarele operații:+**4.** Implementarea clientului presupune un fir de execuție pe care sunt realizate următoarele operații:
   * deschiderea unui canal de comunicație folosind parametrii de conexiune la server (adresa Internet, port);   * deschiderea unui canal de comunicație folosind parametrii de conexiune la server (adresa Internet, port);
   * obținerea unor obiecte de tip ''​BufferedReader''​ și ''​PrintWriter''​ (prin apelul metodelor statice din clasa ''​Utilities'':​ ''​getReader()''​ și ''​getWriter()''​),​ prin care se vor realiza operațiile de citire și scriere pe canalul de comunicație;​   * obținerea unor obiecte de tip ''​BufferedReader''​ și ''​PrintWriter''​ (prin apelul metodelor statice din clasa ''​Utilities'':​ ''​getReader()''​ și ''​getWriter()''​),​ prin care se vor realiza operațiile de citire și scriere pe canalul de comunicație;​
Line 561: Line 615:
  
 <code java> <code java>
-@Override 
 public void run() { public void run() {
   try {   try {
Line 570: Line 623:
     }     }
     BufferedReader bufferedReader = Utilities.getReader(socket);​     BufferedReader bufferedReader = Utilities.getReader(socket);​
-    PrintWriter ​   printWriter ​   = Utilities.getWriter(socket);​ +    PrintWriter printWriter = Utilities.getWriter(socket);​ 
-    if (bufferedReader ​!= null && ​printWriter ​!= null) { +    if (bufferedReader ​== null || printWriter ​== null) { 
-      printWriter.println(city);​ +      ​Log.e(Constants.TAG,​ "​[CLIENT THREAD] Buffered Reader / Print Writer are null!"​);​ 
-      printWriter.flush();​ +      return; 
-      printWriter.println(informationType);​ +    } 
-      printWriter.flush();​ +    ​printWriter.println(city);​ 
-      String weatherInformation;​ +    printWriter.flush();​ 
-      while ((weatherInformation = bufferedReader.readLine()) != null) { +    printWriter.println(informationType);​ 
-        final String ​finalizedWeatherInformation ​= weatherInformation;​ +    printWriter.flush();​ 
-        weatherForecastTextView.post(new Runnable() { +    String weatherInformation;​ 
-          @Override +    while ((weatherInformation = bufferedReader.readLine()) != null) { 
-          public void run() { +      final String ​finalizedWeateherInformation ​= weatherInformation;​ 
-            weatherForecastTextView.append(finalizedWeatherInformation + "​\n"​); +      weatherForecastTextView.post(new Runnable() { 
-          } +        @Override 
-        }); +        public void run() { 
-      } +          weatherForecastTextView.setText(finalizedWeateherInformation); 
-    } else { +        } 
-      Log.e(Constants.TAG,​ "​[CLIENT THREAD] BufferedReader / PrintWriter are null!");+      });
     }     }
-    socket.close();​ 
   } catch (IOException ioException) {   } catch (IOException ioException) {
     Log.e(Constants.TAG,​ "​[CLIENT THREAD] An exception has occurred: " + ioException.getMessage());​     Log.e(Constants.TAG,​ "​[CLIENT THREAD] An exception has occurred: " + ioException.getMessage());​
     if (Constants.DEBUG) {     if (Constants.DEBUG) {
       ioException.printStackTrace();​       ioException.printStackTrace();​
 +    }
 +  } finally {
 +    if (socket != null) {
 +      try {
 +        socket.close();​
 +      } catch (IOException ioException) {
 +        Log.e(Constants.TAG,​ "​[CLIENT THREAD] An exception has occurred: " + ioException.getMessage());​
 +        if (Constants.DEBUG) {
 +          ioException.printStackTrace();​
 +        }
 +      }
     }     }
   }   }
Line 599: Line 662:
 </​code>​ </​code>​
  
-{{ :​eim:​colocvii:​colocviu02:​graphic_interface02.png?​nolink&​400 }}+{{ :​eim:​colocvii:​colocviu02:​09graphicuserinterfacefunctionality.png?​nolink&​400 }}
  
 Pornirea firului de execuție corespunzător clientului va fi realizată pe metoda de callback a obiectului ascultător pentru evenimentul de tip apăsare a butonului aferent din interfața grafică. Pornirea firului de execuție corespunzător clientului va fi realizată pe metoda de callback a obiectului ascultător pentru evenimentul de tip apăsare a butonului aferent din interfața grafică.
Line 610: Line 673:
 <code java> <code java>
 private class GetWeatherForecastButtonClickListener implements Button.OnClickListener { private class GetWeatherForecastButtonClickListener implements Button.OnClickListener {
 +
   @Override   @Override
   public void onClick(View view) {   public void onClick(View view) {
     String clientAddress = clientAddressEditText.getText().toString();​     String clientAddress = clientAddressEditText.getText().toString();​
-    String clientPort ​   = clientPortEditText.getText().toString();​ +    String clientPort = clientPortEditText.getText().toString();​ 
-    if (clientAddress == null || clientAddress.isEmpty() || +    if (clientAddress == null || clientAddress.isEmpty() 
-      ​clientPort == null || clientPort.isEmpty()) { +        ​|| clientPort == null || clientPort.isEmpty()) { 
-      Toast.makeText( +      Toast.makeText(getApplicationContext(),​ "[MAIN ACTIVITY] ​Client connection parameters should be filled!",​ Toast.LENGTH_SHORT).show();​
-        ​getApplicationContext(),​ +
-        ​"​Client connection parameters should be filled!",​ +
-        ​Toast.LENGTH_SHORT +
-      ​).show();+
       return;       return;
     }     }
     if (serverThread == null || !serverThread.isAlive()) {     if (serverThread == null || !serverThread.isAlive()) {
-      ​Log.e(Constants.TAG, "[MAIN ACTIVITY] There is no server to connect to!");+      ​Toast.makeText(getApplicationContext(), "[MAIN ACTIVITY] There is no server to connect to!", Toast.LENGTH_SHORT).show();
       return;       return;
     }     }
     String city = cityEditText.getText().toString();​     String city = cityEditText.getText().toString();​
     String informationType = informationTypeSpinner.getSelectedItem().toString();​     String informationType = informationTypeSpinner.getSelectedItem().toString();​
-    if (city == null || city.isEmpty() || +    if (city == null || city.isEmpty() 
-      ​informationType == null || informationType.isEmpty()) { +        ​|| informationType == null || informationType.isEmpty()) { 
-      Toast.makeText( +      Toast.makeText(getApplicationContext(),​ "[MAIN ACTIVITY] ​Parameters from client (city / information type) should be filled",​ Toast.LENGTH_SHORT).show();​
-        ​getApplicationContext(),​ +
-        ​"​Parameters from client (city / information type) should be filled!", +
-        ​Toast.LENGTH_SHORT +
-      ​).show();+
       return;       return;
     }     }
 +
     weatherForecastTextView.setText(Constants.EMPTY_STRING);​     weatherForecastTextView.setText(Constants.EMPTY_STRING);​
 +
     clientThread = new ClientThread(     clientThread = new ClientThread(
-      clientAddress,​ +      clientAddress,​ Integer.parseInt(clientPort),​ city, informationType, ​weatherForecastTextView 
-      ​Integer.parseInt(clientPort),​ +    );
-      ​city, +
-      ​informationType,​ +
-      ​weatherForecastTextView);+
     clientThread.start();​     clientThread.start();​
   }   }
 +
 } }
 </​code>​ </​code>​
Line 661: Line 717:
  
 <​code>​ <​code>​
-student@eim2016:~$ nc 192.168.56.101 5000+student@eim:~$ nc 192.168.56.101 5000
 Bucuresti Bucuresti
 all all
Line 674: Line 730:
  
 <​code>​ <​code>​
-C:\Users\Eim2016> telnet 192.168.56.101 5000+C:\Users\Eim> telnet 192.168.56.101 5000
 Bucuresti Bucuresti
 all all
Line 687: Line 743:
 </​columns>​ </​columns>​
  
-**7.** Pentru încărcarea codului în contextul depozitului din cadrul contului Github personal:+**5.** Pentru încărcarea codului în contextul depozitului din cadrul contului Github personal:
   - se transferă modificările din zona de lucru în zona de lucru în zona de așteptare prin intermediul comenzii ''​git add'',​ indicându-se și fișierele respective (pot fi folosite șabloane pentru a desemna mai multe fișiere);   - se transferă modificările din zona de lucru în zona de lucru în zona de așteptare prin intermediul comenzii ''​git add'',​ indicându-se și fișierele respective (pot fi folosite șabloane pentru a desemna mai multe fișiere);
   - se consemnează modificările din zona de așteptare în directorul Git prin intermediul comenzii ''​git commit -m'',​ precizându-se și un mesaj sugestiv:   - se consemnează modificările din zona de așteptare în directorul Git prin intermediul comenzii ''​git commit -m'',​ precizându-se și un mesaj sugestiv:
Line 695: Line 751:
  
 <​code>​ <​code>​
-student@eim2016:​~/​PracticalTest02$ git add * +student@eim:​~/​PracticalTest02$ git add * 
-student@eim2016:​~/​PracticalTest02$ git commit -m "​finished tasks for PracticalTest02"​ +student@eim:​~/​PracticalTest02$ git commit -m "​finished tasks for PracticalTest02"​ 
-student@eim2016:​~/​PracticalTest02$ git push origin master+student@eim:​~/​PracticalTest02$ git push origin master
 </​code>​ </​code>​
  
Line 703: Line 759:
  
 <​code>​ <​code>​
-student@eim2016:​~/​PracticalTest02$ git config --global user.name "​Perfect Student"​ +student@eim:​~/​PracticalTest02$ git config --global user.name "​Perfect Student"​ 
-student@eim2016:​~/​PracticalTest02$ git config --global user.email perfectstudent@cs.pub.ro+student@eim:​~/​PracticalTest02$ git config --global user.email perfectstudent@cs.pub.ro
 </​code>​ </​code>​
 +<​hidden>​
 +**6.** Protocolul SIP pornește de la o presupunere optimistă conform căreia atât sursa cât și destinația se găsesc în cadrul aceluiași sistem autonom, astfel încât nu sunt necesare credențiale pentru autentificare. Din acest model, o cerere de tip ''​REGISTER''​ se transmite inițial de către user agent fără aceste informații. În condițiile în care răspunsul furnizat de registration server este //Status: 401 Unauthorized//,​ mesajul este retransmis împreună cu informațiile necesare identificării (SIP Authorization ID, Password). Se poate observa că dimensiunile celor două pachete sunt diferite (mesajul fără credențiale 1095 octeți, mesajul cu credențiale 1249 octeți). În cazul în care informațiile de autentificare sunt valide, răspunsul va fi //Status: 200 OK//.
  
-**8.** Înregistrarea unui serviciu în cadrul rețelei locale se face prin intermediul unor obiecte ''​NsdServiceInfo''​ (Android NSD) respectiv ''​ServiceInfo''​ (JmDNS). Acestea sunt transmise ca parametrii ai metodelor ''​registerService()''​ din clasele ''​NsdManager'',​ respectiv ''​JmDNS''​.+{{ :​eim:​laboratoare:​laborator09:​wireshark01.png?​nolink&​800 }} 
 +</​hidden>​
  
-Aceste obiecte sunt caracterizate prin parametrii de conectare la serviciu: +**6.** Se configurează ​''​avahi-daemon''​ (fișierul ''​/​etc/​avahi/​services/​chat.service''​) ​astfel: 
-  ​* ''​NsdServiceInfo'' ​adresaInternet (metoda ​''​setHost()''​) ​și port (metoda ​''​setPort()''​); +<code xml> 
-  * ''​ServiceInfo''​ - portul, configurat prin intermediul constructorului.+<?xml version="​1.0"​ standalone='no'?> 
 +<​!DOCTYPE service-group SYSTEM "​avahi-service.dtd">​ 
 +<​service-group>​ 
 +  <​name>​My desktop</​name>​ 
 +  <​service>​ 
 +    <​type>​_myservice._tcp</​type>​ 
 +    <​port>​5555</​port>​ 
 +  </​service>​ 
 +</​service-group>​ 
 +</​code>​
  
-Se recomandă ca portul să fie furnizat de sistemul de operare, pentru a evita situațiile în care utilizatorul poate specifica un port care este ocupat. În acest sens, se instanțiază un obiect de tip ''​ServerSocket''​ care primește parametrul ''​0'',​ indicându-se astfel faptul că se dorește utilizarea unui port aleator, care poate fi folosit la momentul respectiv. Ulterior, parametrii de conectare pot fi obținuți folosind metodele specifice ale unui astfel de obiect: +    * se restarteaza demonul ​ 
-  ​* adresa Internet ​''​getInetAddress()'';​ +<code shell> ​  
-  * port ''​getLocalPort()''​.+root@eg106:​~#​ /​etc/​init.d/​avahi-daemon restart 
 +[ ok ] Restarting avahi-daemon ​(via systemctl): avahi-daemon.service. 
 +root@eg106:​~#​ </​code> ​
  
-<​code ​java+**7a.** Folosind utilitarul din linie de comandă ''​avahi-browse''​ cu parametrii ''​-rca''​ se capturează mesajele advertisment și se identifică interfețele raportate din output. 
-ServerSocket serverSocket ​new ServerSocket(0); + 
-if (serverSocket !null) { +Exemplu de output: 
-  ​InetAddress inetAddress ​serverSocket.getInetAddress();​ +<​code>​ 
-  ​int ​port = serverSocket.getLocalPort();​ ++ vboxnet4 IPv6 My desktop ​                                   _myservice._tcp ​     local 
-}++ vboxnet4 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 ++ vmnet8 IPv6 My desktop ​                                   _myservice._tcp ​     local 
 ++ vmnet8 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 ++ vmnet1 IPv6 My desktop ​                                   _myservice._tcp ​     local 
 ++ vmnet1 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 ++ docker0 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 ++   eno1 IPv6 My desktop ​                                   _myservice._tcp ​     local 
 ++   eno1 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 +vboxnet4 IPv6 My desktop ​                                   _myservice._tcp ​     local 
 +   ​hostname = [eg106-2.local] 
 +   ​address = [fe80::​800:​27ff:​fe00:​4] 
 +   port = [5555] 
 +   txt = [] 
 += vboxnet4 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 +   ​hostname = [eg106-2.local] 
 +   ​address = [192.168.60.1] 
 +   port = [5555] 
 +   txt = [] 
 += vmnet8 IPv6 My desktop ​                                   _myservice._tcp ​     local 
 +   ​hostname = [eg106-2.local] 
 +   ​address = [fe80::​250:​56ff:​fec0:​8] 
 +   port = [5555] 
 +   txt = [] 
 += vmnet8 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 +   ​hostname = [eg106-2.local] 
 +   ​address = [192.168.207.1] 
 +   port = [5555] 
 +   txt = [] 
 += vmnet1 IPv6 My desktop ​                                   _myservice._tcp ​     local 
 +   ​hostname = [eg106-2.local] 
 +   ​address = [fe80::​250:​56ff:​fec0:​1] 
 +   port = [5555] 
 +   txt = [] 
 += vmnet1 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 +   ​hostname = [eg106-2.local] 
 +   ​address = [172.16.238.1] 
 +   port = [5555] 
 +   txt = [] 
 += docker0 IPv4 My desktop ​                                   _myservice._tcp ​     local 
 +   ​hostname = [eg106-2.local] 
 +   ​address = [172.17.0.1] 
 +   port [5555] 
 +   txt [] 
 +=   eno1 IPv6 My desktop ​                                   _myservice._tcp      local 
 +   hostname = [eg106-2.local] 
 +   ​address = [fe80::​ed2d:​c447:​8f31:​8128] 
 +   port = [5555] 
 +   txt = [] 
 +=   eno1 IPv4 My desktop ​                                   _myservice._tcp      local 
 +   hostname = [eg106-2.local] 
 +   ​address = [172.16.4.53] 
 +   port = [5555] 
 +   txt = []
 </​code>​ </​code>​
  
-**9.** Protocolul SIP pornește ​de la o presupunere optimistă conform căreia atât sursa cât și destinația se găsesc în cadrul aceluiași sistem autonom, astfel încât nu sunt necesare credențiale pentru autentificare. Din acest model, o cerere de tip ''​REGISTER'' ​se transmite inițial de către user agent fără aceste informațiiÎn condițiile în care răspunsul furnizat de registration server este //Status401 Unauthorized//​mesajul este retransmis împreună cu informațiile necesare identificării (SIP Authorization IDPassword)Se poate observa că dimensiunile celor două pachete sunt diferite (mesajul fără credențiale 1095 octețimesajul cu credențiale 1249 octeți)În cazul în care informațiile de autentificare sunt validerăspunsul va fi //Status: 200 OK//.+Numele ​de masina publicat este ''​eg106-2.local''​, pe interfețele vmnet1, vmnet8, docker0, eno1, etcIP-urile sunt respectiv''​192.168.207.1172.16.238.1172.17.0.1 ​172.16.4.53''​etc
  
-{{ :​eim:​laboratoare:​laborator09:​wireshark01.png?nolink&​800 }}+**7b.** Să se captureze în emulator publicarea serviciului. Ce adresă IPv4 este folosită în advertisementRăspuns: ​
  
-**10.** Harta Google este implementată ​în SDK-ul Android: +<code shell> 
-  prin intermediul unei componente grafice de tipul [[https://developer.android.com/​reference/​com/​google/​android/​gms/​maps/​MapView.html|MapView]]; +root@eg106:​~#​ adb shell  
-  * în cadrul unui fragment, de tipul [[http://developer.android.com/reference/com/google/android/gms/maps/​MapFragment.html|MapFragment]].+root@vbox86p:/​ # tcpdump -ni eth0 'udp port 5353'  
 +# atenție eth1 în configurația default este NAT-ul către internet, acolo nu este nimeni (din DNS-SD)! 
 +...  
 +06:​39:​44.024608 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::​fb.5353:​ 0*[0q] 2/0/0[|domain] 
 +06:39:44.207351 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::fb.5353: 0 [2n][|domain] 
 +06:​39:​44.207385 IP 192.168.60.1.5353 > 224.0.0.251.5353:​ 0 [2nANY (QM)? My desktop._myservice._tcp.local. (91) 
 +06:​39:​44.458079 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::​fb.5353:​ 0 [2n][|domain] 
 +06:39:​44.458108 IP 192.168.60.1.5353 > 224.0.0.251.5353:​ 0 [2n] ANY (QM)? My desktop._myservice._tcp.local. (91) 
 +06:​39:​44.708406 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::​fb.5353:​ 0 [2n][|domain] 
 +06:​39:​44.708439 IP 192.168.60.1.5353 > 224.0.0.251.5353:​ 0 [2n] ANY (QM)? My desktop._myservice._tcp.local. (91) 
 +06:​39:​44.909356 IP 192.168.60.1.5353 > 224.0.0.251.5353:​ 0*- [0q] 6/0/0[|domain] 
 +06:39:44.909381 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::​fb.5353:​ 0*- [0q] 5/0/0[|domain] 
 +06:​39:​45.156505 IP 192.168.60.1.5353 > 224.0.0.251.5353:​ 0*- [0q] 2/0/0[|domain] 
 +06:​39:​45.156532 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::​fb.5353:​ 0*- [0q] 1/0/0 (111) 
 +06:39:45.240427 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::​fb.5353:​ 0 [2q] PTR (QM)? _ipp._tcp.local.[|domain] 
 +06:​39:​45.240458 IP 192.168.60.1.5353 > 224.0.0.251.5353:​ 0 [2qPTR (QM)? _ipp._tcp.local. PTR (QM)? _ipps._tcp.local. (45) 
 +06:​39:​46.041712 IP 192.168.60.1.5353 > 224.0.0.251.5353:​ 0*- [0q] 6/​0/​0[|domain] 
 +06:​39:​46.041740 IP6 fe80::​800:​27ff:​fe00:​4.5353 > ff02::​fb.5353:​ 0*- [0q] 5/​0/​0[|domain] 
 +</​code>​
  
-Referințele către aceste obiecte se obțin în mod obișnuit, prin intermediul metodelor ​''​findViewById()''​, respectiv ''​findFragmentById()''​.+Se foloseste adresa ​''​192.168.60.1'' ​
  
-Pe baza controalelor grafice ''​MapView''​ sau ''​MapFragment'',​ se poate obține o instanță a unui obiect [[https://​developer.android.com/​reference/​com/​google/​android/​gms/​maps/​GoogleMap.html|GoogleMap]],​ prin intermediul metodelor ''​getMap()'',​ respectiv ''​getMapAsync()''​. Se recomandă să se folosească metoda asincronă care garantează faptul că obiectul furnizat este nenul. Metoda de callback ''​onMapReady()''​ a clasei ascultător ''​OnMapReadyCallback''​ nu va fi apelată în situația în care serviciul Google Play Services nu este disponibil pe dispozitivul mobil sau obiectul este distrus imediat după ce a fost creat. 
- 
-<code java> 
-if (googleMap == null) { 
-  ((MapFragment)getFragmentManager().findFragmentById(R.id.google_map)).getMapAsync(new OnMapReadyCallback() { 
-    @Override 
-    public void onMapReady(GoogleMap readyGoogleMap) { 
-      googleMap = readyGoogleMap;​ 
-    } 
-  }); 
-} 
-</​code>​ 
eim/colocvii/colocviu02.1463356205.txt.gz · Last modified: 2016/05/16 02:50 by tmp.andrei.cojocaru
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0