From f22e8c9322278b608ec77d5ff8808058fa93041d Mon Sep 17 00:00:00 2001 From: Moez Bhatti Date: Thu, 28 Jun 2018 17:44:04 -0400 Subject: [PATCH] Convert Transaction to Kotlin, remove unused classes --- android-smsmms/build.gradle | 2 + .../mms/transaction/MmsMessageSender.java | 178 ------ .../mms/transaction/TransactionSettings.java | 18 +- .../java/com/google/android/mms/MMSPart.java | 10 - .../java/com/google/android/mms/MMSPart.kt | 7 + .../google/android/mms/pdu_alt/PduBody.java | 20 +- .../klinker/android/send_message/Message.java | 533 ---------------- .../klinker/android/send_message/Settings.kt | 10 +- .../android/send_message/SmsManagerFactory.kt | 2 - .../android/send_message/Transaction.java | 576 ------------------ .../android/send_message/Transaction.kt | 206 +++++++ .../klinker/android/send_message/Utils.java | 6 - .../QKSMS/repository/MessageRepositoryImpl.kt | 24 +- 13 files changed, 248 insertions(+), 1344 deletions(-) delete mode 100755 android-smsmms/src/main/java/com/android/mms/transaction/MmsMessageSender.java delete mode 100755 android-smsmms/src/main/java/com/google/android/mms/MMSPart.java create mode 100755 android-smsmms/src/main/java/com/google/android/mms/MMSPart.kt delete mode 100755 android-smsmms/src/main/java/com/klinker/android/send_message/Message.java delete mode 100755 android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.java create mode 100755 android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.kt diff --git a/android-smsmms/build.gradle b/android-smsmms/build.gradle index 8e93643e..da9f5235 100644 --- a/android-smsmms/build.gradle +++ b/android-smsmms/build.gradle @@ -37,6 +37,8 @@ android { } dependencies { + implementation "androidx.core:core-ktx:$ktx_version" + implementation "com.jakewharton.timber:timber:$timber_version" implementation 'com.klinkerapps:logger:1.0.3' implementation 'com.squareup.okhttp:okhttp:2.5.0' implementation 'com.squareup.okhttp:okhttp-urlconnection:2.5.0' diff --git a/android-smsmms/src/main/java/com/android/mms/transaction/MmsMessageSender.java b/android-smsmms/src/main/java/com/android/mms/transaction/MmsMessageSender.java deleted file mode 100755 index 1b3ee10e..00000000 --- a/android-smsmms/src/main/java/com/android/mms/transaction/MmsMessageSender.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2014 Jacob Klinker - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mms.transaction; - -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.preference.PreferenceManager; -import android.provider.Telephony.Mms; -import android.provider.Telephony.MmsSms; -import android.provider.Telephony.MmsSms.PendingMessages; -import com.android.mms.logs.LogTag; -import com.android.mms.util.SendingProgressTokenManager; -import com.google.android.mms.InvalidHeaderValueException; -import com.google.android.mms.MmsException; -import com.google.android.mms.pdu_alt.EncodedStringValue; -import com.google.android.mms.pdu_alt.GenericPdu; -import com.google.android.mms.pdu_alt.PduHeaders; -import com.google.android.mms.pdu_alt.PduPersister; -import com.google.android.mms.pdu_alt.ReadRecInd; -import com.google.android.mms.pdu_alt.SendReq; -import com.google.android.mms.util_alt.SqliteWrapper; -import com.klinker.android.logger.Log; - -public class MmsMessageSender implements MessageSender { - private static final String TAG = LogTag.TAG; - - private final Context mContext; - private final Uri mMessageUri; - private final long mMessageSize; - - // Default preference values - private static final boolean DEFAULT_DELIVERY_REPORT_MODE = false; - private static final boolean DEFAULT_READ_REPORT_MODE = false; - private static final long DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60; - private static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL; - private static final String DEFAULT_MESSAGE_CLASS = PduHeaders.MESSAGE_CLASS_PERSONAL_STR; - - private static final String DELIVERY_REPORT_PREFERENCE = "delivery_reports"; - private static final String READ_REPORT_PREFERENCE = "read_reports"; - - public MmsMessageSender(Context context, Uri location, long messageSize) { - mContext = context; - mMessageUri = location; - mMessageSize = messageSize; - - if (mMessageUri == null) { - throw new IllegalArgumentException("Null message URI."); - } - } - - public boolean sendMessage(long token) throws MmsException { - // Load the MMS from the message uri - if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { - LogTag.debug("sendMessage uri: " + mMessageUri); - } - PduPersister p = PduPersister.getPduPersister(mContext); - GenericPdu pdu = p.load(mMessageUri); - - if (pdu.getMessageType() != PduHeaders.MESSAGE_TYPE_SEND_REQ) { - throw new MmsException("Invalid message: " + pdu.getMessageType()); - } - - SendReq sendReq = (SendReq) pdu; - - // Update headers. - updatePreferencesHeaders(sendReq); - - // MessageClass. - sendReq.setMessageClass(DEFAULT_MESSAGE_CLASS.getBytes()); - - // Update the 'date' field of the message before sending it. - sendReq.setDate(System.currentTimeMillis() / 1000L); - - sendReq.setMessageSize(mMessageSize); - - p.updateHeaders(mMessageUri, sendReq); - - long messageId = ContentUris.parseId(mMessageUri); - - // Move the message into MMS Outbox. - if (!mMessageUri.toString().startsWith(Mms.Draft.CONTENT_URI.toString())) { - // If the message is already in the outbox (most likely because we created a "primed" - // message in the outbox when the user hit send), then we have to manually put an - // entry in the pending_msgs table which is where TransacationService looks for - // messages to send. Normally, the entry in pending_msgs is created by the trigger: - // insert_mms_pending_on_update, when a message is moved from drafts to the outbox. - ContentValues values = new ContentValues(7); - - values.put(PendingMessages.PROTO_TYPE, MmsSms.MMS_PROTO); - values.put(PendingMessages.MSG_ID, messageId); - values.put(PendingMessages.MSG_TYPE, pdu.getMessageType()); - values.put(PendingMessages.ERROR_TYPE, 0); - values.put(PendingMessages.ERROR_CODE, 0); - values.put(PendingMessages.RETRY_INDEX, 0); - values.put(PendingMessages.DUE_TIME, 0); - - SqliteWrapper.insert(mContext, mContext.getContentResolver(), - PendingMessages.CONTENT_URI, values); - } else { - p.move(mMessageUri, Mms.Outbox.CONTENT_URI); - } - - // Start MMS transaction service - try { - SendingProgressTokenManager.put(messageId, token); - mContext.startService(new Intent(mContext, TransactionService.class)); - } catch (Exception e) { - throw new MmsException("transaction service not registered in manifest"); - } - - return true; - } - - // Update the headers which are stored in SharedPreferences. - private void updatePreferencesHeaders(SendReq sendReq) throws MmsException { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); - - // Expiry. - sendReq.setExpiry(DEFAULT_EXPIRY_TIME); - - // Priority. - sendReq.setPriority(DEFAULT_PRIORITY); - - // Delivery report. - boolean dr = prefs.getBoolean(DELIVERY_REPORT_PREFERENCE, - DEFAULT_DELIVERY_REPORT_MODE); - sendReq.setDeliveryReport(dr?PduHeaders.VALUE_YES:PduHeaders.VALUE_NO); - - // Read report. - boolean rr = prefs.getBoolean(READ_REPORT_PREFERENCE, - // default to delivery report value if read report not available - prefs.getBoolean(DELIVERY_REPORT_PREFERENCE, - DEFAULT_READ_REPORT_MODE)); - sendReq.setReadReport(rr?PduHeaders.VALUE_YES:PduHeaders.VALUE_NO); - } - - public static void sendReadRec(Context context, String to, String messageId, int status) { - EncodedStringValue[] sender = new EncodedStringValue[1]; - sender[0] = new EncodedStringValue(to); - - try { - final ReadRecInd readRec = new ReadRecInd( - new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()), - messageId.getBytes(), - PduHeaders.CURRENT_MMS_VERSION, - status, - sender); - - readRec.setDate(System.currentTimeMillis() / 1000); - - PduPersister.getPduPersister(context).persist(readRec, Mms.Outbox.CONTENT_URI, true, - true, null); - context.startService(new Intent(context, TransactionService.class)); - } catch (InvalidHeaderValueException e) { - Log.e(TAG, "Invalide header value", e); - } catch (MmsException e) { - Log.e(TAG, "Persist message failed", e); - } - } -} diff --git a/android-smsmms/src/main/java/com/android/mms/transaction/TransactionSettings.java b/android-smsmms/src/main/java/com/android/mms/transaction/TransactionSettings.java index 47f95156..7deb7306 100755 --- a/android-smsmms/src/main/java/com/android/mms/transaction/TransactionSettings.java +++ b/android-smsmms/src/main/java/com/android/mms/transaction/TransactionSettings.java @@ -81,27 +81,27 @@ public class TransactionSettings { // // if (cursor == null) { // Log.e(TAG, "Apn is not found in Database!"); - if (Transaction.settings == null) { - Transaction.settings = Utils.getDefaultSendSettings(context); + if (Transaction.Companion.getSettings() == null) { + Transaction.Companion.setSettings(Utils.getDefaultSendSettings(context)); } - mServiceCenter = NetworkUtilsHelper.trimV4AddrZeros(Transaction.settings.getMmsc()); - mProxyAddress = NetworkUtilsHelper.trimV4AddrZeros(Transaction.settings.getProxy()); + mServiceCenter = NetworkUtilsHelper.trimV4AddrZeros(Transaction.Companion.getSettings().getMmsc()); + mProxyAddress = NetworkUtilsHelper.trimV4AddrZeros(Transaction.Companion.getSettings().getProxy()); // Set up the agent, profile url and tag name to be used in the mms request if they are attached in settings - String agent = Transaction.settings.getAgent(); + String agent = Transaction.Companion.getSettings().getAgent(); if (agent != null && !agent.trim().equals("")) { MmsConfig.setUserAgent(agent); Log.v(TAG, "set user agent"); } - String uaProfUrl = Transaction.settings.getUserProfileUrl(); + String uaProfUrl = Transaction.Companion.getSettings().getUserProfileUrl(); if (uaProfUrl != null && !uaProfUrl.trim().equals("")) { MmsConfig.setUaProfUrl(uaProfUrl); Log.v(TAG, "set user agent profile url"); } - String uaProfTagName = Transaction.settings.getUaProfTagName(); + String uaProfTagName = Transaction.Companion.getSettings().getUaProfTagName(); if (uaProfTagName != null && !uaProfTagName.trim().equals("")) { MmsConfig.setUaProfTagName(uaProfTagName); Log.v(TAG, "set user agent profile tag name"); @@ -109,9 +109,9 @@ public class TransactionSettings { if (isProxySet()) { try { - mProxyPort = Integer.parseInt(Transaction.settings.getPort()); + mProxyPort = Integer.parseInt(Transaction.Companion.getSettings().getPort()); } catch (NumberFormatException e) { - Log.e(TAG, "could not get proxy: " + Transaction.settings.getPort(), e); + Log.e(TAG, "could not get proxy: " + Transaction.Companion.getSettings().getPort(), e); } } // } diff --git a/android-smsmms/src/main/java/com/google/android/mms/MMSPart.java b/android-smsmms/src/main/java/com/google/android/mms/MMSPart.java deleted file mode 100755 index 582c9b4b..00000000 --- a/android-smsmms/src/main/java/com/google/android/mms/MMSPart.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.google.android.mms; - -import android.net.Uri; - -public class MMSPart { - public String Name = ""; - public String MimeType = ""; - public byte[] Data; - public Uri Path; -} \ No newline at end of file diff --git a/android-smsmms/src/main/java/com/google/android/mms/MMSPart.kt b/android-smsmms/src/main/java/com/google/android/mms/MMSPart.kt new file mode 100755 index 00000000..053f4f1a --- /dev/null +++ b/android-smsmms/src/main/java/com/google/android/mms/MMSPart.kt @@ -0,0 +1,7 @@ +package com.google.android.mms + +class MMSPart @JvmOverloads constructor( + var name: String = "", + var mimeType: String = "", + var data: ByteArray? = null +) \ No newline at end of file diff --git a/android-smsmms/src/main/java/com/google/android/mms/pdu_alt/PduBody.java b/android-smsmms/src/main/java/com/google/android/mms/pdu_alt/PduBody.java index fbb8841e..a5a9ffb0 100755 --- a/android-smsmms/src/main/java/com/google/android/mms/pdu_alt/PduBody.java +++ b/android-smsmms/src/main/java/com/google/android/mms/pdu_alt/PduBody.java @@ -21,23 +21,23 @@ import java.util.Map; import java.util.Vector; public class PduBody { - private Vector mParts = null; + private Vector mParts; - private Map mPartMapByContentId = null; - private Map mPartMapByContentLocation = null; - private Map mPartMapByName = null; - private Map mPartMapByFileName = null; + private Map mPartMapByContentId; + private Map mPartMapByContentLocation; + private Map mPartMapByName; + private Map mPartMapByFileName; /** * Constructor. */ public PduBody() { - mParts = new Vector(); + mParts = new Vector<>(); - mPartMapByContentId = new HashMap(); - mPartMapByContentLocation = new HashMap(); - mPartMapByName = new HashMap(); - mPartMapByFileName = new HashMap(); + mPartMapByContentId = new HashMap<>(); + mPartMapByContentLocation = new HashMap<>(); + mPartMapByName = new HashMap<>(); + mPartMapByFileName = new HashMap<>(); } private void putPartToMaps(PduPart part) { diff --git a/android-smsmms/src/main/java/com/klinker/android/send_message/Message.java b/android-smsmms/src/main/java/com/klinker/android/send_message/Message.java deleted file mode 100755 index 383987f9..00000000 --- a/android-smsmms/src/main/java/com/klinker/android/send_message/Message.java +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Copyright 2013 Jacob Klinker - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.klinker.android.send_message; - -import android.graphics.Bitmap; -import com.klinker.android.logger.Log; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Class to hold all relevant message information to send - * - * @author Jake Klinker - */ -public class Message { - - public static final class Part { - private byte[] media; - private String contentType; - private String name; - public Part(byte[] media, String contentType, String name) { - this.media = media; - this.contentType = contentType; - this.name = name; - } - - public byte[] getMedia() { - return media; - } - - public String getContentType() { - return contentType; - } - - public String getName() { - return name; - } - } - private String text; - private String subject; - private String[] addresses; - private Bitmap[] images; - private String[] imageNames; - private List parts = new ArrayList<>(); - private boolean save; - private int delay; - - /** - * Default constructor - */ - public Message() { - this("", new String[]{""}); - } - - /** - * Constructor - * - * @param text is the message to send - * @param address is the phone number to send to - */ - public Message(String text, String address) { - this(text, address.trim().split(" ")); - } - - /** - * Constructor - * - * @param text is the message to send - * @param address is the phone number to send to - * @param subject is the subject of the mms message - */ - public Message(String text, String address, String subject) { - this(text, address.trim().split(" "), subject); - } - - /** - * Constructor - * - * @param text is the message to send - * @param addresses is an array of phone numbers to send to - */ - public Message(String text, String[] addresses) { - this.text = text; - this.addresses = addresses; - this.images = new Bitmap[0]; - this.subject = null; - this.save = true; - this.delay = 0; - } - - /** - * Constructor - * - * @param text is the message to send - * @param addresses is an array of phone numbers to send to - * @param subject is the subject of the mms message - */ - public Message(String text, String[] addresses, String subject) { - this.text = text; - this.addresses = addresses; - this.images = new Bitmap[0]; - this.subject = subject; - this.save = true; - this.delay = 0; - } - - /** - * Constructor - * - * @param text is the message to send - * @param address is the phone number to send to - * @param image is the image that you want to send - */ - public Message(String text, String address, Bitmap image) { - this(text, address.trim().split(" "), new Bitmap[]{image}); - } - - /** - * Constructor - * - * @param text is the message to send - * @param address is the phone number to send to - * @param image is the image that you want to send - * @param subject is the subject of the mms message - */ - public Message(String text, String address, Bitmap image, String subject) { - this(text, address.trim().split(" "), new Bitmap[]{image}, subject); - } - - /** - * Constructor - * - * @param text is the message to send - * @param addresses is an array of phone numbers to send to - * @param image is the image that you want to send - */ - public Message(String text, String[] addresses, Bitmap image) { - this(text, addresses, new Bitmap[]{image}); - } - - /** - * Constructor - * - * @param text is the message to send - * @param addresses is an array of phone numbers to send to - * @param image is the image that you want to send - * @param subject is the subject of the mms message - */ - public Message(String text, String[] addresses, Bitmap image, String subject) { - this(text, addresses, new Bitmap[]{image}, subject); - } - - /** - * Constructor - * - * @param text is the message to send - * @param address is the phone number to send to - * @param images is an array of images that you want to send - */ - public Message(String text, String address, Bitmap[] images) { - this(text, address.trim().split(" "), images); - } - - /** - * Constructor - * - * @param text is the message to send - * @param address is the phone number to send to - * @param images is an array of images that you want to send - * @param subject is the subject of the mms message - */ - public Message(String text, String address, Bitmap[] images, String subject) { - this(text, address.trim().split(" "), images, subject); - } - - /** - * Constructor - * - * @param text is the message to send - * @param addresses is an array of phone numbers to send to - * @param images is an array of images that you want to send - */ - public Message(String text, String[] addresses, Bitmap[] images) { - this.text = text; - this.addresses = addresses; - this.images = images; - this.subject = null; - this.save = true; - this.delay = 0; - } - - /** - * Constructor - * - * @param text is the message to send - * @param addresses is an array of phone numbers to send to - * @param images is an array of images that you want to send - * @param subject is the subject of the mms message - */ - public Message(String text, String[] addresses, Bitmap[] images, String subject) { - this.text = text; - this.addresses = addresses; - this.images = images; - this.subject = subject; - this.save = true; - this.delay = 0; - } - - /** - * Sets the message - * - * @param text is the string to set message to - */ - public void setText(String text) { - this.text = text; - } - - /** - * Sets recipients - * - * @param addresses is the array of recipients to send to - */ - public void setAddresses(String[] addresses) { - this.addresses = addresses; - } - - /** - * Sets single recipient - * - * @param address is the phone number of the recipient - */ - public void setAddress(String address) { - this.addresses = new String[1]; - this.addresses[0] = address; - } - - /** - * Sets images - * - * @param images is the array of images to send to recipient - */ - public void setImages(Bitmap[] images) { - this.images = images; - } - - /** - * Sets image names - * - * @param names - */ - public void setImageNames(String[] names) { - this.imageNames = names; - } - - /** - * Sets image - * - * @param image is the single image to send to recipient - */ - public void setImage(Bitmap image) { - this.images = new Bitmap[1]; - this.images[0] = image; - } - - /** - * Sets audio file. Must be in wav format. - * - * @param audio is the single audio sample to send to recipient - */ - @Deprecated - public void setAudio(byte[] audio) { - addAudio(audio); - } - - /** - * Sets audio file. Must be in wav format. - * - * @param audio is the single audio sample to send to recipient - */ - public void addAudio(byte[] audio) { - addAudio(audio, null); - } - - /** - * Sets audio file. Must be in wav format. - * - * @param audio is the single audio sample to send to recipient - * @param name is the name of the file - */ - public void addAudio(byte[] audio, String name) { - addMedia(audio, "audio/wav", name); - } - - /** - * Sets video file - * - * @param video is the single video sample to send to recipient - */ - @Deprecated - public void setVideo(byte[] video) { - addVideo(video); - } - - /** - * Adds video file - * - * @param video is the single video sample to send to recipient - */ - public void addVideo(byte[] video) { - addVideo(video, null); - } - - /** - * Adds video file - * - * @param video is the single video sample to send to recipient - * @param name is the name of the video file - */ - public void addVideo(byte[] video, String name) { - addMedia(video, "video/3gpp", name); - } - - /** - * Sets other media - * - * @param media is the media you want to send - * @param mimeType is the mimeType of the media - */ - @Deprecated - public void setMedia(byte[] media, String mimeType) { - addMedia(media, mimeType); - } - - /** - * Adds other media - * - * @param media is the media you want to send - * @param mimeType is the mimeType of the media - */ - public void addMedia(byte[] media, String mimeType) { - this.parts.add(new Part(media, mimeType, null)); - } - - /** - * Adds other media - * - * @param media is the media you want to send - * @param mimeType is the mimetype of the media - * @param name is the name of the file - */ - public void addMedia(byte[] media, String mimeType, String name) { - this.parts.add(new Part(media, mimeType, name)); - } - - /** - * Sets the subject - * - * @param subject is the subject of the mms message - */ - public void setSubject(String subject) { - this.subject = subject; - } - - /** - * Sets whether or not to save a message to the database - * - * @param save is whether or not to save the message - */ - public void setSave(boolean save) { - this.save = save; - } - - /** - * Sets the time delay before sending a message - * NOTE: this is only applicable for SMS messages - * - * @param delay the time delay in milliseconds - */ - public void setDelay(int delay) { - this.delay = delay; - } - - /** - * Method to add another recipient to the object - * - * @param address is the string of the recipients phone number to add to end of recipients array - */ - public void addAddress(String address) { - String[] temp = this.addresses; - - if (temp == null) { - temp = new String[0]; - } - - this.addresses = new String[temp.length + 1]; - - System.arraycopy(temp, 0, this.addresses, 0, temp.length); - - this.addresses[temp.length] = address; - } - - /** - * Add another image to the object - * - * @param image is the image that you want to add to the end of the bitmaps array - */ - public void addImage(Bitmap image) { - Bitmap[] temp = this.images; - - if (temp == null) { - temp = new Bitmap[0]; - } - - this.images = new Bitmap[temp.length + 1]; - - System.arraycopy(temp, 0, this.images, 0, temp.length); - - this.images[temp.length] = image; - } - - /** - * Gets the text of the message to send - * - * @return the string of the message to send - */ - public String getText() { - return this.text; - } - - /** - * Gets the addresses of the message - * - * @return an array of strings with all of the addresses - */ - public String[] getAddresses() { - return this.addresses; - } - - /** - * Gets the images in the message - * - * @return an array of bitmaps with all of the images - */ - public Bitmap[] getImages() { - return this.images; - } - - /** - * Gets image names for the message - * - * @return - */ - public String[] getImageNames() { - return this.imageNames; - } - - /** - * Gets the audio sample in the message - * - * @return an array of bytes with audio information for the message - */ - public List getParts() { - return this.parts; - } - - /** - * Gets the subject of the mms message - * - * @return a string with the subject of the message - */ - public String getSubject() { - return this.subject; - } - - /** - * Gets whether or not to save the message to the database - * - * @return a boolean of whether or not to save - */ - public boolean getSave() { - return this.save; - } - - /** - * Gets the time to delay before sending the message - * - * @return the delay time in milliseconds - */ - public int getDelay() { - return this.delay; - } - - /** - * Static method to convert a bitmap into a byte array to easily send it over http - * - * @param image is the image to convert - * @return a byte array of the image data - */ - public static byte[] bitmapToByteArray(Bitmap image) { - byte[] output = new byte[0]; - if (image == null) { - Log.v("Message", "image is null, returning byte array of size 0"); - return output; - } - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - try { - image.compress(Bitmap.CompressFormat.JPEG, 90, stream); - output = stream.toByteArray(); - } finally { - try { - stream.close(); - } catch (IOException e) {} - } - return output; - } -} diff --git a/android-smsmms/src/main/java/com/klinker/android/send_message/Settings.kt b/android-smsmms/src/main/java/com/klinker/android/send_message/Settings.kt index b6bc1a89..73fcb3a7 100755 --- a/android-smsmms/src/main/java/com/klinker/android/send_message/Settings.kt +++ b/android-smsmms/src/main/java/com/klinker/android/send_message/Settings.kt @@ -29,12 +29,4 @@ class Settings @JvmOverloads constructor( var uaProfTagName: String? = "", // SMS options - var deliveryReports: Boolean = false, - var split: Boolean = false, - var splitCounter: Boolean = false, - var stripUnicode: Boolean = false, - var signature: String? = null, - var preText: String? = null, - var sendLongAsMms: Boolean = true, - var sendLongAsMmsAfter: Int = 3, - var subscriptionId: Int = -1) + var stripUnicode: Boolean = false) diff --git a/android-smsmms/src/main/java/com/klinker/android/send_message/SmsManagerFactory.kt b/android-smsmms/src/main/java/com/klinker/android/send_message/SmsManagerFactory.kt index 1f0e447c..10d078e4 100755 --- a/android-smsmms/src/main/java/com/klinker/android/send_message/SmsManagerFactory.kt +++ b/android-smsmms/src/main/java/com/klinker/android/send_message/SmsManagerFactory.kt @@ -5,8 +5,6 @@ import android.telephony.SmsManager object SmsManagerFactory { - fun createSmsManager(settings: Settings) = createSmsManager(settings.subscriptionId) - fun createSmsManager(subscriptionId: Int): SmsManager { var manager: SmsManager? = null diff --git a/android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.java b/android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.java deleted file mode 100755 index 65ad543c..00000000 --- a/android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.java +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright 2013 Jacob Klinker - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.klinker.android.send_message; - -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Bundle; -import android.telephony.SmsManager; -import android.text.TextUtils; -import com.android.mms.MmsConfig; -import com.android.mms.dom.smil.parser.SmilXmlSerializer; -import com.android.mms.util.DownloadManager; -import com.android.mms.util.RateController; -import com.google.android.mms.ContentType; -import com.google.android.mms.InvalidHeaderValueException; -import com.google.android.mms.MMSPart; -import com.google.android.mms.MmsException; -import com.google.android.mms.pdu_alt.CharacterSets; -import com.google.android.mms.pdu_alt.EncodedStringValue; -import com.google.android.mms.pdu_alt.PduBody; -import com.google.android.mms.pdu_alt.PduComposer; -import com.google.android.mms.pdu_alt.PduHeaders; -import com.google.android.mms.pdu_alt.PduPart; -import com.google.android.mms.pdu_alt.PduPersister; -import com.google.android.mms.pdu_alt.SendReq; -import com.google.android.mms.smil.SmilHelper; -import com.klinker.android.logger.Log; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.Set; - -/** - * Class to process transaction requests for sending - * - * @author Jake Klinker - */ -public class Transaction { - - private static final String TAG = "Transaction"; - public static Settings settings; - private Context context; - - private boolean saveMessage = true; - - public String SMS_SENT = ".SMS_SENT"; - public String SMS_DELIVERED = ".SMS_DELIVERED"; - - public static final String MMS_SENT = "com.moez.QKSMS.MMS_SENT"; - public static final String EXTRA_CONTENT_URI = "content_uri"; - public static final String EXTRA_FILE_PATH = "file_path"; - - public static String NOTIFY_SMS_FAILURE = ".NOTIFY_SMS_FAILURE"; - public static final String MMS_UPDATED = "com.moez.QKSMS.MMS_UPDATED"; - public static final String MMS_ERROR = "com.klinker.android.send_message.MMS_ERROR"; - public static final String REFRESH = "com.klinker.android.send_message.REFRESH"; - public static final String MMS_PROGRESS = "com.klinker.android.send_message.MMS_PROGRESS"; - public static final String NOTIFY_OF_DELIVERY = "com.klinker.android.send_message.NOTIFY_DELIVERY"; - public static final String NOTIFY_OF_MMS = "com.klinker.android.messaging.NEW_MMS_DOWNLOADED"; - - - public static final long DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60; - public static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL; - - /** - * Sets context and initializes settings to default values - * - * @param context is the context of the activity or service - */ - public Transaction(Context context) { - this(context, new Settings()); - } - - /** - * Sets context and settings - * - * @param context is the context of the activity or service - * @param settings is the settings object to process send requests through - */ - public Transaction(Context context, Settings settings) { - Transaction.settings = settings; - this.context = context; - - SMS_SENT = context.getPackageName() + SMS_SENT; - SMS_DELIVERED = context.getPackageName() + SMS_DELIVERED; - - if (NOTIFY_SMS_FAILURE.equals(".NOTIFY_SMS_FAILURE")) { - NOTIFY_SMS_FAILURE = context.getPackageName() + NOTIFY_SMS_FAILURE; - } - } - - /** - * Called to send a new message depending on settings and provided Message object - * If you want to send message as mms, call this from the UI thread - * - * @param message is the message that you want to send - * @param threadId is the thread id of who to send the message to (can also be set to Transaction.NO_THREAD_ID) - */ - public void sendNewMessage(Message message, long threadId) { - this.saveMessage = message.getSave(); - - RateController.init(context); - DownloadManager.init(context); - sendMmsMessage(message.getText(), message.getAddresses(), message.getImages(), message.getImageNames(), message.getParts(), message.getSubject()); - } - - private void sendMmsMessage(String text, String[] addresses, Bitmap[] image, String[] imageNames, List parts, String subject) { - // create the parts to send - ArrayList data = new ArrayList<>(); - - for (int i = 0; i < image.length; i++) { - // turn bitmap into byte array to be stored - byte[] imageBytes = Message.bitmapToByteArray(image[i]); - - MMSPart part = new MMSPart(); - part.MimeType = "image/jpeg"; - part.Name = (imageNames != null) ? imageNames[i] : ("image_" + System.currentTimeMillis()); - part.Data = imageBytes; - data.add(part); - } - - // add any extra media according to their mimeType set in the message - // eg. videos, audio, contact cards, location maybe? - if (parts != null) { - for (Message.Part p : parts) { - MMSPart part = new MMSPart(); - if (p.getName() != null) { - part.Name = p.getName(); - } else { - part.Name = p.getContentType().split("/")[0]; - } - part.MimeType = p.getContentType(); - part.Data = p.getMedia(); - data.add(part); - } - } - - if (text != null && !text.equals("")) { - // add text to the end of the part and send - MMSPart part = new MMSPart(); - part.Name = "text"; - part.MimeType = "text/plain"; - part.Data = text.getBytes(); - data.add(part); - } - - Log.v(TAG, "sending mms"); - try { - final String fileName = "send." + String.valueOf(Math.abs(new Random().nextLong())) + ".dat"; - File mSendFile = new File(context.getCacheDir(), fileName); - - SendReq sendReq = buildPdu(context, addresses, subject, data); - PduPersister persister = PduPersister.getPduPersister(context); - Uri messageUri = persister.persist(sendReq, Uri.parse("content://mms/outbox"), true, true, null); - - Intent sentIntent = new Intent(MMS_SENT); - BroadcastUtils.addClassName(context, sentIntent, MMS_SENT); - - sentIntent.putExtra(EXTRA_CONTENT_URI, messageUri.toString()); - sentIntent.putExtra(EXTRA_FILE_PATH, mSendFile.getPath()); - final PendingIntent sentPI = PendingIntent.getBroadcast( - context, 0, sentIntent, PendingIntent.FLAG_CANCEL_CURRENT); - - Intent updatedIntent = new Intent(MMS_UPDATED); - updatedIntent.putExtra("uri", messageUri.toString()); - BroadcastUtils.addClassName(context, updatedIntent, MMS_UPDATED); - context.sendBroadcast(updatedIntent); - - Uri writerUri = (new Uri.Builder()) - .authority(context.getPackageName() + ".MmsFileProvider") - .path(fileName) - .scheme(ContentResolver.SCHEME_CONTENT) - .build(); - FileOutputStream writer = null; - Uri contentUri = null; - try { - writer = new FileOutputStream(mSendFile); - writer.write(new PduComposer(context, sendReq).make()); - contentUri = writerUri; - } catch (final IOException e) { - Log.e(TAG, "Error writing send file", e); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (IOException e) { - } - } - } - - Bundle configOverrides = new Bundle(); - configOverrides.putBoolean(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED, true); - String httpParams = MmsConfig.getHttpParams(); - if (!TextUtils.isEmpty(httpParams)) { - configOverrides.putString(SmsManager.MMS_CONFIG_HTTP_PARAMS, httpParams); - } - configOverrides.putInt(SmsManager.MMS_CONFIG_MAX_MESSAGE_SIZE, MmsConfig.getMaxMessageSize()); - - if (contentUri != null) { - SmsManagerFactory.INSTANCE.createSmsManager(settings).sendMultimediaMessage(context, - contentUri, null, configOverrides, sentPI); - } else { - Log.e(TAG, "Error writing sending Mms"); - try { - sentPI.send(SmsManager.MMS_ERROR_IO_ERROR); - } catch (PendingIntent.CanceledException ex) { - Log.e(TAG, "Mms pending intent cancelled?", ex); - } - } - } catch (Exception e) { - Log.e(TAG, "error using system sending method", e); - } - } - - public static MessageInfo getBytes(Context context, boolean saveMessage, String[] recipients, MMSPart[] parts, - String subject) throws MmsException { - final SendReq sendRequest = new SendReq(); - - // create send request addresses - for (String recipient : recipients) { - final EncodedStringValue[] phoneNumbers = EncodedStringValue.extract(recipient); - - if (phoneNumbers != null && phoneNumbers.length > 0) { - sendRequest.addTo(phoneNumbers[0]); - } - } - - if (subject != null) { - sendRequest.setSubject(new EncodedStringValue(subject)); - } - - sendRequest.setDate(Calendar.getInstance().getTimeInMillis() / 1000L); - - try { - sendRequest.setFrom(new EncodedStringValue(Utils.getMyPhoneNumber(context))); - } catch (Exception e) { - Log.e(TAG, "error getting from address", e); - } - - final PduBody pduBody = new PduBody(); - - // assign parts to the pdu body which contains sending data - long size = 0; - if (parts != null) { - for (MMSPart part : parts) { - if (part != null) { - try { - PduPart partPdu = new PduPart(); - partPdu.setName(part.Name.getBytes()); - partPdu.setContentType(part.MimeType.getBytes()); - - if (part.MimeType.startsWith("text")) { - partPdu.setCharset(CharacterSets.UTF_8); - } - // Set Content-Location. - partPdu.setContentLocation(part.Name.getBytes()); - int index = part.Name.lastIndexOf("."); - String contentId = (index == -1) ? part.Name - : part.Name.substring(0, index); - partPdu.setContentId(contentId.getBytes()); - partPdu.setData(part.Data); - - pduBody.addPart(partPdu); - size += ((2 * part.Name.getBytes().length) + part.MimeType.getBytes().length + part.Data.length + contentId.getBytes().length); - } catch (Exception e) { - } - } - } - } - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - SmilXmlSerializer.serialize(SmilHelper.createSmilDocument(pduBody), out); - PduPart smilPart = new PduPart(); - smilPart.setContentId("smil".getBytes()); - smilPart.setContentLocation("smil.xml".getBytes()); - smilPart.setContentType(ContentType.APP_SMIL.getBytes()); - smilPart.setData(out.toByteArray()); - pduBody.addPart(0, smilPart); - - sendRequest.setBody(pduBody); - - Log.v(TAG, "setting message size to " + size + " bytes"); - sendRequest.setMessageSize(size); - - // add everything else that could be set - sendRequest.setPriority(PduHeaders.PRIORITY_NORMAL); - sendRequest.setDeliveryReport(PduHeaders.VALUE_NO); - sendRequest.setExpiry(1000 * 60 * 60 * 24 * 7); - sendRequest.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes()); - sendRequest.setReadReport(PduHeaders.VALUE_NO); - - // create byte array which will actually be sent - final PduComposer composer = new PduComposer(context, sendRequest); - final byte[] bytesToSend; - - try { - bytesToSend = composer.make(); - } catch (OutOfMemoryError e) { - throw new MmsException("Out of memory!"); - } - - MessageInfo info = new MessageInfo(); - info.bytes = bytesToSend; - - if (saveMessage) { - try { - PduPersister persister = PduPersister.getPduPersister(context); - info.location = persister.persist(sendRequest, Uri.parse("content://mms/outbox"), true, true, null); - } catch (Exception e) { - Log.v("sending_mms_library", "error saving mms message"); - Log.e(TAG, "exception thrown", e); - - // use the old way if something goes wrong with the persister - insert(context, recipients, parts, subject); - } - } - - try { - Cursor query = context.getContentResolver().query(info.location, new String[]{"thread_id"}, null, null, null); - if (query != null && query.moveToFirst()) { - info.token = query.getLong(query.getColumnIndex("thread_id")); - query.close(); - } else { - // just default sending token for what I had before - info.token = 4444L; - } - } catch (Exception e) { - Log.e(TAG, "exception thrown", e); - info.token = 4444L; - } - - return info; - } - - private static SendReq buildPdu(Context context, String[] recipients, String subject, List parts) { - final SendReq req = new SendReq(); - // From, per spec - final String lineNumber = Utils.getMyPhoneNumber(context); - if (!TextUtils.isEmpty(lineNumber)) { - req.setFrom(new EncodedStringValue(lineNumber)); - } - // To - for (String recipient : recipients) { - req.addTo(new EncodedStringValue(recipient)); - } - // Subject - if (!TextUtils.isEmpty(subject)) { - req.setSubject(new EncodedStringValue(subject)); - } - // Date - req.setDate(System.currentTimeMillis() / 1000); - // Body - PduBody body = new PduBody(); - // Add text part. Always add a smil part for compatibility, without it there - // may be issues on some carriers/client apps - int size = 0; - for (int i = 0; i < parts.size(); i++) { - MMSPart part = parts.get(i); - size += addTextPart(body, part, i); - } - - // add a SMIL document for compatibility - ByteArrayOutputStream out = new ByteArrayOutputStream(); - SmilXmlSerializer.serialize(SmilHelper.createSmilDocument(body), out); - PduPart smilPart = new PduPart(); - smilPart.setContentId("smil".getBytes()); - smilPart.setContentLocation("smil.xml".getBytes()); - smilPart.setContentType(ContentType.APP_SMIL.getBytes()); - smilPart.setData(out.toByteArray()); - body.addPart(0, smilPart); - - 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 req; - } - - private static int addTextPart(PduBody pb, MMSPart p, int id) { - String filename = p.Name; - final PduPart part = new PduPart(); - // Set Charset if it's a text media. - if (p.MimeType.startsWith("text")) { - part.setCharset(CharacterSets.UTF_8); - } - // Set Content-Type. - part.setContentType(p.MimeType.getBytes()); - // Set Content-Location. - part.setContentLocation(filename.getBytes()); - int index = filename.lastIndexOf("."); - String contentId = (index == -1) ? filename - : filename.substring(0, index); - part.setContentId(contentId.getBytes()); - part.setData(p.Data); - pb.addPart(part); - - return part.getData().length; - } - - public static class MessageInfo { - public long token; - public Uri location; - public byte[] bytes; - } - - private static Uri insert(Context context, String[] to, MMSPart[] parts, String subject) { - try { - Uri destUri = Uri.parse("content://mms"); - - Set recipients = new HashSet<>(); - recipients.addAll(Arrays.asList(to)); - long thread_id = Utils.getOrCreateThreadId(context, recipients); - - // Create a new message entry - long now = System.currentTimeMillis(); - ContentValues mmsValues = new ContentValues(); - mmsValues.put("thread_id", thread_id); - mmsValues.put("date", now / 1000L); - mmsValues.put("msg_box", 4); - //mmsValues.put("m_id", System.currentTimeMillis()); - mmsValues.put("read", true); - mmsValues.put("sub", subject != null ? subject : ""); - mmsValues.put("sub_cs", 106); - mmsValues.put("ct_t", "application/vnd.wap.multipart.related"); - - long imageBytes = 0; - - for (MMSPart part : parts) { - imageBytes += part.Data.length; - } - - mmsValues.put("exp", imageBytes); - - mmsValues.put("m_cls", "personal"); - mmsValues.put("m_type", PduHeaders.MESSAGE_TYPE_SEND_REQ); // 132 (RETRIEVE CONF) 130 (NOTIF IND) 128 (SEND REQ) - mmsValues.put("v", 19); - mmsValues.put("pri", 129); - mmsValues.put("tr_id", "T" + Long.toHexString(now)); - mmsValues.put("resp_st", 128); - - // Insert message - Uri res = context.getContentResolver().insert(destUri, mmsValues); - String messageId = res.getLastPathSegment().trim(); - - // Create part - for (MMSPart part : parts) { - if (part.MimeType.startsWith("image")) { - createPartImage(context, messageId, part.Data, part.MimeType); - } else if (part.MimeType.startsWith("text")) { - createPartText(context, messageId, new String(part.Data, "UTF-8")); - } - } - - // Create addresses - for (String addr : to) { - createAddr(context, messageId, addr); - } - - //res = Uri.parse(destUri + "/" + messageId); - - return res; - } catch (Exception e) { - Log.v("sending_mms_library", "still an error saving... :("); - Log.e(TAG, "exception thrown", e); - } - - return null; - } - - // create the image part to be stored in database - private static Uri createPartImage(Context context, String id, byte[] imageBytes, String mimeType) throws Exception { - ContentValues mmsPartValue = new ContentValues(); - mmsPartValue.put("mid", id); - mmsPartValue.put("ct", mimeType); - mmsPartValue.put("cid", "<" + System.currentTimeMillis() + ">"); - Uri partUri = Uri.parse("content://mms/" + id + "/part"); - Uri res = context.getContentResolver().insert(partUri, mmsPartValue); - - // Add data to part - OutputStream os = context.getContentResolver().openOutputStream(res); - ByteArrayInputStream is = new ByteArrayInputStream(imageBytes); - byte[] buffer = new byte[256]; - - for (int len; (len = is.read(buffer)) != -1; ) { - if (os != null) { - os.write(buffer, 0, len); - } - } - - if (os != null) { - os.close(); - } - is.close(); - - return res; - } - - // create the text part to be stored in database - private static Uri createPartText(Context context, String id, String text) throws Exception { - ContentValues mmsPartValue = new ContentValues(); - mmsPartValue.put("mid", id); - mmsPartValue.put("ct", "text/plain"); - mmsPartValue.put("cid", "<" + System.currentTimeMillis() + ">"); - mmsPartValue.put("text", text); - Uri partUri = Uri.parse("content://mms/" + id + "/part"); - return context.getContentResolver().insert(partUri, mmsPartValue); - } - - // add address to the request - private static Uri createAddr(Context context, String id, String addr) throws Exception { - ContentValues addrValues = new ContentValues(); - addrValues.put("address", addr); - addrValues.put("charset", "106"); - addrValues.put("type", PduHeaders.TO); - Uri addrUri = Uri.parse("content://mms/" + id + "/addr"); - return context.getContentResolver().insert(addrUri, addrValues); - } - - /** - * A method for checking whether or not a certain message will be sent as mms depending on its contents and the settings - * - * @param message is the message that you are checking against - * @return true if the message will be mms, otherwise false - */ - public boolean checkMMS(Message message) { - return message.getImages().length != 0 || - (message.getParts().size() != 0) || - (settings.getSendLongAsMms() && Utils.getNumPages(settings, message.getText()) > settings.getSendLongAsMmsAfter()) || - (message.getAddresses().length > 1) || - message.getSubject() != null; - } - -} diff --git a/android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.kt b/android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.kt new file mode 100755 index 00000000..e5589d82 --- /dev/null +++ b/android-smsmms/src/main/java/com/klinker/android/send_message/Transaction.kt @@ -0,0 +1,206 @@ +/* + * Copyright 2013 Jacob Klinker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.klinker.android.send_message + +import android.app.PendingIntent +import android.content.ContentResolver +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.telephony.SmsManager +import androidx.core.os.bundleOf +import com.android.mms.MmsConfig +import com.android.mms.dom.smil.parser.SmilXmlSerializer +import com.android.mms.util.DownloadManager +import com.android.mms.util.RateController +import com.google.android.mms.ContentType +import com.google.android.mms.InvalidHeaderValueException +import com.google.android.mms.MMSPart +import com.google.android.mms.pdu_alt.CharacterSets +import com.google.android.mms.pdu_alt.EncodedStringValue +import com.google.android.mms.pdu_alt.PduBody +import com.google.android.mms.pdu_alt.PduComposer +import com.google.android.mms.pdu_alt.PduHeaders +import com.google.android.mms.pdu_alt.PduPart +import com.google.android.mms.pdu_alt.PduPersister +import com.google.android.mms.pdu_alt.SendReq +import com.google.android.mms.smil.SmilHelper +import timber.log.Timber +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.util.* + +/** + * Class to process transaction requests for sending + * + * @author Jake Klinker + */ +class Transaction @JvmOverloads constructor(private val context: Context, settings: Settings = Settings()) { + + companion object { + var settings: Settings = Settings() + + const val MMS_SENT = "com.moez.QKSMS.MMS_SENT" + const val EXTRA_CONTENT_URI = "content_uri" + const val EXTRA_FILE_PATH = "file_path" + + const val NOTIFY_SMS_FAILURE = "com.moez.QKSMS.NOTIFY_SMS_FAILURE" + const val MMS_UPDATED = "com.moez.QKSMS.MMS_UPDATED" + const val MMS_ERROR = "com.klinker.android.send_message.MMS_ERROR" + const val REFRESH = "com.klinker.android.send_message.REFRESH" + const val MMS_PROGRESS = "com.klinker.android.send_message.MMS_PROGRESS" + const val NOTIFY_OF_DELIVERY = "com.klinker.android.send_message.NOTIFY_DELIVERY" + const val NOTIFY_OF_MMS = "com.klinker.android.messaging.NEW_MMS_DOWNLOADED" + + + const val DEFAULT_EXPIRY_TIME = (7 * 24 * 60 * 60).toLong() + const val DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL + } + + init { + Transaction.settings = settings + } + + /** + * Called to send a new message depending on settings and provided Message object + * If you want to send message as mms, call this from the UI thread + * + * @param threadId is the thread id of who to send the message to (can also be set to Transaction.NO_THREAD_ID) + */ + fun sendNewMessage(subId: Int, threadId: Long, addresses: List, parts: List, subject: String?) { + + RateController.init(context) + DownloadManager.init(context) + + Timber.v("sending mms") + try { + val fileName = "send." + Math.abs(Random().nextLong()).toString() + ".dat" + val sendFile = File(context.cacheDir, fileName) + + val sendReq = buildPdu(context, addresses, subject, parts) + val persister = PduPersister.getPduPersister(context) + val messageUri = persister.persist(sendReq, Uri.parse("content://mms/outbox"), true, true, null) + + val sentIntent = Intent(MMS_SENT) + BroadcastUtils.addClassName(context, sentIntent, MMS_SENT) + + sentIntent.putExtra(EXTRA_CONTENT_URI, messageUri.toString()) + sentIntent.putExtra(EXTRA_FILE_PATH, sendFile.path) + val sentPI = PendingIntent.getBroadcast(context, 0, sentIntent, PendingIntent.FLAG_CANCEL_CURRENT) + + val updatedIntent = Intent(MMS_UPDATED).putExtra("uri", messageUri.toString()) + BroadcastUtils.addClassName(context, updatedIntent, MMS_UPDATED) + context.sendBroadcast(updatedIntent) + + val contentUri: Uri? = try { + FileOutputStream(sendFile).use { writer -> + writer.write(PduComposer(context, sendReq).make()) + Uri.Builder() + .authority(context.packageName + ".MmsFileProvider") + .path(fileName) + .scheme(ContentResolver.SCHEME_CONTENT) + .build() + } + } catch (e: IOException) { + Timber.e(e, "Error writing send file") + null + } + + val configOverrides = bundleOf( + Pair(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED, true), + Pair(SmsManager.MMS_CONFIG_MAX_MESSAGE_SIZE, MmsConfig.getMaxMessageSize())) + + MmsConfig.getHttpParams() + .takeIf { it.isNotEmpty() } + .let { configOverrides.putString(SmsManager.MMS_CONFIG_HTTP_PARAMS, it) } + + if (contentUri != null) { + SmsManagerFactory.createSmsManager(subId).sendMultimediaMessage(context, contentUri, null, configOverrides, sentPI) + } else { + Timber.e("Error writing sending Mms") + try { + sentPI.send(SmsManager.MMS_ERROR_IO_ERROR) + } catch (e: PendingIntent.CanceledException) { + Timber.e(e) + } + + } + } catch (e: Exception) { + Timber.e(e, "error using system sending method") + } + + } + + private fun buildPdu(context: Context, recipients: List, subject: String?, parts: List): SendReq { + val req = SendReq() + + Utils.getMyPhoneNumber(context)?.takeIf(String::isNotEmpty)?.let(::EncodedStringValue)?.let(req::setFrom) // From + recipients.map(::EncodedStringValue).forEach(req::addTo) // To + subject?.takeIf(String::isNotEmpty)?.let(::EncodedStringValue)?.let(req::setSubject) // Subject + + req.date = System.currentTimeMillis() / 1000 + req.body = PduBody() + + // SMIL document for compatibility + req.body.addPart(PduPart().apply { + contentId = "smil".toByteArray() + contentLocation = "smil.xml".toByteArray() + contentType = ContentType.APP_SMIL.toByteArray() + data = ByteArrayOutputStream() + .apply { SmilXmlSerializer.serialize(SmilHelper.createSmilDocument(req.body), this) } + .toByteArray() + }) + + // Parts + parts.map(this::partToPduPart).forEach { req.body.addPart(it) } + + req.messageSize = parts.mapNotNull { it.data?.size }.sum().toLong() + req.messageClass = PduHeaders.MESSAGE_CLASS_PERSONAL_STR.toByteArray() + req.expiry = DEFAULT_EXPIRY_TIME + + try { + req.priority = DEFAULT_PRIORITY + req.deliveryReport = PduHeaders.VALUE_NO + req.readReport = PduHeaders.VALUE_NO + } catch (e: InvalidHeaderValueException) { + Timber.w(e) + } + + return req + } + + private fun partToPduPart(part: MMSPart): PduPart = PduPart().apply { + val filename = part.name + + // Set Charset if it's a text media. + if (part.mimeType.startsWith("text")) { + charset = CharacterSets.UTF_8 + } + + // Set Content-Type. + contentType = part.mimeType.toByteArray() + + // Set Content-Location. + contentLocation = filename.toByteArray() + val index = filename.lastIndexOf(".") + contentId = (if (index == -1) filename else filename.substring(0, index)).toByteArray() + data = part.data + } + +} \ No newline at end of file diff --git a/android-smsmms/src/main/java/com/klinker/android/send_message/Utils.java b/android-smsmms/src/main/java/com/klinker/android/send_message/Utils.java index 2edfec58..c04508b0 100755 --- a/android-smsmms/src/main/java/com/klinker/android/send_message/Utils.java +++ b/android-smsmms/src/main/java/com/klinker/android/send_message/Utils.java @@ -357,13 +357,7 @@ public class Utils { sendSettings.setAgent(sharedPrefs.getString("mms_agent", "")); sendSettings.setUserProfileUrl(sharedPrefs.getString("mms_user_agent_profile_url", "")); sendSettings.setUaProfTagName(sharedPrefs.getString("mms_user_agent_tag_name", "")); - sendSettings.setDeliveryReports(sharedPrefs.getBoolean("delivery_reports", false)); - sendSettings.setSplit(sharedPrefs.getBoolean("split_sms", false)); - sendSettings.setSplitCounter(sharedPrefs.getBoolean("split_counter", false)); sendSettings.setStripUnicode(sharedPrefs.getBoolean("strip_unicode", false)); - sendSettings.setSignature(sharedPrefs.getString("signature", "")); - sendSettings.setSendLongAsMms(true); - sendSettings.setSendLongAsMmsAfter(3); return sendSettings; } diff --git a/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt index d00c18f1..8a0fb107 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/MessageRepositoryImpl.kt @@ -30,16 +30,14 @@ import android.provider.Telephony import android.telephony.SmsManager import androidx.core.content.contentValuesOf import com.google.android.mms.ContentType +import com.google.android.mms.MMSPart import com.klinker.android.send_message.BroadcastUtils -import com.klinker.android.send_message.Settings import com.klinker.android.send_message.StripAccents import com.klinker.android.send_message.Transaction import com.moez.QKSMS.compat.TelephonyCompat import com.moez.QKSMS.extensions.anyOf import com.moez.QKSMS.extensions.insertOrUpdate -import com.moez.QKSMS.extensions.map import com.moez.QKSMS.manager.KeyManager -import com.moez.QKSMS.mapper.CursorToConversation import com.moez.QKSMS.model.Attachment import com.moez.QKSMS.model.Conversation import com.moez.QKSMS.model.Message @@ -225,22 +223,25 @@ class MessageRepositoryImpl @Inject constructor( sendSms(message) } } else { // MMS - val settings = Settings().apply { subscriptionId = subId } - val message = com.klinker.android.send_message.Message(body, addresses.toTypedArray()) + val parts = arrayListOf() + + if (body.isNotBlank()) { + parts += MMSPart("text", ContentType.TEXT_PLAIN, body.toByteArray()) + } // Add the GIFs as attachments. The app currently can't compress them, which may result // in a lot of these messages failing to send // TODO Add support for GIF compression - attachments + parts += attachments .filter { attachment -> attachment.isGif(context) } .map { attachment -> attachment.getUri() } .map { uri -> context.contentResolver.openInputStream(uri) } .map { inputStream -> inputStream.readBytes() } - .forEach { bitmap -> message.addMedia(bitmap, ContentType.IMAGE_GIF) } + .map { bitmap -> MMSPart("image", ContentType.IMAGE_GIF, bitmap) } // Compress the images and add them as attachments var totalImageBytes = 0 - attachments + parts += attachments .filter { attachment -> !attachment.isGif(context) } .mapNotNull { attachment -> attachment.getUri() } .mapNotNull { uri -> tryOrNull { imageRepository.loadImage(uri) } } @@ -249,10 +250,11 @@ class MessageRepositoryImpl @Inject constructor( val byteRatio = bitmap.allocationByteCount / totalImageBytes.toFloat() shrink(bitmap, (prefs.mmsSize.get() * 1024 * byteRatio).toInt()) } - .forEach { bitmap -> message.addMedia(bitmap, ContentType.IMAGE_JPEG) } + .map { bitmap -> MMSPart("image", ContentType.IMAGE_JPEG, bitmap) } - val transaction = Transaction(context, settings) - transaction.sendNewMessage(message, threadId) + + val transaction = Transaction(context) + transaction.sendNewMessage(subId, threadId, addresses, parts, null) } }