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 !
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, unnull
sera suffisant. text
contient le texte à envoyer.- Il vous est possible d'insérer un
PendingIntent
danssentIntent
si vous souhaitez avoir des nouvelles de la transmission du message. CePendingIntent
sera transmis à tout le système de façon à ce que vous puissiez le récupérer si vous le voulez. LePendingIntent
contiendra le code de résultatActivity.RESULT_OK
si tout s'est bien passé. Enfin, vous pouvez aussi très bien mettrenull
. - Encore une fois, vous pouvez mettre un
PendingIntent
dansdeliveryIntent
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 avecPhoneStateListener
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.