Envío y recepción de SMS y MMS en Android (pre Kit Kat Android 4.4)

He descubierto cómo enviar y recibir mensajes SMS. Para enviar mensajes SMS tuve que llamar a los sendTextMessage() y sendMultipartTextMessage() de la clase SmsManager . Para recibir mensajes SMS, tuve que registrar un receptor en el archivo AndroidMainfest.xml . Luego tuve que anular el método onReceive() del BroadcastReceiver . He incluido ejemplos a continuación.

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

                <activity //Main activity...      <activity //Activity 2 ...  //More acitivies ... // SMS Receiver        

Sin embargo, me preguntaba si podría enviar y recibir mensajes MMS de manera similar. Después de investigar un poco, muchos ejemplos proporcionados en los blogs simplemente pasan un Intent a la aplicación de mensajería nativa. Estoy tratando de enviar un MMS sin salir de mi aplicación. No parece haber una forma estándar de enviar y recibir MMS. ¿Alguien ha conseguido que esto funcione?

Además, soy consciente de que SMS / MMS ContentProvider no forma parte del SDK oficial de Android, pero pensé que alguien podría haberlo implementado. Cualquier ayuda es muy apreciada.

Actualizar

He agregado un BroadcastReceiver al archivo AndroidManifest.xml para recibir mensajes MMS

       

En la clase MMSReceiver, el método onReceive() solo puede captar el número de teléfono desde el que se envió el mensaje. ¿Cómo captas otras cosas importantes de un MMS, como la ruta del archivo al archivo adjunto multimedia (imagen / audio / video) o el texto en el 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 acuerdo con la documentación de android.provider.Telephony :

Acción de difusión: el dispositivo ha recibido un nuevo mensaje de texto basado en texto. La intención tendrá los siguientes valores adicionales:

pdus – Un Object[] de byte[] s que contiene las PDU que componen el mensaje.

Los valores extra se pueden extraer utilizando getMessagesFromIntent(android.content.Intent) Si un BroadcastReceiver encuentra un error al procesar este bash, debe establecer el código de resultado de manera apropiada.

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

Acción de difusión: el dispositivo ha recibido un nuevo mensaje de texto basado en datos. La intención tendrá los siguientes valores adicionales:

pdus – Un Object[] de byte[] s que contiene las PDU que componen el mensaje.

Los valores adicionales se pueden extraer utilizando getMessagesFromIntent (android.content.Intent). Si un BroadcastReceiver encuentra un error al procesar esta intención, debe establecer el código de resultado de manera apropiada.

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

Acción de difusión: el dispositivo ha recibido un nuevo mensaje WAP PUSH. La intención tendrá los siguientes valores adicionales:

transactionId (Integer) – La ID de transacción WAP

