I am trying to make an app that would send a MMS without using the native Android messaging app. I followed the example here. My log statements seem to be correctly printing, but I can't figure out why the MMS is not being sent.
Also on a different note, I am a bit confused about where in the example the attachment (like an image) is being selected to send as MMS. I tried to import the demo into Android Studio but I ran into issues.
My function for sending MMS is below:
public void sendMMS() {
Log.d(TAG, "sendMMS()");
Random random = new Random();
final String fileName = "send." + String.valueOf(Math.abs(random.nextLong())) + ".dat";
final File mSendFile = new File(mContext.getCacheDir(), fileName);
// Making RPC call in non-UI thread
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
final byte[] pdu = buildPdu();
Uri writerUri = (new Uri.Builder())
.authority("com.example.appname")
.path(fileName)
.scheme(ContentResolver.SCHEME_CONTENT)
.build();
Log.d(TAG, "sendMMS(): Uri: " + writerUri.toString());
FileOutputStream writer = null;
Uri contentUri = null;
try {
writer = new FileOutputStream(mSendFile);
writer.write(pdu);
contentUri = writerUri;
Log.d(TAG, "sendMMS(): just wrote file");
} catch (final IOException e) {
Log.d(TAG, "sendMMS(): FAILED: couldn't write file");
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
}
}
}
if (contentUri != null) {
SmsManager.getDefault().sendMultimediaMessage(mContext, contentUri, null, null, null);
Log.d(TAG, "sendMMS(): just sent");
} else {
Log.d(TAG, "sendMMS(): FAILED: couldn't write file so didn't send");
}
}
});
}
Helper functions
private byte[] buildPdu() {
final SendReq req = new SendReq();
// from
final String lineNumber = getSimNumber();
if (!TextUtils.isEmpty(lineNumber)) {
req.setFrom(new EncodedStringValue(lineNumber));
}
// to
String[] destsArray = mDestList.toArray(new String[mDestList.size()]);
EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(destsArray);
if (encodedNumbers != null) {
req.setTo(encodedNumbers);
}
// date
req.setDate(System.currentTimeMillis() / 1000);
// body
PduBody body = new PduBody();
// message text
final int size = addMessagePart(body, true/* add text smil */);
req.setBody(body);
// message size
req.setMessageSize(size);
// message class
req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
// expiry
req.setExpiry(DEFAULT_EXPIRY_TIME);
try {
// priority
req.setPriority(DEFAULT_PRIORITY);
// delivery report
req.setDeliveryReport(PduHeaders.VALUE_NO);
// read report
req.setReadReport(PduHeaders.VALUE_NO);
} catch (InvalidHeaderValueException e) {}
return new PduComposer(mContext, req).make();
}
private String getSimNumber() {
TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
return telephonyManager.getLine1Number();
}
private int addMessagePart(PduBody pb, boolean addTextSmil) {
PduPart part = new PduPart();
part.setCharset(CharacterSets.UTF_8);
part.setContentType(ContentType.TEXT_PLAIN.getBytes());
part.setContentLocation(TEXT_PART_FILENAME.getBytes());
int index = TEXT_PART_FILENAME.lastIndexOf(".");
String contentId = (index == -1) ? TEXT_PART_FILENAME : TEXT_PART_FILENAME.substring(0, index);
part.setContentId(contentId.getBytes());
part.setData(mMessage.getBytes());
pb.addPart(part);
if (addTextSmil) {
String smil = String.format(sSmilText, TEXT_PART_FILENAME);
addSmilPart(pb, smil);
}
return part.getData().length;
}
private void addSmilPart(PduBody pb, String smil) {
PduPart smilPart = new PduPart();
smilPart.setContentId("smil".getBytes());
smilPart.setContentType(ContentType.APP_SMIL.getBytes());
smilPart.setContentLocation("smil.xml".getBytes());
smilPart.setData(smil.getBytes());
pb.addPart(0, smilPart);
}
Relevant parts of my manifest
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Relevant instance variables
private final long DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60;
private final String TEXT_PART_FILENAME = "text_0.txt";
private final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
private String mMessage;
private ArrayList<String> mDestList;
private Context mContext;
private static final String sSmilText =
"<smil>" +
"<head>" +
"<layout>" +
"<root-layout/>" +
"<region height=\"100%%\" id=\"Text\" left=\"0%%\" top=\"0%%\" width=\"100%%\"/>" +
"</layout>" +
"</head>" +
"<body>" +
"<par dur=\"8000ms\">" +
"<text src=\"%s\" region=\"Text\"/>" +
"</par>" +
"</body>" +
"</smil>";
I already do input checks, so by the time sendMMS() is called, my message and destList are not null.
The flow should be as such:
- Create the Mms send-request -
new SendReq()
and config its date, body, to, etc. - Create the Mms body -
new PduBody()
. - Create Parts via
new PduPart()
for each attachment, and add to the body:body.addPart(pdu)
- Add the body to the request -
req.setBody(body)
- Convert the send-request to a byte[] ready to be sent by calling
new PduComposer(context, mySendReq).make()
- note that you'll need to copy lots of code from Android's source code to get the PduComposer class. - Now's the interesting part - you save the byte[] to a local file accessible to your app only, and add
ContentProvider
class that allows other apps to request access to your file, this isMmsFileProvider
class in the sample app, don't forget to declare your provider in your manifest file. - Now, when you call the
SmsManager.sendMultimediaMessage
api, your file provider will wake up to serve the file containing the pdu bytes to the system SmsManager that will read it and send it on the wire.
Having that said, this API is only working for me on some devices (e.g. Nexuses), but not on some others (e.g. HTC One). See my SO question here: SmsManager MMS APIs on HTC/LG
来源:https://stackoverflow.com/questions/28862925/unable-to-send-mms-using-smsmanager