---
# System prepended metadata

title: Android
tags: [LPDIM]

---

---
tags: LPDIM
title: Android
---

# TP --- Android Advanced :airplane: 

## Android Jetpack 🚀

:::info
Question 1 👉 Quel est l’interêt de Jetpack du point de vue d’un développeur ? Qu’est ce que la séparation des responsabilités ?
:::

:::success
Jetpack est une library qui est destinée aux développeur, pour les aider.
:::

Au point de vue des développeurs, l'interêt de Jetpack est de facilité le code sur toutes les versions d'Android et tous les appareils. Ce qui le rend plus efficace et qui fait gagner du temps pour les développeurs.

La séparation des responsabilités c'est un principe à suivre lors du développement. Celon lui, le logiciel doit être divisé en fonction des types des tâches qu’il effectue. C'est aussi un aspect fondamental de l’utilisation des couches dans les architectures d’applications.

:::info
Question 2 👉 Quels sont les avantages, selon vous, d’avoir un code plus concis, plus court ? Les inconvénients ?
:::

:100: Avantages :100:

Selon moi, avoir un code plus concis, plus court permet de se retrouver plus facilement dans notre développement et aussi un code plus lisible.


:collision: inconvénients :collision:

L'inconvénient c'est que si le code est plus court, il y a moins de détails donc plus difficile a comprendre.

--- 

## Pourquoi les architectures components ?

:::info
Question 1 :point_right: Qu’est ce qu’un viewModel provider ? Est-ce qu’un view model doit connaitre l’état de l’UI ?
:::

:::success
Un viewModel provider est une classe qui s'ajoute entre la vue et une activité.
:::

Un viewModel ne connaît pas l'état de l'UI mais il se réfère à celui-là, il stock et gère ses données.

:::info
Question 2 :point_right: Qu’est ce qu’une Live Data ? A quoi cela sert ? Décrivez le design pattern Observer avec vos mots
:::

:::success
Une LiveData est une classe observable qui est conscient du lifecycle. Ainsi, il ne met que a jour les observateurs de composants d'applications qui sont dans un état de lifecycle actif. Il respect alors les activités, les fragments ou les services.
:::

:gem: Design Pattern Observer :gem: 

Le design Pattern Observer définit une relation entre un objet, si un objet change d'état, tous les composants qui sont dépendant de lui se mettent à jour.

:::info
Question 3 :point_right: Que signifie qu’un composant est lifecyle aware concrètement ?
:::

:::success
Le Lifecyle aware effectue des actions en réponse à un changement de l'état du lifecycle d'un autre composant (fragments et activités).
:::

Concrètement, lorsqu'un composant est Lifecyle aware signifie que lorsqu'un utilisateur fait une action, comme une rotation d'écran ou encore un back, le Lifecycle aware n'agit pas sur les mise à jour de l'activité et/ou du fragment.

:::info
Question 4 :point_right: Faut il exposer un MutableLiveData<> au viewModel ? La vue s’abonne à une LiveData avec quelle instruction ?
:::

:::success
Un MutableLiveData<> diffère de LiveData, il rent les méthodes `setValue()` et `postValue()` public, puisque dans le LiveData, ses méthode sont privées.
:::

Il faut exposer un MutableLiveData<> au viewModel puisque le viexModel n'utilise que lui.

La vue s’abonne à une LiveData avec l'instruction Observer.

:hot_pepper: Voici l'exemple :hot_pepper: 