pduType (Integer) – El tipo de PDU WAP`

header (byte[]) – El encabezado del mensaje

data (byte[]) – La carga de datos del mensaje

contentTypeParameters (HashMap) – Cualquier parámetro asociado con el tipo de contenido (decodificado desde el encabezado WSP Content-Type)

Si un BroadcastReceiver encuentra un error al procesar esta intención, debe establecer el código de resultado de manera apropiada. El valor adicional de contentTypeParameters es un mapa de parámetros de contenido codificados por sus nombres. Si se encuentran parámetros conocidos no asignados, la clave del mapa será ‘unassigned / 0x …’, donde ‘…’ es el valor hexadecimal del parámetro no asignado. Si un parámetro tiene valor nulo, el valor en el mapa será nulo.

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

Actualización # 2

He descubierto cómo pasar extras en un PendingIntent para ser recibido por un BroadcastReceiver : Android PendingIntent extras, no recibidos por BroadcastReceiver

Sin embargo, el extra pasa al SendBroadcastReceiver no al SMSReceiver . ¿Cómo puedo pasar un extra al SMSReceiver ?

Actualización # 3

Recibiendo MMS

Entonces, después de investigar más, vi algunas sugerencias de registrar un ContentObserver . De esta forma puede detectar cuándo hay cambios en el content://mms-sms/conversations Proveedor de contenido, lo que le permite detectar MMS entrantes. Aquí está el ejemplo más cercano para hacer que funcione esto que he encontrado: Recibir MMS

Sin embargo, hay una variable mainActivity de tipo ServiceController . ¿Dónde se implementa la clase ServiceController ? ¿Hay alguna otra implementación de un ContentObserver registrado?

Enviando MMS

En cuanto al envío de MMS, he encontrado este ejemplo: Enviar MMS

El problema es que intenté ejecutar este código en mi Nexus 4, que está en Android v4.2.2, y recibo este error:

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

El error se getMMSApns() después de consultar el getMMSApns() Carriers en el método getMMSApns() de la clase APNHelper .

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

Aparentemente no puedes leer APN en Android 4.2

¿Cuál es la alternativa para todas las aplicaciones que usan datos móviles para realizar operaciones (como enviar MMS) y no conocen la configuración de APN predeterminada presente en el dispositivo?

Actualización # 4

Enviando MMS

He intentado seguir este ejemplo: Enviar MMS

Como @Sam sugirió en su respuesta:

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.

Así que ahora ya no recibo los errores SecurityException. Estoy probando ahora en un Nexus 5 en Android KitKat. Después de ejecutar el código de muestra, me da un código de respuesta 200 después de la llamada a

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

Sin embargo, verifiqué con la persona a la que intenté enviar el MMS. Y dijeron que nunca recibieron el MMS.

Tuve exactamente el mismo problema que describes arriba (Galaxy Nexus en t-mobile USA) porque los datos móviles están apagados.

En Jelly Bean es: Configuración> Uso de datos> datos móviles

Tenga en cuenta que tengo que tener datos móviles activados ANTES de enviar un MMS O recibir uno. Si recibo un MMS con datos móviles desactivados, recibiré la notificación de un nuevo mensaje y recibiré el mensaje con un botón de descarga. Pero si no tengo datos móviles anteriores, no se recibirán los archivos adjuntos de MMS entrantes. Incluso si lo enciendo después de que se recibió el mensaje.

Por alguna razón, cuando su proveedor de telefonía lo habilita con la capacidad de enviar y recibir MMS, debe tener los Datos móviles habilitados, incluso si está utilizando Wifi, si los Datos móviles están habilitados, podrá recibir y enviar MMS, incluso si Wifi se muestra como su internet en su dispositivo.

Es un verdadero dolor, como si no lo tuviera activado, el mensaje puede colgar mucho, incluso cuando se encienden los datos móviles, y puede requerir un reinicio del dispositivo.

No hay soporte oficial de API, lo que significa que no está documentado para el público y las bibliotecas pueden cambiar en cualquier momento. Me doy cuenta de que no quieres dejar la aplicación, pero así es como lo haces con la intención de que alguien más se lo pregunte.

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

No he descubierto completamente cómo hacer cosas como rastrear la entrega del mensaje, pero esto debería ser enviado.

Puede recibir una alerta para recibir mms de la misma manera que sms. El filtro de intención en el receptor debería verse así.

     

Para enviar un mms para Android 4.0 api 14 o superior sin permiso para escribir la configuración de la aplicación, puede usar esta biblioteca : Recuperar códigos mnc y mcc de android, luego llamar

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

Para usar esto, agregue el jsoup y el prisma droide a la ruta de comstackción e importe com.droidprism. *;

No creo que haya ningún soporte SDK para enviar mms en Android. Mira aquí Al menos aún no he encontrado. Pero un tipo afirmó tenerlo. Eche un vistazo a esta publicación.

Enviar MMS desde Mi aplicación en android

No entiendo las frustraciones. ¿Por qué no simplemente crear un receptor de difusión que se filtre con esta intención?

 android.provider.Telephony.MMS_RECEIVED 

Revisé un poco más y es posible que necesite acceso a nivel del sistema para obtener esto (teléfono rooteado).

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

Actividad

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

Manifiesto