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:laborator04 [2015/03/15 23:12]
Andrei Roșu-Cojocaru [Ciclul de viață al unui fragment]
laboratoare:laborator04 [2015/03/16 14:37] (current)
Andrei Roșu-Cojocaru
Line 551: Line 551:
   * implementează o anumită funcționalitate.   * implementează o anumită funcționalitate.
  
-Este necesar să fie suprascrisă cel puțin ​metoda ''​onCreateView()''​, în situația în care fragmentul ​conține o interfață grafică, încărcarea acesteia realizându-se ​în acest moment.+Pentru a construi interfața grafică corespunzătoare unui fragment, în codul sursă se va suprascrie ​metoda ''​onCreateView()'' ​ce utilizează:​ 
 +  * un obiect de tip ''​LayoutInflator''​ pentru a încărca fișierul XML; 
 +  * un obiect de tip ''​ViewGroup''​ ce referă containerul din care face parte fragmentul ​(de obicei, acesta aparține activității părinte); 
 +  * un obiect de tip ''​Bundle''​ utilizat pentru restaurarea stării în situația fragmentul este reluat (după ce anterior a fost întrerupt temporar).
  
 <file java SomeFragment.java>​ <file java SomeFragment.java>​
Line 571: Line 574:
 } }
 </​file>​ </​file>​
 +
 +Se observă că metoda ''​inflate()''​ din clasa ''​LayoutInflater''​ primește următorii parametrii:
 +  * un întreg, reprezentând identificatorul către o resursă de tip mecanism de control al conținutului;​
 +  * un obiect de tip ''​android.view.ViewGroup'';​
 +  * o valoare de tip adevărat/​fals,​ care indică dacă parametrul anterior este folosit ca părinte pentru interfața grafică încărcată sau dacă acesta este utilizat numai pentru a-i prelua parametrii de tip ''​LayoutParams'',​ spre a afișa în mod corect componentele conținute.
  
 Pentru fiecare fragment trebuie să existe în directorul ''/​res/​layout''​ un fișier XML corespunzător,​ descriind interfața grafică similar cu modul în care se realiza acest lucru în cadrul unei activități (exceptând atributul ''​tools:​context''​ din cadrul elementului rădăcină de tip layout): Pentru fiecare fragment trebuie să existe în directorul ''/​res/​layout''​ un fișier XML corespunzător,​ descriind interfața grafică similar cu modul în care se realiza acest lucru în cadrul unei activități (exceptând atributul ''​tools:​context''​ din cadrul elementului rădăcină de tip layout):
  
-<file xml fragment1.xml>+<file xml some_fragment.xml>
 <?xml version="​1.0"​ encoding="​UTF-8"?>​ <?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"​
Line 598: Line 606:
 În fișierul ''​AndroidManifest.xml''​ NU este necesar să existe nici o referință la fragmente, întrucât existența acestora este limitată la activitatea în care sunt conținute. În fișierul ''​AndroidManifest.xml''​ NU este necesar să existe nici o referință la fragmente, întrucât existența acestora este limitată la activitatea în care sunt conținute.
  
-==== Ciclul de viață al unui fragment ​====+==== Ciclul de Viață al unui Fragment ​====
  
 Ca și în cazul unei activități,​ ciclul de viață al unui fragment poate fi controlat prin intermediul unor metode care sunt apelate în mod automat. În cazul în care sunt suprascrise,​ acestea trebuie să apeleze în mod necesar metodele din clasa părinte. Pe lângă metodele apelate în momentul în care fragmentul este creat, pornit, reluat, întrerupt, oprit și distrus, există și metode specifice care semnalizează atașarea și detașarea sa în cadrul unei activități,​ crearea și distrugerea ierarhiei de controale grafice precum și crearea activității părinte. Ca și în cazul unei activități,​ ciclul de viață al unui fragment poate fi controlat prin intermediul unor metode care sunt apelate în mod automat. În cazul în care sunt suprascrise,​ acestea trebuie să apeleze în mod necesar metodele din clasa părinte. Pe lângă metodele apelate în momentul în care fragmentul este creat, pornit, reluat, întrerupt, oprit și distrus, există și metode specifice care semnalizează atașarea și detașarea sa în cadrul unei activități,​ crearea și distrugerea ierarhiei de controale grafice precum și crearea activității părinte.
  
-{{ :​laboratoare:​laborator04:​ciclul_de_viata_al_unui_fragment.png?​nolink&​1000 }}+{{ :​laboratoare:​laborator04:​ciclul_de_viata_al_unui_fragment.png?​nolink&​900 }}
  
   * la creare sunt apelate metodele: ''​onAttach()'',​ ''​onCreate()'',​ ''​onCreateView()''​ și ''​onActivityCreated()''​   * la creare sunt apelate metodele: ''​onAttach()'',​ ''​onCreate()'',​ ''​onCreateView()''​ și ''​onActivityCreated()''​
   * în momentul în care devine vizibil, interacționând cu utilizatorul,​ sunt apelate ''​onStart()''​ și ''​onResume()''​   * în momentul în care devine vizibil, interacționând cu utilizatorul,​ sunt apelate ''​onStart()''​ și ''​onResume()''​
   * în momentul în care nu mai este vizibil, nemaiinteracționând cu utilizatorul,​ sunt apelate ''​onPause()''​ și ''​onStop()''​   * în momentul în care nu mai este vizibil, nemaiinteracționând cu utilizatorul,​ sunt apelate ''​onPause()''​ și ''​onStop()''​
-  * la distrugere sunt apelate ​în plus și metodele ''​onDestroyView()'',​ ''​onDestroy()''​ și ''​onDetach()''​+  * la distrugere sunt apelate metodele ''​onDestroyView()'',​ ''​onDestroy()''​ și ''​onDetach()''​
  
 Unele dintre aceste metode sunt aceleași ca în cazul activităților,​ având aceeași funcționalitate,​ în timp ce altele sunt specifice: Unele dintre aceste metode sunt aceleași ca în cazul activităților,​ având aceeași funcționalitate,​ în timp ce altele sunt specifice:
-  * ''​onAttach(Activity)''​ - apelată în momentul în care fragmentul este atașat la activitate +  * ''​onAttach(Activity)''​ - apelată în momentul în care fragmentul este atașat la activitate; de regulă, în această metodă se reține referința către activitate; 
-  * ''​onCreateView(LayoutInflator,​ ViewGroup, Bundle)''​ - apelată pentru a crea interfața grafică a fragmentului +  * ''​onCreateView(LayoutInflator,​ ViewGroup, Bundle)''​ - apelată pentru a crea interfața grafică a fragmentului, fie prin încărcarea din fișierul XML corespunzător (folosind metoda ''​inflate()''​ din clasa ''​LayoutInflater''​),​ fie prin construirea programatică;​ interfața grafică (obiect de tip ''​android.view.View''​) trebuie întoarsă ca rezultat al acestei metode; pentru fragmentele care nu dețin o interfață grafică se întoarce ''​null'';​ 
-  * ''​onActivityCreated(Bundle)''​ - apelată în momentul în care s-a terminat metoda ''​onCreate()''​ a activității care conține fragmentul +  * ''​onActivityCreated(Bundle)''​ - apelată în momentul în care s-a terminat metoda ''​onCreate()''​ a activității care conține fragmentul; în cadrul acestei metode este finalizat procesul de inițializare al fragmentului,​ fiind executate operații pentru care este necesar ca activitatea să fie creată; 
-  * ''​onDestroyView()''​ - apelată pentru a distruge ​interfața grafică ​a fragmentului+  * ''​onDestroyView()''​ - apelată pentru a elibera resursele aferente controalelor din cadrul fragmentului,​ atunci când interfața grafică ​este descărcată;​
   * ''​onDetach()''​ - apelată în momentul în care fragmentul este detașat de la activitate   * ''​onDetach()''​ - apelată în momentul în care fragmentul este detașat de la activitate
  
-Se observă faptul că restaurarea stării se poate face în oricare din metodele ''​onCreate()'',​ ''​onCreateView()'',​ respectiv ''​onActivityCreated()''​.+<​note>​Se observă faptul că restaurarea stării se poate face în oricare din metodele ''​onCreate()'',​ ''​onCreateView()'',​ respectiv ''​onActivityCreated()''​.</​note>​
  
-<note important>​O distincție între activități și fragmente constă în faptul ​că fragmentele nu sunt adăugate în mod automat pe stivă, asemenea activităților în momentul ​în care aplicația nu mai este vizibilă astfel încât restaurarea stării nu se poate realiza ​corespunzător. În acest sens, va trebui apelată explicit metoda ​''​addToBackStack(null)'' ​pentru obiectul de tip ''​FragmentTransaction'' ​care se ocupă de atașarea și detașarea fragmentelor de la activitate.</​note>​+Se observă că ciclul de viață al unui fragment se desfășoară între momentele ​în care acesta ​este atașat la activitatea din care face parte, respectiv este detașat din cadrul acesteia, metodele ​corespunzătoare fiind ''​onAttach()'' ​și ''​onDetach()''​.
  
 +Metoda ''​onAttach()''​ este apelată înainte de a se crea interfața grafică a fragmentului,​ fragmentul însuși și chiar activitatea din care acesta face parte. În cadrul său, de regulă se obține o referință către activitate pentru a se realiza alte operații de inițializare.
  
-=======+Se consideră că un fragment există între metodele ''​onCreate()''​ și ''​onDestroy()''​.
  
-Referința la acest fragment trebuie ​să se realizeze ​în fișierul XML corespunzător activității care o va conține:+În cadrul metodei ''​onCreate()''​ se recomandă ​să fie instanțiate orice obiecte care vor fi utilizate ​în cadrul fragmentului. Tot aici sunt realizate ​și alte operații de inițializare.
  
