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

Implemented notification support to notify the user about the newly received SMS/MMS when KDE Connect will be set as the default SMS app.

This commit is contained in:
Aniket Kumar 2020-08-03 18:00:19 +05:30
parent 2758fba3d0
commit 0528de7fde
11 changed files with 604 additions and 5 deletions

View File

@ -95,6 +95,12 @@
android:enabled="true"
android:taskAffinity="com.klinker.android.messaging.MMS_SENT" />
<receiver
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.NotificationReplyReceiver"
android:enabled="true"
android:exported="true">
</receiver>
<service
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.HeadlessSmsSendService"
android:exported="true"

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#F67400"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM9,11L7,11L7,9h2v2zM13,11h-2L11,9h2v2zM17,11h-2L15,9h2v2z"/>
</vector>

View File

@ -308,6 +308,7 @@
<string name="notification_channel_media_control">Media control</string>
<string name="notification_channel_filetransfer">File transfer</string>
<string name="notification_channel_high_priority">High priority</string>
<string name="notification_channel_sms_mms">New Message</string>
<string name="mpris_stop">Stop the current player</string>
<string name="copy_url_to_clipboard">Copy URL to clipboard</string>
@ -374,6 +375,9 @@
<string name="bigscreen_optional_permission_explanation">To share microphone input from your phone you need to give access to the phone\'s audio input</string>
<string name="bigscreen_speech_extra_prompt">Speech</string>
<string name="message_reply_label">REPLY</string>
<string name="mark_as_read_label">MARK AS READ</string>
<string name="user_display_name">You</string>
<string name="set_default_sms_app_title">Send MMS</string>
<string name="set_group_message_as_mms_title">Send group MMS</string>
<string name="set_group_message_as_mms" translatable="false">set_group_message_as_mms</string>

View File

@ -19,6 +19,7 @@ public class NotificationHelper {
public final static String DEFAULT = "default";
public final static String MEDIA_CONTROL = "media_control";
public final static String FILETRANSFER = "filetransfer";
public final static String SMS_MMS = "sms_mms";
public final static String RECEIVENOTIFICATION = "receive";
public final static String HIGHPRIORITY = "highpriority";
}
@ -83,6 +84,12 @@ public class NotificationHelper {
NotificationManager.IMPORTANCE_DEFAULT)
);
manager.createNotificationChannel(new NotificationChannel(
Channels.SMS_MMS,
context.getString(R.string.notification_channel_sms_mms),
NotificationManager.IMPORTANCE_DEFAULT)
);
NotificationChannel highPriority = new NotificationChannel(Channels.HIGHPRIORITY, context.getString(R.string.notification_channel_high_priority), NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(highPriority);
}

View File

@ -58,6 +58,7 @@ import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.Plugins.SMSPlugin.NotificationReplyReceiver;
import org.kde.kdeconnect.UserInterface.MainActivity;
import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect.UserInterface.StartActivityAlertDialogFragment;
@ -203,7 +204,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
String packageName = statusBarNotification.getPackageName();
String appName = AppsHelper.appNameLookup(context, packageName);
if ("com.facebook.orca".equals(packageName) &&
(statusBarNotification.getId() == 10012) &&
"Messenger".equals(appName) &&
@ -219,8 +219,18 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
}
if ("org.kde.kdeconnect_tp".equals(packageName)) {
// Don't send our own notifications
return;
// Don't send our own notifications except notifications posted by SMSPlugin
String groupKey = "";
// SMS Notifications on devices running API's lower than Lollipop are not supported
// as groupKey's are not supported on API's older than Lollipop
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
groupKey = statusBarNotification.getGroupKey();
}
if (!groupKey.contains(NotificationReplyReceiver.SMS_NOTIFICATION_GROUP_KEY)) {
return;
}
}
NetworkPacket np = new NetworkPacket(PACKET_TYPE_NOTIFICATION);

View File

