Licence CC BY-NC-SA

La téléphonie

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Il y a de grandes chances pour que votre appareil sous Android soit un téléphone. Et comme tous les téléphones, il est capable d'appeler ou d'envoyer des messages. Et comme nous sommes sous Android, il est possible de supplanter ces fonctionnalités natives pour les gérer nous-mêmes.

Encore une fois, tout le monde n'aura pas besoin de ce dont on va parler. Mais il peut très bien arriver que vous ayez envie qu'appuyer sur un bouton appelle un numéro d'urgence, ou le numéro d'un médecin, ou quoi que ce soit.

De plus, il faut savoir que le SMS — vous savez les petits messages courts qui font 160 caractères au maximum — est le moyen le plus courant pour communiquer entre deux appareils mobiles, il est donc très courant qu'un utilisateur ait envie d'en envoyer un à un instant t. Même s'ils sont beaucoup moins utilisés, les MMS — comme un SMS, mais avec un média (son, vidéo ou image) qui l'accompagne — sont monnaie courante.

Téléphoner

La première chose qu'on va faire, c'est s'assurer que l'appareil sur lequel fonctionnera votre application peut téléphoner, sinon notre application de téléphonie n'aura absolument aucun sens. Pour cela, on va indiquer dans notre Manifest que l'application ne peut marcher sans la téléphonie, en lui ajoutant la ligne suivante :

1
2
<uses-feature android:name="android.hardware.telephony"
  android:required="true" />

De cette manière, les utilisateurs ne pourront pas télécharger votre application sur le Play Store s'ils se trouvent sur leur tablette par exemple.

Maintenant, d'un point de vue technique, nous allons utiliser l'API téléphonique qui est incarnée par la classe TelephonyManager. Les méthodes fournies dans cette classe permettent d'obtenir des informations sur le réseau et d'accéder à des informations sur l'abonné. Vous l'aurez remarqué, il s'agit encore une fois d'un Manager ; ainsi, pour l'obtenir, on doit en demander l'accès au Context :

1
TelephonyManager manager = Context.getSystemService(Context.TELEPHONY_SERVICE);

Obtenir des informations

Ensuite, si nous voulons obtenir des informations sur l'appareil, il faut demander la permission :

1
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

Informations statiques

Tout d'abord, on peut déterminer le type du téléphone avec int getPhoneType(). Cette méthode peut retourner trois valeurs :

  • TelephonyManager.PHONE_TYPE_NONE si l'appareil n'est pas un téléphone ou ne peut pas téléphoner.
  • TelephonyManager.PHONE_TYPE_GSM si le téléphone exploite la norme de téléphonie mobile GSM. En France, ce sera le cas tout le temps.
  • TelephonyManager.PHONE_TYPE_CDMA si le téléphone exploite la norme de téléphonie mobile CDMA. C'est une technologie vieillissante, mais encore très en vogue en Amérique du Nord.

Pour obtenir un identifiant unique de l'appareil, vous pouvez utiliser String getDeviceId(), et il est (parfois) possible d'obtenir le numéro de téléphone de l'utilisateur avec String getLineNumber().

Informations dynamiques

Les informations précédentes étaient statiques, il y avait très peu de risques qu'elles évoluent pendant la durée de l'exécution de l'application. Cependant, il existe des données liées au réseau qui risquent de changer de manière régulière. Pour observer ces changements, on va passer par une interface dédiée : PhoneStateListener. On peut ensuite indiquer quels changements d'état on veut écouter avec le TelephonyManager en utilisant la méthode void listen (PhoneStateListener listener, int events) avec events des flags pour indiquer quels évènements on veut écouter. On note par exemple la présence des flags suivants :

  • PhoneStateListener.LISTEN_CALL_STATE pour savoir que l'appareil déclenche ou reçoit un appel.
  • PhoneStateListener.LISTEN_DATA_CONNECTION_STATE pour l'état de la connexion avec internet.
  • PhoneStateListener.LISTEN_DATA_ACTIVITY pour l'état de l'échange des données avec internet.
  • PhoneStateListener.LISTEN_CELL_LOCATION pour être notifié des déplacements de l'appareil.
1
2
3
TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// Pour écouter les trois évènements
manager.listen(new PhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_DATA_ACTIVITY | PhoneStateListener.LISTEN_CELL_LOCATION);

Bien entendu, si vous voulez que l'appareil puisse écouter les déplacements, il vous faudra demander la permission avec ACCESS_COARSE_LOCATION, comme pour la localisation expliquée au chapitre précédent.

Ensuite, à chaque évènement correspond une méthode de callback à définir dans votre implémentation de PhoneStateListener :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
protected String TAG = "TelephonyExample";

