2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-05 16:45:08 +00:00

Added MMS support to the SMSPlugin using Klinker library.

This commit is contained in:
Aniket Kumar
2020-07-05 13:32:44 +05:30
parent edbf3ccaab
commit b119de8e76
17 changed files with 1023 additions and 31 deletions

View File

@@ -39,7 +39,6 @@ import android.provider.Telephony;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
@@ -63,6 +62,11 @@ import java.util.concurrent.locks.ReentrantLock;
import androidx.core.content.ContextCompat;
import com.klinker.android.send_message.ApnUtils;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import com.klinker.android.logger.Log;
import static org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin.PACKET_TYPE_TELEPHONY;
@PluginFactory.LoadablePlugin
@@ -125,9 +129,10 @@ public class SMSPlugin extends Plugin {
* <p>
* The body should look like so:
* { "sendSms": true,
* "phoneNumber": "542904563213",
* "messageBody": "Hi mom!",
* "sub_id": "3859358340534"
* "phoneNumber": "542904563213" // For older desktop versions of SMS app this packet carries phoneNumber field
* "addresses": <List of Addresses> // For newer desktop versions of SMS app it contains addresses field instead of phoneNumber field
* "messageBody": "Hi mom!",
* "sub_id": "3859358340534"
* }
*/
private final static String PACKET_TYPE_SMS_REQUEST = "kdeconnect.sms.request";
@@ -210,33 +215,64 @@ public class SMSPlugin extends Plugin {
*/
@Override
public void onChange(boolean selfChange) {
// Lock so no one uses the mostRecentTimestamp between the moment we read it and the
// moment we update it. This is because reading the Messages DB can take long.
mostRecentTimestampLock.lock();
if (mostRecentTimestamp == 0) {
// Since the timestamp has not been initialized, we know that nobody else
// has requested a message. That being the case, there is most likely
// nobody listening for message updates, so just drop them
mostRecentTimestampLock.unlock();
// If the KDE Connect is set as default Sms app
// prevent from reading the latest message in the database before the sentReceivers mark it as sent
if (Utils.isDefaultSmsApp(context)) {
return;
}
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
if (message == null || message.date <= mostRecentTimestamp) {
// onChange can trigger many times for a single message. Don't make unnecessary noise
mostRecentTimestampLock.unlock();
return;
}
// Update the most recent counter
mostRecentTimestamp = message.date;
mostRecentTimestampLock.unlock();
// Send the alert about the update
device.sendPacket(constructBulkMessagePacket(Collections.singleton(message)));
sendLatestMessage();
}
}
/**
* This receiver will be invoked only when the app will be set as the default sms app
* Whenever the app will be set as the default, the database update alert will be sent
* using messageUpdateReceiver and not the contentObserver class
*/
private final BroadcastReceiver messagesUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Transaction.REFRESH.equals(action)) {
sendLatestMessage();
}
}
};
/**
* Helper method to read the latest message from the sms-mms database and sends it to the desktop
*/
private void sendLatestMessage() {
// Lock so no one uses the mostRecentTimestamp between the moment we read it and the
// moment we update it. This is because reading the Messages DB can take long.
mostRecentTimestampLock.lock();
if (mostRecentTimestamp == 0) {
// Since the timestamp has not been initialized, we know that nobody else
// has requested a message. That being the case, there is most likely
// nobody listening for message updates, so just drop them
mostRecentTimestampLock.unlock();
return;
}
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
if (message == null || message.date <= mostRecentTimestamp) {
// onChange can trigger many times for a single message. Don't make unnecessary noise
mostRecentTimestampLock.unlock();
return;
}
// Update the most recent counter
mostRecentTimestamp = message.date;
mostRecentTimestampLock.unlock();
// Send the alert about the update
device.sendPacket(constructBulkMessagePacket(Collections.singleton(message)));
Log.e("sent", "update");
}
/**
@@ -304,6 +340,10 @@ public class SMSPlugin extends Plugin {
filter.setPriority(500);
context.registerReceiver(receiver, filter);
IntentFilter refreshFilter = new IntentFilter(Transaction.REFRESH);
refreshFilter.setPriority(500);
context.registerReceiver(messagesUpdateReceiver, refreshFilter);
Looper helperLooper = SMSHelper.MessageLooper.getLooper();
ContentObserver messageObserver = new MessageContentObserver(new Handler(helperLooper));
SMSHelper.registerObserver(messageObserver, context);
@@ -312,6 +352,13 @@ public class SMSPlugin extends Plugin {
Log.w("SMSPlugin", "This is a very old version of Android. The SMS Plugin might not function as intended.");
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
ApnUtils.initDefaultApns(context, null);
}
// To see debug messages for Klinker library, uncomment the below line
//Log.setDebug(true);
return true;
}
@@ -334,8 +381,22 @@ public class SMSPlugin extends Plugin {
case PACKET_TYPE_SMS_REQUEST_CONVERSATION:
return this.handleRequestConversation(np);
case PACKET_TYPE_SMS_REQUEST:
// Fall through to old-style handling
// This space may be filled in differently once MMS support is implemented
if (np.getBoolean("sendSms")) {
String textMessage = np.getString("messageBody");
long subID = np.getLong("subID", -1);
List<SMSHelper.Address> addressList = SMSHelper.jsonArrayToAddressList(np.getJSONArray("addresses"));
if (addressList == null) {
// If the List of Address is null, then the SMS_REQUEST packet is
// most probably from the older version of the desktop app.
addressList = new ArrayList<>();
addressList.add(new SMSHelper.Address(np.getString("phoneNumber")));
}
SmsMmsUtils.sendMessage(context, textMessage, addressList, (int) subID);
}
break;
case TelephonyPlugin.PACKET_TYPE_TELEPHONY_REQUEST:
if (np.getBoolean("sendSms")) {
String phoneNo = np.getString("phoneNumber");
@@ -432,6 +493,17 @@ public class SMSPlugin extends Plugin {
conversation = SMSHelper.getMessagesInRange(this.context, threadID, rangeStartTimestamp, numberToGet);
}
// Sometimes when desktop app is kept open while android app is restarted for any reason
// mostRecentTimeStamp must be updated in that scenario too if a user request for a
// single conversation and not the entire conversation list
mostRecentTimestampLock.lock();
for (SMSHelper.Message message : conversation) {
if (message.date > mostRecentTimestamp) {
mostRecentTimestamp = message.date;
}
}
mostRecentTimestampLock.unlock();
NetworkPacket reply = constructBulkMessagePacket(conversation);
device.sendPacket(reply);
@@ -451,6 +523,10 @@ public class SMSPlugin extends Plugin {
return false;
}
@Override
public boolean hasSettings() {
return true;
}
@Override
public String[] getSupportedPacketTypes() {
@@ -478,6 +554,19 @@ public class SMSPlugin extends Plugin {
};
}
/**
* Permissions required for sending and receiving MMs messages
*/
public static String[] getMmsPermissions() {
return new String[]{
Manifest.permission.RECEIVE_SMS,
Manifest.permission.RECEIVE_MMS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CHANGE_NETWORK_STATE,
Manifest.permission.WAKE_LOCK,
};
}
/**
* With versions older than KITKAT, lots of the content providers used in SMSHelper become
* un-documented. Most manufacturers *did* do things the same way as was done in mainline