@ -24,17 +24,31 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.app.NotificationManager;
import android.app.PendingIntent;
import androidx.core.app.NotificationCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.TelephonyHelper;
import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Receiver for notifying user when a new MMS has been received by the device. By default it will
* persist the message to the internal database and notification service to notify the users will be
* implemented later.
* persist the message to the internal database and it will also show a notification in the status bar.
*/
public class MmsReceivedReceiver extends com.klinker.android.send_message.MmsReceivedReceiver {
@ -47,11 +61,23 @@ public class MmsReceivedReceiver extends com.klinker.android.send_message.MmsRec
// Notify messageUpdateReceiver about the arrival of the new MMS message
Intent refreshIntent = new Intent(Transaction.REFRESH);
context.sendBroadcast(refreshIntent);
// Fetch the latest message from the database
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
// Notify the user about the received mms message
createMmsNotification(context, message);
}
@Override
public void onError(Context context, String error) {
Log.v("MmsReceived", "error: " + error);
// Fetch the latest message from the database
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
// Notify the user about the received mms message
createMmsNotification(context, message);
}
public void getPreferredApn(Context context, Intent intent) {
@ -80,4 +106,72 @@ public class MmsReceivedReceiver extends com.klinker.android.send_message.MmsRec
}
return null;
}
private void createMmsNotification(Context context, SMSHelper.Message mmsMessage) {
ArrayList<String> addressList = new ArrayList<>();
for (SMSHelper.Address address : mmsMessage.addresses) {
addressList.add(address.toString());
}
Person sender = NotificationReplyReceiver.getMessageSender(context, addressList.get(0));
int notificationId;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationId = (int) Utils.getOrCreateThreadId(context, new HashSet<>(addressList));
} else {
notificationId = (int) System.currentTimeMillis();
}
// Todo: When SMSHelper.Message class will be modified to contain thumbnail of the image or video attachment, add them here to display.
// Create pending intent for reply action through notification
PendingIntent replyPendingIntent = NotificationReplyReceiver.createReplyPendingIntent(
context,
addressList,
notificationId,
true
);
RemoteInput remoteReplyInput = new RemoteInput.Builder(NotificationReplyReceiver.KEY_TEXT_REPLY)
.setLabel(context.getString(R.string.message_reply_label))
.build();
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.message_reply_label), replyPendingIntent)
.addRemoteInput(remoteReplyInput)
.setAllowGeneratedReplies(true)
.build();
// Create pending intent for marking the message as read in database through mark as read action
PendingIntent markAsReadPendingIntent = NotificationReplyReceiver.createMarkAsReadPendingIntent(
context,
addressList,
notificationId
);
NotificationCompat.Action markAsReadAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.mark_as_read_label), markAsReadPendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.MessagingStyle messagingStyle = NotificationReplyReceiver.createMessagingStyle(
context,
notificationId,
mmsMessage.body,
TextUtils.join(",", addressList),
mmsMessage.date,
sender,
notificationManager
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationReplyReceiver.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_sms_24)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(messagingStyle)
.setAutoCancel(true)
.addAction(replyAction)
.addAction(markAsReadAction)
.setGroup(NotificationReplyReceiver.SMS_NOTIFICATION_GROUP_KEY);
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
}

View File

@ -26,6 +26,10 @@ import android.content.Intent;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
public class MmsSentReceiver extends com.klinker.android.send_message.MmsSentReceiver {
@Override
@ -41,5 +45,23 @@ public class MmsSentReceiver extends com.klinker.android.send_message.MmsSentRec
@Override
public void onMessageStatusUpdated(Context context, Intent intent, int resultCode) {
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
ArrayList<String> addressList = new ArrayList<>();
for (SMSHelper.Address address : message.addresses) {
addressList.add(address.toString());
}
Intent repliedNotification = new Intent(context, NotificationReplyReceiver.class);
repliedNotification.setAction(NotificationReplyReceiver.SMS_MMS_REPLY_ACTION);
repliedNotification.putExtra(NotificationReplyReceiver.TEXT_BODY, message.body);
repliedNotification.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, Integer.parseInt(message.threadID.toString()));
repliedNotification.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
// SEND_ACTION value is required to differentiate between the intents sent from reply action or
// SentReceivers inorder to avoid posting duplicate notifications
repliedNotification.putExtra(NotificationReplyReceiver.SEND_ACTION, false);
context.sendBroadcast(repliedNotification);
}
}

View File