-<code xml>+<note important>​Spre diferență de activități,​ interfața grafică a unui fragment NU este încărcată în cadrul metodei ''​onCreate()''​.</​note>​ 
 + 
 +În cazul unui fragment, interfața grafică este construită,​ respectiv distrusă în cadrul metodelor ''​onCreateView()''​ și ''​onDestroyView()''​. 
 + 
 +În cadrul metodei ''​onCreateView()''​ se recomandă să se încarce interfața grafică din fișierul XML asociat (respectiv să se creeze programatic),​ obținându-se referințe către controalele grafice respective și asociindu-li-se obiecte de tip ascultător pentru diferite tipuri de evenimente din cadrul interacțiunii cu utilizatorul. Tot acum pot fi pornite diferite servicii sau cronometre. 
 + 
 +<note important>​Este obligatoriu ca metoda ''​onCreateView()''​ să întoarcă un obiect de tip ''​android.view.View''​ reprezentând interfața grafică a fragmentului respectiv.</​note>​ 
 + 
 +În cazul în care este necesar ca fragmentul să acceseze obiecte din interfața grafică a activității din care face parte, astfel de operații nu pot fi realizate decât după ce a fost apelată metoda ''​onActivityCreated()'',​ semnificând faptul că procesul de inițializare al acestuia a fost terminat. În caz contrar, este destul de probabil să se genereze o excepție de tipul ''​NullPointerException''​. 
 + 
 +Stările unui fragment sunt strâns legate de cele ale activității din care face parte. În plus, un fragment poate fi atașat sau detașat în mod dinamic unei activități,​ astfel încât acesta poate parcurge stările de creat, vizibil și activ de mai multe ori în timpul ciclului de viață al containerului din care face parte. 
 + 
 +Gestiunea tranzițiilor între diferitele stări din cadrul ciclului de viață al unui fragment este foarte importantă pentru a se asigura o experiență adecvată a utilizatorilor. Diferitele treceri între stările de inactivitate și activitate ale fragmentului trebuie să se realizeze în mod transparent pentru utilizatorii aplicației Android. De aceea, este foarte important ca să se asigure persistența controalelor grafice și a celorlalte date, salvându-se starea atunci când fragmentul este întrerupt temporar și oprit, restaurarea fiind realizată atunci când acesta este (re)pornit și reluat. 
 + 
 +<note tip>Este posibil ca după întreruperea temporară a activității / fragmentului să nu mai fie apelată nici o metodă, dacă necesarul de memorie impune ca acestea să fie distruse.</​note>​ 
 + 
 +<file java SomeFragment.java>​ 
 +package ro.pub.cs.systems.pdsd.lab04;​ 
 + 
 +import android.app.Activity;​ 
 +import android.app.Fragment;​ 
 +import android.os.Bundle;​ 
 +import android.view.LayoutInflater;​ 
 +import android.view.View;​ 
 +import android.view.ViewGroup;​ 
 + 
 +public class SomeFragment extends Fragment { 
 + 
 +  @Override 
 +  public void onAttach(Activity activity) { 
 +    super.onAttach(activity);​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onCreate(Bundle savedInstanceState) { 
 +    super.onCreate(savedInstanceState);​ 
 +    // ... 
 +  } 
 +   
 +  @Override 
 +  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
 +    return inflater.inflate(R.layout.some_fragment,​ container, false); 
 +  } 
 + 
 +  @Override 
 +  public void onActivityCreated(Bundle savedInstanceState) { 
 +    super.onActivityCreated(savedInstanceState);​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onStart() { 
 +    super.onStart();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onResume() { 
 +    super.onResume();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onPause(){ 
 +    super.onPause();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onSaveInstanceState(Bundle savedInstanceState) { 
 +    super.onSaveInstanceState(savedInstanceState);​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onStop() { 
 +    super.onStop();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onDestroyView() { 
 +    super.onDestroyView();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onDestroy() { 
 +    super.onDestroy();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public void onDetach() { 
 +    super.onDetach();​ 
 +    // ... 
 +  } 
 +} 
 +</​file>​ 
 + 
 +==== Gestiunea Fragmentelor din cadrul unei Activități ==== 
 + 
 +Fiecare activitate dispune de un proces de gestiune a fragmentelor conține, acesta fiind o instanță a clasei ''​FragmentManager'',​ putând fi obținut din cadrul contextului asociat și punând la dispoziția utilizatorilor metode pentru adăugarea și scoaterea, respectiv înlocuirea unui fragment din cadrul său. 
 + 
 +<code java> 
 +FragmentManager fragmentManager = getFragmentManager();​ 
 +</​code>​ 
 + 
 +=== Adăugarea unui Fragment === 
 + 
 +Operația de adăugare a unui fragment poate fi realizată:​ 
 +  - static, în cadrul fișierului XML, prin intermediul elementului ''<​fragment>'',​ în situația în care sunt definite interfețe grafice pentru fiecare dintre tipurile de suprafețe de afișare suportate; ​<code xml>
 <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​ <​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​
   xmlns:​tools="​http://​schemas.android.com/​tools"​   xmlns:​tools="​http://​schemas.android.com/​tools"​
Line 637: Line 758:
   tools:​context="​.MainActivity"​ >   tools:​context="​.MainActivity"​ >
   <​fragment   <​fragment
-    android:​name="​ro.pub.cs.pdsd.laborator03.Fragment1"​+    android:​name="​ro.pub.cs.systems.pdsd.labo04.Fragment1"​
     android:​id="​@+id/​fragment1"​     android:​id="​@+id/​fragment1"​
     android:​layout_weight="​1"​     android:​layout_weight="​1"​
     android:​layout_width="​match_parent"​     android:​layout_width="​match_parent"​
-    android:​layout_height="​match_parent" /> +    android:​layout_height="​wrap_content" /> 
-    ​ +  <fragment 
-  <!-- add other fragments --> +    ​android:​name="​ro.pub.cs.systems.pdsd.labo04.Fragment2"​ 
-    ​+    android:​id="​@+id/​fragment2"​ 
 +    android:​layout_weight="​1"​ 
 +    android:​layout_width="​match_parent"​ 
 +    android:​layout_height="​wrap_content"​ />
 </​LinearLayout>​ </​LinearLayout>​
 </​code>​ </​code>​
 +  - dinamic (programatic),​ în cadrul codului sursă, prin definirea unor containere (obiecte de tip ''​FrameLayout'',​ care pot conține un sigur element vizibil) în cadrul interfețele grafice în care pot fi plasate fragmentele la diferite momente de timp. <code xml>
 +<​LinearLayout xmlns:​android="​http://​schemas.android.com/​apk/​res/​android"​
 +  xmlns:​tools="​http://​schemas.android.com/​tools"​
 +  android:​layout_width="​match_parent"​
 +  android:​layout_height="​match_parent"​
 +  android:​paddingBottom="​@dimen/​activity_vertical_margin"​
 +  android:​paddingLeft="​@dimen/​activity_horizontal_margin"​
 +  android:​paddingRight="​@dimen/​activity_horizontal_margin"​
 +  android:​paddingTop="​@dimen/​activity_vertical_margin"​
 +  android:​orientation="​vertical"​
 +  tools:​context="​.MainActivity"​ >
 +  <​FrameLayout
 +    android:​id="​@+id/​frame1"​
 +    android:​layout_weight="​1"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​wrap_content"​ />
 +  <​FrameLayout
 +    android:​id="​@+id/​frame2"​
 +    android:​layout_weight="​1"​
 +    android:​layout_width="​match_parent"​
 +    android:​layout_height="​wrap_content"​ />
 +</​LinearLayout>​
 +</​code>​ În metoda ''​onCreate()''​ corespunzătoare activității,​ se creează un obiect de tip ''​FragmentManager''​ pentru care se definesc operațiile care trebuie realizate (atomic) în cadrul unei tranzacții (obiectul de tip ''​FragmentTransaction''​ fiind obținut prin invocarea metodei ''​beginTransaction()''​). O tranzacție poate specifica suplimentar animațiile asociate unei tranziții prcum și dacă se dorește ca tranzacție să fie stocată și pe stivă. Realizarea propriu-zisă a operațiilor indicate se realizează doar atunci când se apelează metoda ''​commit()''​.\\ Pentru adăugarea unui fragment în cadrul unei activități,​ trebuie să se precizeze, ca parametrii ai metodei ''​add()''​ din clasa ''​FragmentTransaction''​ identificatorul containerului în care va fi plasat fragmentul, obiectul de tip fragment și (opțional) o etichetă (de tip șir de caractere) prin intermediul căreia acesta poate fi ulterior referit.<​code java>
 +@Override
 +protected void onCreate(Bundle savedInstanceState) {
 +  FragmentManager fragmentManager = getFragmentManager();​
 +  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​
 +  fragmentTransaction.add(android.R.id.frame1,​ new Fragment1());​
 +  fragmentTransaction.add(android.R.id.frame2,​ new Fragment2(),​ Constants.FRAGMENT2_TAG);​
 +  fragmentTransaction.commit();​
 +}
 +</​code>​ De remarcat faptul că referințele ''​R.id.frame1''​ și ''​R.id.frame2''​ se referă la conținutul activității.
 +
 +Pentru a asigura o experiență a utilizatorului consistentă,​ sistemul de operare Android asigură în mod automat persistența mecanismului de dispunere a framentelor și stiva asociată atunci când activitatea părinte este distrusă și (re)creată din cauza unei modificări a configurației. Din acest motiv, se recomandă ca toate interfețele grafice corespunzătoare diferitelor configurații să conțină toate containerele implicate în tranzacții de fragmente. Altfel, atunci când se va încerca restaurarea unor fragmente (în containere care nu exista în configurația respectivă),​ se pot produce anumite excepții. Dacă nu se dorește ca un container să fie afișat în cadrul unei interfețe grafice aferente unei configurații,​ se poate folosi atributul ''​visibility''​.
 +
 +<columns 100% 50%>
 +
 +<code xml>
 +<​LinearLayout ... >
 +  <​FrameLayout
 +    android:​id=@+id/​frame1
 +    ... />
 +  <​FrameLayout
 +    android:​id=@+id/​frame2
 +    android:​visibility="​gone"​
 +    ... />
 +</​LinearLayout>​
 +</​code>​
 +
 +<​newcolumn>​
 +
 +<code java>
 +FrameLayout frame1 = (FrameLayout)findViewById(R.id.frame1);​
 +FrameLayout frame2 = (FrameLayout)findViewById(R.id.frame2);​
 +frame2.setVisibility(Visibility.GONE);​
 +</​code>​
 +
 +</​columns>​
 +
 +=== Scoaterea unui Fragment ===
  
-Alternativ, fragmentul poate fi atașat în mod dinamic doar din codul sursă al activității, fără a mai fi specificat în fișierul XML:+Pentru scoaterea unui fragment ​din cadrul unei activități, trebuie ca anterior să se obțină o referință către acesta, pe baza identificatorului (metoda ''​findFragmentById()''​),​ respectiv pe baza etichetei (metoda ''​findFragmentByTag()''​). Ulterior, se apelează metoda ''​remove()''​ din clasa ''​FragmentTransaction''​ care primește ca parametru această referință.
  
 <code java> <code java>
 FragmentManager fragmentManager = getFragmentManager();​ FragmentManager fragmentManager = getFragmentManager();​
 FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​
-Fragment1 ​fragment1 = new Fragment1(); +Fragment ​fragment1 = fragmentManager.findFragmentById(R.id.fragment1); 
-fragmentTransaction.replace(android.R.id.content, ​fragment1);+Fragment fragment2 = fragmentManager.findFragmentByTag(Constants.FRAGMENT2_TAG);​ 
 +fragmentTransaction.remove(fragment1); 
 +fragmentTransaction.remove(fragment2);
 fragmentTransaction.commit();​ fragmentTransaction.commit();​
 </​code>​ </​code>​
  
-De remarcat faptul că referința ​''​R.id.content'' ​se referă la conținutul activității. Pentru a atașa / detașa fragmente de la o activitate se pot folosi și metodele ''​add()''​, respectiv ​''​replace()''​.+Metoda ​''​findViewById()'' ​poate primi ca parametru și identificatorul containerului care conține fragmentul. În cazul în care aceasta nu are nici un conținut, rezultatul metodei este ''​null''​.
  
-Pentru a crea interfața grafică corespunzătoare fragmentului respectiv, în codul sursă (indicat ​de proprietatea ​''​android:​name''​) se va suprascrie ​metoda ''​onCreateView()'' ​ce utilizează un obiect de tip ''​LayoutInflator'' ​pentru a încărca fișierul XML. Alți parametri ai metodei sunt un ''​ViewGroup'' ​care referă containerul din care face parte fragmentul ​(adică activitatea părinte), respectiv un obiect de tip ''​Bundle'' ​utilizat ​pentru ​restaurarea stării în cazul în care s-a produs un eveniment de tip întrerupere.+=== Înlocuirea unui Fragment === 
 + 
 +Pentru ​înlocuirea unui fragment din cadrul unei activități,​ trebuie să se precizeze, ca parametrii ai metodei ''​replace()''​ din clasa ''​FragmentTransaction''​ identificatorul containerului în care va fi plasat fragmentul, obiectul de tip fragment și (opțional) o etichetă (de tip șir de caractere) prin intermediul căreia acesta poate fi ulterior referit. 
 + 
 +<code java> 
 +FragmentManager fragmentManager = getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
 +Fragment fragment2 = new Fragment2();​ 
 +fragmentTransaction.replace(R.id.frame1,​ fragment2, Constants.FRAGMENT2_TAG);​ 
 +fragmentTransaction.commit();​ 
 +</​code>​ 
 + 
 +=== Utilizarea Stivei de Fragmente === 
 + 
 +Prin intermediul fragmentelor,​ se oferă posibilitatea de a se crea interfețe grafice asociate activităților în mod dinamic. Unele modificări între diferite stări pot fi considerate de utilizatori ca fiind ecrane noi, comportamentul ​așteptat fiind acela de restaurare a acestora atunci când se accesează butonul //Back//. Un astfel de comportament presupune realizarea unei tranzacții de fragmente în sens invers. Această funcționalitate poate fi obținută prin utilizarea unei stive de fragmente, în care o tranzacție poate fi identificată prin intermediul unei etichete. 
 + 
 +În acest sens, va trebui apelată explicit metoda ''​addToBackStack()''​ pentru obiectul ​de tip ''​FragmentTransaction'' ​care se ocupă de atașarea și detașarea fragmentelor de la activitate, înainte de a se apela metoda ''​commit()''​. 
 + 
 +<code java> 
 +FragmentManager fragmentManager = getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
 +fragmentTransaction.add(R.id.frame1,​ new Fragment1(),​ Constants.FRAGMENT1_TAG);​ 
 +Fragment fragment2 = fragmentManager.findFragmentByTag(Constants.FRAGMENT2_TAG);​ 
 +fragmentTransaction.remove(fragment2);​ 
 +fragmentTransaction.addToBackStack(null);​ 
 +fragmentTransaction.commit();​ 
 +</​code>​ 
 + 
 +Astfel, atunci când se apelează ​metoda ''​commit()''​, fragmentul 2 este oprit și salvat pe stiva de fragmente (în loc să fie distrus). Când utilizatorul accesează butonul //Back// fragmentul 1 este distrus și fragemtnul 2 este restaurat de pe stivă și (re)pornit. 
 + 
 +=== Folosirea de Animații la Tranziția dintre Fragmente === 
 + 
 +Tranzițiile între diferitele stări ale activității care implică operații cu fragmente pot fi realizate:​ 
 +  - folosind metoda ''​setTransition()''​ a unui obiect de tip ''​FragmentTransaction''​, utilizând oricare dintre variantele predefinite,​ de tipul ''​FragmentTransaction.TRANSIT_FRAGMENT_...'' ​<code java> 
 +fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_...);​ 
 +</​code>​ 
 +  - folosind metoda ​''​setCustomAnimation()''​ a unui obiect de tip ''​FragmentTransaction''​, pentru ​utilizarea de animații definite de utilizator; metoda primește două argumente, indicând referințele către fișiere XML care descriu animațiile folosite pentru fragmentele care sunt adăugate, respectiv pentru fragmentele care sunt scoase <code java> 
 +fragmentTransaction.setCustomAnimations(R.animator.fade_in,​ R.animator.fade_out);​ 
 +</​code>​
  
 ==== Interacțiunea dintre fragmente ==== ==== Interacțiunea dintre fragmente ====
  
-Elementele grafice dintr-un fragment al unei activități pot fi accesate și din al fragment al activității,​ prin obținerea unei referințe către activitatea ​respectivă (metoda ''​getActivity()''​) și accesarea controlului respectiv prin intermediul identificatorului său din cadrul fragmentului (folosind metoda ''​findViewById()''​).+Elementele grafice dintr-un fragment al unei activități pot fi accesate și din alt fragment al activității,​ prin obținerea unei referințe către activitatea ​părinte (folosind ​metoda ''​getActivity()''​) și accesarea controlului respectiv prin intermediul identificatorului său din cadrul fragmentului (folosind metoda ''​findViewById()''​). 
 + 
 +O astfel de abordare (folosind obiectul de tip ''​Activity''​ ca intermediar al comunicației) este recomandată pentru a asigura cuplarea slabă și autonomia fragmentelor. Altfel, este permisă comunicația directă prin intermediul obiectului de tip ''​FragmentManager''​ asociat activității. 
 + 
 +În cazul evenimentelor produse la nivelul unui fragment, decizia cu privire la impactul pe care acesta îl are asupra interfeței grafice trebuie să aparțină activității. Astfel, fragmentul va invoca o metodă de callback la nivelul activității care va determina modul în care va fi tratat evenimentul. 
 + 
 +<code java> 
 +public interface OnEventProducedListener { 
 +  public void onEventProduced(Event event); 
 +}; 
 +</​code>​ 
 + 
 +<code java> 
 +public class SomeFragment extends Fragment { 
 + 
 +  OnEventProducedListener onEventProducedListener;​ 
 + 
 +  @Override 
 +  public void onAttach(Activity activity) { 
 +    super.onAttach(activity);​ 
 +    try { 
 +      onEventProducedListener = (OnEventProducedListener)activity;​ 
 +    } catch (ClassCastException classCastException) { 
 +      Log.e(Constants.TAG,​ "​Parent activity does not implement OnEventProducedListener!"​);​ 
 +    } 
 +  } 
 +   
 +  private void onEventProduced(Event event) { 
 +    onEventProducedListener.onEventProduced(event);​ 
 +  } 
 +} 
 +</​code>​ 
 + 
 +<code java> 
 +public class SomeActivity extends Activity implements OnEventProducedListener { 
 +  @Override 
 +  protected void onCreate(Bundle savedStateInstance) { 
 +    super.onCreate(savedInstanceState);​ 
 +    FragmentManager framentManager = new FragmentManager();​ 
 +    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
 +    fragmentTransaction.add(R.id.frame,​ new SomeFragment(),​ Constants.SOME_FRAGMENT_TAG);​ 
 +    fragmentTransaction.commit();​ 
 +    // ... 
 +  } 
 +   
 +  @Override 
 +  public void onEventProduced(Event event) { 
 +    // ... 
 +  } 
 +} 
 +</​code>​
  
 ===== Servicii ===== ===== Servicii =====
  
-TODO+În Android, clasa ''​Service''​ este utilizată pentru componente a căror funcționalitate implică procesări complexe, de lungă durată, necesitând anumite resurse, fără a fi necesar să pună la dispoziție o interfață grafică sau un mecanism de interacțiune cu utilizatorul. Prin intermediul unui serviciu, se asigură faptul că aplicația Android continuă să se găsească în execuție, chiar și atunci când interfața grafică a acesteia nu este vizibilă. 
 + 
 +<note tip>​Întrucât prioritatea unui serviciu este mai mare decât a unei activități inactive, este mai puțin probabil ca acestea să fie distruse atunci când trebuie eliberate resursele sistemului de operare. De altfel, un serviciu poate fi configurat să fie repornit imediat ce este posibil sau chiar pentru a i se asocia o prioritate echivalentă cu a unei activități active (dacă distrugerea serviciului are un impact la nivelul interfeței grafice).</​note>​ 
 + 
 +Astfel, un serviciu nu trece prin evenimentele ce fac parte din ciclul de viață al unei activități. Totuși, un serviciu poate fi controlat (pornit, oprit) din contextul altor componente ale unei aplicații Android (activități,​ ascultători de intenții cu difuzare, alte servicii). 
 + 
 +Un serviciu este de regulă rulat pe firul de execuție principal al aplicației Android. De aceea, în cazul în care operațiile pe care le realizează influențează experiența utilizatorului,​ acestea trebuie transferate pe alte fire de execuție din fundal (folosind clasele ''​Thread''​ și ''​AsyncTask''​). 
 + 
 +==== Gestiunea unui Serviciu ==== 
 + 
 +Un serviciu este o clasă derivată din ''​android.app.Service'',​ implementând metodele: 
 +  * ''​onCreate()''​ - realizând operațiile asociate construirii serviciului respectiv;​ 
 +  * ''​onBind()''​ - asigurând asocierea serviciului la o altă componentă a aplicației Android; metoda primește un parametru de tip ''​Intent'',​ reprezentând intenția prin intermediul căruia a fost lansat în execuție. 
 + 
 +<file java SomeService.java>​ 
 +import android.app.Service;​ 
 +import android.content.Intent;​ 
 +import android.os.IBinder;​ 
 + 
 +public class SomeService extends Service { 
 +  @Override 
 +  public void onCreate() { 
 +    super.onCreate();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public IBinder onBind(Intent intent) { 
 +    // ... 
 +    return null; 
 +  } 
 +} 
 +</​file>​ 
 + 
 +Orice serviciu trebuie să fie înregistrat în cadrul fișierului ''​AndroidManifest.xml'',​ prin intermediul etichetei ''<​service>''​ în cadrul elementului ''<​application>''​. Eventual, se poate indica o permisiune necesară pentru pornirea și oprirea serviciului,​ astfel încât aceste operații să poată fi realizate numai de anumite aplicații Android. 
 + 
 +<file xml AndroidManifest.xml>​ 
 +<​manifest ...> 
 +  <​application ...> 
 +    <​service 
 +      android:​name="​ro.pub.cs.systems.pdsd.lab04.SomeService"​ 
 +      android:​enabled="​true"​ 
 +      android:​permission="​ro.pub.cs.systems.pdsd.lab04.SOME_SERVICE_PERMISSION"​ /> 
 +  </​application>​ 
 +</​manifest>​ 
 +</​file>​ 
 + 
 +În momentul în care este pornit (printr-un apel al metodei ''​startService()''​ din cadrul altei componente),​ un serviciu apelează în mod automat metoda ''​onStartCommand()''​. În cadrul acestei metode trebuie realizată procesarea pe care o presupune serviciul respectiv. Având în vedere faptul că această metodă poate fi apelată de mai multe ori pe parcursul ciclului de viață al unui serviciu, tot aici trebuie implementa și comportamentul în cazul în care acesta este repornit. Metoda primește ca parametrii:​ 
 +  * intenția care a invocat serviciul;​ 
 +  * anumite valori prin care poate fi semnalat modul în care a fost pornit: 
 +    * ''​START_FLAG_REDELIVERY''​ - serviciul a fost repornit ca urmare a distrugerii sale de către sistemul de operare înainte de se fi terminat corespunzător;​ 
 +    * ''​START_FLAG_RETRY''​ - serviciul a fost repornit după o execuție anormală;​ 
 +  * un identificator unic prin care se poate face distincția între apeluri diferite ale aceluiași serviciu. 
 + 
 +<code java> 
 +@Override 
 +public int onStartCommand(Intent intent, int flags, int startId) { 
 +  processInBackground(intent,​ startId); 
 +  return Service.START_STICKY;​ 
 +} 
 +</​code>​ 
 + 
 +Comportamentul serviciului în situația în care este repornit poate fi controlată prin intermediul valorii întregi care este furnizată ca rezultat al metodei ''​onStartCommand()'':​ 
 +  * ''​Service.START_STICKY''​ - mecanism standard, folosit de serviciile care își gestionează propriile stări și care sunt pornite și oprite în funcție de necesități (prin intermediul metodelor ''​startService()''​ și ''​stopService()''​);​ prin aceasta, se indică faptul că metoda ''​onStartCommand()''​ va fi invocată de fiecare dată când serviciul este (re)pornit după ce a fost distrus de sistemul de operare Android (situație în care parametrul de tip ''​Intent''​ va avea valoarea ''​null''​);​ 
 +  * ''​Service.START_NOT_STICKY''​ - mecanism utilizat de serviciile utilizate pentru a procesa anumite comenzi, care se opresc singure (printr-un apel al metodei ''​stopSelf()''​) atunci când operațiile pe care trebuiau să le realizeze s-au terminat; serviciul este (re)pornit după ce a fost distrus de sistemul de operare Android numai în situația în care între timp au mai fost realizate apeluri ale metodei ''​startService()'';​ un astfel de comportament este adecvat pentru procese care sunt realizate periodic; 
 +  * ''​Service.START_REDELIVER_INTENT''​ - mecanism utilizat atunci când se dorește să se asigure faptul că procesările asociate serviciului au fost terminate; în situația în care serviciul a fost distrus de sistemul de operare Android, este (re)pornit numai în situația în care între timp au fost realizate apeluri ale metodei ''​startService()''​ sau procesul a fost oprit ca acesta să invoce metoda ''​stopSelf()''​ - metoda ''​onStartCommand()''​ va fi apelată folosind ca parametru intenția originală, a cărei procesări nu a fost terminată corespunzător. 
 + 
 +În toate aceste cazuri, oprirea serviciului trebuie realizată explicit, prin apelul metodelor:​ 
 +  * ''​stopService()''​ din contextul componentei care l-a pornit; 
 +  * ''​stopSelf()''​ din contextul serviciului. 
 + 
 +=== Pornirea unui Serviciu === 
 + 
 +Un serviciu este pornit printr-un apel al metodei ''​startService()''​. Aceasta primește ca parametru un obiect de tip ''​Intent''​ care poate fi creat: 
 +  * explicit, pe baza denumirii clasei care implementează serviciul respectiv; <code java> 
 +Intent intent = new Intent(this,​ SomeService.class);​ 
 +startService(intent);​ 
 +</​code>​ 
 +  * implicit, indicând o acțiune înregistrată ca fiind tratată de serviciul respectiv. <code java> 
 +Intent intent = new Intent(SomeService.SOME_SERVICE);​ 
 +startService(intent);​ 
 +</​code>​ 
 + 
 +Transmiterea de informații suplimentare către serviciu poate fi realizată prin intermediul metodelor ''​putExtras(Bundle)'',​ ''​putExtra(String,​ Parcelable)''​ sau ''​put<​type>​Extra(String,​ <​type>​)''​. 
 + 
 +=== Oprirea unui Serviciu === 
 + 
 +Un serviciu poate fi oprit: 
 +  * de componenta care l-a pornit, printr-un apel al metodei ''​stopService()'',​ ce primește ca parametru un obiect de tip ''​Intent''​ care poate fi creat explicit sau implicit; 
 + 
 +<note important>​Întrucât apelurile metodei ''​startService()''​ nu sunt imbricate, invocarea metodei ''​stopService()''​ oprește numai serviciul corespunzător (dacă se află în execuție).</​note>​ 
 + 
 +  * chiar de el însuși, în momentul în care procesările pe care trebuie să le realizeze s-au terminat, printr-un apel al metodei ''​stopSelf()'',​ eliberând resursele pe care sistemul de operare le-ar fi folosit pentru a-l menține în execuție; metoda poate fi apelată: 
 +    * fără parametri, pentru a forța oprirea imediată;​ 
 +    * transmițând un parametru de tip întreg, reprezentând identificatorul instanței care rulează, pentru a se asigura faptul că procesarea a fost realizată pentru fiecare apel care a fost realizat. 
 + 
 +==== Asocierea unui Serviciu la o Activitate ==== 
 + 
 +Un serviciu poate fi atașat la o activitate, astfel încât activitatea menține o referință către serviciu, prin intermediul căreia poate invoca metode așa cum ar face cu orice alt obiect. Un astfel de comportament este util pentru activitățile care necesită interfațarea cu un serviciu. 
 + 
 +Acestă funcționalitate este realizată prin intermediul metodei ''​onBind()''​ care furnizează o referință către serviciu sub forma unui obiect de tipul ''​IBinder''​. 
 + 
 +<file java SomeService.java>​ 
 +import android.app.Service;​ 
 +import android.content.Intent;​ 
 +import android.os.IBinder;​ 
 + 
 +public class SomeService extends Service { 
 + 
 +  final private IBinder binder = new SomeBinder();​ 
 + 
 +  public class SomeBinder extends Binder { 
 +    SomeService getService() { 
 +      return SomeService.this;​ 
 +    } 
 +  } 
 + 
 +  @Override 
 +  public void onCreate() { 
 +    super.onCreate();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  public IBinder onBind(Intent intent) { 
 +    return binder; 
 +  } 
 +} 
 +</​file>​ 
 + 
 +Legătura dintre serviciu și activitate este reprezentată sub forma unui obiect de tipul ''​ServiceConnection'',​ a cărui implementare presupune definirea metodelor ''​onServiceConnected()'',​ respectiv ''​onServiceDisconnected()''​. Asocierea propriu-zisă dintre serviciu și activitate este realizată de metoda ''​bindService()''​ care primește ca parametri:​ 
 +  * intenția (creată explicit sau implicit) reprezentând serviciul care se asociază activității;​ 
 +  * un obiect de tip ''​ServiceConnection'';​ 
 +  * anumite valori prin care se controlează modul în care este făcută asocierea între cele două componente ale aplicației Android: 
 +    * ''​Context.BIND_AUTO_CREATE''​ - serviciul trebuie creat în momentul în care se realizează asocierea;​ 
 +    * ''​Context.BIND_ADJUST_WITH_ACTIVITY''​ - modifică prioritatea serviciului în funcție de importanța activității de care este legat (mai mare atunci când este activă, mai mică atunci când este inactivă);​ 
 +    * ''​Context.BIND_ABOVE_CLIENT''​ / ''​Context.BIND_IMPORTANT''​ - specifică faptul că prioritatea serviciului este mai mare decât a activității asociate; 
 +    * ''​Context.BIND_NOT_FOREGROUND''​ - asigură faptul că serviciul atașat activității nu va avea niciodată prioritatea necesară pentru a rula pe firul de execuție principal;​ 
 +    * ''​Context.BIND_WAIVE_PRIORITY''​ - face ca prioritatea serviciului asociat să nu fie modificată (implicit, prioritatea relativă a serviciului este mărită). 
 + 
 +<code java> 
 +private SomeService someServiceReferrence;​ 
 + 
 +private ServiceConnection serviceConnection = new ServiceConnection() { 
 +  @Override 
 +  public void onServiceConnected(ComponentName className, IBinder service) { 
 +    serviceConnection = ((SomeService.SomeBinder)service).getService();​ 
 +  } 
 +   
 +  @Override 
 +  public void onServiceDisconnected(ComponentName className) { 
 +    serviceConnection = null; 
 +  } 
 +}; 
 + 
 +Intent intent = new Intent(this,​ SomeService.class);​ 
 +bindService(intent,​ serviceConnection,​ Context.BIND_AUTO_CREATE);​ 
 +</​code>​ 
 + 
 +După ce serviciul a fost atașat activității,​ toate metodele și atributele sale publice sunt disponibile prin intermediul parametrului de tip ''​IBinder''​ din cadrul metodei ''​onServiceConnected()''​. 
 + 
 +==== Categorii de Procesări ==== 
 + 
 +În funcție de firul de execuție pe care rulează precum și de prioritatea care le este atribuită (în funcție de care sistemul de operare Android poate reclama resursele pe care acestea le utilizează),​ operațiile complexe pot fi realizate prin intermediul unor: 
 +  * servicii care rulează în prim-plan;​ 
 +  * procesări realizate în fundal. 
 + 
 +=== Servicii ce Rulează în Prim-Plan === 
 + 
 +În situația în care un serviciu interacționează cu utilizatorul,​ prioritatea acestuia trebuie mărită, astfel încât sistemul de operare Android să nu poată reclama resursele pe care le utilizează.  
 + 
 +Un astfel de comportament poate fi obținut prin marcarea serviciului ca rulând în prim-plan, prin invocarea metodei ''​startForeground()''​ care primește ca parametri:​ 
 +  * identificatorul unei notificări;​ 
 +  * un obiect de tip ''​Notification'',​ care va fi afișată cât timp serviciul se află în prim-plan, întrucât se presupune că serviciul trebuie să aibă o reprezentare vizuală cu care utilizatorul să poată interacționa. 
 + 
 +Este recomandat ca utilizatorii să poată dezactiva ei înșiși rularea serviciului în prim-plan, de regulă prin intermediul obiectului de tip ''​Notification''​. Același lucru trebuie realizat și în momentul în care rularea serviciului în prim-plan nu mai este necesară. În acest sens, trebuie apelată metoda ''​stopForeground(true)'',​ aceasta anulând în mod automat notificarea asociată. 
 + 
 +<note tip>Nu este recomandat să existe mai multe servicii care rulează în prim-plan simultan întrucât acestea nu pot fi distruse de sistemul de operare Android și în situația în care necesarul de memorie devine o problemă critică, performanțele dispozitivului mobil se vor deprecia considerabil.</​note>​ 
 + 
 +=== Procesări Realizate în Fundal === 
 + 
 +Întrucât responsivitatea aplicației Android reprezintă un criteriu foarte important, asigurarea unei experiențe corespunzătoare a utilizatorului poate fi obținută prin transferul operațiilor complexe de procesare și a operațiilor de intrare/​ieșire în fundal, pe un alt fir de execuție. Acest lucru este necesar datorită faptului că pe firul de execuție principal sunt rulate mai multe componente ale aplicației Android (activități,​ servicii, ascultători ai intențiilor cu difuzare), astfel încât interacțiunea cu utilizatorul poate fi blocată în situația în care procesarea realizată de servicii nu este transferată în fundal. 
 + 
 +<​note>​În Android, o activitate care nu răsunde la un eveniment în decurs de 5 secunde sau un ascultător al unei intenții cu difuzare a cărei metodă ''​onReceive()''​ nu se termină în 5 secunde sunt considerate blocate.</​note>​ 
 + 
 +De regulă, sunt plasate pe fire de execuție dedicate operații cu fișiere (scrieri, citiri), căutări în rețea, tranzacții cu baza de date, calcule complexe. 
 + 
 +Android oferă mai multe mecanisme pentru gestiunea serviciilor care rulează în fundal: 
 +  - utilizarea clasei ''​AsyncTask''​ permite definirea unei operații care va fi realizată pe un fir de execuție separat, oferind metode care oferă informații cu privire la progres, ce pot fi consultate de alte fire de execuție;​ 
 +  - folosirea clasei ''​IntentService'';​ 
 +  - definirea unei clase derivate din ''​Loader'';​ 
 +  - implementarea unui fir de execuție definit de utilizator, transmiterea valorilor către interfața grafică fiind realizat prin intermediul clasei ''​Handler''​. 
 + 
 +== Clasa AsyncTask == 
 + 
 +Prin intermediul clasei ''​AsyncTask''​ se permite mutarea operațiilor costisitoare din punct de vedere al utilizării procesorului și al memoriei pe fire de execuție rulate în fundal, oferind sincronizarea cu firul de execuție ce randează interfața grafică, acesta fiind notificat cu privire la progresul realizat cât și cu privire la momentul în care procesarea a fost terminată. 
 + 
 +<note tip>​Întrucât clasa ''​AsyncTask''​ nu este persistentă în cazul (re)pornirii unei activități (situație în care firul de execuție asociat este distrus), se recomandă utilizarea sa pentru procese de fundal care nu durează o perioadă de timp prea mare.</​note>​ 
 + 
 +O implementare a clasei ''​AsyncTask''​ poate fi parametrizată cu tipurile de date care sunt folosite pentru intrare, pentru raportarea progresului și pentru ieșire (rezultate). 
 + 
 +<note tip>În situația în care nu se dorește transmiterea unor parametrii de intrare / ieșire sau nu este necesară raportarea progresului,​ se poate utiliza valoarea ''​Void''​ pentru toate aceste tipuri.</​note>​ 
 + 
 +Este necesară suprascrierea următoarelor metode: 
 +  * ''​doInBackground()''​ - metoda care va fi executată pe firul de execuție dedicat, care primește parametrii de intrare de tipul specificat; acesta trebuie să conțină procesările complexe care se doresc a fi realizate fără a interacționa însă cu firul de execuție principal; notificările sunt realizate:​ 
 +    * prin intermediul metodei ''​publishProgress()'',​ care primește ca parametru progresul realizat, valorile fiind transmise metodei ''​onProgressUpdate()'';​ 
 +    * prin întoarcerea unui rezultat, atunci când procesarea este terminată, valoarea fiind transmisă metodei ''​onPostExecute()'';​ 
 +  * ''​onProgressUpdate()''​ - folosită pentru actualizarea interfeței grafice, cu valorile primite de la metoda ''​publishProgress()'';​ metoda este sincronizată cu firul de execuție principal (care randează interfața grafică), astfel încât diferitele controale pot fi accesate de aici; 
 +  * ''​onPostExecute()''​ - apelată în momentul în care procesarea este terminată, primind ca parametru rezultatul întors de metoda ''​doInBackground()'';​ metoda este sincronizată cu firul de execuție principal (care randează interfața grafică), astfel încât diferitele controale pot fi accesate de aici. 
 + 
 +<code java> 
 +private class SomeAsyncTask extends AsyncTask<​String,​ Integer, String> { 
 +  @Override 
 +  protected String doInBackground(String... parameter) { 
 +    String result = new String(); 
 +    int progress = 0; 
 +    for (int k = 1; k <= parameter[0].length();​ k++) { 
 +      progress = k; 
 +      result += parameter[0].charAt(parameter[0].length() - k); 
 +      try { 
 +        Thread.sleep(100);​ 
 +      } catch (InterruptedException interruptedException) { 
 +        Log.e(Constants.TAG,​ "An exception has occurred: "​+interruptedException.getMessage());​ 
 +      } 
 +      publishProgress(myProgress);​ 
 +    } 
 +    return result; 
 +  } 
 + 
 +  @Override 
 +  protected void onProgressUpdate(Integer... progress) { 
 +    asyncTextView.setText(progress[0].toString());​ 
 +  } 
 + 
 +  @Override 
 +  protected void onPostExecute(String result) { 
 +    asyncTextView.setText(result);​ 
 +  } 
 +} 
 +</​code>​ 
 + 
 +Rularea unui obiect de tip ''​AsyncTask''​ se face prin invocarea metodei ''​execute()''​ care primește ca parametru informațiile care se doresc a fi procesate. 
 + 
 +<note important>​Fiecare obiect de tip ''​AsyncTask''​ poate fi rulat o singură dată. Apelurile ulterioare ale metodei ''​execute()''​ vor genera excepții. </​note>​ 
 + 
 +== Clasa IntentService == 
 + 
 +Recursul la clasa ''​IntentService''​ este adecvată în situația în care se dorește implementarea unor servicii care rulează în fundal, realizând un set de operații la un moment dat de timp, atunci când sunt solicitate. 
 + 
 +Un obiect de acest tip este lansat în execuție prin pornirea unui serviciu și transmiterea unui obiect de tip ''​Intent''​ care conține toți parametrii necesari pentru realizarea sarcinii respective. Toate operațiile solicitate sunt înregistrate și executate succesiv. După ce procesarea a fost finalizată,​ procesul se oprește singur. 
 + 
 +O implementare este o clasă derivată din ''​IntentService'',​ definind metoda ''​onHandleIntent()''​ ce primește ca parametru intenția conținând parametrii ce se doresc a fi procesați, execuția sa fiind realizată pe un fir de execuție ce rulează în fundal (câte unul pentru fiecare invocare). 
 + 
 +<code java> 
 +import android.app.IntentService;​ 
 +import android.content.Intent;​ 
 + 
 +public class SomeIntentService extends IntentService { 
 +  public SomeIntentService(String name) { 
 +    super(name);​ 
 +    // ... 
 +  } 
 +   
 +  @Override 
 +  public void onCreate() { 
 +    super.onCreate();​ 
 +    // ... 
 +  } 
 + 
 +  @Override 
 +  protected void onHandleIntent(Intent intent) { 
 +    // ... 
 +  } 
 +} 
 +</​code>​ 
 + 
 +== Clasa Abstractă Loader == 
 + 
 +Clasa abstractă ''​Loader''​ implementează practicile recomandate pentru încărcarea asincronă a informațiilor în cadrul unor controale grafice din interfața cu utilizatorul (afișate în activități sau fragmente). 
 + 
 +O astfel de abordare este utilizată pentru: 
 +  * a încărca date asincron; 
 +  * a monitoriza sursele din care sunt încărcate datele, oferind informații cu privire la rezultatele obținute. 
 + 
 +De regulă, se folosește o implementare a clasei ''​AsyncTaskLoader''​. 
 + 
 +== Utilizarea firelor de execuție definite de utilizator == 
 + 
 +Folosirea unor fire de execuție definite de utilizator și sincronizarea manuală cu interfața grafică poate fi necesară în situația în care trebuie realizată o gestiune mai complexă decât cea oferită de clasele ''​AsyncTask'',​ ''​IntentService''​ sau ''​Loader''​. 
 + 
 +În acest sens, este folosită o implementare a clasei ''​Thread'',​ procesarea pe un fir de execuție separat fiind realizată în cadrul metodei ''​run()''​. 
 + 
 +<code java> 
 +private void executeOnSeparateThread() { 
 +  Thread separateThread = new Thread(new Runnable() { 
 +    @Override 
 +    public void run() { 
 +      // ... 
 +    } 
 +  }); 
 +  separateThread.start();​ 
 +} 
 +</​code>​ 
 + 
 +Dacă se dorește actualizarea controalelor din cadrul interfeței grafice, este necesar ca firele de execuție care rulează în fundal să fie sincronizate cu firul de execuție principal anterior acestei operații. Acest lucru poate fi realizat: 
 +  - folosind metoda ''​runOnUiThread()''​ care forțează codul transmis să fie executat pe același fir de execuție care redă interfața grafică: <code java> 
 +runOnUiThread(new Runnable() { 
 +  @Override 
 +  public void run() { 
 +    // ... 
 +  } 
 +}); 
 +</​code>​ 
 +  - utilizând un obiect de tip ''​Handler''​ pentru a realiza actualizări în contextul firului de execuție în care acesta a fost creat <code java> 
 +private Handler handler = new Handler(); // created on the main thread 
 + 
 +private void executeOnSeparateThread() { 
 +  Thread separateThread = new Thread(new Runnable() { 
 +    @Override 
 +    public void run() { 
 +      // do some background processing here 
 +      handler.post(new Runnable() { 
 +        @Override 
 +        public void run() { 
 +          // access the graphical user interface here 
 +        } 
 +      }); 
 +    } 
 +  }); 
 +  separateThread.start();​ 
 +} 
 +</​code>​ Clasa ''​Handler''​ pune la dispoziție și metode pentru a executa anumite metode la un moment dat de timp: 
 +  * ''​postDelayed()''​ - realizează o modificare la nivelul interfeței grafice cu o întârziere specificată ca parametru (exprimat în milisecunde);​ 
 +  * ''​postAtTime()''​ - realizează o modificare la nivelul interfeței grafice la un moment de timp specificat ca parametru (exprimat ca număr de milisecunde ce au trecut de la 1 ianuarie 1970. 
 + 
 +==== Realizarea de Procesări prin Intermediul Alarmelor ==== 
 + 
 +O alarmă reprezintă un mecanism de transmitere a unor intenții la momente predefinite de timp sau periodic, după trecerea unui anumit interval. Acestea există în afara domeniului de existență a unei aplicații Android, astfel încât acestea pot fi utilizate pentru a realiza anumite acțiuni chiar și în situația în care acestea nu mai există. Din acest motiv, alarmele reprezintă o metodă foarte utilă pentru a difuza intenții, pentru a porni servicii sau lansa în execuție activități,​ fără a fi necesar ca aplicația să se afle în execuție. Se asigură astfel și optimizarea cerințelor legate de resursele utilizate de aplicație. 
 + 
 +Cele mai frecvente utilizări ale alarmelor sunt legate de planificarea unor actualizări bazate pe căutări în rețea, programarea unor operații (consumatoare de resurse) la momente de timp în care solicitările sunt mai reduse, organizarea unor noi încercări pentru operații care nu au putut fi realizate anterior. 
 + 
 +<note tip>​Evenimentele care pot fi produse doar pe parcursul ciclului de viață al unei aplicații Android trebuie tratate prin fire de execuție separate implementate de utilizator și sincronizate (manual) cu interfața grafică. Utilizarea de alarme trebuie limitată doar la evenimente programate în afara ferestrei temporale în care se desfășoară aplicația.</​note>​ 
 + 
 +<​note>​În Android, alarmele rămân active chiar și atunci când dispozitivul mobil se găsește într-o stare de latență (//eng.// sleep mode), putând fi utilizate pentru a-l scoate din aceasta. Totuși, ele devin inactive în momentul în care dispozitivul mobil este repornit.</​note>​ 
 + 
 +Operațiile cu alarme sunt realizate prin intermediul serviciului de sistem ''​AlarmManager''​ care poate fi accesat prin intermediul metodei ''​getSystemService()''​ care primește ca parametru argumentul ''​Context.ALARM_SERVICE''​. 
 + 
 +Există mai multe tipuri de alarme: 
 +  * ''​RTC_WAKEUP''​ - scoate dispozitivul mobil din starea de latență prin transmiterea unei intenții în așteptare, la un anumit moment de timp specificat;​ 
 +  * ''​RTC''​ - transmite o intenție în așteptare, la un anumit moment de timp specificat, fără a scoate dispozitivul mobil din starea de latență;​ 
 +  * ''​ELAPSED_REALTIME''​ - transmite o intenție în așteptare, la un anumit interval de timp scurs de la momentul în care dispozitivul mobil a fost pornit, fără a-l scoate din starea de latență;​ 
 +  * ''​ELAPSED_REALTIME_WAKEUP''​ - scoate dispozitivul mobil din starea de latență prin transmiterea unei intenții în așteptare, la un anumit interval de timp scurs de la momentul în care acesta a fost pornit. 
 + 
 +În funcție de aceste valori, parametrul furnizat metodei ''​set()''​ a obiectului de tip ''​AlarmManager''​ reprezintă un moment de timp sau un interval. 
 + 
 +În momentul în care se declanșează,​ obiectul de tip ''​PendingIntent''​ este distribuit la nivelul tuturor componentelor sistemului de operare. 
 + 
 +<note tip>​Specificarea unei alte alarme folosind același obiect de tip ''​PendingIntent''​ o înlocuiește pe cea existentă.</​note>​ 
 + 
 +<code java> 
 +AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);​ 
 +alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,​ 1000, PendingIntent.getBroadcast(this,​ 0, new Intent("​ALARM_ACTION"​),​ 0)); 
 +</​code>​ 
 + 
 +Anularea unei alarme se face prin intermediul metodei ''​cancel()''​ a obiectului de tip ''​AlarmManager''​ care primește ca parametru intenția în așteptare care nu mai trebuie transmisă. 
 + 
 +<code java> 
 +alarmManager.cancel(PendingIntent.getBroadcast(this,​ 0, new Intent("​ALARM_ACTION"​),​ 0)); 
 +</​code>​ 
 + 
 +Utilizatorul are și posibilitatea de a indica anumite intervale de timp la care alarma este repetată, prin intermediul unor metode definite în clasa ''​AlarmManager'':​ 
 +  * ''​setRepeating()''​ - utilizată când se dorește un control foarte exact asupra intervalului de timp la care alarma este repetată, exprimat la nivel de milisecunde;​ 
 +  * ''​setInexactRepeating()''​ - folosit pentru a economisi consumul de baterie realizat la scoaterea dispozitivului mobil din starea de latență de fiecare dată când este necesară realizarea unor sarcini planificate (care nu se suprapun); astfel, sistemul de operare Android va sincroniza mai multe alarme cu modul de repetare inexact, declanșându-le simultan; metoda primește ca parametru una dintre constantele:​ 
 +    * ''​INTERVAL_FIFTEEN_MINUTES''​ 
 +    * ''​INTERVAL_HALF_HOUR''​ 
 +    * ''​INTERVAL_HOUR''​ 
 +    * ''​INTERVAL_HALF_DAY''​ 
 +    * ''​INTERVAL_DAY''​ 
 +Ambele metode primesc ca parametrii tipul de alarmă, un moment de timp la care alarma va fi declanșată inițial și o intenție în așteptare care va fi transmisă. 
 + 
 +<code java> 
 +AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);​ 
 +alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,​ AlarmManager.INTERVAL_FIFTEEN_MINUTES,​ AlarmManager.INTERVAL_DAY,​ PendingIntent.getBroadcast(this,​ 0, new Intent("​ALARM_ACTION"​),​ 0)); 
 +</​code>​ 
 + 
 +<note important>​Impactul pe care alarmele recurente îl au asupra consumului de baterie poate fi semnificativ. De aceea, este recomandat ca frecvența de repetare a alarmei să fie cât mai mică, scoțând dispozitivul mobil din starea de latență numai atunci când este necesar și utilizând tipul de repetare inexact, dacă este posibil.</​note>​ 
 + 
 +Anularea unei alarme recurente se face tot prin intermediul metodei ''​cancel()''​ a obiectului de tip ''​AlarmManager''​ care primește ca parametru intenția în așteptare care nu mai trebuie transmisă.
  
 ===== Activitate de Laborator ===== ===== Activitate de Laborator =====
  
-==== Intenții ==== +Se dorește implementarea unei aplicații Android, conținând ​o activitate conținând două fragmente, ​care să ofere utilizatorilor funcționalitatea necesară pentru a stoca un număr de telefon ​în agenda ​de contacte, specificând pentru acesta mai multe informații.
-  - Să se creeze încă ​o fereastră (activitate) pe care să se afișeze o imagine. Aceasta trebuie să apară pe ecran în momentul în care se apasă butonul //​Fereastră Nouă//. +
-  - Să se adauge la fereastra creată anterior un buton de revenire. +
-  - Să se afișeze fereastra creată anterior și atunci când se apasă butonul //Inserați Fereastra//​. Să se afișeze sub imagine, într-un câmp text, care a fost butonul ce a determinat invocarea ferestrei. +
-  - Să se adauge la fereastra creată anterior un buton prin intermediul căruia să se schimbe imaginea existentă cu o alta.+
  
-==== Fragmente ==== +{{ :​laboratoare:​laborator04:​activitate_de_laborator01.png?​nolink&​300 }} 
-  ​- ​Să se plaseze ​conținutul ​celor două ferestre create anterior ​în două fragmente ​aparținând aceleiași activități, afișându-se simultan ​pe ecran. + 
-  ​- Să se adauge ​un două butoane ​în cadrul primului ​fragment ​pentru ​atașarea, respectiv ​detașarea ​celui de-al doilea fragment ​de la activitate.+{{ :​laboratoare:​laborator04:​activitate_de_laborator02.png?​nolink&​300 }} 
 + 
 +**1.** În contul Github personal, să se creeze un depozit denumit '​Laborator04'​. Acesta trebuie să conțină unui fișier ''​README.md'',​ un fișier ''​.gitignore''​ specific unei aplicații Android și un fișier ''​LICENSE''​ care să descrie condițiile pentru utilizarea aplicației. 
 + 
 +**2.** ​Să se cloneze într-un director de pe discul local conținutul ​depozitului la distanță astfel creat. În urma acestei operații, directorul Laborator04 va trebui să se conțină fișierele ''​README.md'',​ ''​.gitignore''​ care indică tipurile de fișiere (extensiile) ignorate și ''​LICENSE''​. <​code>​ 
 +student@pdsd2015:​~$ git clone https://​www.github.com/​perfectstudent/​Laborator04 
 +</​code>​ 
 + 
 +**3.** În directorul Laborator04 de pe discul local, să se creeze un proiect Eclipse denumit //​ContactsManager//​. 
 + 
 +Se utilizează următoarele detalii ale proiectului:​ 
 +  * **Application Name** - //Contacts Manager// 
 +  * **Project Name** - //​ContactsManager//​ 
 +  * **Package Name** - ''​ro.pub.cs.systems.pdsd.lab04.contactsmanager''​ 
 +  * **Minimum Required SDK** - API 16: Android 4.1 (Jelly Bean) 
 +  * **Target SDK** - API 16: Android 4.1 (Jelly Bean) 
 +  * **Compile With** - API 16: Android 4.1 (Jelly Bean) 
 +  * **Theme** - Holo Light with Dark Action Bar 
 + 
 +Ceilalți parametrii de configurare sunt impliciți:​ 
 +  * denumirea activității - ''​ContactsManagerActivity'';​ 
 +  * denumirea fișierului XML din ''​res/​layout''​ în care va fi construită interfața grafică - ''​activity_contacts_manager''​. 
 + 
 +**4.** În fișierul ''​activity_contacts_manager''​ din directorul ''​res/​layout''​ să se construiască interfața grafică folosind: 
 +  * editorul vizual (//​Graphical Layout//) 
 +  * editorul XML (manual) 
 + 
 +Acesta va fi format din două containere de tip ''​FrameLayout'' ​în care vor fi plasate fragmentele,​ dispuse vertical. 
 + 
 +**5.** Să se definească ​două fragmente: 
 +  * ''​BasicDetailsFragment''​ cu interfața grafică în fișierul ''​fragment_basic_details.xml''​ din directorul ''​res/​layout'';​ acesta va fi afișat în permanență în cadrul ​activității (pe metoda ''​onCreate()''​ a acesteia se adaugă o instanță a fragmentului la containerul ​ <code java> 
 +FragmentManager fragmentManager = getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
 +fragmentTransaction.add(R.id.containerTop, new BasicDetailsFragment());​ 
 +fragmentTransaction.commit();​ 
 +</​code>​ 
 +  * ''​AdditionalDetailsFragment''​ cu interfața grafică în fișierul ''​fragment_additional_details.xml''​ din directorul ''​res/​layout'';​ acesta va fi atașat / detașat la activitate la accesarea butonului //Show Additional Fields//, respectiv //Hide Additional Fields//. 
 + 
 +O clasă asociată unui fragment este derivată din ''​android.app.Fragment''​ și implementează metoda ''​onCreateView()'' ​pe care se încarcă interfața grafică corespunzătoare:​ 
 + 
 +<code java> 
 +public class SomeFragment extends Fragment { 
 +  @Override 
 +  public View onCreateView(LayoutInflater layoutInflater,​ ViewGroup container, Bundle state) { 
 +    return layoutInflater.inflate(R.layout.fragment_some,​ container, false); 
 +  ​} 
 +} 
 +</​code>​ 
 + 
 +**6**. ​Să se implementeaze intefețele grafice ale fragmentelor,​ conținând următoarele controale:​ 
 +  * ''​fragment_basic_details.xml''​ conține mai multe elemente dispuse vertical și ocupând pe lățime întregul spațiu avut la dispoziție:​ 
 +    * un buton (''​Button''​) având mesajul //Show Additional Fields// în cazul în care celălalt fragment nu este afișat, respectiv mesajul //Hide Additional Fields// ​în cazul în care celălalt ​fragment ​este afișat, determinând ​atașarea ​/ detașarea ​acestuia la activitate;​ 
 +    * patru controale ​de tip câmpuri text (''​EditText''​) prin care se introduc: 
 +      * numele; 
 +      * numărul de telefon ​- acest câmp este dezactivat (are proprietatea ''​android:​enabled="​false"''​),​ urmând ca valoarea sa să fie preluată din câmpul ''​extra'' ​al unei intenții;​ 
 +      * adresa electronică;​ 
 +      * adresa poștală. 
 +  * ''​fragment_additional_details.xml''​ conține patru controale ​de tip câmpuri text dispuse vertical și ocupând pe lățime întregul spațiu avut la dispoziție,​ prin care se introduc: 
 +    * poziția ocupată; 
 +    * denumirea companiei;​ 
 +    * site-ul web; 
 +    * identificatorul pentru mesagerie instantanee. 
 + 
 +**7.** Să se implementeaze interacțiunea cu utilizatorul a aplicației.  
 +  * în metoda ''​onActivityCreated()''​ a fragmentului ''​BasicDetailsFragment''​ se obțin referințe către butoanele //Show Additional Details// / //Hide Additional Details//, respectiv //Save// și //Cancel// prin intermediul metodei ''​getActivity().findViewById(R.id....)'';​ 
 +  * se implementează o clasă ascultător pentru butoane, care implementează ''​View.OnClickListener''​ și (re)definește metoda ''​onClick(View v)'';​ în funcție de identificatorul butonului care este transmis ca parametru al metodei, sunt realizate următoarele acțiuni: 
 +    * butonul //Show Additional Details// / //Hide Additional Details// - atașează / detașează fragmentul ''​AdditionalDetailsFragment'' ​la activitate ​în funcție de existența / inexistența acestuia, modificând corespunzător textul afișat pe buton: <​code>​ 
 +FragmentManager fragmentManager = getActivity().getFragmentManager();​ 
 +FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();​ 
 +AdditionalDetailsFragment additionalDetailsFragment = (AdditionalDetailsFragment)fragmentManager.findFragmentById(R.id.containerBottom);​ 
 +if (additionalDetailsFragment == null) { 
 +  fragmentTransaction.add(R.id.containerBottom,​ new AdditionalDetailsFragment());​ 
 +  ((Button)v).setText(getActivity().getResources().getString(R.string.hide_additional_fields));​ 
 +  fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK);​ 
 +} else { 
 +  fragmentTransaction.remove(additionalDetailsFragment);​ 
 +  ((Button)v).setText(getActivity().getResources().getString(R.string.show_additional_fields));​ 
 +  fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_EXIT_MASK);​ 
 +} 
 +fragmentTransaction.commit();​ 
 +</​code>​ 
 +    * butonul //Save// - lansează în execuție aplicația Android nativă pentru stocarea unui contact în agenda telefonică,​ după ce în prealabil au fost preluate informațiile din controalele grafice: <code java> 
 +Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);​ 
 +intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);​ 
 +if (name != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.NAME,​ name); 
 +} 
 +if (phone != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.PHONE,​ phone); 
 +} 
 +if (email != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.EMAIL,​ email); 
 +} 
 +if (address != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.POSTAL,​ address); 
 +} 
 +if (jobTitle != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE,​ jobTitle);​ 
 +} 
 +if (company != null) { 
 +  intent.putExtra(ContactsContract.Intents.Insert.COMPANY,​ company); 
 +} 
 +ArrayList<​ContentValues>​ contactData = new ArrayList<​ContentValues>​();​ 
 +if (website != null) { 
 +  ContentValues websiteRow = new ContentValues();​ 
 +  websiteRow.put(ContactsContract.Data.MIMETYPE,​ ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);​ 
 +  websiteRow.put(ContactsContract.CommonDataKinds.Website.URL,​ website); 
 +  contactData.add(websiteRow);​ 
 +} 
 +if (im != null) { 
 +  ContentValues imRow = new ContentValues();​ 
 +  imRow.put(ContactsContract.Data.MIMETYPE,​ ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);​ 
 +  imRow.put(ContactsContract.CommonDataKinds.Im.DATA,​ im); 
 +  contactData.add(imRow);​ 
 +} 
 +intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA,​ contactData);​ 
 +getActivity().startActivity(intent);​ 
 +</​code>​ Intenția pentru realizarea acestei operații are asociată acțiunea ''​ContactsContract.Intents.Insert.ACTION''​ și tipul ''​ContactsContract.RawContacts.CONTENT_TYPE''​. Informațiile care se doresc a fi completate sunt atașate în câmpul ''​extra''​ al acesteia, având cheile:\\ ✔ ''​ContactsContract.Intents.Insert.NAME'';​\\ ✔ ''​ContactsContract.Intents.Insert.PHONE'';​\\ ✔ ''​ContactsContract.Intents.Insert.EMAIL'';​\\ ✔ ''​ContactsContract.Intents.Insert.POSTAL'';​\\ ✔ ''​ContactsContract.Intents.Insert.JOB_TITLE'';​\\ ✔ ''​ContactsContract.Intents.Insert.COMPANY'';​\\ Pentru site-ul web și identificatorul de mesagerie instantanee,​ se folosește un tablou de elemente ''​ContentValues''​ în care se specifică înregistrări de tipul ''​CommonDataKinds.Website.URL'',​ respectiv ''​CommonDataKinds.Im.DATA'';​\\ Pentru a putea gestiona agenda telefonică,​ este necesar ca în fișierul ''​AndroidManifest.xml''​ să fie specificate următoarele permisiuni: <code xml> 
 +<​uses-permission 
 +  android:​name="​android.permission.READ_CONTACTS"​ /> 
 +<​uses-permission 
 +  android:​name="​android.permission.WRITE_CONTACTS"​ /> 
 +</​code>​ 
 +    * butonul //Cancel// - termină aplicația Android: <code java> 
 +getActivity().finish();​ 
 +</​code>​ 
 +  * se înregistrează o instanță a clasei ascultător ca mecanism de tratare a evenimentelor de tip accesare a butoanelor din cadrul interfeței grafice, prin apelul metodei ''​setOnClickListener()''​. 
 + 
 +**8.** Să se modifice aplicația Android [[https://​github.com/​pdsd2015/​Laborator03/​tree/​master/​labtaks/​PhoneDialer|Phone Dialer]] astfel încât să conțină un buton suplimentar prin care este invocată aplicația //Contacts Manager// căreia îi transmite numărul de telefon format și așteptând un rezultat cu privire la stocarea contactului în agenda telefonică. 
 + 
 +{{ :​laboratoare:​laborator04:​activitate_de_laborator03.png?​nolink&​300 }} 
 + 
 +{{ :​laboratoare:​laborator04:​activitate_de_laborator04.png?​nolink&​500 }} 
 + 
 +Metoda de tratare a evenimentului de tip accesare a butonului de stocare a numărului de telefon în agenda telefonică invocă o intenție asociată aplicației //Contacts Manager//, transmițând și numărul de telefon în câmpul ''​extra''​ asociat acesteia, identificabil prin intermediul unei chei. <code java> 
 +if (phoneNumber.length() > 0) { 
 +  intent = new Intent("​ro.pub.cs.systems.pdsd.lab04.contactsmanager.intent.action.ContactsManagerActivity"​);​ 
 +  intent.putExtra("​ro.pub.cs.systems.pdsd.lab04.contactsmanager.PHONE_NUMBER_KEY",​ phoneNumber);​ 
 +  startActivityForResult(intent,​ Constants.CONTACTS_MANAGER_REQUEST_CODE);​ 
 +} else { 
 +  Toast.makeText(getApplication(),​ getResources().getString(R.string.phone_error),​ Toast.LENGTH_LONG).show();​ 
 +} 
 +</​code>​ 
 + 
 +**9.** Să se modifice aplicația Android //Contacts Manager// astfel încât să poată fi lansată în execuție doar din contextul altei activități,​ prin intermediul unei intenții care conține în câmpul ''​extra''​ un număr de telefon, identificabil prin cheia ''​ro.pub.cs.systems.pdsd.lab04.contactsmanager.PHONE_NUMBER_KEY'',​ acesta fiind plasat în câmpul text needitabil corespunzător. Totodată, va transmite înapoi rezultatul operației de stocare (''​Activity.RESULT_OK''​ sau ''​Activity.RESULT_CANCELED''​). 
 + 
 +  * în fișierul ''​AndroidManifest.xml''​ se modifică filtrul de intenții (acțiunea și categoria), astfel încât activitatea să poată fi rulată doar prin intermediul unei intenții <file xml AndroidManifest.xml>​ 
 +<​manifest ...> 
 +  <​application ...> 
 +    <​activity 
 +      android:​name="​.graphicuserinterface.ContactsManagerActivity"​ 
 +      android:​label="​@string/​app_name"​ > 
 +      <​intent-filter>​ 
 +        <action android:​name="​ro.pub.cs.systems.pdsd.lab04.contactsmanager.intent.action.ContactsManagerActivity"​ /> 
 +        <​category android:​name="​android.intent.category.DEFAULT"​ /> 
 +      </​intent-filter>​ 
 +    </​activity>​ 
 +  </​application>​ 
 +</​manifest>​ 
 +</​file>​ 
 +  * în metoda ''​onActivityCreated()''​ asociată fragmentului ''​BasicDetailsFragment''​ este verificată intenția cu care este pornită activitatea părinte, și în cazul în care aceasta nu este nulă, este preluată informația din secțiunea ''​extra'',​ identificată prin cheia ''​ro.pub.cs.systems.pdsd.lab04.contactsmanager.PHONE_NUMBER_KEY'',​ conținutul său fiind plasat în cadrul câmpului text corespunzător:​ <code java> 
 +if (intent != null) { 
 +  String phone = intent.getStringExtra("​ro.pub.cs.systems.pdsd.lab04.contactsmanager.PHONE_NUMBER_KEY"​);​ 
 +  if (phone != null) { 
 +    phoneEditText.setText(phone);​ 
 +  } else { 
 +    Activity activity = getActivity();​ 
 +    Toast.makeText(activity,​ activity.getResources().getString(R.string.phone_error),​ Toast.LENGTH_LONG).show();​ 
 +  } 
 +}  
 +</​code>​ 
 +  * pe metodele de tratare a evenimentelor de accesare a butoanelor:​ 
 +    * //Save// - este lansată în execuție aplicația nativă pentru gestiunea agendei telefonice, folosind un cod de cerere prin intermediul căruia se va verifica rezultatul furnizat: <code java> 
 +getActivity().startActivityForResult(intent,​ Constants.CONTACTS_MANAGER_REQUEST_CODE);​ 
 +</​code>​ 
 +    * //Cancel// - se transmite înapoi rezultatul <code java> 
 +getActivity().setResult(Activity.RESULT_CANCELED,​ new Intent());​ 
 +</​code>​ 
 + 
 +  * în metoda ''​onActivityResult()''​ asociată activității ''​ContactsManagerActivity'',​ în momentul în care s-a părăsit aplicația nativă pentru gestiunea agendei telefonice, se verifică codul de cerere și se transmite înapoi un rezultat: <code java> 
 +public void onActivityResult(int requestCode,​ int resultCode, Intent intent) { 
 +switch(requestCode) { 
 +  case Constants.CONTACTS_MANAGER_REQUEST_CODE:​ 
 +    setResult(resultCode,​ new Intent());​ 
 +    finish(); 
 +    break; 
 +  } 
 +} 
 +</​code>​ 
 + 
 +**10.** ​ Să se încarce modificările realizate în cadrul depozitului '​Laborator04'​ de pe contul Github personal, folosind un mesaj sugestiv. <​code>​ 
 +student@pdsd2015:​~/​Laborator04$ git add * 
 +student@pdsd2015:​~/​Laborator04$ git commit -m "​implemented taks for laboratory 04" 
 +student@pdsd2015:​~/​Laborator04$ git push origin master 
 +</​code>​
  
 ===== Resurse Utile ===== ===== Resurse Utile =====
laboratoare/laborator04.1426453922.txt.gz · Last modified: 2015/03/15 23:12 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