Envio e recebimento de SMS e MMS no Android (pré Kit Kat Android 4.4)

Eu descobri como enviar e receber mensagens SMS. Para enviar mensagens SMS eu tive que ligar para osendTextMessage() esendMultipartTextMessage() métodos doSmsManager classe. Para receber mensagens SMS, tive que registrar um receptor noAndroidMainfest.xml Arquivo. Então eu tive que substituir oonReceive() método doBroadcastReceiver. Eu incluí exemplos abaixo.

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>

No entanto, eu queria saber se você poderia enviar e receber mensagens MMS de maneira semelhante. Depois de fazer algumas pesquisas, muitos exemplos fornecidos em blogs simplesmente passamIntent para o aplicativo nativo Messaging. Estou tentando enviar um MMS sem sair do meu aplicativo. Não parece haver uma maneira padrão de enviar e receber MMS. Alguém já conseguiu isso para trabalhar?

Além disso, estou ciente de que o SMS / MMS ContentProvider não faz parte do SDK oficial do Android, mas eu estava pensando que alguém poderia ter implementado isso. Qualquer ajuda é muito apreciada.

Atualizar

Eu adicionei umBroadcastReceiver aoAndroidManifest.xml arquivo para receber mensagens 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>

Na classe MMSReceiver, oonReceive() método só é capaz de pegar o phoneNumber que a mensagem foi enviada. Como você pega outras coisas importantes de um MMS, como o caminho do arquivo para o anexo de mídia (imagem / áudio / vídeo) ou o texto no 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...
    }
}

De acordo comDocumentação de android.provider.Telephony:

Ação de Broadcast: Uma nova mensagem SMS baseada em texto foi recebida pelo dispositivo. A intenção terá os seguintes valores extras:

pdus - AObject[] dobyte[]s contendo as PDUs que compõem a mensagem.

Os valores extras podem ser extraídos usandogetMessagesFromIntent(android.content.Intent) Se um BroadcastReceiver encontrar um erro ao processar essa intenção, ele deverá definir o código de resultado adequadamente.

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

Ação de Broadcast: Uma nova mensagem SMS baseada em dados foi recebida pelo dispositivo. A intenção terá os seguintes valores extras:

pdus - AObject[] dobyte[]s contendo as PDUs que compõem a mensagem.

Os valores extras podem ser extraídos usando getMessagesFromIntent (android.content.Intent). Se um BroadcastReceiver encontrar um erro ao processar essa intenção, ele deverá definir o código de resultado adequadamente.

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

Ação de transmissão: Uma nova mensagem WAP PUSH foi recebida pelo dispositivo. A intenção terá os seguintes valores extras:

transactionId (Integer) - O ID da transação WAP

pduType (Integer) - O tipo de PDU WAP`

header (byte[]) - O cabeçalho da mensagem

data (byte[]) - A carga de dados da mensagem

contentTypeParameters (HashMap<String,String>) - Quaisquer parâmetros associados ao tipo de conteúdo (decodificado no cabeçalho WSP Content-Type)

Se um BroadcastReceiver encontrar um erro ao processar essa intenção, ele deverá definir o código de resultado adequadamente. O valor extra contentTypeParameters é o mapa dos parâmetros de conteúdo codificados por seus nomes. Se algum parâmetro conhecido não atribuído for encontrado, a chave do mapa será 'unassigned / 0x ...', onde '...' é o valor hexadecimal do parâmetro não atribuído. Se um parâmetro tiver No-Value, o valor no mapa será nulo.

@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
Atualização 2

Eu descobri como passar extras em umPendingIntent para ser recebido por umBroadcastReceiver: Android PendingIntent extras, não recebidos pelo BroadcastReceiver

No entanto, o extra é passado para oSendBroadcastReceiver não oSMSReceiver. Como posso passar um extra para oSMSReceiver?

Atualização 3

Recebendo MMS

Então, depois de fazer mais pesquisas, eu vi algumas sugestões de registrar umContentObserver. Dessa forma, você pode detectar quando há alguma alteração nocontent://mms-sms/conversations Provedor de Conteúdo, permitindo que você detecte MMSs recebidos. Aqui está o exemplo mais próximo para fazer isso funcionar:Recebendo MMS

No entanto, existe uma variávelmainActivity do tipoServiceController. Onde estáServiceController classe implementada? Existem outras implementações de umaContentObserver?

Envio de MMS

Quanto ao envio de MMS, me deparei com este exemplo:Enviar MMS

O problema é que tentei executar esse código no meu Nexus 4, que está no Android v4.2.2, e estou recebendo este erro:

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

O erro é lançado depois de consultar oCarriers ContentProvider nogetMMSApns() método doAPNHelper classe.

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

Aparentemente você não podeleia APNs no Android 4.2

Qual é a alternativa para todos os aplicativos que usam dados móveis para executar operações (como enviar MMS) e não sabem a configuração padrão de APN presente no dispositivo?

Atualização 4

Envio de MMS

Eu tentei seguir este exemplo:Enviar MMS

Como @Sam sugeriu em sua resposta:

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.

Então agora eu não consigo mais os erros SecurityException. Estou testando agora em um Nexus 5 no Android KitKat. Depois de executar o código de exemplo, ele me fornece um código de resposta 200 após a chamada para

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

No entanto, verifiquei com a pessoa que tentei enviar o MMS para. E eles disseram que nunca receberam o MMS.

questionAnswers(6)

yourAnswerToTheQuestion