@ -0,0 +1,303 @@
/*
* Copyright 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.IconCompat;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.ContactsHelper;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Helpers.SMSHelper;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
public class NotificationReplyReceiver extends BroadcastReceiver {
public static final String SMS_MMS_REPLY_ACTION = "org.kde.kdeconnect.Plugins.SMSPlugin.sms_mms_reply_action";
public static final String SMS_MMS_MARK_ACTION = "org.kde.kdeconnect.Plugins.SMSPlugin.sms_mms_mark_action";
public static final String ADDRESS_LIST = "address_list";
public static final String CHANNEL_ID = NotificationHelper.Channels.SMS_MMS;
public static final String KEY_TEXT_REPLY = "key_text_reply";
public static final String NOTIFICATION_ID = "notification_id";
public static final String SEND_ACTION = "send_action";
public static final String TEXT_BODY = "text_body";
public static final String SMS_NOTIFICATION_GROUP_KEY = "Plugins.SMSPlugin.sms_notification_group_key";
@Override
public void onReceive(Context context, Intent intent) {
if (!Utils.isDefaultSmsApp(context) || intent == null) {
return;
}
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
final int notificationId = intent.getIntExtra(NotificationReplyReceiver.NOTIFICATION_ID, 0);
final ArrayList<String> addressList = intent.getStringArrayListExtra(ADDRESS_LIST);
final boolean sentUsingReplyButton = intent.getBooleanExtra(SEND_ACTION, false);
if (intent.getAction().equals(SMS_MMS_REPLY_ACTION)) {
String inputString = null;
if (sentUsingReplyButton) {
inputString = remoteInput.getCharSequence(NotificationReplyReceiver.KEY_TEXT_REPLY).toString();
ArrayList<SMSHelper.Address> addresses = new ArrayList<>();
for (String address : addressList) {
addresses.add(new SMSHelper.Address(address));
}
SmsMmsUtils.sendMessage(context, inputString, addresses, -1);
} else {
inputString = intent.getStringExtra(TEXT_BODY);
repliedMessageNotification(context, notificationId, inputString, addressList);
}
}
// Mark the conversation as read
if (intent.getAction().equals(SMS_MMS_MARK_ACTION)) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
SmsMmsUtils.markConversationRead(context, new HashSet<>(addressList));
}
}
/**
* Updates the active notification with the newly replied message
*/
private void repliedMessageNotification(Context context, int notificationId, String inputString, ArrayList<String> addressList) {
Person sender = new Person.Builder()
.setName(context.getString(R.string.user_display_name))
.build();
// Create pending intent for reply action through notification
PendingIntent replyPendingIntent = createReplyPendingIntent(context, addressList, notificationId, true);
RemoteInput remoteReplyInput = new RemoteInput.Builder(NotificationReplyReceiver.KEY_TEXT_REPLY)
.setLabel(context.getString(R.string.message_reply_label))
.build();
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.message_reply_label), replyPendingIntent)
.addRemoteInput(remoteReplyInput)
.setAllowGeneratedReplies(true)
.build();
// Create pending intent for marking the message as read in database through mark as read action
PendingIntent markAsReadPendingIntent = createMarkAsReadPendingIntent(context, addressList, notificationId);
NotificationCompat.Action markAsReadAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.mark_as_read_label), markAsReadPendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.MessagingStyle.Message message = new NotificationCompat.MessagingStyle.Message(
inputString,
System.currentTimeMillis(),
sender
);
NotificationCompat.MessagingStyle messagingStyle = restoreActiveMessagingStyle(notificationId, notificationManager);
if (messagingStyle == null) {
// Return when there is no active notification in the statusBar with the above notificationId
return;
}
messagingStyle.addMessage(message);
NotificationCompat.Builder repliedNotification = new NotificationCompat.Builder(context, NotificationReplyReceiver.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_sms_24)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setOnlyAlertOnce(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(messagingStyle)
.setAutoCancel(true)
.addAction(replyAction)
.addAction(markAsReadAction)
.setGroup(NotificationReplyReceiver.SMS_NOTIFICATION_GROUP_KEY);
NotificationHelper.notifyCompat(notificationManager, notificationId, repliedNotification.build());
}
/**
* This method creates a new messaging style for newer conversations and if there is already an active notification
* of the same id, it just adds to the previous and returns the modified messagingStyle object.
*/
public static NotificationCompat.MessagingStyle createMessagingStyle(
Context context,
int notificationId,
String textMessage,
String phoneNumbers,
long date,
Person sender,
NotificationManager notificationManager
) {
NotificationCompat.MessagingStyle messageStyle = NotificationReplyReceiver.restoreActiveMessagingStyle(
notificationId,
notificationManager
);
NotificationCompat.MessagingStyle.Message message = new NotificationCompat.MessagingStyle.Message(
textMessage,
date,
sender
);
if (messageStyle == null) {
// When no active notification is found for matching conversation create a new one
String senderName = phoneNumbers;
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumbers);
if (contactInfo.containsKey("name")) {
senderName = contactInfo.get("name");
}
messageStyle = new NotificationCompat.MessagingStyle(sender)
.setConversationTitle(senderName);
}
messageStyle.addMessage(message);
return messageStyle;
}
/**
* This method is responsible for searching the notification for same conversation ID and if there is an active notification
* of save ID found in the status menu it extracts and returns the messagingStyle object of that notification
*/
public static NotificationCompat.MessagingStyle restoreActiveMessagingStyle(
int notificationId,
NotificationManager notificationManager
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StatusBarNotification notifications[] = notificationManager.getActiveNotifications();
for (StatusBarNotification notification : notifications) {
if (notification.getId() == notificationId) {
return NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(notification.getNotification());
}
}
}
return null;
}
/**
* returns the sender of the message as a Person object
*/
public static Person getMessageSender(Context context, String address) {
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, address);
String senderName = address;
if (contactInfo.containsKey("name")) {
senderName = contactInfo.get("name");
}
Bitmap contactPhoto = null;
if (contactInfo.containsKey("photoID")) {
String photoUri = contactInfo.get("photoID");
if (photoUri != null) {
try {
String base64photo = ContactsHelper.photoId64Encoded(context, photoUri);
if (!TextUtils.isEmpty(base64photo)) {
byte[] decodedString = Base64.decode(base64photo, Base64.DEFAULT);
contactPhoto = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
}
} catch (Exception e) {
Log.e("SMS Notification", "Failed to get contact photo");
}
}
}
Person.Builder personBuilder = new Person.Builder()
.setName(senderName);
if (contactPhoto != null) {
personBuilder.setIcon(IconCompat.createWithBitmap(contactPhoto));
}
return personBuilder.build();
}
/**
* Create pending intent for reply action through notification
*/
public static PendingIntent createReplyPendingIntent(
Context context,
ArrayList<String> addressList,
int notificationId,
boolean isFromSendAction
) {
Intent replyIntent = new Intent(context, NotificationReplyReceiver.class);
replyIntent.setAction(NotificationReplyReceiver.SMS_MMS_REPLY_ACTION);
replyIntent.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, notificationId);
replyIntent.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
replyIntent.putExtra(NotificationReplyReceiver.SEND_ACTION, isFromSendAction);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
context,
notificationId,
replyIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
return replyPendingIntent;
}
/**
* Create pending intent for marking the message as read in database through mark as read action
*/
public static PendingIntent createMarkAsReadPendingIntent(
Context context,
ArrayList<String> addressList,
int notificationId
) {
Intent markAsReadIntent = new Intent(context, NotificationReplyReceiver.class);
markAsReadIntent.setAction(NotificationReplyReceiver.SMS_MMS_MARK_ACTION);
markAsReadIntent.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, notificationId);
markAsReadIntent.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
PendingIntent markAsReadPendingIntent = PendingIntent.getBroadcast(
context,
notificationId,
markAsReadIntent,
PendingIntent.FLAG_CANCEL_CURRENT
);
return markAsReadPendingIntent;
}
}

