0% ont trouvé ce document utile (0 vote)
5 vues16 pages

S5.TP Android

Ce TP guide les utilisateurs dans la création d'une application de gestion de contacts sur Android en utilisant Kotlin, incluant l'ajout, l'affichage, l'appel et l'envoi de SMS à des contacts, avec persistance des données via Room. Le document couvre l'installation d'Android Studio, la configuration du projet, et fournit des exemples de code pour chaque étape. Des tests sur émulateur et des exercices pratiques sont également inclus pour renforcer l'apprentissage.

Transféré par

diaky7871
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
5 vues16 pages

S5.TP Android

Ce TP guide les utilisateurs dans la création d'une application de gestion de contacts sur Android en utilisant Kotlin, incluant l'ajout, l'affichage, l'appel et l'envoi de SMS à des contacts, avec persistance des données via Room. Le document couvre l'installation d'Android Studio, la configuration du projet, et fournit des exemples de code pour chaque étape. Des tests sur émulateur et des exercices pratiques sont également inclus pour renforcer l'apprentissage.

Transféré par

diaky7871
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

TP Android Studio (Kotlin) : Application « Contacts »

Ajout, affichage, appel & SMS d’un contact + Tests sur émulateur

Master Informatique – Développement Mobile Android


2025

Résumé
Ce TP guidé pas à pas vous conduit de l’installation d’Android Studio à la création
d’une application fonctionnelle de gestion de contacts : ajout d’un contact, affichage en
liste, lancement d’un appel et d’un SMS via Intents, avec persistance locale en Room
(SQLite) et tests sur émulateur Android. Tout le code est fourni en Kotlin avec
RecyclerView, ViewModel et ViewBinding.

Table des matières


1 Objectifs pédagogiques 3

2 Prérequis & environnement 3

3 Installation d’Android Studio (30–40 min) 3

4 Création du projet (10 min) 3

5 Dépendances et configuration Gradle 4


5.1 Module app/[Link] . . . . . . . . . . . . . . . . . . . . . . . . . . 4

6 Architecture de l’application 5

7 Implémentation & code complet (pas à pas) 5


7.1 Données : Room (Entity, DAO, Database) . . . . . . . . . . . . . . . . . . . . 5
7.2 UI : Layouts XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
7.3 Ressources de chaînes res/values/[Link] . . . . . . . . . . . . . . . . . 9
7.4 Adapter RecyclerView ui/[Link] . . . . . . . . . . . . . . . . . . 10
7.5 ViewModel ui/[Link] . . . . . . . . . . . . . . . . . . . . . . 10
7.6 Dialog d’ajout ui/[Link] . . . . . . . . . . . . . . . . . . . . . 11
7.7 Activité principale [Link] . . . . . . . . . . . . . . . . . . . . . . . 12
7.8 Manifest src/main/[Link] . . . . . . . . . . . . . . . . . . . . 13

8 Guide de test sur émulateur (10–15 min) 14

9 Dépannage & erreurs fréquentes 14

10 Exercices guidés (avec indications) 14

11 Évaluation (proposition) 15

1
12 Annexes 15
12.1 Arborescence attendue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
12.2 Fiche mémo & bonnes pratiques . . . . . . . . . . . . . . . . . . . . . . . . . . 15
12.3 Option : permission CALL_PHONE (avancé) . . . . . . . . . . . . . . . . . . 16

13 Chrono conseillé (2h30–3h) 16

2
1 Objectifs pédagogiques
— Installer et configurer Android Studio, le SDK et un AVD (émulateur).
— Créer un projet Android Kotlin propre et modulaire.
— Concevoir une UI : formulaire d’ajout + RecyclerView d’affichage.
— Persister les données avec Room (Entity, DAO, Database).
— Déclencher un appel (ACTION_DIAL/ACTION_CALL) et un SMS (ACTION_SENDTO).
— Gérer les autorisations runtime (optionnel pour CALL_PHONE).
— Tester & déboguer sur émulateur Android.

2 Prérequis & environnement


— Connaissances de base en Java/Kotlin et XML Android.
— Poste avec virtualisation activée (Windows : BIOS/Hyper-V ; macOS : Apple Silicon
OK).
— Android Studio (version récente).

3 Installation d’Android Studio (30–40 min)