PhoneStateListener stateListener = new PhoneStateListener() {

  // Appelée quand est déclenché l'évènement LISTEN_CALL_STATE
  @Override
  public void onCallStateChanged (int state, String incomingNumber) {
    switch (state) {
    case TelephonyManager.CALL_STATE_IDLE :
      Log.d(TAG, "Pas d'appel en cours");
      break;
    case TelephonyManager.CALL_STATE_OFFHOOK :
      Log.d(TAG, "Il y a une communication téléphonique en cours");
      break;
    case TelephonyManager.CALL_STATE_RINGING :
      Log.d(TAG, "Le téléphone sonne, l'appelant est " + incomingNumber);
      break;
    default : 
      Log.d(TAG, "Etat inconnu");
    }
  }

  // Appelée quand est déclenché l'évènement LISTEN_DATA_CONNECTION_STATE 
  @Override
  public void onDataConnectionStateChanged (int state) {
    switch (state) {
    case TelephonyManager.DATA_CONNECTED :
      Log.d(TAG, "L'appareil est connecté.");
      break;
    case TelephonyManager.DATA_CONNECTING :
      Log.d(TAG, "L'appareil est en train de se connecter.");
      break;
    case TelephonyManager.DATA_DISCONNECTED :
      Log.d(TAG, "L'appareil est déconnecté.");
      break;
    case TelephonyManager.DATA_SUSPENDED :
      Log.d(TAG, "L'appareil est suspendu de manière temporaire.");
      break;
    }
  }

  // Appelée quand est déclenché l'évènement LISTEN_DATA_ACTIVITY 
  @Override
  public void onDataActivity (int direction) {
    switch (direction) {
    case TelephonyManager.DATA_ACTIVITY_IN :
      Log.d(TAG, "L'appareil est en train de télécharger des données.");
      break;
    case TelephonyManager.DATA_ACTIVITY_OUT :
      Log.d(TAG, "L'appareil est en train d'envoyer des données."); 
      break;
    case TelephonyManager.DATA_ACTIVITY_INOUT :
      Log.d(TAG, "L'appareil est en train de télécharger ET d'envoyer des données.");
      break;
    case TelephonyManager.DATA_ACTIVITY_NONE :
      Log.d(TAG, "L'appareil n'envoie pas de données et n'en télécharge pas.");
      break;
    }
  }

  // Appelée quand est déclenché l'évènement LISTEN_SERVICE_STATE
  @Override
  public void onServiceStateChanged(ServiceState serviceState) {
    // Est-ce que l'itinérance est activée ?
    Log.d(TAG, "L'itinérance est activée : " + serviceState.getRoaming());
    switch (serviceState.getState()) {
    case ServiceState.STATE_IN_SERVICE :
      Log.d(TAG, "Conditions normales d'appel");
      // Pour obtenir un identifiant de l'opérateur
      Log.d(TAG, "L'opérateur est " + serviceState.getOperatorAlphaLong());
      break;
    case ServiceState.STATE_EMERGENCY_ONLY :
      Log.d(TAG, "Seuls les appels d'urgence sont autorisés.");
      break;
    case ServiceState.STATE_OUT_OF_SERVICE :
      Log.d(TAG, "Ce téléphone n'est pas lié à un opérateur actuellement.");
      break;
    case ServiceState.STATE_POWER_OFF :
      Log.d(TAG, "Le téléphone est en mode avion");
      break;
    default : 
      Log.d(TAG, "Etat inconnu");
    }
  }
};

Téléphoner

Pour téléphoner, c'est très simple. En fait, vous savez déjà le faire. Vous l'avez peut-être même déjà fait. Il vous suffit de lancer un Intent qui a pour action Intent.ACTION_CALL et pour données tel:numéro_de_téléphone :

1
2
Intent appel = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:0102030405"));
startActivity(appel);

Cette action va lancer l'activité du combiné téléphonique pour que l'utilisateur puisse initier l'appel par lui-même. Ainsi, il n'y a pas besoin d'autorisation puisqu'au final l'utilisateur doit amorcer l'appel manuellement. Cependant, il se peut que vous souhaitiez que votre application lance l'appel directement, auquel cas vous devrez demander une permission particulière :

1
<uses-permission android:name="android.permission.CALL_PHONE" />

Envoyer et recevoir des SMS et MMS

L'envoi

Tout comme pour passer des appels, il existe deux manières de faire : soit avec l'application liée aux SMS, soit directement par l'application.

Prise en charge par une autre application

Pour transmettre un SMS à une application qui sera en charge de l'envoyer, il suffit d'utiliser un Intent. Il utilisera comme action Intent.ACTION_SENDTO, aura pour données un smsto:numéro_de_téléphone pour indiquer à qui sera envoyé le SMS, et enfin aura un extra de titre sms_body qui indiquera le contenu du message :

1
2
3
Intent sms = new Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:0102030405");
sms.putExtra("sms_body", "Salut les Zéros !");
startActivity(sms);

Pour faire de même avec un MMS, c'est déjà plus compliqué. Déjà, les MMS ne fonctionnent pas avec SENDTO mais avec SEND tout court. De plus, le numéro de téléphone de destination devra être défini dans un extra qui s'appellera address. Enfin, le média associé au MMS sera ajouté à l'intent dans les données et dans un extra de nom Intent.EXTRA_STREAM à l'aide d'une URI qui pointe vers lui :