View File

@ -32,12 +32,21 @@ import com.klinker.android.send_message.Settings;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import android.content.ContentUris;
import android.content.ContentValues;
import android.provider.Telephony;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.RequiresApi;
import org.apache.commons.lang3.ArrayUtils;
import org.kde.kdeconnect.Helpers.SMSHelper;
import org.kde.kdeconnect.Helpers.TelephonyHelper;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class SmsMmsUtils {
@ -119,4 +128,38 @@ public class SmsMmsUtils {
com.klinker.android.logger.Log.e(SENDING_MESSAGE, "Exception", e);
}
}
/**
* Marks a conversation as read in the database.
*
* @param context the context to get the content provider with.
* @param recipients the phone numbers to find the conversation with.
*/
public static void markConversationRead(Context context, HashSet<String> recipients) {
new Thread() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void run() {
try {
long threadId = Utils.getOrCreateThreadId(context, recipients);
markAsRead(context, ContentUris.withAppendedId(Telephony.Threads.CONTENT_URI, threadId), threadId);
} catch (Exception e) {
// the conversation doesn't exist
e.printStackTrace();
}
}
}.start();
}
private static void markAsRead(Context context, Uri uri, long threadId) {
Log.v("SMSPlugin", "marking thread with threadId " + threadId + " as read at Uri" + uri);
if (uri != null && context != null) {
ContentValues values = new ContentValues(2);
values.put("read", 1);
values.put("seen", 1);
context.getContentResolver().update(uri, values, "(read=0 OR seen=0)", null);
}
}
}

