Chap5 Intents
Chap5 Intents
Chap5 Intents
Introduction
Les Intents sont des messages asynchrones qui permettent aux composants d'une application
de demander des fonctionnalités à d'autres composants. Par exemple, une activité peut
démarrer une autre activité. Android supporte deux types d'Intents, les Intents explicites et
implicites.
Pour créer un intent explicite il suffit de donner un Context qui appartient au package où se
trouve la classe de destination :
Intent i = new Intent(Context context, Class<?> cls);
Exemple
Intent i = new Intent(this, AutreActivity.class);
startActivity(i);
Dans certain cas, il ne faut pas mettre this mais faire appel à la méthode
getApplicationContext() si l'objet manipulant l'Intent n'hérite pas de Context.
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
1
<activity android:name=".MainActivity"> //declaration de l'activité
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"
</intent-filter>
</activity>
<activity android:name=".classe1">
Exemple
Intent i = new Intent(this, AutreActivity.class);
i.putExtra("myKey", Value);
startActivity(i);
xxx le type de l'extra et defaultValue la valeur qui sera retournée si la clé passée ne
correspond à aucun extra de l'intent.
Exemple
Intent i = getIntent();
int a = i.getIntExtra("key", 0);
En revanche, pour les types plus complexes tels que les tableaux, on ne peut préciser des
valeurs par défaut. Par exemple, pour un tableau de float on peut utiliser la méthode:
float[] getFloatArrayExtra(String key).
2
Exemple
Intent i = new Intent();
String[] noms = new String[] {"ali", "aziz"};
i.putExtra("cle1", noms); //On envoie les données
Remarque
Il existe deux façons de lancer l'Intent, selon que le composant de destination renvoie ou non
une réponse.
L'activité principale
public class MainActivity extends Activity {
private Button b = null;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
b = (Button) findViewById(R.id.bouton1);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, IntentActivity.class);
i.putExtra("AGE", 31);
startActivity(i);
} });
}}
La seconde activité
public class IntentActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_example);
// On récupère l'intent qui a lancé cette activité
Intent i = getIntent();
// Puis on récupère l'âge donné dans l'autre activité, ou 0 si cet extra n'est pas dans l'intent
int age = i.getIntExtra("AGE", 0); // 0 par défaut
if(age != 0)
// Traiter l'âge
}}
3
2- Avec retour : Récupérer le résultat d'une sous-activité
Cette fois, on veut qu'au retour de l'activité qui vient d'être appelée cette dernière renvoie un
feedback à l'activité source.
Quand la sous-activité se termine, elle retourne des données dans la méthode finish() à
l'appelant avec un intent.
Dans l'activité destination, on peut définir un résultat (à envoyer par putExtra à l'activité
principale) avec la méthode void setResult(int resultCode, Intent data) ou
void setResult (int resultCode)
Exemple
Activité principale
public class MainActivity extends Activity {
private Button mPasserelle = null;
// L'identifiant de notre requête
public final static int CHOOSE_BUTTON_REQUEST = 0;
4
mPasserelle.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, IntentExample.class);
i.putExtra("Valeur1", " Valeur un pour ActivityTwo ");
i.putExtra("Valeur2", " Valeur deux pour ActivityTwo");
startActivityForResult(i, CHOOSE_BUTTON_REQUEST);
}});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// On vérifie à quel Intent on fait référence ici à l'aide de notre identifiant
if (requestCode == CHOOSE_BUTTON_REQUEST) {
// On vérifie aussi que l'opération s'est bien déroulée
if (resultCode == RESULT_OK) {
Toast.makeText(this, "Vous avez choisi le bouton " + data.getStringExtra(BUTTONS),
Toast.LENGTH_SHORT).show();
//Toast.makeText(this, data.getExtras().getString("BUTTONS "), Toast.LENGTH_SHORT).show();
} } }}
La seconde activité
public class IntentExample extends Activity {
private Button mButton1 = null;
Intent I;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_example);
i = getIntent();
Toast.makeText(this, i.getExtras().getString("valeur1"), Toast.LENGTH_SHORT).show();
Exercice : réaliser deux taches différentes selon le résultat envoyé par la seconde activité.
public void onClick(View v) {
setResult(1);
finish();
}
protected void onActivityResult(int requestCode, int resultCode, Intent
data){
if (resultCode == 1)
Toast.makeText(this, "tache 1", Toast.LENGTH_LONG).show();
if (resultCode == 2)
Toast.makeText(this, "tache 2", Toast.LENGTH_LONG).show();
}
5
Les parcelables
Pour passer un objet complexe entre activités, en utilisant le Bundle, il suffit que la classe
représentant l'objet implémente l’interface Parcelable.
void writeToParcel(Parcel dest, int flags) : Sert à écrire l’objet dans un Parcel.
o dest est le Parcel dans lequel les attributs de l'objet seront insérés
o flags un entier.
Les attributs sont à insérer dans le Parcel dans l'ordre de déclaration dans la classe.
Une dernière étape est nécessaire, il s'agit de créer un objet CREATOR de la classe
Parcelable ainsi qu’un constructeur prenant comme argument un Parcel pour la classe
Contact.
Creator
Il faut ajouter un champ statique de type Parcelable.Creator et qui s'appellera
impérativement "CREATOR", sinon on ne peut reconstruire un objet qui est passé par un
Parcel.
Exemple
public static final Parcelable.Creator<Contact> CREATOR = new Parcelable.Creator<Contact>() {
public Contact createFromParcel(Parcel source) {
return new Contact(source);
}
public Contact[] newArray(int size) {
return new Contact[size];
}};
Utilisation
Un parcelable peut être ajouté dans un intent avec putExtra et on peut le récupérer avec
getParcelableExtra ou getParcelable.
7
Les intents implicites
Dans le cas des Intents implicites, la requête est envoyée à un destinataire sans le connaitre.
Les applications destinataires sont soit fournies par Android, soit par d'autres applications
organisées selon une certaine syntaxe.
Un Intent implicite spécifie l’action à exécuter et une URI (Uniform Resource Identifier)
optionnelle qui sera utilisée par cette action pour spécifier pour les données.
Pour les intents explicites, le composant cible est nommé, on active alors de tel composant.
Pour les intents implicites le composant cible n'est pas nommé, c'est au système de trouver la
ou les activités qui peuvent convenir à travers l'action et data.
Les données
Les données sont formatées à l'aide des URI (une chaîne de caractères qui permet d'identifier
un endroit).
Uri.parse() crée un objet Uri en utilisant le paramètre String. Cet objet permet de
faire référence à des ressources.
Pour créer un objet URI, on utilise la méthode statique Uri.parse(String uri).
Par exemple, pour envoyer un SMS à une personne, l'URI sera :
sms = Uri.parse("sms:0606060606");
Type MIME
8
Multipurpose Internet Mail Extensions (MIME) ou Extensions multifonctions du courrier
Internet : pour supporter des textes sous différents codage de caractères autres que l'ASCII,
des contenus non textuels.
Si une donnée est accompagnée du type MIME text, alors les données sont du texte. Mais
on trouve aussi audio et video...
Pour affiner les informations sur les données, on peut préciser un sous-type, par exemple
audio/mp3 et audio/wav sont deux types MIME.
Intent.setType(String type)
Ex : Intent.setType(image/jpeg)
Intent.setDataAndType(Uri data, String type)
Exemple
String s=editText1.getText().toString();
Intent intent=new Intent(Intent.ACTION_VIEW, Uri.parse(s));
startActivity(intent);
L'action
Une action est une constante qui se trouve dans la classe Intent et qui commence toujours
par "ACTION_" suivi d'un verbe (en anglais) de façon à bien comprendre qu'il s'agit d'une
action.
Par exemple, pour visualiser une donnée, on utilise l'action ACTION_VIEW.
Si on utilise une ACTION_VIEW sur un numéro de téléphone, alors le numéro de téléphone
s'affichera dans le composeur de numéros de téléphone.
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse("tel:+15555555555"));
Si un Intent implicite est envoyé au système Android, il recherche tous les composants qui
sont enregistrés avec l'action spécifique et le type de données approprié.
Si un seul composant est trouvé, Android démarre directement ce composant. Si plusieurs
composants sont identifiés par le système Android, une boîte de dialogue de sélection permet
à l'utilisateur d'indiquer quel composant utiliser.
9
Exemple : Créer un intent qui ouvre le composeur téléphonique dans une nouvelle activité :
Uri telephone = Uri.parse("tel:0606060606");
Intent i = new Intent(Intent.ACTION_DIAL, telephone);
startActivity(i);
La résolution d'intents est un processus d'association des intents avec les activités
voulant les recevoir.
Les filtres d'intents permettent de savoir quels intents seront traités par une activité.
Si un composant n'a aucun filtre, il ne peut recevoir que des intents explicites.
L'action
Indique le type d’action effectuée par l'activité (par exemple affichage, édition …).
Permet de filtrer les opérations en fonction du champ Action d'un intent dans le nœud
<intent-filter>.
Pour réussir ce test, l'action spécifiée dans l'objet Intent doit correspondre à l'une des
actions répertoriées dans le filtre.
Un intent sera accepté si tout ce qui se trouve dans son champ Action se trouve parmi les
actions du filtre, comme (New Intent(Intent.ACTION_VIEW))
Et si un intent ne précise pas d'action, alors il sera automatiquement accepté pour ce test.
Si le filtre n'a aucune action, alors tous les intents échouent le test.
Remarque
Les intents explicites sont toujours acceptés puisqu'ils n'ont pas de champ Action,
10
Exemple
<activity>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SENDTO" />
</intent-filter>
</activity>
Cette activité ne pourra intercepter que les intents qui ont dans leur champ action
ACTION_VIEW et/ou ACTION_SENDTO, car toutes ses actions sont acceptées par le filtre.
Comme :
public void onClick(View v) {
Intent intent = new Intent("android.intent.action.VIEW ");
startActivity(intent);
};
Mais si un intent a pour action ACTION_VIEW et ACTION_SEARCH, alors il ne sera pas accepté,
car une de ses actions n'est pas acceptée par le filtre.
La catégorie
Les catégories d'Intent permettent de grouper les applications par grands types de
fonctionnalités (clients emails, navigateurs, players de musique, etc...). Elles permettent
d'ajouter des informations supplémentaires. Par exemple CATEGORY_BROWSABLE
indique une activité qui peut être appelée par un navigateur.
Pour passer ce test il faut que toutes les catégories de l'Intent correspondent à des
catégories du filtre.
Le filtre peut contenir des catégories supplémentaires, mais il ne peut en omettre
aucun qui soit dans l'intention.
Les catégories du filtre doivent être le super-ensemble des catégories de l'objet Intent
Un Intent sans catégories doit toujours réussir ce test, quel que soit le contenu du filtre
Si un Intent n'a pas de catégorie, alors il passe automatiquement ce test, mais dès qu'un Intent
est utilisé avec la méthode startActivity(), alors on lui ajoute la catégorie
CATEGORY_DEFAULT.
Android traite tous les intents implicites passés à startActivity () comme s’ils contenaient au
moins une catégorie: "android.intent.category.DEFAULT"
Donc, pour qu'un composant accepte les Intents implicites, il faut rajouter cette catégorie au
filtre.
Pour les actions et les catégories, la syntaxe est différente entre Java et XML.
Java
Action : ACTION_VIEW, on utilise android.intent.action.VIEW
Catégorie : CATEGORY_DEFAULT on utilise android.intent.category.DEFAULT.
Filtres d'Intent
<activity>
<intent-filter>
11
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.exe4.intent.category.categorie1"/>
</intent-filter>
</activity>
Remarque
Ci-dessus, il faut que l'intent ait pour action ACTION_VIEW et/ou ACTION_SEARCH. Mais pour
les catégories, il doit avoir CATEGORY_DEFAULT et CATEGORY_categorie1.
Catégories
android.intent.category.LAUNCHER activité proposée au lancement par Android
android.intent.category.DEFAULT activité pouvant être lancée explicitement
android.intent.category.BROWSABLE peut afficher une information désignée par un lien
android.intent.category.TAB activité associée dans un onglet d’interface (TabHost).
Par exemple pour répondre à un clic sur un lien http://google.com, il faut construire un filtre
d'Intent utilisant la catégorie définissant ce qui est "navigable" tout en combinant cette
catégorie avec un type d'action signifiant que l'on souhaite "voir" la ressource. Il faut en plus
utiliser le tag data dans la construction du filtre:
Les données
Data indique le type de données transmises à l’activité lancée ou le type de réponse attendue
ainsi que le protocole (http, content, file …).
Il est possible de préciser plusieurs informations sur les données que cette activité peut traiter
comme android:scheme ou android:mimeType.
Chaque élément <data> peut spécifier un type de données (type de média MIME) et un URI.
Par exemple, si une application traite des fichiers textes qui proviennent d'Internet, on aura
besoin du type "texte" et du schéma "Internet" :
Un Intent qui contient à la fois un URI et un type de données (ou un type de données
pouvant être déduit de l'URI) ne transmet la partie du type de données du test que si son
type correspond à un type répertorié dans le filtre.
L'Intent passe le test si son URI correspond à un URI dans le filtre ou s'il a un contenu
alors le filtre ne spécifie pas d'URI.
12
Remarques
Les activités pouvant démarrer des applications ont des filtres avec
"android.intent.action.MAIN" spécifié comme action.
S'ils doivent être représentés dans le lanceur d'application, ils spécifient également la
catégorie "android.intent.category.LAUNCHER":
<intent-filter . . . >
<action android:name="code android.intent.action.MAIN" />
<category android:name="code android.intent.category.LAUNCHER" />
</intent-filter>
Le système Android remplit le lanceur d'applications (l'écran de niveau supérieur qui montre
les applications qui sont disponibles pour l'utilisateur à lancer), en trouvant toutes les activités
avec des filtres d'intention qui spécifient l'action "android.intent.action.MAIN" et de catégorie
"android.intent.category.LAUNCHER". Il affiche ensuite les icônes et les libellés de ces
activités dans le lanceur.
Si on n'a qu'un résultat, comme dans le cas des Intents explicites, alors ce résultat va
directement se lancer.
Exemple
public class MainActivity extends Activity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.recipient);
// Use ACTION_SENDTO action with correct data
Button sms1 = (Button) findViewById(R.id.sendto_sms);
sms1.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String uri = "smsto:" + edit.getText().toString();
Intent i = new Intent(android.content.Intent.ACTION_SENDTO,
Uri.parse(uri));
startActivity(i);
}
13
});
// Use our custom SMS_INTENT intent with correct data
Button sms2 = (Button) findViewById(R.id.smsintent_sms);
sms2.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String uri = "smsto:" + edit.getText().toString();
Intent i = new Intent("com.example.probook.SMS_INTENT",
Uri.parse(uri));
// put extra field
i.putExtra("from", "javacode ");
startActivity(i);
}
});
// Use our custom SMS_INTENT intent with incorrect data
Button exception = (Button) findViewById(R.id.exception);
exception.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
String uri = "mailto:" + edit.getText().toString();
Intent i = new Intent("com.example.probook.SMS_INTENT",
Uri.parse(uri));
i.putExtra("from", "javacode");
startActivity(i);
}
});
}
}
public class MySmsActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.intent_view);
String output = null;
TextView dataIntent = (TextView) findViewById(R.id.output_intent);
// take the data and the extras of the intent
Uri url = getIntent().getData();
Bundle extras = getIntent().getExtras();
output = url.toString();
// if there are extras, add them to the output string
if(extras != null){
output = output+ " from "+ extras.getString("from");
}
dataIntent.setText(output);
}
}
Le fichier manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.javacodegeeks.android.intentfiltertest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
14
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".MySmsActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.SENDTO" />
<action android:name="com.example.probook.SMS_INTENT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="smsto" />
</intent-filter>
</activity>
</application>
</manifest>
Package Manager
il est possible d'obliger l'utilisateur à choisir parmi plusieurs éventualités à l'aide de la
méthode createChooser(Intent target, CharSequence titre).
Dans tous les cas, on peut vérifier si un composant va réagir à un Intent à l'aide du
Package Manager.
Le Package Manager est un objet qui permet d'obtenir des informations sur les packages qui
sont installés sur l'appareil.
On fait appel au Package Manager avec la méthode getPackageManager().
Puis on peut demander à l'Intent le nom de l'activité qui va le gérer à l'aide de la méthode
ComponentName resolveActivity (PackageManager pm) :
15
Intents de Diffusion
Les Intents peuvent aussi être utilisées pour envoyer des messages de diffusion
au système Android.
Ce type d'intent est très utilisé au niveau du système pour transmettre un message à but
informatif, comme par exemple l'état de la batterie ou du réseau. Ces Intents s'appellent
Intents de diffusion ou broadcast intents.
Ainsi, toutes les applications pourront capturer ce message et récupérer l'information.
1. Créer un intent
Intent i = new Intent("nom.du.filtre");
3. Envoyer un broadcast
sendBroadcast( i );
Cette méthode contient le code que réalisera le BroadcastReceiver à la réception des Intents.
public class MyReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Intent Detected.", Toast.LENGTH_LONG).show();
}
}
16
Enregistrement d'un BroadcastReceiver :
Deux façons possibles :
- Statique : via AndroidManifest.xml
- Dynamique : via la méthode registerReceiver() de la classe Context :
public abstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter).
Enregistrement Statique :
Exemple
<receiver
android:name=".bonjourReceiver">
<intent-filter>
<action android:name="com.example.exemple1.intent.action.action1" />
<category android:name = "android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
}
Les étapes :
- Créer un BroadcastReceiver
- Enregistrer le BroadcastReceiver afin qu'il reçoive l'IntentFilter par la méthode
registerReceiver de la classe Context.
- Au besoin "désenregistrer" le BroadcastReceiver par la méthode unRegisterReceiver(...) de
la classe Context.
On crée une classe qui dérive de BroadcastReceiver sans l'enregistrer dans le Manifest.
Ensuite, lui rajouter des lois de filtrage avec la classe IntentFilter,
Exemple
Recevoir des broadcast intents et dire bonjour à l'utilisateur, mais uniquement quand
l'application se lance et qu'elle n'est pas en pause.
18
}
//Si vous déclarez le receiver dans onResume, n'oubliez pas de l'arrêter dans onPause
public void onPause() {
super.onPause();
unregisterReceiver(receiver);
}}
Il existe quelques messages diffusés par le système de manière native. Comme par exemple
ACTION_CAMERA_BUTTON qui est lancé dès que l'utilisateur appuie sur le bouton de
l'appareil photo.
Sécurité
N'importe quelle application peut envoyer des broadcast intents à votre receiver, ce
qui pose un problème au niveau sécurité.
On peut faire en sorte que le receiver déclaré dans le Manifest ne soit accessible qu'à
l'intérieur d'une application donnée en lui ajoutant l'attribut android:exported="false".
Exemple
<receiver android:name="BonjourReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.example.exemple1.intent.action.bonjour" />
</intent-filter>
</receiver>
Afin de déterminer qui peut recevoir un broadcast intent, il suffit de lui ajouter une
permission à l'aide de la méthode :
void sendBroadcast (Intent intent, String receiverPermission),
avec receiverPermission une permission à déterminer. Ainsi, seuls les receivers qui
déclarent cette permission pourront recevoir ces broadcast intents :
19
Lecture et réception des SMS
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<intent-filter android:priority="100">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
}}}}}
20