1
2
3
4
5
6
7
8
9
Uri image = Uri.fromFile("/sdcard/images/zozor.jpg");
Intent mms = new Intent(Intent.ACTION_SEND, image);
mms.putExtra(Intent.EXTRA_STREAM, image);
mms.setType("image/jpeg");

mms.putExtra("sms_body", "Salut les Zéros (mais avec une image !)");
mms.putExtra("address", "0102030405");

startActivity(mms);

Prise en charge directe

Tout d'abord, on a besoin de demander la permission :

1
<uses-permission android:name="android.permission.SEND_SMS" />

Pour envoyer directement un SMS sans passer par une application externe, on utilise la classe SmsManager.

Ah ! J'imagine qu'on peut la récupérer en faisant Context.getSystemService(Context.SMS_SERVICE), j'ai compris le truc maintenant ! :D

Pour une fois, même si le nom de la classe se termine par « Manager », on va instancier un objet avec la méthode static SmsManager SmsManager.getDefault(). Pour envoyer un message, il suffit d'utiliser la méthode void sendTextMessage(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) :

  • Il vous faut écrire le numéro du destinataire dans destinationAddress.
  • Vous pouvez spécifier un centre de service d'adressage qui va gérer l'envoi du SMS dans scAddress. Si vous n'avez aucune idée de quoi mettre, un null sera suffisant.
  • text contient le texte à envoyer.
  • Il vous est possible d'insérer un PendingIntent dans sentIntent si vous souhaitez avoir des nouvelles de la transmission du message. Ce PendingIntent sera transmis à tout le système de façon à ce que vous puissiez le récupérer si vous le voulez. Le PendingIntent contiendra le code de résultat Activity.RESULT_OK si tout s'est bien passé. Enfin, vous pouvez aussi très bien mettre null.
  • Encore une fois, vous pouvez mettre un PendingIntent dans deliveryIntent si vous souhaitez avoir des informations complémentaires sur la transmission.

On peut ainsi envisager un exemple simple :

1
2
SmsManager manager = SmsManager.getDefault();
manager .sendTextMessage("0102030405", null, "Salut les Zéros !", null, null);

La taille maximum d'un SMS est de 160 caractères ! Vous pouvez cependant couper un message trop long avec ArrayList<String> divideMessage(String text), puis vous pouvez envoyer les messages de façon à ce qu'ils soient liés les uns aux autres avec void sendMultipartTextMessage(String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents), les paramètres étant analogues à ceux de la méthode précédente.

Malheureusement, envoyer des MMS directement n'est pas aussi simple, c'est même tellement complexe que je ne l'aborderai pas !

Recevoir des SMS

On va faire ici quelque chose d'un peu étrange. Disons le carrément, on va s'enfoncer dans la quatrième dimension. En fait, recevoir des SMS n'est pas réellement prévu de manière officielle dans le SDK. C'est à vous de voir si vous voulez le faire.

La première chose à faire est de demander la permission dans le Manifest :

1
<uses-permission android:name="android.permission.RECEIVE_SMS" />

Ensuite, dès que le système reçoit un nouveau SMS, un broadcast intent est émis avec comme action android.provider.Telephony.SMS_RECEIVED. C'est donc à vous de développer un broadcast receiver qui gérera la réception du message. L'Intent qui enclenchera le Receiver contiendra un tableau d'objets qui s'appelle « pdus » dans les extras. Les PDU sont les données qui sont transmises et qui représentent le message, ou les messages s'il a été divisé en plusieurs. Vous pourrez ensuite, à partir de ce tableau d'objets, créer un tableau de SmsMessage avec la méthode static SmsMessage SmsMessage.createFromPdu(byte[] pdu) :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// On récupère tous les extras
Bundle bundle = intent.getExtras();
if(bundle != null) {
  // Et on récupère le tableau d'objets qui s'appelle « pdus »
  Object[] pdus = (Object[]) bundle.get("pdus");

  // On crée un tableau de SmsMessage pour chaque message
  SmsMessage[] msg = new SmsMessage[pdus.length];

  // Puis, pour chaque tableau, on crée un message qu'on insère dans le tableau
  for(Object pdu : pdus)
    msg[i] = SmsMessage.createFromPdu((byte[]) pdu);
}

Vous pouvez ensuite récupérer des informations sur le message avec diverses méthodes : le contenu du message avec String getMessageBody(), le numéro de l'expéditeur avec String getOriginatingAddress() et le moment de l'envoi avec long getTimestampMillis().


  • On peut obtenir beaucoup d'informations différentes sur le téléphone et le réseau de téléphonie auquel il est relié à l'aide de TelephonyManager. Il est même possible de surveiller l'état d'un téléphone avec PhoneStateListener de manière à pouvoir réagir rapidement à ses changements d'état.
  • Envoyer des SMS se fait aussi assez facilement grâce à SmsMessage ; en revanche, envoyer des MMS est beaucoup plus complexe et demande un travail important.