View File

@ -20,6 +20,8 @@
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -29,10 +31,22 @@ import android.telephony.SmsMessage;
import android.provider.Telephony.Sms;
import android.net.Uri;
import android.content.ContentValues;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Arrays;
public class SmsReceiver extends BroadcastReceiver {
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
@ -75,8 +89,77 @@ public class SmsReceiver extends BroadcastReceiver {
Intent refreshIntent = new Intent(Transaction.REFRESH);
context.sendBroadcast(refreshIntent);
}
String body = message[i].getMessageBody();
String phoneNo = message[i].getOriginatingAddress();
long date = message[i].getTimestampMillis();
createSmsNotification(context, body, phoneNo, date);
}
}
}
}
private void createSmsNotification(Context context, String body, String phoneNo, long date) {
int notificationId;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationId = (int) Utils.getOrCreateThreadId(context, phoneNo);
} else {
notificationId = (int) System.currentTimeMillis();
}
Person sender = NotificationReplyReceiver.getMessageSender(context, phoneNo);
ArrayList<String> addressList = new ArrayList<>(Arrays.asList(phoneNo));
// Create pending intent for reply action through notification
PendingIntent replyPendingIntent = NotificationReplyReceiver.createReplyPendingIntent(
context,
addressList,
notificationId,
true
);
RemoteInput remoteReplyInput = new RemoteInput.Builder(NotificationReplyReceiver.KEY_TEXT_REPLY)
.setLabel(context.getString(R.string.message_reply_label))
.build();
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.message_reply_label), replyPendingIntent)
.addRemoteInput(remoteReplyInput)
.setAllowGeneratedReplies(true)
.build();
// Create pending intent for marking the message as read in database through mark as read action
PendingIntent markAsReadPendingIntent = NotificationReplyReceiver.createMarkAsReadPendingIntent(
context,
addressList,
notificationId
);
NotificationCompat.Action markAsReadAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.mark_as_read_label), markAsReadPendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.MessagingStyle messagingStyle = NotificationReplyReceiver.createMessagingStyle(
context,
notificationId,
body,
phoneNo,
date,
sender,
notificationManager
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationReplyReceiver.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_sms_24)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(messagingStyle)
.setAutoCancel(true)
.addAction(replyAction)
.addAction(markAsReadAction)
.setGroup(NotificationReplyReceiver.SMS_NOTIFICATION_GROUP_KEY);
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
}

View File

@ -27,6 +27,10 @@ import com.klinker.android.send_message.SentReceiver;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
public class SmsSentReceiver extends SentReceiver {
@Override
@ -42,5 +46,23 @@ public class SmsSentReceiver extends SentReceiver {
@Override
public void onMessageStatusUpdated(Context context, Intent intent, int receiverResultCode) {
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
ArrayList<String> addressList = new ArrayList<>();
for (SMSHelper.Address address : message.addresses) {
addressList.add(address.toString());
}
Intent repliedNotification = new Intent(context, NotificationReplyReceiver.class);
repliedNotification.setAction(NotificationReplyReceiver.SMS_MMS_REPLY_ACTION);
repliedNotification.putExtra(NotificationReplyReceiver.TEXT_BODY, message.body);
repliedNotification.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, Integer.parseInt(message.threadID.toString()));
repliedNotification.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
// SEND_ACTION value is required to differentiate between the intents sent from reply action or
// SentReceivers inorder to avoid posting duplicate notifications
repliedNotification.putExtra(NotificationReplyReceiver.SEND_ACTION, false);
context.sendBroadcast(repliedNotification);
}
}