Как читать данные MMS на Android?

Я хочу прочитать данные MMS. Я видел таблицу mmssms.db в mmssms.db где хранятся записи mms; Я использую курсор, и я хочу знать соответствующий URI ; Я использую «content: // mms-sms / conversations» и имена столбцов имен столбца «Адрес» (отправлено в), «Текст» или «Тема» и «Данные».

Я видел схему mmssms.db и их колонку таблицы mmssms.db .

Solutions Collecting From Web of "Как читать данные MMS на Android?"

Трудно найти документацию об этом, поэтому я собираю здесь всю информацию, которую я нашел. Если вы находитесь в спешке или просто не любите читать, перейдите к разделу « Как получить данные из раздела SMS ».

Содержание: // ммс-SMS / разговоры

Это URI провайдера Mms и SMS … который позволяет нам одновременно запрашивать базы данных MMS и SMS и смешивать их в одном потоке (который называется цепочками ).

Почему это важно? Ну, это стандартный способ получения MMS и SMS-сообщений; Например, когда вы получаете SMS-сообщение и нажимаете на панель уведомлений, он отправляет намерение трансляции следующим образом: content://mms-sms/conversations/XXX , где XXX является идентификатором разговора.

Получить список всех разговоров

Единственное, что вам нужно сделать, это запросить 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); 

Примечание. Обычно, когда вы вызываете query и хотите вернуть все столбцы, вы можете передать значение null в качестве параметра projection . Однако вы не можете сделать это с этим провайдером, поэтому я использую * .

Теперь вы можете прокручивать Cursor как обычно. Это наиболее важные столбцы, которые вы хотели бы использовать:

  • _id – это идентификатор сообщения. Капитан очевидный для спасения? На самом деле, нет. Этот идентификатор можно использовать для извлечения подробной информации с использованием любого content://sms или content://mms .
  • date не требуется.
  • thread_id – это идентификатор разговора
  • body Содержание последнего SMS в этом разговоре. Если это MMS, даже если у него есть текстовая часть, это будет null .

Примечание: если вы запрашиваете content://mms-sms/conversations он вернет список разных _id чье _id является последним SMS или MMS в каждом разговоре. Если вы запрашиваете content://mms-sms/conversations/xxx он вернет каждый SMS и / или MMS в разговор, чей идентификатор – xxx .

Как различать SMS и MMS

Обычно вам нужно знать, какой тип сообщения вы обрабатываете. Документация гласит:

В запросе для запроса может быть запрошен виртуальный столбец MmsSms.TYPE_DISCRIMINATOR_COLUMN . Его значение равно «mms» или «sms», в зависимости от того, является ли сообщение, представленное этой строкой, MMS-сообщением или SMS-сообщением, соответственно.

Я думаю, что это относится к этой переменной … однако я не смог заставить ее работать. Если вы скажете мне, как это изменить или отредактировать.

Пока это то, что я сделал, и это работает, но должны быть лучшие способы:

 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()); } 

Как получить данные из SMS

Таким образом, у вас есть идентификатор SMS, тогда единственное, что вам нужно сделать, это:

 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")); 

Как получить данные из данных MMS?

MMS-сообщения немного отличаются. Они могут быть построены с различными частями (текст, аудио, изображения и т. Д.); Поэтому здесь будет показано, как извлекать каждый вид данных отдельно.

Итак, давайте предположим, что у меня есть идентификатор MMS в переменной mmsId . Мы можем получить подробную информацию об этом MMS, используя content://mms/ provider:

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

Однако read только один интересный столбец, который равен 1 если сообщение уже было прочитано.

Как получить текстовый контент из MMS

Здесь мы должны использовать content://mms/part … например:

 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()); } 

Он может содержать разные части текста … но обычно это будет только один. Поэтому, если вы хотите удалить цикл, он будет работать большую часть времени. Вот как getMmsText метод getMmsText :

 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(); } 

Как получить изображение из MMS

Это то же самое, что и получение текстовой части … Единственное различие заключается в том, что вы будете искать другой тип mime:

 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()); } 

Вот как getMmsImage метод getMmsImage :

 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; } 

Как получить адрес отправителя

Вам нужно будет использовать content://mms/xxx/addr provider, где xxx – это идентификатор 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; } 

Последние мысли

  • Не могу понять, почему Google, с тысячами миллионов долларов, не платит студенту или кому-то другому документировать этот API. Вы должны проверить исходный код, чтобы узнать, как он работает, и, что еще хуже, они не обнародуют те константы, которые используются в столбцах базы данных, поэтому мы должны писать их вручную.
  • Для других видов данных внутри MMS вы можете применить ту же мысль, что и выше … это всего лишь вопрос знания типа mime.

Ответ Кристиана превосходный. Однако метод получения адреса отправителя не работал для меня. Заявление Long.parseLong ничего не делает, кроме как, возможно, выдает исключение и новую String (…)?.

На моем устройстве число курсоров равно 2 или более. Первый обычно имеет «тип» 137, а остальные имеют «тип» 151. Я не могу найти, где это задокументировано, но можно сделать вывод 137 «от», а 151 – «к». Таким образом, если я запускаю метод как есть, я не получаю исключение, и он возвращает последнюю строку, которая является получателем и только одна из нескольких во многих случаях.

Также AFAICT выбор не нужен, поскольку все строки имеют одинаковый msg_id. Однако это не повредит.

