mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 18:07:55 +00:00
Added MMS support to the SMSPlugin using Klinker library.
This commit is contained in:
parent
edbf3ccaab
commit
b119de8e76
@ -22,6 +22,8 @@
|
|||||||
<!-- <uses-permission android:name="android.permission.BLUETOOTH" /> -->
|
<!-- <uses-permission android:name="android.permission.BLUETOOTH" /> -->
|
||||||
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> -->
|
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> -->
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||||
@ -31,9 +33,13 @@
|
|||||||
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
|
||||||
<uses-permission android:name="android.permission.SEND_SMS" />
|
<uses-permission android:name="android.permission.SEND_SMS" />
|
||||||
<uses-permission android:name="android.permission.READ_SMS" />
|
<uses-permission android:name="android.permission.READ_SMS" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_SMS" />
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||||
|
<uses-permission android:name="android.permission.RECEIVE_MMS" />
|
||||||
|
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
@ -49,6 +55,67 @@
|
|||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:theme="@style/KdeConnectTheme"
|
android:theme="@style/KdeConnectTheme"
|
||||||
android:name="org.kde.kdeconnect.MyApplication">
|
android:name="org.kde.kdeconnect.MyApplication">
|
||||||
|
<receiver
|
||||||
|
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.SmsReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BROADCAST_SMS">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.provider.Telephony.SMS_DELIVER" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter android:priority="999">
|
||||||
|
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.SmsSentReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
android:taskAffinity="${applicationId}.SMS_SENT" />
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name="com.android.mms.transaction.PushReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BROADCAST_WAP_PUSH">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
|
||||||
|
|
||||||
|
<data android:mimeType="application/vnd.wap.mms-message" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.DelegatingMmsReceivedReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:taskAffinity="com.klinker.android.messaging.MMS_RECEIVED" />
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.MmsSentReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
android:enabled="true"
|
||||||
|
android:taskAffinity="com.klinker.android.messaging.MMS_SENT" />
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.HeadlessSmsSendService"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:scheme="sms" />
|
||||||
|
<data android:scheme="smsto" />
|
||||||
|
<data android:scheme="mms" />
|
||||||
|
<data android:scheme="mmsto" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name="com.android.mms.transaction.TransactionService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true" />
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="org.kde.kdeconnect.BackgroundService"
|
android:name="org.kde.kdeconnect.BackgroundService"
|
||||||
android:enabled="true" />
|
android:enabled="true" />
|
||||||
@ -69,10 +136,29 @@
|
|||||||
android:name="org.kde.kdeconnect.UserInterface.MainActivity"
|
android:name="org.kde.kdeconnect.UserInterface.MainActivity"
|
||||||
android:label="KDE Connect"
|
android:label="KDE Connect"
|
||||||
android:theme="@style/KdeConnectTheme.NoActionBar">
|
android:theme="@style/KdeConnectTheme.NoActionBar">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<action android:name="android.intent.action.SENDTO" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data android:scheme="sms" />
|
||||||
|
<data android:scheme="smsto" />
|
||||||
|
<data android:scheme="mms" />
|
||||||
|
<data android:scheme="mmsto" />
|
||||||
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="android.app.role.SMS"/>
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
|
<data android:mimeType="image/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
@ -314,4 +400,4 @@
|
|||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -165,6 +165,8 @@ dependencies {
|
|||||||
implementation 'org.atteo.classindex:classindex:3.6'
|
implementation 'org.atteo.classindex:classindex:3.6'
|
||||||
annotationProcessor 'org.atteo.classindex:classindex:3.6'
|
annotationProcessor 'org.atteo.classindex:classindex:3.6'
|
||||||
|
|
||||||
|
implementation 'com.klinkerapps:android-smsmms:5.2.6' //For SMS and MMS purposes
|
||||||
|
|
||||||
// Testing
|
// Testing
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
testImplementation 'org.powermock:powermock-core:2.0.0'
|
testImplementation 'org.powermock:powermock-core:2.0.0'
|
||||||
|
4
proguard-rules.pro
vendored
4
proguard-rules.pro
vendored
@ -44,3 +44,7 @@
|
|||||||
-dontwarn android.test.**
|
-dontwarn android.test.**
|
||||||
-dontwarn java.lang.management.**
|
-dontwarn java.lang.management.**
|
||||||
-dontwarn javax.**
|
-dontwarn javax.**
|
||||||
|
|
||||||
|
-dontwarn android.net.ConnectivityManager
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||||
|
-dontwarn android.net.LinkProperties
|
||||||
|
@ -265,6 +265,8 @@
|
|||||||
<string name="no_file_browser">There are no file browsers installed.</string>
|
<string name="no_file_browser">There are no file browsers installed.</string>
|
||||||
<string name="pref_plugin_telepathy">Send SMS</string>
|
<string name="pref_plugin_telepathy">Send SMS</string>
|
||||||
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
|
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
|
||||||
|
<string name="pref_plugin_telepathy_mms">Send MMS</string>
|
||||||
|
<string name="pref_plugin_telepathy_mms_desc">To be able to send MMS from KDE Connect you need to set it as the default SMS app.</string>
|
||||||
<string name="findmyphone_title">Find my phone</string>
|
<string name="findmyphone_title">Find my phone</string>
|
||||||
<string name="findmyphone_title_tablet">Find my tablet</string>
|
<string name="findmyphone_title_tablet">Find my tablet</string>
|
||||||
<string name="findmyphone_title_tv">Find my TV</string>
|
<string name="findmyphone_title_tv">Find my TV</string>
|
||||||
@ -372,6 +374,38 @@
|
|||||||
<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_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="bigscreen_speech_extra_prompt">Speech</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>
|
||||||
|
<string name="set_long_text_as_mms_title">Send long text as MMS</string>
|
||||||
|
<string name="set_long_text_as_mms" translatable="false">set_long_text_as_mms</string>
|
||||||
|
<string name="convert_to_mms_after_title">Convert to MMS</string>
|
||||||
|
<string name="convert_to_mms_after" translatable="false">convert_to_mms_after</string>
|
||||||
|
<string name="sms_pref_set_mmsc_dialog_desc">Set MMSC</string>
|
||||||
|
<string name="sms_pref_set_mmsc_title">MMSC</string>
|
||||||
|
<string name="sms_pref_set_mmsc" translatable="false">sms_pref_set_mmsc</string>
|
||||||
|
<string name="sms_pref_set_mms_proxy_dialog_desc">Set MMS proxy</string>
|
||||||
|
<string name="sms_pref_set_mms_proxy_title">MMS proxy</string>
|
||||||
|
<string name="sms_pref_set_mms_proxy" translatable="false">sms_pref_set_mms_proxy</string>
|
||||||
|
<string name="sms_pref_set_mms_port_dialog_desc">Set MMS port</string>
|
||||||
|
<string name="sms_pref_set_mms_port_title">MMS port</string>
|
||||||
|
<string name="sms_pref_set_mms_port" translatable="false">sms_pref_set_mms_port</string>
|
||||||
|
<string name="convert_to_mms_after_default" translatable="false">3</string>
|
||||||
|
<string-array name="convert_to_mms_after_entries">
|
||||||
|
<item>After one message</item>
|
||||||
|
<item>After two messages</item>
|
||||||
|
<item>After three messages</item>
|
||||||
|
<item>After four messages</item>
|
||||||
|
<item>After five messages</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="convert_to_mms_after_values" translatable="false">
|
||||||
|
<item>1</item>
|
||||||
|
<item>2</item>
|
||||||
|
<item>3</item>
|
||||||
|
<item>4</item>
|
||||||
|
<item>5</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string name="theme_dialog_title">Choose theme</string>
|
<string name="theme_dialog_title">Choose theme</string>
|
||||||
<string-array name="theme_list">
|
<string-array name="theme_list">
|
||||||
<item>Set by Battery Saver</item>
|
<item>Set by Battery Saver</item>
|
||||||
|
51
res/xml/smsplugin_preferences.xml
Normal file
51
res/xml/smsplugin_preferences.xml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:keep="@xml/smsplugin_preferences">
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:dialogMessage="@string/sms_pref_set_mmsc_dialog_desc"
|
||||||
|
android:key="@string/sms_pref_set_mmsc"
|
||||||
|
android:title="@string/sms_pref_set_mmsc_title"
|
||||||
|
android:defaultValue="Not set"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:dialogMessage="@string/sms_pref_set_mms_proxy_dialog_desc"
|
||||||
|
android:key="@string/sms_pref_set_mms_proxy"
|
||||||
|
android:title="@string/sms_pref_set_mms_proxy_title"
|
||||||
|
android:defaultValue="Not set"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
android:dialogMessage="@string/sms_pref_set_mms_port_dialog_desc"
|
||||||
|
android:key="@string/sms_pref_set_mms_port"
|
||||||
|
android:title="@string/sms_pref_set_mms_port_title"
|
||||||
|
android:defaultValue="Not set"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:id="@+id/group_message_preference"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:key="@string/set_group_message_as_mms"
|
||||||
|
android:title="@string/set_group_message_as_mms_title" />
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:id="@+id/long_text_message_preference"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/set_long_text_as_mms"
|
||||||
|
android:title="@string/set_long_text_as_mms_title" />
|
||||||
|
|
||||||
|
<ListPreference
|
||||||
|
android:id="@+id/convert_to_mms_after_preference"
|
||||||
|
android:defaultValue="@string/convert_to_mms_after_default"
|
||||||
|
android:entries="@array/convert_to_mms_after_entries"
|
||||||
|
android:entryValues="@array/convert_to_mms_after_values"
|
||||||
|
android:key="@string/convert_to_mms_after"
|
||||||
|
android:summary="%s"
|
||||||
|
android:title="@string/convert_to_mms_after_title" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@ -61,6 +61,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
public class SMSHelper {
|
public class SMSHelper {
|
||||||
|
|
||||||
@ -295,6 +297,13 @@ public class SMSHelper {
|
|||||||
// of any MMSes
|
// of any MMSes
|
||||||
List<String> userPhoneNumbers = TelephonyHelper.getAllPhoneNumbers(context);
|
List<String> userPhoneNumbers = TelephonyHelper.getAllPhoneNumbers(context);
|
||||||
|
|
||||||
|
if (Utils.isDefaultSmsApp(context)) {
|
||||||
|
// Due to some reason, which I'm not able to find out yet, when message sending fails, no sent receiver
|
||||||
|
// gets invoked to mark the message as failed to send. This is the reason we have to delete the failed
|
||||||
|
// messages pending in the outbox before fetching new messages from the database
|
||||||
|
deleteFailedMessages(uri, context, fetchColumns, selection, selectionArgs, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
try (Cursor myCursor = context.getContentResolver().query(
|
try (Cursor myCursor = context.getContentResolver().query(
|
||||||
uri,
|
uri,
|
||||||
fetchColumns.toArray(new String[]{}),
|
fetchColumns.toArray(new String[]{}),
|
||||||
@ -376,6 +385,83 @@ public class SMSHelper {
|
|||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes messages which are failed to send due to some reason
|
||||||
|
*
|
||||||
|
* @param uri Uri indicating the messages database to read
|
||||||
|
* @param context android.content.Context running the request.
|
||||||
|
* @param fetchColumns List of columns to fetch
|
||||||
|
* @param selection Parameterizable filter to use with the ContentResolver query. May be null.
|
||||||
|
* @param selectionArgs Parameters for selection. May be null.
|
||||||
|
* @param sortOrder Sort ordering passed to Android's content resolver. May be null for unspecified
|
||||||
|
*/
|
||||||
|
private static void deleteFailedMessages(
|
||||||
|
@NonNull Uri uri,
|
||||||
|
@NonNull Context context,
|
||||||
|
@NonNull Collection<String> fetchColumns,
|
||||||
|
@Nullable String selection,
|
||||||
|
@Nullable String[] selectionArgs,
|
||||||
|
@Nullable String sortOrder
|
||||||
|
) {
|
||||||
|
try (Cursor myCursor = context.getContentResolver().query(
|
||||||
|
uri,
|
||||||
|
fetchColumns.toArray(new String[]{}),
|
||||||
|
selection,
|
||||||
|
selectionArgs,
|
||||||
|
sortOrder)
|
||||||
|
) {
|
||||||
|
if (myCursor != null && myCursor.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
String id = null;
|
||||||
|
String type = null;
|
||||||
|
String msgBox = null;
|
||||||
|
|
||||||
|
for (int columnIdx = 0; columnIdx < myCursor.getColumnCount(); columnIdx++) {
|
||||||
|
String colName = myCursor.getColumnName(columnIdx);
|
||||||
|
|
||||||
|
if (colName.equals("_id")) {
|
||||||
|
id = myCursor.getString(columnIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(colName.equals("type")) {
|
||||||
|
type = myCursor.getString(columnIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colName.equals("msg_box")) {
|
||||||
|
msgBox = myCursor.getString(columnIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != null && id != null) {
|
||||||
|
if (type.equals(Telephony.Sms.MESSAGE_TYPE_OUTBOX) || type.equals(Telephony.Sms.MESSAGE_TYPE_FAILED)) {
|
||||||
|
Log.v("Deleting sms", "content://sms/" + id);
|
||||||
|
context.getContentResolver().delete(Uri.parse("content://sms/" + id), null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msgBox != null && id != null) {
|
||||||
|
if (msgBox.equals(Telephony.Mms.MESSAGE_BOX_OUTBOX) || msgBox.equals(Telephony.Mms.MESSAGE_BOX_FAILED)) {
|
||||||
|
Log.v("Deleting mms", "content://mms/" + id);
|
||||||
|
context.getContentResolver().delete(Uri.parse("content://mms/" + id), null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (myCursor.moveToNext());
|
||||||
|
}
|
||||||
|
} catch (SQLiteException e) {
|
||||||
|
String[] unfilteredColumns = {};
|
||||||
|
try (Cursor unfilteredColumnsCursor = context.getContentResolver().query(uri, null, null, null, null)) {
|
||||||
|
if (unfilteredColumnsCursor != null) {
|
||||||
|
unfilteredColumns = unfilteredColumnsCursor.getColumnNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unfilteredColumns.length == 0) {
|
||||||
|
throw new MessageAccessException(uri, e);
|
||||||
|
} else {
|
||||||
|
throw new MessageAccessException(unfilteredColumns, uri, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets messages which match the selection
|
* Gets messages which match the selection
|
||||||
*
|
*
|
||||||
@ -818,6 +904,29 @@ public class SMSHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* converts a given JSONArray into List<Address>
|
||||||
|
*/
|
||||||
|
public static List<Address> jsonArrayToAddressList(JSONArray jsonArray) {
|
||||||
|
if (jsonArray == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Address> addresses = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
|
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||||
|
String address = jsonObject.getString("address");
|
||||||
|
addresses.add(new Address(address));
|
||||||
|
Log.e("address", address);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return addresses;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicate that some error has occurred while reading a message.
|
* Indicate that some error has occurred while reading a message.
|
||||||
* More useful for logging than catching and handling
|
* More useful for logging than catching and handling
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A small BroadcastReceiver wrapper for MMSReceivedReceiver to load user preferences
|
||||||
|
*/
|
||||||
|
public class DelegatingMmsReceivedReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
MmsReceivedReceiver delegate = new MmsReceivedReceiver();
|
||||||
|
|
||||||
|
delegate.loadFromPreferences(context);
|
||||||
|
delegate.onReceive(context, intent);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.Intent;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service for sending messages to a conversation without a UI present. These messages could come
|
||||||
|
* from something like Phone, needed to make default sms app
|
||||||
|
*/
|
||||||
|
public class HeadlessSmsSendService extends Service {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.klinker.android.send_message.Transaction;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public class MmsReceivedReceiver extends com.klinker.android.send_message.MmsReceivedReceiver {
|
||||||
|
|
||||||
|
private String mmscUrl = null;
|
||||||
|
private String mmsProxy = null;
|
||||||
|
private String mmsPort = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageReceived(Context context, Uri messageUri) {
|
||||||
|
Log.v("MmsReceived", "message received: " + messageUri.toString());
|
||||||
|
|
||||||
|
// Notify messageUpdateReceiver about the arrival of the new MMS message
|
||||||
|
Intent refreshIntent = new Intent(Transaction.REFRESH);
|
||||||
|
context.sendBroadcast(refreshIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Context context, String error) {
|
||||||
|
Log.v("MmsReceived", "error: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadFromPreferences(Context context) {
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
mmscUrl = prefs.getString(context.getString(R.string.sms_pref_set_mmsc), "");
|
||||||
|
mmsProxy = prefs.getString(context.getString(R.string.sms_pref_set_mms_proxy), "");
|
||||||
|
mmsPort = prefs.getString(context.getString(R.string.sms_pref_set_mms_port), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* some carriers will download duplicate MMS messages without this ACK. When using the
|
||||||
|
* system sending method, apparently Android does not do this for us. Not sure why.
|
||||||
|
* We might have to have users manually enter their APN settings if we cannot get them
|
||||||
|
* from the system somehow.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public MmscInformation getMmscInfoForReceptionAck() {
|
||||||
|
|
||||||
|
if (mmscUrl != null || mmsProxy != null || mmsPort != null) {
|
||||||
|
try {
|
||||||
|
return new MmscInformation(mmscUrl, mmsProxy, Integer.parseInt(mmsPort));
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("MmsReceivedReceiver", "Exception", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.klinker.android.send_message.Transaction;
|
||||||
|
import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
|
public class MmsSentReceiver extends com.klinker.android.send_message.MmsSentReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateInInternalDatabase(Context context, Intent intent, int resultCode) {
|
||||||
|
super.updateInInternalDatabase(context, intent, resultCode);
|
||||||
|
|
||||||
|
if (Utils.isDefaultSmsApp(context)) {
|
||||||
|
// Notify messageUpdateReceiver about the successful sending of the mms message
|
||||||
|
Intent refreshIntent = new Intent(Transaction.REFRESH);
|
||||||
|
context.sendBroadcast(refreshIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageStatusUpdated(Context context, Intent intent, int resultCode) {
|
||||||
|
}
|
||||||
|
}
|
@ -39,7 +39,6 @@ import android.provider.Telephony;
|
|||||||
import android.telephony.PhoneNumberUtils;
|
import android.telephony.PhoneNumberUtils;
|
||||||
import android.telephony.SmsManager;
|
import android.telephony.SmsManager;
|
||||||
import android.telephony.SmsMessage;
|
import android.telephony.SmsMessage;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@ -63,6 +62,11 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
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;
|
import static org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin.PACKET_TYPE_TELEPHONY;
|
||||||
|
|
||||||
@PluginFactory.LoadablePlugin
|
@PluginFactory.LoadablePlugin
|
||||||
@ -125,9 +129,10 @@ public class SMSPlugin extends Plugin {
|
|||||||
* <p>
|
* <p>
|
||||||
* The body should look like so:
|
* The body should look like so:
|
||||||
* { "sendSms": true,
|
* { "sendSms": true,
|
||||||
* "phoneNumber": "542904563213",
|
* "phoneNumber": "542904563213" // For older desktop versions of SMS app this packet carries phoneNumber field
|
||||||
* "messageBody": "Hi mom!",
|
* "addresses": <List of Addresses> // For newer desktop versions of SMS app it contains addresses field instead of phoneNumber field
|
||||||
* "sub_id": "3859358340534"
|
* "messageBody": "Hi mom!",
|
||||||
|
* "sub_id": "3859358340534"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
private final static String PACKET_TYPE_SMS_REQUEST = "kdeconnect.sms.request";
|
private final static String PACKET_TYPE_SMS_REQUEST = "kdeconnect.sms.request";
|
||||||
@ -210,33 +215,64 @@ public class SMSPlugin extends Plugin {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
// Lock so no one uses the mostRecentTimestamp between the moment we read it and the
|
// If the KDE Connect is set as default Sms app
|
||||||
// moment we update it. This is because reading the Messages DB can take long.
|
// prevent from reading the latest message in the database before the sentReceivers mark it as sent
|
||||||
mostRecentTimestampLock.lock();
|
if (Utils.isDefaultSmsApp(context)) {
|
||||||
|
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
|
sendLatestMessage();
|
||||||
|
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
filter.setPriority(500);
|
||||||
context.registerReceiver(receiver, filter);
|
context.registerReceiver(receiver, filter);
|
||||||
|
|
||||||
|
IntentFilter refreshFilter = new IntentFilter(Transaction.REFRESH);
|
||||||
|
refreshFilter.setPriority(500);
|
||||||
|
context.registerReceiver(messagesUpdateReceiver, refreshFilter);
|
||||||
|
|
||||||
Looper helperLooper = SMSHelper.MessageLooper.getLooper();
|
Looper helperLooper = SMSHelper.MessageLooper.getLooper();
|
||||||
ContentObserver messageObserver = new MessageContentObserver(new Handler(helperLooper));
|
ContentObserver messageObserver = new MessageContentObserver(new Handler(helperLooper));
|
||||||
SMSHelper.registerObserver(messageObserver, context);
|
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.");
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,8 +381,22 @@ public class SMSPlugin extends Plugin {
|
|||||||
case PACKET_TYPE_SMS_REQUEST_CONVERSATION:
|
case PACKET_TYPE_SMS_REQUEST_CONVERSATION:
|
||||||
return this.handleRequestConversation(np);
|
return this.handleRequestConversation(np);
|
||||||
case PACKET_TYPE_SMS_REQUEST:
|
case PACKET_TYPE_SMS_REQUEST:
|
||||||
// Fall through to old-style handling
|
if (np.getBoolean("sendSms")) {
|
||||||
// This space may be filled in differently once MMS support is implemented
|
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:
|
case TelephonyPlugin.PACKET_TYPE_TELEPHONY_REQUEST:
|
||||||
if (np.getBoolean("sendSms")) {
|
if (np.getBoolean("sendSms")) {
|
||||||
String phoneNo = np.getString("phoneNumber");
|
String phoneNo = np.getString("phoneNumber");
|
||||||
@ -432,6 +493,17 @@ public class SMSPlugin extends Plugin {
|
|||||||
conversation = SMSHelper.getMessagesInRange(this.context, threadID, rangeStartTimestamp, numberToGet);
|
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);
|
NetworkPacket reply = constructBulkMessagePacket(conversation);
|
||||||
|
|
||||||
device.sendPacket(reply);
|
device.sendPacket(reply);
|
||||||
@ -451,6 +523,10 @@ public class SMSPlugin extends Plugin {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSettings() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getSupportedPacketTypes() {
|
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
|
* 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
|
* un-documented. Most manufacturers *did* do things the same way as was done in mainline
|
||||||
|
121
src/org/kde/kdeconnect/Plugins/SMSPlugin/SmsMmsUtils.java
Normal file
121
src/org/kde/kdeconnect/Plugins/SMSPlugin/SmsMmsUtils.java
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.klinker.android.send_message.Message;
|
||||||
|
import com.klinker.android.send_message.MmsSentReceiver;
|
||||||
|
import com.klinker.android.send_message.Settings;
|
||||||
|
import com.klinker.android.send_message.Transaction;
|
||||||
|
import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Helpers.SMSHelper;
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SmsMmsUtils {
|
||||||
|
|
||||||
|
private static final String SENDING_MESSAGE = "Sending message";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends SMS or MMS message.
|
||||||
|
*
|
||||||
|
* @param context context in which the method is called.
|
||||||
|
* @param textMessage text body of the message to be sent.
|
||||||
|
* @param addressList List of addresses.
|
||||||
|
* @param subID Note that here subID is of type int and not long because klinker library requires it as int
|
||||||
|
* I don't really know the exact reason why they implemented it as int instead of long
|
||||||
|
*/
|
||||||
|
public static void sendMessage(Context context, String textMessage, List<SMSHelper.Address> addressList, int subID) {
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
boolean longTextAsMms = prefs.getBoolean(context.getString(R.string.set_long_text_as_mms), false);
|
||||||
|
boolean groupMessageAsMms = prefs.getBoolean(context.getString(R.string.set_group_message_as_mms), true);
|
||||||
|
int sendLongAsMmsAfter = Integer.parseInt(
|
||||||
|
prefs.getString(context.getString(R.string.convert_to_mms_after),
|
||||||
|
context.getString(R.string.convert_to_mms_after_default)));
|
||||||
|
|
||||||
|
try {
|
||||||
|
Settings settings = new Settings();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
// If the build version is less than lollipop then we have to manually take the APN settings
|
||||||
|
// from the user in order to be able to send MMS.
|
||||||
|
settings.setMmsc(prefs.getString(context.getString(R.string.sms_pref_set_mmsc), ""));
|
||||||
|
settings.setProxy(prefs.getString(context.getString(R.string.sms_pref_set_mms_proxy), ""));
|
||||||
|
settings.setPort(prefs.getString(context.getString(R.string.sms_pref_set_mms_port), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.setUseSystemSending(true);
|
||||||
|
|
||||||
|
if (Utils.isDefaultSmsApp(context)) {
|
||||||
|
settings.setSendLongAsMms(longTextAsMms);
|
||||||
|
settings.setSendLongAsMmsAfter(sendLongAsMmsAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.setGroup(groupMessageAsMms);
|
||||||
|
|
||||||
|
if (subID != -1) {
|
||||||
|
settings.setSubscriptionId(subID);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction transaction = new Transaction(context, settings);
|
||||||
|
transaction.setExplicitBroadcastForSentSms(new Intent(context, SmsSentReceiver.class));
|
||||||
|
transaction.setExplicitBroadcastForSentMms(new Intent(context, MmsSentReceiver.class));
|
||||||
|
|
||||||
|
List<String> addresses = new ArrayList<>();
|
||||||
|
for (SMSHelper.Address address : addressList) {
|
||||||
|
addresses.add(address.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Message message = new Message(textMessage, addresses.toArray(new String[0]));
|
||||||
|
message.setSave(true);
|
||||||
|
|
||||||
|
// Sending MMS on android requires the app to be set as the default SMS app,
|
||||||
|
// but sending SMS doesn't needs the app to be set as the default app.
|
||||||
|
// This is the reason why there are separate branch handling for SMS and MMS.
|
||||||
|
if (transaction.checkMMS(message)) {
|
||||||
|
if (Utils.isDefaultSmsApp(context)) {
|
||||||
|
if (Utils.isMobileDataEnabled(context)) {
|
||||||
|
com.klinker.android.logger.Log.v("", "Sending new MMS");
|
||||||
|
transaction.sendNewMessage(message, Transaction.NO_THREAD_ID);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
com.klinker.android.logger.Log.v(SENDING_MESSAGE, "KDE Connect is not set to default SMS app.");
|
||||||
|
//TODO: Notify other end that they need to enable the mobile data in order to send MMS
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
com.klinker.android.logger.Log.v(SENDING_MESSAGE, "Sending new SMS");
|
||||||
|
transaction.sendNewMessage(message, Transaction.NO_THREAD_ID);
|
||||||
|
}
|
||||||
|
//TODO: Notify other end
|
||||||
|
} catch (Exception e) {
|
||||||
|
//TODO: Notify other end
|
||||||
|
com.klinker.android.logger.Log.e(SENDING_MESSAGE, "Exception", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
src/org/kde/kdeconnect/Plugins/SMSPlugin/SmsReceiver.java
Normal file
82
src/org/kde/kdeconnect/Plugins/SMSPlugin/SmsReceiver.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.telephony.SmsMessage;
|
||||||
|
import android.provider.Telephony.Sms;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
|
||||||
|
import com.klinker.android.send_message.Transaction;
|
||||||
|
import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
|
public class SmsReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (!Utils.isDefaultSmsApp(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intent != null && intent.getAction().equals(SMS_RECEIVED)) {
|
||||||
|
Bundle dataBundle = intent.getExtras();
|
||||||
|
|
||||||
|
if (dataBundle != null) {
|
||||||
|
Object[] smsExtra = (Object[]) dataBundle.get("pdus");
|
||||||
|
final SmsMessage[] message = new SmsMessage[smsExtra.length];
|
||||||
|
|
||||||
|
for (int i = 0; i < smsExtra.length; ++i) {
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
String format = dataBundle.getString("format");
|
||||||
|
message[i] = SmsMessage.createFromPdu((byte[]) smsExtra[i], format);
|
||||||
|
} else {
|
||||||
|
message[i] = SmsMessage.createFromPdu((byte[]) smsExtra[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the received sms to the sms provider
|
||||||
|
for (SmsMessage msg : message) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(Sms.ADDRESS, msg.getDisplayOriginatingAddress());
|
||||||
|
values.put(Sms.BODY, msg.getMessageBody());
|
||||||
|
values.put(Sms.DATE, System.currentTimeMillis()+"");
|
||||||
|
values.put(Sms.TYPE, Sms.MESSAGE_TYPE_INBOX);
|
||||||
|
values.put(Sms.STATUS, msg.getStatus());
|
||||||
|
values.put(Sms.READ, 0);
|
||||||
|
values.put(Sms.SEEN, 0);
|
||||||
|
context.getApplicationContext().getContentResolver().insert(Uri.parse("content://sms/"), values);
|
||||||
|
|
||||||
|
// Notify messageUpdateReceiver about the arrival of the new sms message
|
||||||
|
Intent refreshIntent = new Intent(Transaction.REFRESH);
|
||||||
|
context.sendBroadcast(refreshIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.klinker.android.send_message.SentReceiver;
|
||||||
|
import com.klinker.android.send_message.Transaction;
|
||||||
|
import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
|
public class SmsSentReceiver extends SentReceiver {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateInInternalDatabase(Context context, Intent intent, int receiverResultCode) {
|
||||||
|
super.updateInInternalDatabase(context, intent, receiverResultCode);
|
||||||
|
|
||||||
|
if (Utils.isDefaultSmsApp(context)) {
|
||||||
|
// Notify messageUpdateReceiver about the successful sending of the sms message
|
||||||
|
Intent refreshIntent = new Intent(Transaction.REFRESH);
|
||||||
|
context.sendBroadcast(refreshIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessageStatusUpdated(Context context, Intent intent, int receiverResultCode) {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Erik Duisters <e.duisters1@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.UserInterface;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.role.RoleManager;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.Telephony;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
|
||||||
|
public class DefaultSmsAppAlertDialogFragment extends AlertDialogFragment {
|
||||||
|
private static final String KEY_PERMISSIONS = "Permissions";
|
||||||
|
private static final String KEY_REQUEST_CODE = "RequestCode";
|
||||||
|
|
||||||
|
private String[] permissions;
|
||||||
|
private int requestCode;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Bundle args = getArguments();
|
||||||
|
|
||||||
|
if (args == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions = args.getStringArray(KEY_PERMISSIONS);
|
||||||
|
requestCode = args.getInt(KEY_REQUEST_CODE, 0);
|
||||||
|
|
||||||
|
setCallback(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void onPositiveButtonClicked() {
|
||||||
|
Activity host = requireActivity();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
|
RoleManager roleManager = host.getSystemService(RoleManager.class);
|
||||||
|
|
||||||
|
if (roleManager.isRoleAvailable(RoleManager.ROLE_SMS)) {
|
||||||
|
if (!roleManager.isRoleHeld(RoleManager.ROLE_SMS)) {
|
||||||
|
Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS);
|
||||||
|
host.startActivityForResult(roleRequestIntent, requestCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
|
||||||
|
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, getActivity().getPackageName());
|
||||||
|
host.startActivityForResult(intent, requestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivityCompat.requestPermissions(requireActivity(), permissions, requestCode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder extends AlertDialogFragment.AbstractBuilder<DefaultSmsAppAlertDialogFragment.Builder, DefaultSmsAppAlertDialogFragment> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder getThis() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setPermissions(String[] permissions) {
|
||||||
|
args.putStringArray(KEY_PERMISSIONS, permissions);
|
||||||
|
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setRequestCode(int requestCode) {
|
||||||
|
args.putInt(KEY_REQUEST_CODE, requestCode);
|
||||||
|
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DefaultSmsAppAlertDialogFragment createFragment() {
|
||||||
|
return new DefaultSmsAppAlertDialogFragment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,14 +40,18 @@ import androidx.appcompat.app.AlertDialog;
|
|||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.klinker.android.send_message.Utils;
|
||||||
|
|
||||||
import org.kde.kdeconnect.BackgroundService;
|
import org.kde.kdeconnect.BackgroundService;
|
||||||
import org.kde.kdeconnect.Device;
|
import org.kde.kdeconnect.Device;
|
||||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||||
import org.kde.kdeconnect.Plugins.Plugin;
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
|
import org.kde.kdeconnect.Plugins.SMSPlugin.SMSPlugin;
|
||||||
import org.kde.kdeconnect.UserInterface.List.PluginListHeaderItem;
|
import org.kde.kdeconnect.UserInterface.List.PluginListHeaderItem;
|
||||||
import org.kde.kdeconnect.UserInterface.List.FailedPluginListItem;
|
import org.kde.kdeconnect.UserInterface.List.FailedPluginListItem;
|
||||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||||
import org.kde.kdeconnect.UserInterface.List.PluginItem;
|
import org.kde.kdeconnect.UserInterface.List.PluginItem;
|
||||||
|
import org.kde.kdeconnect.UserInterface.List.SetDefaultAppPluginListItem;
|
||||||
import org.kde.kdeconnect_tp.R;
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -337,6 +341,29 @@ public class DeviceFragment extends Fragment {
|
|||||||
dialog.show(getChildFragmentManager(), null);
|
dialog.show(getChildFragmentManager(), null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add a button to the pluginList for setting KDE Connect as default sms app for allowing it to send mms
|
||||||
|
// for now I'm not able to integrate it with other plugin list, but this needs to be reimplemented in a better way.
|
||||||
|
if (!Utils.isDefaultSmsApp(mActivity)) {
|
||||||
|
for (Plugin p : plugins) {
|
||||||
|
if (p.getPluginKey().equals("SMSPlugin")) {
|
||||||
|
pluginListItems.add(new SetDefaultAppPluginListItem(p, mActivity.getResources().getString(R.string.pref_plugin_telepathy_mms), (action) -> {
|
||||||
|
DialogFragment dialog = new DefaultSmsAppAlertDialogFragment.Builder()
|
||||||
|
.setTitle(R.string.set_default_sms_app_title)
|
||||||
|
.setMessage(R.string.pref_plugin_telepathy_mms_desc)
|
||||||
|
.setPositiveButton(R.string.ok)
|
||||||
|
.setNegativeButton(R.string.cancel)
|
||||||
|
.setPermissions(SMSPlugin.getMmsPermissions())
|
||||||
|
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
if (dialog != null) {
|
||||||
|
dialog.show(getChildFragmentManager(), null);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ListAdapter adapter = new ListAdapter(mActivity, pluginListItems);
|
ListAdapter adapter = new ListAdapter(mActivity, pluginListItems);
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* 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.UserInterface.List;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Plugins.Plugin;
|
||||||
|
|
||||||
|
public class SetDefaultAppPluginListItem extends SmallEntryItem {
|
||||||
|
|
||||||
|
public interface Action {
|
||||||
|
void action(Plugin plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SetDefaultAppPluginListItem(Plugin plugin, String displayName, SetDefaultAppPluginListItem.Action action) {
|
||||||
|
super(displayName, (view) -> action.action(plugin));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user