![](https://i.imgur.com/HONPlmt.png)


:::info
(Optionnel) Question 5 :point_right: Expliquer la différence entre Databinding et Binding. Est-ce que le Databinding est nécessaire pour travailler sur Android ?
:::

:::success
Le Databinding est une library intégrée dans le support library du SDK, il permet de lier les composants de l'interface utilisateur.
:::

:::success
Le Binding est une fonctionnalité qui permet d'écrir du code plus facilement, il intéragit avec la vue.
:::

La différence entre Databinding et Binding c'est que le DataBinding se fait dans le XML tandis que le Binding, lui se fait dans le fragment, c'est à dire l'UI.

***DataBinding :***

Dans le fragment : 

![](https://i.imgur.com/A1fLG4q.png)

Dans le XML : 

![](https://i.imgur.com/OfvrNLy.png)

![](https://i.imgur.com/tzTfPYu.png)

![](https://i.imgur.com/BM06suV.png)

***Binding :***

Dans le fragment : 

![](https://i.imgur.com/0M0mrCr.png)

![](https://i.imgur.com/h6objf3.png)

---

## Gestion des données - :zap: L’accès au réseau

### Les permissions :closed_lock_with_key: 

:::info
Question 1 :point_right: Décrivez le code à utiliser pour afficher une demande explicite de permission à l’utilisateur. Créez une application simple qui demande l’accès explicitement aux fichiers de l’appareil. Faites valider par l’enseignant
:::

***MainActivity.kt***

```kotlin=
package com.example.permissions

import android.Manifest
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

private const val CAMERA_PERMISSION_CODE = 100
private const val STORAGE_PERMISSION_CODE = 101

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        goPermission(Manifest.permission.READ_EXTERNAL_STORAGE, STORAGE_PERMISSION_CODE);
        goPermission(Manifest.permission.CAMERA, CAMERA_PERMISSION_CODE);
    }

    private fun goPermission(permission: String, requestCode: Int) {
        if (ContextCompat.checkSelfPermission(this@MainActivity, permission) == PackageManager.PERMISSION_DENIED) {
            Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show()
            ActivityCompat.requestPermissions(this@MainActivity,
                arrayOf(permission), requestCode)
        } else {
            Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show()
        }
    }

}
```

***activitymain.xml***

```kotlin=
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

</androidx.constraintlayout.widget.ConstraintLayout>
```

***AndroidManifest.xml***

```kotlin=
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.permissions">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Permissions">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
```

***Emulator***

![](https://i.imgur.com/0iWc9k5.png =300x)

---

## Faire une requête vers un web service :outbox_tray: :desktop_computer: 


:::info
Question 2 :point_right: Une erreur a du survenir, faites valider votre appel de requête par l’enseignant. Ou si vous avez compris l’erreur, corrigez la et faites valider. Expliquez pourquoi cette erreur est survenue ?
:::

Cette erreur est survenue parce que la requête était synchrone et cette requête ne peut pas se faire en même temps que le démarrage du MainActivity, qui lui utilise déjà le thread principale.
Pour ne plus avoir cette erreur, il faut faire une requête asynchrone et non synchrone.

:::info
Question 3 :point_right: Lancez maintenant une requête pour récuperer un personnage en particulier. Faites valider.

:::

```kotlin=
val request = Request.Builder()
 .url("https://rickandmortyapi.com/api/character/2")
 .build()
```

---

## Faire une requête, c’est cool, utiliser les données c’est mieux. :bar_chart:

:::info
Question 4 :point_right: Lancez une requête pour récuperer un personnage, et le parser dans une instance de data class Character. Faites valider par l’enseignant. Quel Content negociator avez vous utilisé ?
:::

J'ai utiliser le Content negociator Moshi, celui qui est recommander par OkHttp.

```kotlin=
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        run()
    }

    private val TAG = "MainActivity"

    private val client = OkHttpClient()
    private val moshi = Moshi.Builder().build()
    private val characterJsonAdapter = moshi.adapter(Character::class.java)

    fun run() {
        val request = Request.Builder()
                .url("https://rickandmortyapi.com/api/character/24")
                .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                e.printStackTrace()
            }

            override fun onResponse(call: Call, response: Response) {
                response.use {
                    if (!response.isSuccessful) throw IOException("Unexpected code $response")

                    val character = characterJsonAdapter.fromJson(response.body!!.source())
                    Log.i(TAG, character?.name.toString())
                }
            }
        })
    }

    @JsonClass(generateAdapter = true)
    data class Character (
            val id: Long,
            val name: String,
            val status: String,
            val species: String,
            val type: String,
            val gender: String,
            val origin: Location,
            val location: Location,
            val image: String,
            val episode: List<String>,
            val url: String,
            val created: String
    )

    @JsonClass(generateAdapter = true)
    data class Location (
            val name: String,
            val url: String
    )
}
```

:::info
Question 5 :point_right: Faites une requête pour récuperer une liste de personnages, et désérialiser le tout dans des objets Kotlin. Faites valider.
:::

```kotlin=
    fun run() {
        val request = Request.Builder()
                .url("https://rickandmortyapi.com/api/character")
                .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                e.printStackTrace()
            }

            override fun onResponse(call: Call, response: Response) {
                response.use {
                    if (!response.isSuccessful) throw IOException("Unexpected code $response")

                    val character = moshi.adapter(CharacterList::class.java).fromJson(response.body!!.source())
                    Log.i(TAG, character.toString())
                }
            }
        })
    }
    
    @JsonClass(generateAdapter = true)
    data class CharacterList (
            val results: List<Character>,
            val info: Info
    )
```

---

Léa PORTIER - 25/01/2021.