Отправка и получение SMS и MMS в Android (до Kit Kat Android 4.4)

Я разобрался, как отправлять и получать смс сообщения. Для отправки SMS-сообщений мне пришлось позвонить sendTextMessage() а также sendMultipartTextMessage() методы SmsManager учебный класс. Чтобы получать SMS-сообщения, мне нужно было зарегистрировать получателя в AndroidMainfest.xml файл. Тогда мне пришлось отменить onReceive() метод BroadcastReceiver, Я включил примеры ниже.

MainActivity.java

public class MainActivity extends Activity {
    private static String SENT = "SMS_SENT";
    private static String DELIVERED = "SMS_DELIVERED";
    private static int MAX_SMS_MESSAGE_LENGTH = 160;

    // ---sends an SMS message to another device---
    public static void sendSMS(String phoneNumber, String message) {

        PendingIntent piSent = PendingIntent.getBroadcast(mContext, 0, new Intent(SENT), 0);
        PendingIntent piDelivered = PendingIntent.getBroadcast(mContext, 0,new Intent(DELIVERED), 0);
        SmsManager smsManager = SmsManager.getDefault();

        int length = message.length();          
        if(length > MAX_SMS_MESSAGE_LENGTH) {
            ArrayList<String> messagelist = smsManager.divideMessage(message);          
            smsManager.sendMultipartTextMessage(phoneNumber, null, messagelist, null, null);
        }
        else
            smsManager.sendTextMessage(phoneNumber, null, message, piSent, piDelivered);
        }
    }

    //More methods of MainActivity ...
}

SMSReceiver.java

public class SMSReceiver extends BroadcastReceiver {
    private final String DEBUG_TAG = getClass().getSimpleName().toString();
    private static final String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
    private Context mContext;
    private Intent mIntent;

    // Retrieve SMS
    public void onReceive(Context context, Intent intent) {
        mContext = context;
        mIntent = intent;

        String action = intent.getAction();

        if(action.equals(ACTION_SMS_RECEIVED)){

            String address, str = "";
            int contactId = -1;

            SmsMessage[] msgs = getMessagesFromIntent(mIntent);
            if (msgs != null) {
                for (int i = 0; i < msgs.length; i++) {
                    address = msgs[i].getOriginatingAddress();
                    contactId = ContactsUtils.getContactId(mContext, address, "address");
                    str += msgs[i].getMessageBody().toString();
                    str += "\n";
                }
            }   

            if(contactId != -1){
                showNotification(contactId, str);
            }

            // ---send a broadcast intent to update the SMS received in the
            // activity---
            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction("SMS_RECEIVED_ACTION");
            broadcastIntent.putExtra("sms", str);
            context.sendBroadcast(broadcastIntent);
        }

    }

    public static SmsMessage[] getMessagesFromIntent(Intent intent) {
        Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
        byte[][] pduObjs = new byte[messages.length][];

        for (int i = 0; i < messages.length; i++) {
            pduObjs[i] = (byte[]) messages[i];
        }
        byte[][] pdus = new byte[pduObjs.length][];
        int pduCount = pdus.length;
        SmsMessage[] msgs = new SmsMessage[pduCount];
        for (int i = 0; i < pduCount; i++) {
            pdus[i] = pduObjs[i];
            msgs[i] = SmsMessage.createFromPdu(pdus[i]);
        }
        return msgs;
    }

