76 votes

Comment lire les données MMS sur Android ?

Je veux lire les données MMS que j'ai vues dans la table des parties dans le fichier mmssms.db où les entrées MMS sont stockées; j'utilise un curseur et je veux connaître le URI approprié; j'utilise "content://mms-sms/conversations" et les noms de colonnes de "Address"(Envoyé à), "Texte" ou "Sujet" et le nom de colonne "Data" de l'image.

J'ai vu le schéma de mmssms.db et leurs colonnes de la table des parties.

0 votes

La base de données mmssms.db fait partie du micrologiciel et n'est pas accessible par les applications Android. Le fournisseur de contenu content://mms-sms/conversations ne fait pas partie du SDK et ne doit pas être accédé par les applications Android.

0 votes

Je fais quelque chose de similaire ICI! stackoverflow.com/questions/11556633/…

284voto

Cristian Points 92147

Il est assez difficile de trouver de la documentation à ce sujet, je vais donc rassembler ici toutes les informations que j'ai trouvées. Si vous êtes pressé ou si vous n'aimez pas lire, passez directement à l'adresse suivante Comment obtenir des données à partir d'un SMS section.

contenu://mms-sms/conversations

Il s'agit de l'URI de la Fournisseur de MMS et de SMS ... qui nous permet d'interroger les bases de données MMS et SMS en même temps, et de les mélanger dans un seul thread (qui sont appelés conversations ).

Pourquoi l'URI est-il important ? Eh bien, c'est la manière standard de recevoir des MMS et des SMS ; par exemple, lorsque vous recevez un SMS et que vous cliquez sur la barre de notification, celle-ci envoie une intention de diffusion comme celle-ci : content://mms-sms/conversations/XXXXXX est l'identifiant de la conversation.

Obtenir une liste de toutes les conversations

La seule chose que vous devez faire est d'interroger le site Web de la Commission européenne. content://mms-sms/conversations Uri :

ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"*"};
Uri uri = Uri.parse("content://mms-sms/conversations/");
Cursor query = contentResolver.query(uri, projection, null, null, null);

Note : En général, quand vous appelez query et que vous voulez retourner toutes les colonnes, vous pouvez passer null comme le projection paramètre. Cependant, vous ne pouvez pas le faire avec ce fournisseur, c'est pourquoi j'utilise le paramètre * .

Maintenant vous pouvez boucler à travers le Cursor comme d'habitude. Ce sont les colonnes les plus importantes que vous voudrez utiliser :

  • _id est l'identifiant du message. Le capitaine évident à la rescousse ? Pas vraiment. Cet identifiant peut être utilisé pour récupérer des informations détaillées en utilisant soit content://sms ou content://mms .
  • date aucune explication n'est nécessaire.
  • thread_id est l'ID de la conversation
  • body Le contenu du dernier SMS de cette conversation. S'il s'agit d'un MMS, même s'il a une partie texte, ce sera null .

Note : si vous interrogez content://mms-sms/conversations il retournera une liste de différentes conversations dont _id est le dernier SMS ou MMS de chaque conversation. Si vous interrogez content://mms-sms/conversations/xxx il retournera chaque SMS et/ou MMS sur la conversation dont l'ID est xxx .

Comment faire la différence entre les SMS et les MMS

En général, vous voudrez savoir quel type de message vous traitez. La documentation le dit :

Une colonne virtuelle, MmsSms.TYPE_DISCRIMINATOR_COLUMN peut être demandée dans la projection d'une requête. Sa valeur est soit "mms", soit "sms", selon que le message message représenté par la ligne est un message message MMS ou un message SMS, respectivement.

Je pense qu'il s'agit de cette variable ... mais je n'ai pas réussi à le faire fonctionner. Si vous l'avez fait, dites-moi comment ou modifiez ce message.

Jusqu'à présent, c'est ce que j'ai fait et cela semble fonctionner, mais il doit y avoir de meilleures méthodes :

ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"_id", "ct_t"};
Uri uri = Uri.parse("content://mms-sms/conversations/");
Cursor query = contentResolver.query(uri, projection, null, null, null);
if (query.moveToFirst()) {
    do {
        String string = query.getString(query.getColumnIndex("ct_t"));
        if ("application/vnd.wap.multipart.related".equals(string)) {
            // it's MMS
        } else {
            // it's SMS
        }
    } while (query.moveToNext());
}

