Invio e ricezione di SMS e MMS in Android (pre Kit Kat Android 4.4)


131

Ho capito come inviare e ricevere messaggi SMS. Per inviare messaggi SMS ho dovuto chiamare i metodi sendTextMessage()e sendMultipartTextMessage()della SmsManagerclasse. Per ricevere messaggi SMS, ho dovuto registrare un destinatario nel AndroidMainfest.xmlfile. Quindi ho dovuto ignorare il onReceive()metodo di BroadcastReceiver. Ho incluso esempi di seguito.

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>

Tuttavia, mi chiedevo se fosse possibile inviare e ricevere messaggi MMS in modo simile. Dopo aver fatto qualche ricerca, molti esempi forniti sui blog passano semplicemente Intentall'applicazione di messaggistica nativa. Sto cercando di inviare un MMS senza lasciare la mia domanda. Non sembra esserci un modo standard per inviare e ricevere MMS. Qualcuno l'ha fatto funzionare?

Inoltre, sono consapevole che ContentProvider SMS / MMS non fa parte dell'SDK Android ufficiale, ma pensavo che qualcuno potesse essere in grado di implementarlo. Qualsiasi aiuto è molto apprezzato.

Aggiornare

Ho aggiunto BroadcastReceivera al AndroidManifest.xmlfile per ricevere messaggi 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>

Nella classe MMSReceiver, il onReceive()metodo è in grado di catturare solo il numero di telefono da cui è stato inviato il messaggio. Come afferrare altre cose importanti da un MMS come il percorso del file all'allegato multimediale (immagine / audio / video) o il testo nell'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...
    }
}

Secondo la documentazione di android.provider.Telephony :

Broadcast Action: il dispositivo ha ricevuto un nuovo messaggio SMS basato su testo. L'intento avrà i seguenti valori extra:

pdus- Uno Object[]di byte[]s contenente le PDU che compongono il messaggio.

I valori extra possono essere estratti usando getMessagesFromIntent(android.content.Intent) Se un BroadcastReceiver rileva un errore durante l'elaborazione di questo intento, dovrebbe impostare il codice del risultato in modo appropriato.

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

Broadcast Action: il dispositivo ha ricevuto un nuovo messaggio SMS basato su dati. L'intento avrà i seguenti valori extra:

pdus- Uno Object[]di byte[]s contenente le PDU che compongono il messaggio.

I valori extra possono essere estratti usando getMessagesFromIntent (android.content.Intent). Se BroadcastReceiver rileva un errore durante l'elaborazione di questo intento, dovrebbe impostare il codice risultato in modo appropriato.

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

Azione di trasmissione: un nuovo messaggio WAP PUSH è stato ricevuto dal dispositivo. L'intento avrà i seguenti valori extra:

transactionId (Integer) - L'ID transazione WAP