    /**
    * The notification is the icon and associated expanded entry in the status
    * bar.
    */
    protected void showNotification(int contactId, String message) {
        //Display notification...
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myexample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_MMS" />
    <uses-permission android:name="android.permission.WRITE" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:debuggable="true"
        android:icon="@drawable/ic_launcher_icon"
        android:label="@string/app_name" >

        <activity
            //Main activity...
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            //Activity 2 ...
        </activity>
        //More acitivies ...

        // SMS Receiver
        <receiver android:name="com.myexample.receivers.SMSReceiver" >
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

    </application>
</manifest>

Однако мне было интересно, могли бы вы отправлять и получать MMS-сообщения аналогичным образом. После некоторого исследования многие примеры, представленные в блогах, просто проходят Intent в родное приложение обмена сообщениями. Я пытаюсь отправить MMS, не выходя из приложения. Кажется, нет стандартного способа отправки и получения MMS. Кто-нибудь получил это на работу?

Кроме того, я знаю, что SMS/MMS ContentProvider не является частью официального Android SDK, но я подумал, что кто-то мог реализовать это. Любая помощь с благодарностью.

Обновить

Я добавил BroadcastReceiver к AndroidManifest.xml файл для получения MMS-сообщений

<receiver android:name="com.sendit.receivers.MMSReceiver" >
    <intent-filter>
        <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />

        <data android:mimeType="application/vnd.wap.mms-message" />
    </intent-filter>
</receiver>

В классе MMSReceiver onReceive() Метод может получить только номер телефона, с которого было отправлено сообщение. Как вы получаете другие важные вещи из MMS, такие как путь к файлу к мультимедийному вложению (изображение / аудио / видео) или текст в MMS?

MMSReceiver.java

public class MMSReceiver extends BroadcastReceiver {
    private final String DEBUG_TAG = getClass().getSimpleName().toString();
    private static final String ACTION_MMS_RECEIVED = "android.provider.Telephony.WAP_PUSH_RECEIVED";
    private static final String MMS_DATA_TYPE = "application/vnd.wap.mms-message";

     // Retrieve MMS
    public void onReceive(Context context, Intent intent) {

        String action = intent.getAction();
        String type = intent.getType();

        if(action.equals(ACTION_MMS_RECEIVED) && type.equals(MMS_DATA_TYPE)){

            Bundle bundle = intent.getExtras();

            Log.d(DEBUG_TAG, "bundle " + bundle);
            SmsMessage[] msgs = null;
            String str = "";
            int contactId = -1;
            String address;

            if (bundle != null) {

                byte[] buffer = bundle.getByteArray("data");
                Log.d(DEBUG_TAG, "buffer " + buffer);
                String incomingNumber = new String(buffer);
                int indx = incomingNumber.indexOf("/TYPE");
                if(indx>0 && (indx-15)>0){
                    int newIndx = indx - 15;
                    incomingNumber = incomingNumber.substring(newIndx, indx);
                    indx = incomingNumber.indexOf("+");
                    if(indx>0){
                        incomingNumber = incomingNumber.substring(indx);
                        Log.d(DEBUG_TAG, "Mobile Number: " + incomingNumber);
                    }
                }

                int transactionId = bundle.getInt("transactionId");
                Log.d(DEBUG_TAG, "transactionId " + transactionId);

                int pduType = bundle.getInt("pduType");
                Log.d(DEBUG_TAG, "pduType " + pduType);

                byte[] buffer2 = bundle.getByteArray("header");      
                String header = new String(buffer2);
                Log.d(DEBUG_TAG, "header " + header);

                if(contactId != -1){
                    showNotification(contactId, str);
                }

                // ---send a broadcast intent to update the MMS received in the
                // activity---
                Intent broadcastIntent = new Intent();
                broadcastIntent.setAction("MMS_RECEIVED_ACTION");
                broadcastIntent.putExtra("mms", str);
                context.sendBroadcast(broadcastIntent);

            }
        }

    }

    /**
    * The notification is the icon and associated expanded entry in the status
    * bar.
    */
    protected void showNotification(int contactId, String message) {
        //Display notification...
    }
}

Согласно Документации android.provider.Telephony:

Broadcast Action: Новое текстовое SMS-сообщение получено устройством. Намерение будет иметь следующие дополнительные значения:

pdus - Ан Object[] из byte[]s, содержащие PDU, которые составляют сообщение.

Дополнительные значения могут быть извлечены с помощью getMessagesFromIntent(android.content.Intent) Если BroadcastReceiver обнаружит ошибку при обработке этого намерения, он должен установить код результата соответствующим образом.

 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
 public static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";

Широковещательное действие: новое SMS-сообщение на основе данных получено устройством. Намерение будет иметь следующие дополнительные значения:

pdus - Ан Object[] из byte[]s, содержащие PDU, которые составляют сообщение.

Дополнительные значения могут быть извлечены с использованием getMessagesFromIntent(android.content.Intent). Если BroadcastReceiver обнаружит ошибку при обработке этого намерения, он должен установить код результата соответствующим образом.

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String DATA_SMS_RECEIVED_ACTION = "android.intent.action.DATA_SMS_RECEIVED";

Широковещательное действие: новое сообщение WAP PUSH получено устройством. Намерение будет иметь следующие дополнительные значения:

transactionId (Integer) - идентификатор транзакции WAP

pduType (Integer) - Тип WAP PDU`

header (byte[]) - заголовок сообщения

data (byte[]) - данные полезной нагрузки сообщения

contentTypeParameters (HashMap<String,String>) - Любые параметры, связанные с типом контента (декодируются из заголовка WSP Content-Type)

Если BroadcastReceiver обнаружит ошибку при обработке этого намерения, он должен установить код результата соответствующим образом. Дополнительное значение contentTypeParameters - это карта параметров содержимого с указанием их имен. Если встречаются какие-либо неназначенные общеизвестные параметры, ключом карты будет "unassigned/0x...", где "..." - шестнадцатеричное значение неназначенного параметра. Если параметр имеет значение No-Value, значение на карте будет нулевым.

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";

Обновление № 2

Я понял, как передать статистику в PendingIntent быть принятым BroadcastReceiver: Дополнения Android PendingIntent, не полученные BroadcastReceiver

Однако дополнительные данные передаются SendBroadcastReceiver, а не SMSReceiver. Как я могу передать дополнительный SMSReceiver?

Обновление № 3

Получение MMS

Поэтому после проведения дополнительных исследований я увидел несколько предложений о регистрации ContentObserver, Таким образом, вы можете обнаружить, когда есть какие-либо изменения в content://mms-sms/conversations Контент-провайдер, следовательно, позволяет обнаруживать входящие MMS. Вот самый близкий пример, чтобы заставить это работать, которое я нашел: Получение MMS

Тем не менее, есть переменная mainActivity типа ServiceController, Где ServiceController класс реализован? Существуют ли другие реализации зарегистрированного ContentObserver?

Отправка MMS

Что касается отправки MMS, я наткнулся на этот пример: отправить MMS

Проблема в том, что я попытался запустить этот код на моем Nexus 4, который находится на Android v4.2.2, и я получаю эту ошибку:

java.lang.SecurityException: No permission to write APN settings: Neither user 10099 nor current process has android.permission.WRITE_APN_SETTINGS.

Ошибка выдается после запроса Carriers ContentProvider в getMMSApns() метод APNHelper учебный класс.

final Cursor apnCursor = this.context.getContentResolver().query(Uri.withAppendedPath(Carriers.CONTENT_URI, "current"), null, null, null, null);

Видимо, вы не можете читать APN в Android 4.2

Какова альтернатива для всех тех приложений, которые используют мобильные данные для выполнения операций (таких как отправка MMS) и не знают настройки APN по умолчанию, присутствующей в устройстве?

Обновление № 4

Отправка MMS

Я попробовал следующий пример: Отправить MMS

Как @Sam предложил в своем ответе:

You have to add jsoup to the build path, the jar to the build path and import com.droidprism.*; To do that in android, add the jars to the libs directory first, then configure the project build path to use the jars already in the libs directory, then on the build path config click order and export and check the boxes of the jars and move jsoup and droidprism jar to the top of the build order.

Так что теперь я больше не получаю ошибки SecurityException. Сейчас я тестирую Nexus 5 на Android KitKat. После запуска примера кода он дает мне код ответа 200 после вызова

MMResponse mmResponse = sender.send(out, isProxySet, MMSProxy, MMSPort);

Однако я проверил человека, которому пытался отправить MMS. И они сказали, что никогда не получали MMS.

6 ответов

У меня была та же самая проблема, которую вы описали выше (Galaxy Nexus на t-mobile USA), потому что мобильные данные отключены.

В Jelly Bean это: Настройки> Использование данных> Мобильные данные

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

По какой-то причине, когда ваш телефонный провайдер предоставляет вам возможность отправлять и получать MMS, у вас должны быть включены Мобильные данные, даже если вы используете Wi-Fi, если мобильные данные включены, вы сможете получать и отправлять MMS, даже если Wi-Fi показывает, как ваш интернет на вашем устройстве.

Это настоящая боль, так как если у вас ее нет, сообщение может сильно зависать даже при включении Mobile Data и может потребовать перезагрузки устройства.

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

public void sendData(int num){
    String fileString = "..."; //put the location of the file here
    Intent mmsIntent = new Intent(Intent.ACTION_SEND);
    mmsIntent.putExtra("sms_body", "text");
    mmsIntent.putExtra("address", num);
    mmsIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(fileString)));
    mmsIntent.setType("image/jpeg");
    startActivity(Intent.createChooser(mmsIntent, "Send"));

}

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

Вы можете быть предупреждены о получении мм так же, как смс. Фильтр намерений на приемнике должен выглядеть следующим образом.

<intent-filter>
    <action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
    <data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>

Чтобы отправить mms для Android 4.0 api 14 или выше без разрешения на запись настроек apn, вы можете использовать эту библиотеку: получить коды mnc и mcc из android, затем вызвать

Carrier c = Carrier.getCarrier(mcc, mnc);
if (c != null) {
    APN a = c.getAPN();
    if (a != null) {
        String mmsc = a.mmsc;
        String mmsproxy = a.proxy; //"" if none
        int mmsport = a.port; //0 if none
    }
}

Чтобы использовать это, добавьте Jsoup и droid prism jar к пути сборки и импортируйте com.droidprism.*;

Я не думаю, что есть какая-либо поддержка SDK для отправки MMS в Android. Посмотри здесь, по крайней мере, я еще не нашел. Но парень утверждал, что есть. Посмотрите на этот пост.

Отправить MMS из Моего приложения в Android

Я не понимаю разочарования. Почему бы просто не сделать широковещательный приемник, который фильтрует это намерение:

android.provider.Telephony.MMS_RECEIVED

Я проверил немного дальше, и вам может потребоваться доступ на системном уровне, чтобы получить это (рутированный телефон).

SmsListenerClass

public class SmsListener extends BroadcastReceiver {

static final String ACTION =
        "android.provider.Telephony.SMS_RECEIVED";

@Override
public void onReceive(Context context, Intent intent) {

    Log.e("RECEIVED", ":-:-" + "SMS_ARRIVED");

    // TODO Auto-generated method stub
    if (intent.getAction().equals(ACTION)) {

        Log.e("RECEIVED", ":-" + "SMS_ARRIVED");

        StringBuilder buf = new StringBuilder();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {

            Object[] pdus = (Object[]) bundle.get("pdus");

            SmsMessage[] messages = new SmsMessage[pdus.length];
            SmsMessage message = null;

            for (int i = 0; i < messages.length; i++) {

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    String format = bundle.getString("format");
                    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
                } else {
                    messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                }

                message = messages[i];
                buf.append("Received SMS from  ");
                buf.append(message.getDisplayOriginatingAddress());
                buf.append(" - ");
                buf.append(message.getDisplayMessageBody());
            }

            MainActivity inst = MainActivity.instance();
            inst.updateList(message.getDisplayOriginatingAddress(),message.getDisplayMessageBody());

        }

        Log.e("RECEIVED:", ":" + buf.toString());

        Toast.makeText(context, "RECEIVED SMS FROM :" + buf.toString(), Toast.LENGTH_LONG).show();

    }
}

Деятельность

@Override
public void onStart() {
    super.onStart();
    inst = this;
}

public static MainActivity instance() {
    return inst;
}

public void updateList(final String msg_from, String msg_body) {

    tvMessage.setText(msg_from + " :- " + msg_body);

    sendSMSMessage(msg_from, msg_body);

}

protected void sendSMSMessage(String phoneNo, String message) {

    try {
        SmsManager smsManager = SmsManager.getDefault();
        smsManager.sendTextMessage(phoneNo, null, message, null, null);
        Toast.makeText(getApplicationContext(), "SMS sent.", Toast.LENGTH_LONG).show();
    } catch (Exception e) {
        Toast.makeText(getApplicationContext(), "SMS faild, please try again.", Toast.LENGTH_LONG).show();
        e.printStackTrace();
    }
}

манифест

<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.SEND_SMS"/>

<receiver android:name=".SmsListener">
        <intent-filter>
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>
Другие вопросы по тегам