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

131

Я понял, как отправлять и получать SMS-сообщения. Для отправки SMS - сообщений , которые я должен был назвать sendTextMessage()и sendMultipartTextMessage()методы SmsManagerкласса. Чтобы получать смс-сообщения, мне пришлось прописать получателя в 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. У кого-нибудь это работает?

Кроме того, я знаю, что ContentProvider SMS / MMS не является частью официального 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 :

Действие широковещательной рассылки: устройство получило новое текстовое 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) - ID транзакции WAP

pduType (Integer) - Тип WAP PDU`

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

data (byte[]) - Полезные данные сообщения

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

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

@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/conversationsContent Provider, что позволит вам обнаруживать входящие 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.

Ошибка возникает после запроса CarriersContentProvider в 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.

toobsco42
источник
Вы смотрели это руководство раньше? maximbogatov.wordpress.com/2011/08/13/mms-in-android
HaemEternal
3
Да. Я попытался собрать ответ Максима вместе, но не могу заставить его работать. Там много классов, которые импортируют android.provider.telephony, который, кажется, устарел.
toobsco42
И, вероятно, после прочтения ответа
@Sahil
Я не уверен, как собрать этот ответ вместе, хотя он очень похож на ответ @Sahil.
toobsco42
Привет, @ toobsco42. Вы можете найти решение для всех упомянутых выше запросов ..?
kamal_tech_view

Ответы:

15

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

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

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

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

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

Манан Шарма
источник
Также вы должны знать, что отправка SMS и MMS - это две совершенно разные вещи в фоновом режиме. MMS - это, скорее, сетевая услуга на базе Интернета, поскольку для этого требуется отправка дополнительных элементов (мультимедиа) с текстом. Данный код отлично работает на нескольких устройствах, которые я тестировал. ps: вы можете игнорировать часть NOKIA.
Манан Шарма
Когда я запускаю этот пример, в LogCat он печатает: 02-24 13: 32: 40.872: V / SendMMSActivity (5686): TYPE_MOBILE_MMS не подключен, залог 02-24 13: 32: 40.882: V / SendMMSActivity (5686): тип равен не TYPE_MOBILE_MMS, залог. Также говорится: java.lang.SecurityException: Нет разрешения на запись настроек APN: Ни у пользователя 10099, ни у текущего процесса нет android.permission.WRITE_APN_SETTINGS. Похоже, он не может выполнить этот запрос: final Cursor apnCursor = this.context.getContentResolver (). Query (Uri.withAppendedPath (Carriers.CONTENT_URI, "текущий"), null, null, null, null); Я тестирую Nexus 4.
toobsco42
Также это тот же пример, который предоставил @Sahil.
toobsco42
7

Официальной поддержки 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>
user1959417
источник
Разве это не просто запускает собственное приложение обмена сообщениями?
toobsco42
1
да, извините за это. Я только что понял, что ты уже умеешь это делать. Я все же добавил, как получать ммс.
user1959417
Спасибо, я недавно внедрил часть MMS BroadcastReceiverи использовал то, Intent Filterчто вы разместили. Я скоро обновлю этот вопрос.
toobsco42
4

Чтобы отправить 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. *;

Сэм Адамс
источник
Привет, @Sam, я добавил файл .jar в свой проект, но получаю эту ошибку в строке, которая создает экземпляр объекта Carrier: java.lang.NoClassDefFoundError: com.droidprism.Carrier это происходит с вами?
toobsco42
нет. Вы должны добавить jsoup в путь сборки, jar в путь сборки и импортировать com.droidprism. *; Я отредактирую ответ. Чтобы сделать это в android, сначала добавьте банки в каталог libs, затем настройте путь сборки проекта, чтобы использовать банки, уже находящиеся в каталоге libs, затем в конфигурации пути сборки щелкните порядок и экспортируйте и установите флажки для файлов jar и переместите jsoup и droidprism jar в начало порядка сборки.
Sam Adamsh
Добавление Jsoup .jar разрешило ошибку NoClassDefFoundError. Теперь я могу получить настройки APN. Следующий шаг - выяснить, как отправлять MMS.
toobsco42
3

Я не думаю, что есть поддержка sdk для отправки ммс в android. Посмотрите здесь По крайней мере, я еще не нашел. Но парень утверждал, что у него это есть. Взгляните на этот пост.

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

Сахил Махаджан МДж
источник
Я просмотрел комментарии к сообщению androidbridge.blogspot.com о реализации Nokia, и похоже, что у многих людей возникают проблемы с тем, чтобы заставить это работать на своих устройствах.
toobsco42
@ toobsco42 Так что, возможно, он еще не поддержан.
Sahil Mahajan Mj
-2

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

android.provider.Telephony.MMS_RECEIVED

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

оборота j2emanue
источник
3
Привет, @ j2emanue, проблема в том, что после того, как вы получили это намерение, как вы на самом деле получаете содержимое MMS? Если MMS содержит изображение и текст, как извлечь эти компоненты?
toobsco42
но я заметил, что есть дополнительный массив байтов, который вы можете получить, если сделаете это так. Я уже говорил .... byte [] data = intent.getByteArrayExtra ("data"); Я не уверен, как это разобрать, хотя извините.
j2emanue
я могу разобрать это. но все, что я могу получить, - это тема, от кого пришло ммс, и местоположение содержимого, где хранится содержимое ммс. однако этот URL недоступен.
toobsco42
-2

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>
Нарендра Соратия
источник