読みたいMMSデータmmssms.db
でmmsエントリが保存されているパーツテーブルを見ました;カーソルを使用しており、適切なURI
; "content:// mms-sms/conversations"と列名の "Address"(送信先)、 "Text"または "Subject"および "Data"列名を使用しています画像の。
mmssms.db
のスキーマとpart Tableの列を見ました。
これに関するドキュメントを見つけるのは難しいので、ここで見つけたすべての情報を収集します。急いでいる場合、または単に読みたくない場合は、SMSセクションからデータを取得する方法にジャンプしてください。
これは MmsおよびSMSプロバイダー ...のURIです。これにより、MMSおよびSMSデータベースを同時に照会し、それらを混合することができます。 (conversationsと呼ばれる)単一スレッドで。
URIが重要な理由それが、MMSおよびSMSメッセージを取得する標準的な方法です。たとえば、SMSを受信して通知バーをクリックすると、次のようなブロードキャストインテントが送信されます:content://mms-sms/conversations/XXX
、ここでXXX
は会話のIDです。
しなければならないことは、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
は、メッセージのIDです。 救助隊に明らかなキャプテン?そうでもない。このIDは、content://sms
またはcontent://mms
のいずれかを使用して詳細情報を取得するために使用できます。date
説明は不要です。thread_id
は会話のIDですbody
この会話の最後のSMSの内容。 MMSの場合、テキスト部分が含まれていても、null
になります。注:content://mms-sms/conversations
を照会すると、_id
が最後のSMSであるさまざまな会話のリストが返されますまたは各会話でMMS。 content://mms-sms/conversations/xxx
を照会すると、IDが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のIDを持っているので、あなたがしなければならない唯一のことは:
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は少し異なります。それらは異なる部分(テキスト、オーディオ、画像など)で構築できます。そのため、ここでは各種類のデータを個別に取得する方法を説明します。
mmsId
変数にMMS idがあると仮定しましょう。 content://mms/
プロバイダーを使用して、このMMSに関する詳細情報を取得できます。
Uri uri = Uri.parse("content://mms/");
String selection = "_id = " + mmsId;
Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
ただし、興味深い列はread
のみで、メッセージが既に読み取られている場合は1
です。
ここでは、たとえば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());
}
テキストのさまざまな部分を含めることもできますが、通常は1つだけです。したがって、ループを削除する場合、ほとんどの場合に機能します。 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();
}
テキスト部分を取得するのと同じです...唯一の違いは、異なる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
メソッドは次のようになります。
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
プロバイダーを使用する必要があります。xxx
はMMSのIDです。
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;
}
クリスチャンによる答えは素晴らしいです。しかし、送信者のアドレスを取得する方法はうまくいきませんでした。 Long.parseLongステートメントは、例外と新しいString(...)をスローする可能性があること以外は何もしません。
私のデバイスでは、カーソル数は2以上です。通常、最初の137の「タイプ」と他の151の「タイプ」があります。これが記載されている場所はわかりませんが、137が「from」で、151が「to」であると推測できます。したがって、メソッドをそのまま実行すると、例外は発生せず、最後の行が返されます。これは多くの場合、受信者であり、いくつかのうちの1つだけです。
また、すべての行が同じ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;
}
すべて数値であるかどうかは気にしませんでしたが、必要に応じてコメントとして数字以外をすべて削除する方法を含めました。すべての受信者を返すように簡単に変更することもできます。
私は彼のために働いたと思います。最初の行で例外が発生した場合、正しい答えが得られるようです。
私はこれに苦労しています。しかし、ようやく機能するようになり、このスレッドは私の経験から恩恵を受けると思いました。
content://mms-sms/conversations/ (Telephony.Threads.CONTENT_URI)
でクエリを実行し、スレッドで役立つ説明に従ってアドレスとパーツを取得できましたが、このURIではonlyがMMSであったスレッドを取得できませんでしたそれらの中のメッセージ-たとえば、3人以上の通信者がいるスレッド。
AOSP MMSアプリソースを掘り下げた後、Telephony.Threads.CONTENT_URI
のバリアントを使用してその会話リストを生成していることがわかりました。値 "true"でパラメーター "simple"を追加していました「。このパラメーターを追加すると、プロバイダーが完全に異なるテーブルをクエリすることがわかりました。テーブルには実際にSMSおよびMMSスレッドがすべて含まれていました。
このテーブルには、通常のTelephony.Threads.CONTENT_URIのものとはまったく異なるスキーマ(???)があります。これは、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はスレッドのIDです。したがって、Telephony.Sms.CONTENT_URIまたはTelephony.Mms.CONTENT_URIへのIDです。
この奇妙な詳細を発見した後、物事はずっと良くなり始めました!ただし、「simple = true」バリアントのDATE列は信頼できないため、代わりに最新のSmsまたはMmsメッセージの日付を使用する必要がありました。
おそらく言及すべきもう1つのことは、特定のスレッドのメッセージの適切なリストを取得するために、MmsプロバイダーとSmsプロバイダーの両方でクエリを実行し、結果を1つのリストに結合し、日付で並べ替える必要があるということです。
Android 5.xおよび7.xで動作を確認しました。
これがもう少し役立つことを願っています。
これを機能させるには、いくつかの修正を行う必要がありました。
cursor.getString(cursor.getColumnIndex( "type"))をmms-sms/conversationsコンテンツから取得すると、( "content:// mms-sms/conversations /")をテストしますnullの「タイプ」フィールドの値。変数が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...
着信MMSと発信MMSを区別するのに非常にうまく機能する唯一の方法は、mms-sms/conversationsコンテンツの「m_id」フィールドのnullステータスをテストすることです。
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;
}
この記事で私の前に行った勇敢な戦士すべてに-私は心の底からあなたに感謝します!
GetMMSAddress()を取得するための上記の答えには、ループwhile(cursor.moveToNext());を含めるべきではありません。カーソルの最初の要素からのみアドレスを抽出する必要があります。何らかの理由で、このカーソルには複数のレコードがあります。最初のものには、送信者のアドレスが含まれています。最初の要素以外のカーソルの他の要素には、受信者のアドレスが含まれています。したがって、コードはそのまま送信者アドレスではなく受信者アドレスを返します。
これは、MMSのコンテンツをクラックするために非常に役立ちました。