Comment obtenir des données à partir d'un SMS

Donc vous avez l'ID du SMS, alors la seule chose que vous avez à faire est :

String selection = "_id = "+id;
Uri uri = Uri.parse("content://sms");
Cursor cursor = contentResolver.query(uri, null, selection, null, null);
String phone = cursor.getString(cursor.getColumnIndex("address"));
int type = cursor.getInt(cursor.getColumnIndex("type"));// 2 = sent, etc.
String date = cursor.getString(cursor.getColumnIndex("date"));
String body = cursor.getString(cursor.getColumnIndex("body"));

Comment récupérer les données d'un MMS ?

Les MMS sont un peu différents. Ils peuvent être constitués de différentes parties (texte, audio, images, etc.). Nous allons donc voir comment récupérer chaque type de données séparément.

Supposons que nous ayons l'identifiant du MMS dans le champ mmsId variable. Nous pouvons obtenir des informations détaillées sur ce MMS en utilisant la variable content://mms/ fournisseur :

Uri uri = Uri.parse("content://mms/");
String selection = "_id = " + mmsId;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);

Cependant, la seule colonne intéressante est read qui est 1 si le message a déjà été lu.

Comment récupérer le contenu textuel d'un MMS

Ici, nous devons utiliser content://mms/part ... par exemple :

String selectionPart = "mid=" + mmsId;
Uri uri = Uri.parse("content://mms/part");
Cursor cursor = getContentResolver().query(uri, null,
    selectionPart, null, null);
if (cursor.moveToFirst()) {
    do {
        String partId = cursor.getString(cursor.getColumnIndex("_id"));
        String type = cursor.getString(cursor.getColumnIndex("ct"));
        if ("text/plain".equals(type)) {
            String data = cursor.getString(cursor.getColumnIndex("_data"));
            String body;
            if (data != null) {
                // implementation of this method below
                body = getMmsText(partId);
            } else {
                body = cursor.getString(cursor.getColumnIndex("text"));
            }
        }
    } while (cursor.moveToNext());
}

Elle peut contenir différentes parties de texte... mais en général, il n'y en a qu'une seule. Ainsi, si vous voulez supprimer la boucle, cela fonctionnera la plupart du temps. Voici comment la getMmsText ressemble à une méthode :

private String getMmsText(String id) {
    Uri partURI = Uri.parse("content://mms/part/" + id);
    InputStream is = null;
    StringBuilder sb = new StringBuilder();
    try {
        is = getContentResolver().openInputStream(partURI);
        if (is != null) {
            InputStreamReader isr = new InputStreamReader(is, "UTF-8");
            BufferedReader reader = new BufferedReader(isr);
            String temp = reader.readLine();
            while (temp != null) {
                sb.append(temp);
                temp = reader.readLine();
            }
        }
    } catch (IOException e) {}
    finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {}
        }
    }
    return sb.toString();
}

Comment obtenir une image à partir d'un MMS

C'est la même chose que de récupérer la partie texte... la seule différence est que vous chercherez un type de mime différent :

String selectionPart = "mid=" + mmsId;
Uri uri = Uri.parse("content://mms/part");
Cursor cPart = getContentResolver().query(uri, null,
    selectionPart, null, null);
if (cPart.moveToFirst()) {
    do {
        String partId = cPart.getString(cPart.getColumnIndex("_id"));
        String type = cPart.getString(cPart.getColumnIndex("ct"));
        if ("image/jpeg".equals(type) || "image/bmp".equals(type) ||
                "image/gif".equals(type) || "image/jpg".equals(type) ||
                "image/png".equals(type)) {
            Bitmap bitmap = getMmsImage(partId);
        }
    } while (cPart.moveToNext());
}

C'est ainsi que le getMmsImage ressemble à une méthode :

private Bitmap getMmsImage(String _id) {
    Uri partURI = Uri.parse("content://mms/part/" + _id);
    InputStream is = null;
    Bitmap bitmap = null;
    try {
        is = getContentResolver().openInputStream(partURI);
        bitmap = BitmapFactory.decodeStream(is);
    } catch (IOException e) {}
    finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {}
        }
    }
    return bitmap;
}

Comment obtenir l'adresse de l'expéditeur

Vous devrez utiliser le content://mms/xxx/addrxxx est l'identifiant du MMS :