Étape 1 : Téléchargez Android Studio depuis [Link] et ins-
tallez en conservant les options par défaut : Android SDK, SDK Platform, Emulator,
Platform-Tools.
Étape 2 : Ouvrez SDK Manager : More Actions → SDK Manager.
Étape 3 : SDK Platforms : cochez l’API la plus récente (Android 14/15) + éventuellement
Android 13 pour compatibilité.
Étape 4 : SDK Tools : cochez Android SDK Build-Tools, Android Emulator, Android Platform-
Tools.
Étape 5 : Créez un AVD : Virtual Device Manager → Create Device (ex. Pixel 5) → choisissez
une image système (x86_64/ARM) → Finish puis lancez-le.
Astuce
Sous Windows, si l’émulateur ne démarre pas, vérifiez la virtualisation (BIOS) et évitez
Hyper-V si vous utilisez les images Intel/AMD.

4 Création du projet (10 min)


Étape 1 : New Project → Empty Views Activity (ou Empty Activity).
Étape 2 : Nom : ContactsApp, Langage : Kotlin, Min SDK : 26 (Android 8.0) ou supérieur.
Étape 3 : Lancez l’appli (Run ▷) pour vérifier l’exécution initiale.

3
5 Dépendances et configuration Gradle
5.1 Module app/[Link]

Listing 1 – Dépendances principales (Room, RecyclerView, ViewBinding).


1 plugins {
2 id ( " com . android . application " )
3 id ( " org . jetbrains . kotlin . android " )
4 kotlin ( " kapt " ) // pour Room
5 }
6

7 android {
8 namespace = " com . example . contactsapp "
9 compileSdk = 34
10

11 defaultConfig {
12 applicationId = " com . example . contactsapp "
13 minSdk = 26
14 targetSdk = 34
15 versionCode = 1
16 versionName = " 1.0 "
17 }
18

19 buildFeatures {
20 viewBinding = true
21 }
22 }
23