pduType (Integer) - Il tipo di PDU WAP`

header (byte[]) - L'intestazione del messaggio

data (byte[]) - Il payload di dati del messaggio

contentTypeParameters (HashMap<String,String>) - Qualsiasi parametro associato al tipo di contenuto (decodificato dall'intestazione Content-Type di WSP)

Se BroadcastReceiver rileva un errore durante l'elaborazione di questo intento, dovrebbe impostare il codice risultato in modo appropriato. Il valore aggiuntivo contentTypeParameters è la mappa dei parametri del contenuto codificati dai loro nomi. Se vengono rilevati parametri noti non assegnati, la chiave della mappa sarà "non assegnata / 0x ...", dove "..." è il valore esadecimale del parametro non assegnato. Se un parametro ha nessun valore, il valore nella mappa sarà nullo.

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

Aggiornamento n. 2

Ho capito come passare gli extra in a che PendingIntentdevono essere ricevuti da BroadcastReceiver: Android PendingIntent extra, non ricevuti da BroadcastReceiver

Tuttavia, l'extra viene passato a SendBroadcastReceiver e non a SMSReceiver . Come posso passare un extra a SMSReceiver ?

Aggiornamento n. 3

Ricezione di MMS

Quindi, dopo aver fatto ulteriori ricerche, ho visto alcuni suggerimenti per la registrazione di a ContentObserver. In questo modo è possibile rilevare eventuali modifiche al content://mms-sms/conversationsprovider di contenuti, consentendo di rilevare gli MMS in arrivo. Ecco l'esempio più vicino per farlo funzionare che ho trovato: Ricezione di MMS

Tuttavia, esiste una variabile mainActivitydi tipo ServiceController. Dove viene ServiceControllerimplementata la classe? Ci sono altre implementazioni di una registrazione ContentObserver?

Invio di MMS

Per quanto riguarda l'invio di MMS, mi sono imbattuto in questo esempio: Invia MMS

Il problema è che ho provato a eseguire questo codice sul mio Nexus 4, che è su Android v4.2.2, e ricevo questo errore:

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

L'errore viene generato dopo aver interrogato CarriersContentProvider nel getMMSApns()metodo della APNHelperclasse.

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

Apparentemente non puoi leggere APN su Android 4.2

Qual è l'alternativa per tutte quelle applicazioni che usano i dati mobili per eseguire operazioni (come l'invio di MMS) e non conoscono l'impostazione APN predefinita presente nel dispositivo?

Aggiornamento n. 4

Invio di MMS

Ho provato a seguire questo esempio: Invia MMS

Come suggerito da @Sam nella sua risposta:

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.

Quindi ora non ricevo più gli errori SecurityException. Sto testando ora su un Nexus 5 su Android KitKat. Dopo aver eseguito il codice di esempio mi dà un codice di 200 risposte dopo la chiamata a

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

Tuttavia, ho verificato con la persona a cui ho provato a inviare l'MMS. E hanno detto di non aver mai ricevuto l'MMS.



3
Sì. Ho provato a mettere insieme la risposta di Maxim ma non sono in grado di farlo funzionare. Ci sono molte classi lì dentro che importano android.provider.telephony che sembra essere deprecato.
toobsco42

E presumibilmente, dopo aver letto @ risposta di Sahil, hai anche provato questo: stackoverflow.com/questions/2972845/...
HaemEternal

Non sono sicuro di come mettere insieme quella risposta, anche se sembra molto simile alla risposta di @ Sahil.
toobsco42,

Ciao @ toobsco42 sei in grado di trovare la soluzione per tutte quelle query che menzioni sopra ..?
kamal_tech_view

Risposte:


15

Ho avuto lo stesso identico problema che descrivi sopra (Galaxy Nexus su t-mobile USA) perché i dati mobili sono disattivati.

In Jelly Bean è: Impostazioni> Utilizzo dati> dati mobili

Nota che devo avere i dati mobili attivati ​​PRIMA di inviare un MMS o di riceverne uno. Se ricevo un MMS con i dati mobili disattivati, riceverò la notifica di un nuovo messaggio e riceverò il messaggio con un pulsante di download. Ma se non ho i dati mobili in precedenza, l'allegato MMS in arrivo non verrà ricevuto. Anche se lo accendo dopo aver ricevuto il messaggio.

Per qualche motivo quando il tuo gestore telefonico ti consente di inviare e ricevere MMS, devi avere abilitato i dati mobili, anche se stai usando Wifi, se i dati mobili sono abilitati sarai in grado di ricevere e inviare MMS, anche se La connessione Wifi viene visualizzata come Internet sul dispositivo.

È una vera seccatura, come se non lo avessi acceso, il messaggio può bloccarsi molto, anche quando si accendono i dati mobili e potrebbe richiedere un riavvio del dispositivo.


Inoltre devi sapere che l'invio di un SMS e MMS sono 2 cose completamente diverse in background. MMS è più un servizio di rete basato su Internet in quanto richiede l'invio di elementi aggiuntivi (Media) con testo. Il codice dato funziona bene su alcuni dispositivi su cui ho testato. ps: puoi ignorare la parte NOKIA.
Manan Sharma,

Quando eseguo questo esempio, in LogCat stampa: 02-24 13: 32: 40.872: V / SendMMSActivity (5686): TYPE_MOBILE_MMS non connesso, cauzione 02-24 13: 32: 40.882: V / SendMMSActivity (5686): type is non TYPE_MOBILE_MMS, cauzione Dice anche: java.lang.SecurityException: nessuna autorizzazione per scrivere le impostazioni APN: né l'utente 10099 né il processo corrente hanno android.permission.WRITE_APN_SETTINGS. Sembra che non possa eseguire questa query: Cursore finale apnCursor = this.context.getContentResolver (). Query (Uri.withAppendedPath (Carriers.CONTENT_URI, "current"), null, null, null, null);
Sto

Anche questo è lo stesso esempio fornito da @Sahil.
toobsco42,

7

Non esiste un supporto API ufficiale, il che significa che non è documentato per il pubblico e le librerie possono cambiare in qualsiasi momento. Mi rendo conto che non vuoi lasciare l'applicazione, ma ecco come lo fai con l'intento di chiunque si stia chiedendo.

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"));

}

Non ho completamente capito come fare cose come tenere traccia della consegna del messaggio, ma questo dovrebbe essere inviato.

Puoi essere avvisato della ricezione di mms allo stesso modo degli sms. Il filtro intenti sul ricevitore dovrebbe apparire così.

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

Questo non avvia semplicemente l'app di messaggistica nativa?
toobsco42,

1
si scusa per quello. mi sono appena reso conto che già sapevi come farlo. ho aggiunto come ricevere mms però.
user1959417

Grazie, recentemente ho implementato parte dell'MMS BroadcastReceivere ho usato quello Intent Filterche hai pubblicato. Aggiornerò presto questa domanda.
toobsco42,

4

Per inviare un mms per Android 4.0 api 14 o versioni successive senza il permesso di scrivere le impostazioni apn, puoi usare questa libreria : Recupera codici mnc e mcc da Android, quindi chiama

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

Per usarlo, aggiungi Jsoup e il vaso prisma droide al percorso di compilazione e importa com.droidprism. *;


Ehi @Sam, ho aggiunto il file .jar al mio progetto ma sto riscontrando questo errore sulla riga che crea un'istanza dell'oggetto Carrier: ti java.lang.NoClassDefFoundError: com.droidprism.Carrier sta succedendo?
toobsco42,

no. Devi aggiungere jsoup al percorso di compilazione, il jar al percorso di compilazione e importare com.droidprism. *; Modificherò la risposta. Per farlo in Android, aggiungi prima i barattoli alla directory libs, quindi configura il percorso di compilazione del progetto per usare i barattoli già nella directory libs, quindi sul percorso di configurazione config fai clic su ordine ed esportazione e seleziona le caselle dei barattoli e sposta jsoup e droidprism jar in cima all'ordine di costruzione.
Sam Adamsh,

L'aggiunta di Jsoup .jar ha risolto NoClassDefFoundError. Ora posso ottenere le impostazioni APN. Il prossimo passo è capire come inviare MMS.
toobsco42

3

Non penso che ci sia alcun supporto SDK per l'invio di mms in Android. Guarda qui non ho ancora trovato Atleast. Ma un ragazzo ha affermato di averlo. Dai un'occhiata a questo post.

Invia MMS dalla mia applicazione in Android


Ho guardato i commenti sul post androidbridge.blogspot.com dell'implementazione Nokia e sembra che molte persone abbiano problemi a far funzionare questo sui loro dispositivi.
toobsco42

@ toobsco42 Quindi, potrebbe non esserci ancora supporto per questo.
Sahil Mahajan Mj,

-2

Non capisco le frustrazioni. Perché non creare semplicemente un ricevitore che filtra per questo intento:

android.provider.Telephony.MMS_RECEIVED

Ho controllato un po 'di più e potresti aver bisogno dell'accesso a livello di sistema per ottenere questo (telefono rootato).


3
Ehi @ j2emanue, il problema è dopo aver ricevuto questo intento, come si ottiene effettivamente il contenuto dell'MMS? Se un MMS contiene immagini e testo, come si estraggono questi componenti.
toobsco42,

ma noto che c'è un array di byte extra che puoi ottenere se lo fai nel modo .i menzionato .... byte [] data = intent.getByteArrayExtra ("data"); non sono sicuro di come analizzarlo anche se mi dispiace.
j2emanue,

sono in grado di analizzarlo. ma tutto quello che posso ottenere è l'argomento, da chi proviene l'MMS e la posizione del contenuto in cui è archiviato il contenuto dell'MMS. tuttavia questo URL non è accessibile.
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();

    }
}

Attività

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

Manifesto

<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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.