web-dev-qa-db-ja.com

読み方MMS Androidのデータ?

読みたいMMSデータmmssms.dbでmmsエントリが保存されているパーツテーブルを見ました;カーソルを使用しており、適切なURI; "content:// mms-sms/conversations"と列名の "Address"(送信先)、 "Text"または "Subject"および "Data"列名を使用しています画像の。

mmssms.dbのスキーマとpart Tableの列を見ました。

71
user321373

これに関するドキュメントを見つけるのは難しいので、ここで見つけたすべての情報を収集します。急いでいる場合、または単に読みたくない場合は、SMSセクションからデータを取得する方法にジャンプしてください。

content:// mms-sms/conversations

これは 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を呼び出してすべての列を返す場合、nullprojectionパラメーターとして渡すことができます。ただし、このプロバイダーではそれができないため、*を使用しています。

これで、通常どおり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を返します。

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の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データからデータを取得する方法は?

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です。

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

テキストのさまざまな部分を含めることもできますが、通常は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();
}

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メソッドは次のようになります。

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

最終的な考え

  • 数千万ドルを費やしたGoogleがこのAPIを文書化するために学生や他の誰かにお金を払わない理由を理解できません。ソースコードをチェックして、それがどのように機能するかを知る必要があり、さらに悪いことに、データベースの列で使用される定数を公開しないため、手動で記述する必要があります。
  • MMS内の他の種類のデータについては、上記で学習したのと同じアイデアを適用できます...これは、MIMEタイプを知るだけの問題です。
267
Cristian

クリスチャンによる答えは素晴らしいです。しかし、送信者のアドレスを取得する方法はうまくいきませんでした。 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;
}

すべて数値であるかどうかは気にしませんでしたが、必要に応じてコメントとして数字以外をすべて削除する方法を含めました。すべての受信者を返すように簡単に変更することもできます。

私は彼のために働いたと思います。最初の行で例外が発生した場合、正しい答えが得られるようです。

7
Kenneth Evans

私はこれに苦労しています。しかし、ようやく機能するようになり、このスレッドは私の経験から恩恵を受けると思いました。

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で動作を確認しました。

これがもう少し役立つことを願っています。

4
Jason Proctor

これを機能させるには、いくつかの修正を行う必要がありました。

  1. 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...
    
  2. ContentObserverを使用してメッセージコンテンツの変更を監視すると、同じメッセージに対して複数の通知が発生します。メッセージを追跡するために、静的変数(この場合はlastMMSID)を使用します。
  3. このコードは、受信メッセージと送信メッセージの両方のコンテンツを取得するのに適しています。 MMSのコンテンツ(テキストおよび/または添付ファイル)にアクセスするには、「content:// mms/part /」uriによって返されるすべてのレコードを反復処理することが重要です。
  4. 着信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;
}

この記事で私の前に行った勇敢な戦士すべてに-私は心の底からあなたに感謝します!

3
Gustav

GetMMSAddress()を取得するための上記の答えには、ループwhile(cursor.moveToNext());を含めるべきではありません。カーソルの最初の要素からのみアドレスを抽出する必要があります。何らかの理由で、このカーソルには複数のレコードがあります。最初のものには、送信者のアドレスが含まれています。最初の要素以外のカーソルの他の要素には、受信者のアドレスが含まれています。したがって、コードはそのまま送信者アドレスではなく受信者アドレスを返します。

これは、MMSのコンテンツをクラックするために非常に役立ちました。

2
Hassan Nabil