Это то, что работает для меня, чтобы получить адрес отправителя:

 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; // Use the first one found if more than one break; } } while (cursor.moveToNext()); } if (cursor != null) { cursor.close(); } // return address.replaceAll("[^0-9]", ""); return address; } 

Меня не волновало, все ли это число, но я включил способ исключить все, кроме цифр, в качестве комментария, если это необходимо. Его можно легко изменить, чтобы вернуть всех получателей.

Я полагаю, это сработало для него. Похоже, что это даст правильный ответ, если исключение произошло в первой строке.

Я должен был внести некоторые изменения, чтобы заставить это работать для меня.

  1. Когда я получаю курсор.getString (cursor.getColumnIndex («type»)) из содержимого mms-sms / conversations («content: // mms-sms / conversations /»), я проверяю значение поля «type» Для null. Если переменная равна нулю, т.е.

     String otype = c.getString(c.getColumnIndex("type")); if(otype != null) { //this is an sms - handle it... 

    Сообщение представляет собой SMS, иначе это MMS. Для MMS-сообщений вам необходимо проверить оба типа mime следующим образом:

     if (("application/vnd.wap.multipart.related".equalsIgnoreCase(msg_type) ||"application/vnd.wap.multipart.mixed".equalsIgnoreCase(msg_type)) && !id.equalsIgnoreCase(lastMMSID)) { //this is a MMS - handle it... 
  2. Когда вы используете ContentObserver для отслеживания содержимого сообщения для изменений, он запускает несколько уведомлений для одного и того же сообщения. Я использую статическую переменную – в моем случае lastMMSID – для отслеживания сообщения.
  3. Этот код хорошо работает для извлечения содержимого как входящих, так и исходящих сообщений. Важно перебирать все записи, возвращаемые «content: // mms / part /» uri, чтобы добраться до содержимого – текста и / или вложений – из MMS.
  4. Единственный способ, которым я мог бы найти, что хорошо работает, чтобы различать входящие и исходящие MMS, заключается в проверке нулевого статуса поля «m_id» в содержании mms-sms / conversations.

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

Последняя мысль о том, как получить поле адреса. По какой-то причине адресный контент не любит запрашивать параметр {"*"}, но это работает: –

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

Если это исходящее сообщение, «тип» для поиска будет равен 151. Для входящего сообщения «тип» будет равен 137. Полностью функциональный фрагмент кода будет выглядеть примерно так:

 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()); } // Outbound messages address type=137 and the value will be 'insert-address-token' // Outbound messages address type=151 and the value will be the address // Additional checking can be done here to return the correct address. return add; } . 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()); } // Outbound messages address type=137 and the value will be 'insert-address-token' // Outbound messages address type=151 and the value will be the address // Additional checking can be done here to return the correct address. return add; } 

Для всех храбрых воинов, которые перешли меня на этот пост, я благодарю тебя от всего сердца!

Ответ, приведенный выше для получения getMMSAddress (), не должен содержать цикл while (cursor.moveToNext ()) ;. Он должен извлекать только адрес из первого элемента в курсоре. По какой-то причине это неизвестно мне, этот курсор имеет более одной записи. Первый содержит адрес отправителя. Остальные элементы курсора за пределами первого содержат адрес получателя. Таким образом, код as возвращает адрес получателей, а не адрес отправителя.

Это было очень полезно для взлома содержимого содержимого MMS.

Я только что боролся с этим; Однако, наконец, я получил его на работу, и я подумал, что этот поток может извлечь выгоду из моего опыта.

Я мог запросить content://mms-sms/conversations/ (Telephony.Threads.CONTENT_URI) и получить адреса и части, которые были хорошо описаны в потоке, но я обнаружил, что этот URI не будет получать потоки, в которых есть только сообщения MMS – например, потоки с более чем двумя корреспондентами.

После некоторого копания в источнике приложения AOSP MMS я обнаружил, что он использовал вариант в Telephony.Threads.CONTENT_URI для создания своего списка разговоров – он добавлял параметр «simple» со значением «true». Когда я добавил этот параметр, я обнаружил, что поставщик будет запрашивать совершенно другую таблицу, в которой действительно есть все потоки SMS и MMS.

Эта таблица имеет совершенно другую схему из обычной Telephony.Threads.CONTENT_URI one (???); Это проекция, которую использует приложение AOSP –

 public static final String[] ALL_THREADS_PROJECTION = { Threads._ID, Threads.DATE, Threads.MESSAGE_COUNT, Threads.RECIPIENT_IDS, Threads.SNIPPET, Threads.SNIPPET_CHARSET, Threads.READ, Threads.ERROR, Threads.HAS_ATTACHMENT }; 

_ID здесь – идентификатор потока – поэтому идентификатор в Telephony.Sms.CONTENT_URI или Telephony.Mms.CONTENT_URI.

После того, как я обнаружил эту странную деталь, все стало работать намного лучше! Обратите внимание, однако, что столбец DATE в варианте «simple = true» не является надежным, я должен был использовать дату из последнего сообщения Sms или Mms.

Еще одна вещь, которую я, вероятно, должен упомянуть, заключается в том, что для получения правильного списка сообщений для определенного потока мне приходилось запрашивать как поставщиков Mms, так и Sms, а затем объединять результаты в один список, а затем сортировать их по дате.

Я проверил поведение на Android 5.x и 7.x.

Надеюсь, это поможет немного больше.