private String getAddressNumber(int id) {
    String selectionAdd = new String("msg_id=" + id);
    String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
    Uri uriAddress = Uri.parse(uriStr);
    Cursor cAdd = getContentResolver().query(uriAddress, null,
        selectionAdd, null, null);
    String name = null;
    if (cAdd.moveToFirst()) {
        do {
            String number = cAdd.getString(cAdd.getColumnIndex("address"));
            if (number != null) {
                try {
                    Long.parseLong(number.replace("-", ""));
                    name = number;
                } catch (NumberFormatException nfe) {
                    if (name == null) {
                        name = number;
                    }
                }
            }
        } while (cAdd.moveToNext());
    }
    if (cAdd != null) {
        cAdd.close();
    }
    return name;
}

Dernières réflexions

  • Je ne comprends pas pourquoi Google, avec ces milliers de millions de dollars, ne paie pas un étudiant ou quelqu'un d'autre pour documenter cette API. Vous devez vérifier le code source pour savoir comment cela fonctionne et, ce qui est pire, ils ne rendent pas publiques les constantes utilisées dans les colonnes de la base de données, nous devons donc les écrire manuellement.
  • Pour d'autres types de données dans un MMS, vous pouvez appliquer la même idée que ci-dessus... il s'agit simplement de connaître le type de mime.

2 votes

En ce qui concerne content://mms-sms/conversations. Cette URL contient une liste de tous les fils de discussion. Mais pas des messages séparés (SMS ou MMS). Il est donc inutile de connaître les SMS ou MMS car il n'y en a aucun.

0 votes

Y aurait-il une raison pour laquelle tous mes types MIME MMS reviennent en tant qu'application/smil?

2 votes

Justin, car les MMS sont stockés dans la base de données sous forme de diaporama en utilisant SMIL.

10voto

Kenneth Evans Points 725

La réponse de Christian est excellente. Cependant, la méthode pour obtenir l'adresse de l'expéditeur n'a pas fonctionné pour moi. L'instruction Long.parseLong ne fait rien sauf peut-être lever une exception et new String(...) ?.

Sur mon appareil, le curseur compte 2 ou plus. Le premier a généralement un "type" de 137 et les autres ont un "type" de 151. Je ne trouve pas où cela est documenté, mais on peut déduire que 137 est "de" et 151 est "à". Ainsi, si j'exécute la méthode telle quelle, je n'obtiens pas d'exception et cela renvoie la dernière ligne, qui est un destinataire et seulement un parmi plusieurs dans de nombreux cas.

Aussi, à ce que je sache, la sélection n'est pas nécessaire car toutes les lignes ont le même msg_id. Cependant, cela ne fait pas de mal.

Voici ce qui fonctionne pour moi pour obtenir l'adresse de l'expéditeur :

public static String getMMSAddress(Context context, String id) {
    String addrSelection = "type=137 AND msg_id=" + id;
    String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
    Uri uriAddress = Uri.parse(uriStr);
    String[] columns = { "address" };
    Cursor cursor = context.getContentResolver().query(uriAddress, columns,
            addrSelection, null, null);
    String address = "";
    String val;
    if (cursor.moveToFirst()) {
        do {
            val = cursor.getString(cursor.getColumnIndex("address"));
            if (val != null) {
                address = val;
                // Utilisez le premier trouvé s'il y en a plus d'un
                break;
            }
        } while (cursor.moveToNext());
    }
    if (cursor != null) {
        cursor.close();
    }
    // return address.replaceAll("[^0-9]", "");
    return address;
}

Je me fichais de savoir si c'était tout numérique, mais j'ai inclus un moyen d'éliminer tout sauf les chiffres comme un commentaire si cela est désiré. Cela peut facilement être modifié pour renvoyer tous les destinataires également.

Je suppose que cela a fonctionné pour lui. Il semble que cela donnerait la bonne réponse si l'exception se produisait sur la première ligne.

1 votes

Je ne tenterais pas d'appeler parseLong sur le champ contact_id ; traitez-le comme une chaîne. En fait, il est tout à fait possible que ce soit une adresse e-mail ou quelque chose du genre, dans le cas des passerelles e-mail vers MMS.

4 votes