24 dependencies {
25 implementation ( " androidx . core : core - ktx :1.13.1 " )
26 implementation ( " androidx . appcompat : appcompat :1.7.0 " )
27 implementation ( " com . google . android . material : material :1.12.0 " )
28 implementation ( " androidx . constraintlayout : constraintlayout
:2.1.4 " )
29

30 // RecyclerView
31 implementation ( " androidx . recyclerview : recyclerview :1.3.2 " )
32

33 // Room
34 val roomVersion = " 2.6.1 "
35 implementation ( " androidx . room : room - runtime : $roomVersion " )
36 kapt ( " androidx . room : room - compiler : $roomVersion " )
37 implementation ( " androidx . room : room - ktx : $roomVersion " )
38

39 // ViewModel / LiveData ( KTX )


40 implementation ( " androidx . lifecycle : lifecycle - viewmodel - ktx
:2.8.4 " )
41 }

4
À retenir
Activez ViewBinding pour générer automatiquement des classes de liaison
(ActivityMainBinding, ItemContactBinding, DialogAddContactBinding) depuis vos
layouts XML.

6 Architecture de l’application
— data/ : entité Room [Link], DAO [Link], base [Link].
— ui/ : [Link], [Link], [Link].
— [Link] : configuration UI + callbacks (Appel / SMS).
— res/layout : activity_main.xml, item_contact.xml, dialog_add_contact.xml.

7 Implémentation & code complet (pas à pas)


7.1 Données : Room (Entity, DAO, Database)
Entité data/[Link]
1 package com . example . contactsapp . data
2

3 import androidx . room . Entity


4 import androidx . room . PrimaryKey
5

6 @Entity ( tableName = " contacts " )


7 data class Contact (
8 @PrimaryKey ( autoGenerate = true ) val id : Long = 0 ,
9 val name : String ,
10 val phone : String ,
11 val email : String ? = null
12 )

DAO data/[Link]
1 package com . example . contactsapp . data
2

3 import androidx . lifecycle . LiveData


4 import androidx . room .*
5

6 @Dao
7 interface ContactDao {
8 @Insert ( onConflict = On Co nf li ct St ra te gy . REPLACE )
9 suspend fun insert ( contact : Contact ) : Long
10

11 @Query ( " SELECT * FROM contacts ORDER BY name ASC " )


12 fun getAll () : LiveData < List < Contact > >
13

14 @Delete
15 suspend fun delete ( contact : Contact )
16 }

5
Base de données data/[Link]
1 package com . example . contactsapp . data
2

3 import android . content . Context


4 import androidx . room . Database
5 import androidx . room . Room
6 import androidx . room . RoomDatabase
7

8 @Database ( entities = [ Contact :: class ] , version = 1 , exportSchema =


false )
9 abstract class AppDatabase : RoomDatabase () {
10 abstract fun contactDao () : ContactDao
11

12 companion object {
13 @Volatile private var INSTANCE : AppDatabase ? = null
14

15 fun get ( context : Context ) : AppDatabase =


16 INSTANCE ?: synchronized ( this ) {
17 INSTANCE ?: Room . databaseBuilder (
18 context . applicationContext ,
19 AppDatabase :: class . java ,
20 " contacts_db "
21 ) . build () . also { INSTANCE = it }
22 }
23 }
24 }

7.2 UI : Layouts XML


Layout principal res/layout/activity_main.xml
1 <? xml version = " 1.0 " encoding = " utf -8 " ? >
2 < androidx . c oordi natorl ayout . widget . Coord inator Layout
3 xmlns:android = " http: // schemas . android . com / apk / res / android "
4 xmlns:app = " http: // schemas . android . com / apk / res - auto "
5 andr o i d : l a y o u t _ w i d t h = " match_parent "
6 a ndr o i d : l a y o u t _ h e i g h t = " match_parent " >
7

8 < androidx . recyclerview . widget . RecyclerView


9 android:id = " @ + id / recyclerContacts "
10 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
11 a n d r o i d : l a y o u t _ h e i g h t = " match_parent "
12 android:padding = " 8 dp "
13 a pp:la youtMa nager = " androidx . recyclerview . widget .
L i ne a r La y o ut M a na g e r " / >
14

15 < com . google . android . material . f l o a t i n g a c t i o n b u t t o n .


FloatingActionButton
16 android:id = " @ + id / fabAdd "
17 a n d r o i d : l a y o u t _ w i d t h = " wrap_content "
18 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content "
19 a n d r o i d : c o n t e n t D e s c r i p t i o n = " @string / add_contact "
20 app:srcCompat = " @andro id:dra wable / ic_input_add "

6
21 a n d r o i d : l a y o u t _ m a r g i n = " 16 dp "
22 a pp:la yout_a nchor = " @id / recyclerContacts "
23 a p p : l a y o u t _ a n c h o r G r a v i t y = " bottom | end " / >
24

25 </ androidx . coordi natorl ayout . widget . Coord inator Layout >

Item de liste res/layout/item_contact.xml


1 <? xml version = " 1.0 " encoding = " utf -8 " ? >
2 < com . google . android . material . card . MaterialCardView
3 xmlns:android = " http: // schemas . android . com / apk / res / android "
4 xmlns:app = " http: // schemas . android . com / apk / res - auto "
5 andr o i d : l a y o u t _ w i d t h = " match_parent "
6 a ndr o i d : l a y o u t _ h e i g h t = " wrap_content "
7 a ndr o i d : l a y o u t _ m a r g i n = " 6 dp "
8 a p p: c a r d U s e C o m p a t P a d d i n g = " true "
9 app:car dElev ation = " 2 dp " >
10

11 < LinearLayout
12 android:padding = " 12 dp "
13 a n dr o i d: o r ie n t at i o n = " vertical "
14 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
15 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " >
16

17 < TextView
18 android:id = " @ + id / txtName "
19 a ndroi d:text Style = " bold "
20 android:textSize = " 18 sp "
21 a n d r o i d : l a y o u t _ w i d t h = " wrap_content "
22 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " / >
23

24 < TextView
25 android:id = " @ + id / txtPhone "
26 a n d r o i d : l a y o u t _ w i d t h = " wrap_content "
27 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " / >
28

29 < LinearLayout
30 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
31 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content "
32 a n dr o i d: o r ie n t at i o n = " horizontal "
33 android:gravity = " end "
34 a n d r o i d : l a y o u t _ m a r g i n T o p = " 8 dp " >
35

36 < ImageButton
37 android:id = " @ + id / btnCall "
38 a n d r o i d : c o n t e n t D e s c r i p t i o n = " @string / call "
39 android:src = " @andro id:dr awable / ic_menu_call "
40 an dr oi d: ba ck gr ou nd = " ? attr /
selectableItemBackgroundBorderless "
41 a n d r o i d : l a y o u t _ w i d t h = " 48 dp "
42 a n d r o i d : l a y o u t _ h e i g h t = " 48 dp " / >
43

44 < ImageButton

7
45 android:id = " @ + id / btnSms "
46 a n d r o i d : c o n t e n t D e s c r i p t i o n = " @string / sms "
47 android:src = " @andro id:dr awable / ic_dialog_email "
48 an dr oi d: ba ck gr ou nd = " ? attr /
selectableItemBackgroundBorderless "
49 a n d r o i d : l a y o u t _ w i d t h = " 48 dp "
50 a n d r o i d : l a y o u t _ h e i g h t = " 48 dp " / >
51 </ LinearLayout >
52 </ LinearLayout >
53 </ com . google . android . material . card . MaterialCardView >

Dialog d’ajout res/layout/dialog_add_contact.xml


1 <? xml version = " 1.0 " encoding = " utf -8 " ? >
2 < ScrollView xmlns:android = " http: // schemas . android . com / apk / res /
android "
3 xmlns:app = " http: // schemas . android . com / apk / res - auto "
4 andr o i d : l a y o u t _ w i d t h = " match_parent "
5 a ndr o i d : l a y o u t _ h e i g h t = " wrap_content " >
6

7 < LinearLayout
8 android:padding = " 16 dp "
9 a n dr o i d: o r ie n t at i o n = " vertical "
10 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
11 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " >
12

13 < com . google . android . material . textfield . TextInputLayout


14 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
15 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content "
16 android:hint = " @string / name " >
17 < com . google . android . material . textfield .
Tex tInput EditTe xt
18 android:id = " @ + id / edtName "
19 androi d:inpu tType = " textPersonName "
20 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
21 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " / >
22 </ com . google . android . material . textfield . TextInputLayout >
23

24 < com . google . android . material . textfield . TextInputLayout


25 a n d r o i d : l a y o u t _ m a r g i n T o p = " 12 dp "
26 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
27 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content "
28 android:hint = " @string / phone " >
29 < com . google . android . material . textfield .
Tex tInput EditTe xt
30 android:id = " @ + id / edtPhone "
31 androi d:inpu tType = " phone "
32 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
33 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " / >
34 </ com . google . android . material . textfield . TextInputLayout >
35

36 < com . google . android . material . textfield . TextInputLayout


37 a n d r o i d : l a y o u t _ m a r g i n T o p = " 12 dp "

8
38 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
39 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content "
40 android:hint = " @string / email " >
41 < com . google . android . material . textfield .
Tex tInput EditTe xt
42 android:id = " @ + id / edtEmail "
43 androi d:inpu tType = " textEmailAddress "
44 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
45 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " / >
46 </ com . google . android . material . textfield . TextInputLayout >
47

48 < LinearLayout
49 a n d r o i d : l a y o u t _ m a r g i n T o p = " 16 dp "
50 a n d r o i d : l a y o u t _ w i d t h = " match_parent "
51 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content "
52 android:gravity = " end "
53 a n dr o i d: o r ie n t at i o n = " horizontal " >
54

55 < Button
56 android:id = " @ + id / btnCancel "
57 android:text = " @string / cancel "
58 a n d r o i d : l a y o u t _ w i d t h = " wrap_content "
59 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " / >
60

61 < Button
62 android:id = " @ + id / btnSave "
63 android:text = " @string / save "
64 a n d r o i d : l a y o u t _ m a r g i n S t a r t = " 12 dp "
65 a n d r o i d : l a y o u t _ w i d t h = " wrap_content "
66 a n d r o i d : l a y o u t _ h e i g h t = " wrap_content " / >
67 </ LinearLayout >
68

69 </ LinearLayout >


70 </ ScrollView >

7.3 Ressources de chaînes res/values/[Link]


1 <? xml version = " 1.0 " encoding = " utf -8 " ? >
2 < resources >
3 < string name = " app_name " > ContactsApp </ string >
4 < string name = " add_contact " > Ajouter un contact </ string >
5 < string name = " name " > Nom </ string >
6 < string name = " phone " > T l p h o n e </ string >
7 < string name = " email " > Email </ string >
8 < string name = " cancel " > Annuler </ string >
9 < string name = " save " > Enregistrer </ string >
10 < string name = " call " > Appeler </ string >
11 < string name = " sms " > SMS </ string >
12 </ resources >

9
7.4 Adapter RecyclerView ui/[Link]
1 package com . example . contactsapp . ui
2

3 import android . view . LayoutInflater


4 import android . view . ViewGroup
5 import androidx . recyclerview . widget . RecyclerView
6 import com . example . contactsapp . data . Contact
7 import com . example . contactsapp . databinding . It em Co nt ac tB in di ng
8

9 class ContactAdapter (
10 private var items : List < Contact > ,
11 private val onCall : ( Contact ) -> Unit ,
12 private val onSms : ( Contact ) -> Unit
13 ) : RecyclerView . Adapter < ContactAdapter . VH >() {
14

15 inner class VH ( val binding : It em Co nt ac tB in di ng ) : RecyclerView


. ViewHolder ( binding . root )
16

17 override fun on Cr ea te Vi ew Ho ld er ( parent : ViewGroup , viewType :


Int ) : VH {
18 val b = It em Co nt ac tB in di ng . inflate ( LayoutInflater . from (
parent . context ) , parent , false )
19 return VH ( b )
20 }
21

22 override fun onBindViewHolder ( holder : VH , position : Int ) {


23 val c = items [ position ]
24 with ( holder . binding ) {
25 txtName . text = c . name
26 txtPhone . text = c . phone
27 btnCall . se tO nC li ck Li st en er { onCall ( c ) }
28 btnSms . se tO nC li ck Li st en er { onSms ( c ) }
29 }
30 }
31

32 override fun getItemCount () = items . size


33

34 fun submit ( newItems : List < Contact >) {


35 items = newItems
36 n o t i f y D a t a S e t C h a n g e d () // suffisant pour un TP ; DiffUtil
c o n s e i l l en prod
37 }
38 }

7.5 ViewModel ui/[Link]


1 package com . example . contactsapp . ui
2

3 import android . app . Application


4 import androidx . lifecycle . AndroidViewModel
5 import androidx . lifecycle . LiveData

10
6 import androidx . lifecycle . viewModelScope
7 import com . example . contactsapp . data . AppDatabase
8 import com . example . contactsapp . data . Contact
9 import kotlinx . coroutines . Dispatchers
10 import kotlinx . coroutines . launch
11

12 class ContactViewModel ( app : Application ) : AndroidViewModel ( app ) {


13 private val dao = AppDatabase . get ( app ) . contactDao ()
14 val contacts : LiveData < List < Contact > > = dao . getAll ()
15

16 fun addContact ( name : String , phone : String , email : String ?) {


17 viewModelScope . launch ( Dispatchers . IO ) {
18 dao . insert ( Contact ( name = name , phone = phone , email =
email ) )
19 }
20 }
21 }

7.6 Dialog d’ajout ui/[Link]


1 package com . example . contactsapp . ui
2

3 import android . app . Dialog


4 import android . os . Bundle
5 import android . view . LayoutInflater
6 import androidx . appcompat . app . AlertDialog
7 import androidx . fragment . app . DialogFragment
8 import androidx . fragment . app . ac ti vi ty Vi ew Mo de ls
9 import com . example . contactsapp . databinding . D i a l o g A d d C o n t a c t B i n d i n g
10

11 class AddContactDialog : DialogFragment () {


12 private var _binding : D i a l o g A d d C o n t a c t B i n d i n g ? = null
13 private val binding get () = _binding !!
14 private val vm : ContactViewModel by ac ti vi ty Vi ew Mo de ls ()
15

16 override fun onCreateDialog ( sa ve dI ns ta nc eS ta te : Bundle ?) :


Dialog {
17 _binding = D i a l o g A d d C o n t a c t B i n d i n g . inflate ( LayoutInflater .
from ( requireContext () ) )
18 return AlertDialog . Builder ( requireContext () )
19 . setTitle ( " Ajouter un contact " )
20 . setView ( binding . root )
21 . create ()
22 }
23

24 override fun onStart () {


25 super . onStart ()
26 binding . btnCancel . se tO nC li ck Li st en er { dismiss () }
27 binding . btnSave . se tO nC li ck Li st en er {
28 val name = binding . edtName . text ?. toString () ?. trim () .
orEmpty ()

11
29 val phone = binding . edtPhone . text ?. toString () ?. trim () .
orEmpty ()
30 val email = binding . edtEmail . text ?. toString () ?. trim () .
takeIf { ! it . isNullOrBlank () }
31

32 var ok = true
33 if ( name . isBlank () ) { binding . edtName . error = " Nom
requis " ; ok = false }
34 if ( phone . isBlank () ) { binding . edtPhone . error = "
T l p h o n e requis " ; ok = false }
35 if (! ok ) r e t u r n @ s e t O n C l i c k L i s t e n e r
36

37 vm . addContact ( name , phone , email )


38 dismiss ()
39 }
40 }
41

42 override fun onDestroyView () {


43 _binding = null
44 super . onDestroyView ()
45 }
46 }

7.7 Activité principale [Link]


1 package com . example . contactsapp
2

3 import android . content . Intent


4 import android . net . Uri
5 import android . os . Bundle
6 import androidx . activity . enableEdgeToEdge
7 import androidx . activity . viewModels
8 import androidx . appcompat . app . AppCom patAc tivity
9 import androidx . recyclerview . widget . L i n ea r L ay o u tM a n ag e r
10 import com . example . contactsapp . databinding . A c t iv i t yM a i nB i n di n g
11 import com . example . contactsapp . ui . AddContactDialog
12 import com . example . contactsapp . ui . ContactAdapter
13 import com . example . contactsapp . ui . ContactViewModel
14

15 class MainActivity : AppCom patAct ivity () {


16 private lateinit var binding : A c t iv i t yM a i nB i n di n g
17 private val vm : ContactViewModel by viewModels ()
18

19 override fun onCreate ( sa ve dI ns ta nc eS ta te : Bundle ?) {


20 super . onCreate ( sa ve dI ns ta nc eS ta te )
21 enableEdgeToEdge ()
22 binding = A c ti v i ty M a in B i nd i n g . inflate ( layoutInflater )
23 setContentView ( binding . root )
24

25 val adapter = ContactAdapter (


26 items = emptyList () ,
27 onCall = { contact ->

12
28 // Sans permission : ouvre l ’ app T l p h o n e avec
le n u m r o
29 val intent = Intent ( Intent . ACTION_DIAL , Uri . parse (
" tel : $ { contact . phone } " ) )
30 startActivity ( intent )
31 // Option ( avec permission ) : Intent . ACTION_CALL
32 },
33 onSms = { contact ->
34 // Ouvre l ’ app Messages avec le n u m r o et un
message p r r e m p l i
35 val intent = Intent ( Intent . ACTION_SENDTO , Uri .
parse ( " smsto : $ { contact . phone } " ) )
36 intent . putExtra ( " sms_body " , " Bonjour $ { contact .
name } , " )
37 startActivity ( intent )
38 }
39 )
40

41 binding . recyclerContacts . layoutManager =


L i ne a r La y o ut M a na g e r ( this )
42 binding . recyclerContacts . adapter = adapter
43

44 vm . contacts . observe ( this ) { list ->


45 adapter . submit ( list )
46 }
47

48 binding . fabAdd . se tO nC li ck Li st en er {
49 AddContactDialog () . show ( supportFragmentManager , " add " )
50 }
51 }
52 }

7.8 Manifest src/main/[Link]


1 <? xml version = " 1.0 " encoding = " utf -8 " ? >
2 < manifest xmlns:android = " http: // schemas . android . com / apk / res /
android " >
3

4 <! -- Optionnel ( uniquement si ACTION_CALL est u t i l i s e ) -- >


5 <! -- < uses - permission android:name = " android . permission .
CALL_PHONE " / > -- >
6

7 < application
8 a n dr o i d: a l lo w B ac k u p = " true "
9 a n dr o i d: s u pp o r ts R t l = " true "
10 android:theme = " @style / Theme . Material3 . DayNight . NoActionBar
"
11 android:label = " @string / app_name " >
12 < activity
13 android:name = " . MainActivity "
14 android:exported = " true " >
15 < intent - filter >

13
16 < action android:name = " android . intent . action . MAIN " /
>
17 < category android:name = " android . intent . category .
LAUNCHER " / >
18 </ intent - filter >
19 </ activity >
20 </ application >
21 </ manifest >

Attention
Pourquoi utiliser ACTION_DIAL et ACTION_SENDTO ?
ACTION_DIAL & ACTION_SENDTO ne nécessitent pas d’autorisations dangereuses.
ACTION_CALL requiert la permission CALL_PHONE + gestion runtime.
Pour un TP rapide et sûr, privilégiez DIAL/SENDTO.

8 Guide de test sur émulateur (10–15 min)


Étape 1 : Démarrez l’AVD (Virtual Device Manager → bouton Play).
Étape 2 : Exécutez l’appli (Run ▷).
Étape 3 : Ajoutez 2–3 contacts via le bouton flottant « + » (nom et téléphone requis).
Étape 4 : Cliquez sur l’icône Appel : l’application Téléphone s’ouvre avec le numéro prérempli.
Étape 5 : Cliquez sur l’icône SMS : l’application Messages s’ouvre avec le destinataire et un texte
prérempli.
Étape 6 : Fermez et relancez l’application : les contacts sont toujours présents (persistance Room).

9 Dépannage & erreurs fréquentes


— Build échoue : Sync Project with Gradle Files ; vérifiez compileSdk/targetSdk et
versions.
— Émulateur lent : diminuez la résolution/ram de l’AVD, fermez d’autres VM, vérifiez
la virtualisation.
— Crash au clic : ouvrez Logcat, vérifiez les NullPointerException ou ActivityNotFoundException.
— ViewBinding non généré : assurez-vous que buildFeatures viewBinding = true
est activé et que les noms d’IDs XML correspondent au code.

10 Exercices guidés (avec indications)


Exercice 1 — Suppression d’un contact
Objectif : au long-press sur un item, afficher un Dialog de confirmation et supprimer via
[Link].
Pistes : ajoutez un setOnLongClickListener dans l’Adapter, puis appelez une méthode du
ViewModel lançant [Link] dans [Link].

14
Exercice 2 — Édition d’un contact
Objectif : réutiliser AddContactDialog prérempli, ajouter un bouton « Modifier » et un
@Update dans le DAO.
Pistes : passez le Contact à éditer en argument du Dialog, validez, puis persistez.

Exercice 3 — Recherche simple


Objectif : ajouter une barre de recherche qui filtre la liste localement.
Pistes : conservez la liste complète en mémoire (LiveData) et appliquez un filtre côté Adapter.

11 Évaluation (proposition)
Critère Poids
Fonctionnalités (ajout, liste, appel, SMS) 40%
Qualité du code (Room, ViewModel, clarté) 25%
UI/UX (validation, lisibilité, cohérence) 20%
Tests et démonstration (AVD) 15%

12 Annexes
12.1 Arborescence attendue
1 app /
2 src / main /
3 AndroidManifest . xml
4 java / com / example / contactsapp /
5 MainActivity . kt
6 data /
7 AppDatabase . kt
8 Contact . kt
9 ContactDao . kt
10 ui /
11 ContactViewModel . kt
12 ContactAdapter . kt
13 AddContactDialog . kt
14 res / layout /
15 activity_main . xml
16 item_contact . xml
17 di al og _a dd _c on ta ct . xml

12.2 Fiche mémo & bonnes pratiques


— Préférez ACTION_DIAL/ACTION_SENDTO pour éviter les permissions dangereuses.
— Travaillez en threads IO ([Link]) pour l’accès base de données.
— Utilisez LiveData/Flow pour réagir aux changements et simplifier la gestion du cycle
de vie.
— En production, remplacez notifyDataSetChanged() par DiffUtil.

15
12.3 Option : permission CALL_PHONE (avancé)

Listing 2 – Demande runtime pour ACTION_CALL (extrait optionnel).


1 /* Exemple : NE PAS ACTIVER sans en avoir besoin . */
2 private fun callDirect ( phone : String ) {
3 val perm = android . Manifest . permission . CALL_PHONE
4 if ( c h ec k S el f P er m i ss i o n ( perm ) == PackageManager .
PE RM IS SI ON _G RA NT ED ) {
5 startActivity ( Intent ( Intent . ACTION_CALL , Uri . parse ( " tel :
$phone " ) ) )
6 } else {
7 re qu es tP er mi ssi on s ( arrayOf ( perm ) , 42)
8 }
9 }

13 Chrono conseillé (2h30–3h)


— 35 min : Installation SDK & AVD.
— 15 min : Création projet & dépendances.
— 60 min : Implémentation complète (Room, UI, Intents).
— 20 min : Tests AVD & débogage.
— 20–30 min : Exercices guidés (suppression/édition/recherche).

16

Vous aimerez peut-être aussi