Pour clarifier les constantes de type, elles proviennent de la classe PduHeaders: 0x97 / 151 est PduHeaders.TO et 0x89 / 137 est PduHeaders.FROM. D'autres valeurs valides de référence sont : 0x81 / 129 est PduHeaders.BCC et 0x82 / 130 est PduHeaders.CC. Voir aussi Telephony.Mms.Addr.

3voto

Gustav Points 21

J'ai dû apporter quelques modifications pour que cela fonctionne pour moi.

  1. Lorsque je récupère le cursor.getString(cursor.getColumnIndex("type")) du contenu mms-sms/conversations, ("content://mms-sms/conversations/") je teste la valeur du champ "type" pour null. Si la variable est null - c'est-à-dire

    String otype = c.getString(c.getColumnIndex("type"));
    if(otype != null) {
        //c'est un sms - traitez-le...

    le message est un SMS, sinon c'est un MMS. Pour les MMS, vous devez tester les deux types MIME comme suit :-

    if (("application/vnd.wap.multipart.related".equalsIgnoreCase(msg_type)
        ||"application/vnd.wap.multipart.mixed".equalsIgnoreCase(msg_type))
        && !id.equalsIgnoreCase(lastMMSID)) {
             //c'est un MMS - traitez-le...
  2. Lorsque vous utilisez un ContentObserver pour surveiller les changements de contenu des messages, il déclenche plusieurs notifications pour le même message. J'utilise une variable statique - dans mon cas lastMMSID - pour suivre le message.

  3. Ce code fonctionne bien pour récupérer le contenu des messages entrants et sortants. Il est important d'itérer à travers tous les enregistrements retournés par l'uri "content://mms/part/" pour accéder au contenu - texte et/ou pièces jointes - du MMS.

  4. La seule manière que j'ai trouvée qui fonctionne assez bien pour différencier entre les MMS entrants et sortants, est de tester le statut null du champ "m_id" du contenu mms-sms/conversations.

    String m_id = c.getString(c.getColumnIndex("m_id"));
    String mDirection = m_id == null? "OUT": "IN";

Une dernière réflexion sur la façon d'obtenir le champ Adresse. Pour une raison quelconque, le contenu de l'adresse n'aime pas être interrogé avec un paramètre {" * "}, mais cela fonctionne :-

final String[] projection = new String[] {"address", "contact_id", "charset", "type"};

S'il s'agit d'un message sortant, le "type" à rechercher sera 151. Pour un message entrant, le "type" sera 137. Un morceau de code totalement fonctionnel ressemblera à ceci :-

private String getANumber(int id) {
    String add = "";
    final String[] projection = new String[] {"address","contact_id","charset","type"};
    final String selection = "type=137 or type=151"; // PduHeaders
    Uri.Builder builder = Uri.parse("content://mms").buildUpon();
    builder.appendPath(String.valueOf(id)).appendPath("addr");

    Cursor cursor = context.getContentResolver().query(
        builder.build(),
        projection,
        selection,
        null, null);

if (cursor.moveToFirst()) {
          do {
              String add = cursor.getString(cursor.getColumnIndex("address"));
              String type: cursor.getString(cursor.getColumnIndex("type"));
          } while(cursor.moveToNext());
      }
      // Les messages sortants ont un type d'adresse 137 et la valeur sera 'insert-address-token'
      // Les messages sortants ont un type d'adresse 151 et la valeur sera l'adresse
      // Des vérifications supplémentaires peuvent être effectuées ici pour retourner la bonne adresse.
      return add;
}

À tous les courageux guerriers qui m'ont précédé dans ce post - Je vous remercie du fond du cœur!

1voto

Asahi Points 5938

Jetez un coup d'œil à ceci. Ça a bien fonctionné pour moi.

1voto

Hassan Nabil Points 11

La réponse donnée ci-dessus pour obtenir getMMSAddress() ne doit pas contenir la boucle while (cursor.moveToNext());. Il doit simplement extraire l'adresse du premier élément dans le curseur. Pour une raison inconnue pour moi, ce curseur contient plus d'un enregistrement. Le premier contient l'adresse de l'expéditeur. Les autres éléments du curseur au-delà du premier contiennent l'adresse du destinataire. Ainsi, le code tel qu'il est renvoie l'adresse du destinataire et non celle de l'expéditeur.

Cela a été très utile pour ouvrir le contenu d'un MMS.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X