2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-02 15:15:09 +00:00

Compare commits

...

21 Commits

Author SHA1 Message Date
Albert Vaca Cintora
baa7032e9d Release 1.15.1 2020-11-02 10:18:05 +01:00
Simon Redman
d206b737d3 [SMS App] Allow sending MMS while not the default messaging app
## Summary

Use [SmsManager.SendMultimediaMessage](https://developer.android.com/reference/android/telephony/SmsManager#sendMultimediaMessage(android.content.Context,%20android.net.Uri,%20java.lang.String,%20android.os.Bundle,%20android.app.PendingIntent)) for sending MMS

This allows us to remove the handling for being the default MMS app, which the Google Play Store is not allowing us to claim

Additionally, rip out the parts of the app necessary for being the default handler (notifications, receivers, etc.) and and make the handling for changes in the message database less fragile (handle more messages at a time and handle MMS)

This is a collaboration between myself and Albert Vaca

## Test Plan

Sent a few plain-text and group MMS. It "works for me".
2020-11-01 23:47:03 +00:00
Albert Vaca Cintora
b4119a18dc Pin gradle plugin 4.0.2 2020-11-01 15:43:37 -08:00
Albert Vaca Cintora
f8e1dfff1c Reduce number of calls to PduPersister.load 2020-10-31 19:42:55 +01:00
Isira Seneviratne
96a51a6e77 Use Long.hashCode() in SMSHelper. 2020-10-31 18:22:52 +00:00
l10n daemon script
34dee23197 GIT_SILENT made messages (after extraction) 2020-10-26 02:22:25 +01:00
l10n daemon script
00460ce1e7 GIT_SILENT made messages (after extraction) 2020-10-23 02:24:07 +02:00
Isira Seneviratne
acf4b02a25 Use TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(). 2020-10-22 14:41:50 +05:30
Isira Seneviratne
86919e002b Use TextViewCompat.setCompoundDrawablesRelative(). 2020-10-22 14:38:42 +05:30
l10n daemon script
839ff29401 GIT_SILENT made messages (after extraction) 2020-10-22 02:24:43 +02:00
l10n daemon script
82e7a8a255 GIT_SILENT made messages (after extraction) 2020-10-20 02:24:22 +02:00
Albert Vaca Cintora
1aa29c56fe Bump gradle version 2020-10-18 14:51:20 +02:00
l10n daemon script
c429bb81b0 GIT_SILENT made messages (after extraction) 2020-10-18 02:23:18 +02:00
l10n daemon script
3865c31820 GIT_SILENT made messages (after extraction) 2020-10-16 02:18:35 +02:00
l10n daemon script
f0fb35bb6e GIT_SILENT made messages (after extraction) 2020-10-15 02:23:15 +02:00
l10n daemon script
0e90a6038d GIT_SILENT made messages (after extraction) 2020-10-13 02:21:22 +02:00
l10n daemon script
d6e654f140 GIT_SILENT made messages (after extraction) 2020-10-12 02:21:28 +02:00
Albert Vaca Cintora
3f703c9732 Bump version 2020-10-11 17:41:08 +02:00
Albert Vaca Cintora
cffafc40f7 Removed something that isn't a permission 2020-10-11 17:40:25 +02:00
Yuri Chornoivan
f7007fd557 Fix minor typo 2020-10-11 08:41:48 +03:00
l10n daemon script
4cee466940 GIT_SILENT made messages (after extraction) 2020-10-11 02:23:02 +02:00
52 changed files with 404 additions and 1024 deletions

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.kde.kdeconnect_tp"
android:versionCode="11501"
android:versionName="1.15.0">
android:versionCode="11510"
android:versionName="1.15.1">
<supports-screens
android:anyDensity="true"
@@ -37,7 +37,6 @@
<uses-permission android:name="android.permission.READ_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.USE_FULL_SCREEN_INTENT" />
@@ -54,22 +53,6 @@
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/KdeConnectTheme"
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"
@@ -82,39 +65,6 @@
</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.MmsSentReceiverImpl"
android:exported="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"
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"
@@ -140,23 +90,10 @@
android:name="org.kde.kdeconnect.UserInterface.MainActivity"
android:label="KDE Connect"
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>
<action android:name="android.intent.action.MAIN" />
<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" />

View File

@@ -12,6 +12,8 @@ buildscript {
google()
}
dependencies {
// Pinned 4.0.2 due to this bug on 4.1: https://issuetracker.google.com/issues/172096891
//noinspection GradleDependency
classpath 'com.android.tools.build:gradle:4.0.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}

View File

@@ -1,6 +1,6 @@
#Tue Jun 23 12:30:12 CEST 2020
#Sun Oct 18 13:33:18 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Bu qoşmanın işləməsi üçün icazələr lazımdır</string>
<string name="optional_permission_explanation">Bütün funksiyaların işləməsi üçün əlavə icazələr verməlisiniz</string>
<string name="plugins_need_optional_permission">Bəzi qoşmalarda icazə çatışmamazlığı səbəbindən bir sıra imkanlar söndürülmüşdür (daha çox məlumat üçün toxunun)</string>
<string name="share_optional_permission_explanation">Telefonunuz və İş Masanız arasında fayllar mübadiləsi üçün telefon yaddaşına girişə icazə verməlisiniz</string>
<string name="share_optional_permission_explanation">Paylaşılan faylları qəbul etmək üçün saxlama qovluğu seçməlisiniz</string>
<string name="telepathy_permission_explanation">İş Masanızdan telefonunuzdakı SMS\'ləri oxumaq və SMS göndərmək üçün SMS\'ə girişə icazə verməlisiniz</string>
<string name="telephony_permission_explanation">İş Masanızda telefon zənglərini görmək üçün Zəng Tarixşəsinə və Zəng yığımı vəziyyətinə icazə verməlisiniz</string>
<string name="telephony_optional_permission_explanation">Telefon nömrəsi əvəzinə kontaktın adını görmək üçün Kontakt Kitabçasına girişə icazə verməlisiniz</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Aquest connector necessita permisos per a funcionar</string>
<string name="optional_permission_explanation">Us caldrà concedir permisos extres per a accedir a totes les característiques</string>
<string name="plugins_need_optional_permission">Alguns connectors tenen característiques desactivades per la falta de permís (toqueu per a més informació):</string>
<string name="share_optional_permission_explanation">Per a compartir fitxers entre el telèfon i l\'escriptori, haureu de donar accés a l\'emmagatzematge del telèfon</string>
<string name="share_optional_permission_explanation">Per a rebre fitxers compartits cal triar un directori de destinació</string>
<string name="telepathy_permission_explanation">Per a llegir i escriure SMS des de l\'escriptori, haureu de donar permís als SMS</string>
<string name="telephony_permission_explanation">Per a veure les trucades telefòniques des de l\'escriptori, haureu de donar permís d\'accés al registre de trucades telefòniques i a l\'estat del telèfon</string>
<string name="telephony_optional_permission_explanation">Per a veure un nom de contacte en comptes d\'un número de telèfon, haureu de donar permís als contactes del telèfon</string>

View File

@@ -242,7 +242,6 @@
<string name="permission_explanation">Tento modul potřebuje pro práci povolení</string>
<string name="optional_permission_explanation">Pro zpřístupnění všech funkcí potřebujete další oprávnění</string>
<string name="plugins_need_optional_permission">Některé moduly mají vypnuté vlastnosti, kvůli nedostatečným oprávněním (ťukněte pro více informací):</string>
<string name="share_optional_permission_explanation">Pro sdílení souborů mezi telefonem a počítačem potřebujete udělit oprávnění k úložišti telefonu</string>
<string name="telepathy_permission_explanation">Pro čtení a psaní SMS z počítače musíte udělit oprávnění k SMS</string>
<string name="telephony_permission_explanation">Pro zobrazení telefonátů v počítači musíte udělit oprávnění k záznamům telefonování a stavu telefonu</string>
<string name="telephony_optional_permission_explanation">Pro zobrazení jména kontaktu u telefonního čísla je potřeba udělit oprávnění ke kontaktům v telefonu</string>

View File

@@ -139,7 +139,6 @@
<string name="permission_explanation">Dette plugin kræver tilladelser for at virke</string>
<string name="optional_permission_explanation">Du skal give ekstra tilladelser for at aktivere alle funktioner</string>
<string name="plugins_need_optional_permission">Nogle plugins har deaktiverede funktioner pga. manglende tilladelser (tap for mere info):</string>
<string name="share_optional_permission_explanation">For at dele filer mellem din telefon og din desktop skal du give adgang til telefonens datalager.</string>
<string name="telepathy_permission_explanation">For at læse og skrive sms\'er fra din desktop, skal du give tilladelse til sms</string>
<string name="telephony_optional_permission_explanation">For at se et kontaktnavn i stedet for et telefonnummer, skal du give adgang til telefonens kontakter</string>
<string-array name="convert_to_mms_after_entries">

View File

@@ -122,6 +122,7 @@
<item quantity="one">Die Datei kann nicht an %1$s gesendet werden</item>
<item quantity="other">%2$d der %3$d Dateien können nicht an %1$s gesendet werden</item>
</plurals>
<string name="tap_to_open">Tippen um zu öffnen</string>
<string name="received_file_text">Tippen um „%1s“ zu öffnen</string>
<string name="cannot_create_file">Die Datei %s kann nicht erstellt werden</string>
<string name="tap_to_answer">Zum Antworten tippen</string>
@@ -208,6 +209,7 @@
<string name="no_file_browser">Es sind keine Dateiverwaltungsprogramme installiert</string>
<string name="pref_plugin_telepathy">SMS senden</string>
<string name="pref_plugin_telepathy_desc">SMS von Ihrer Arbeitsfläche senden</string>
<string name="pref_plugin_telepathy_mms">MMS senden</string>
<string name="findmyphone_title">Mein Telefon suchen</string>
<string name="findmyphone_title_tablet">Mein Tablet suchen</string>
<string name="findmyphone_title_tv">Meinen Fernseher suchen</string>
@@ -219,7 +221,6 @@
<string name="permission_explanation">Dieses Modul benötigt zusätzliche Berechtigungen</string>
<string name="optional_permission_explanation">Es müssen weitere Berechtigungen erteilt werden, um alle Funktionen nutzen zu können</string>
<string name="plugins_need_optional_permission">Einige Module haben eingeschränkte Funktionen wegen fehlender Berechtigungen, tippen Sie für weitere Informationen:</string>
<string name="share_optional_permission_explanation">Um Dateien zwischen Rechner und Telefon auszutauschen, muss der Zugriff auf den Telefonspeicher gewährt werden</string>
<string name="telepathy_permission_explanation">Um SMS vom Rechner aus zu lesen und zu versenden, muss der Zugriff auf die SMS-Funktion gewährt werden</string>
<string name="telephony_permission_explanation">Um eingehende Anrufe auf der Arbeitsfläche anzuzeigen, muss der Zugriff auf die Anrufliste und den Telefonstatus gewährt werden</string>
<string name="telephony_optional_permission_explanation">Um einen Namen anstelle der Telefonnummer anzuzeigen, muss der Zugriff auf das Adressbuch gewährt werden</string>
@@ -295,7 +296,7 @@
<item>Dark</item>
</string-array>
<string-array name="theme_list_v28">
<item/>
<item>Systemvoreinstellung</item>
<item/>
<item>Dark</item>
</string-array>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Αυτό το πρόσθετο χρειάζεται δικαιώματα για να λειτουργήσει</string>
<string name="optional_permission_explanation">Απαιτείται παραχώρηση επιπλέον δικαιωμάτων για την ενεργοποίηση όλων των λειτουργιών</string>
<string name="plugins_need_optional_permission">Κάποια πρόσθετα έχουν λειτουργίες ανενεργές εξαιτίας της απουσίας δικαιωμάτων (χτυπήστε για περισσότερες πληροφορίες):</string>
<string name="share_optional_permission_explanation">Για το διαμοιρασμό αρχείων ανάμεσα στο τηλέφωνο και τον υπολογιστή σας χρειάζεται να παραχωρήσετε πρόσβαση στον αποθηκευτικό χώρο του τηλεφώνου σας</string>
<string name="share_optional_permission_explanation">Για να λάβετε κοινόχρηστα αρχεία πρέπει να επιλέξετε έναν κατάλογο προορισμού</string>
<string name="telepathy_permission_explanation">Για να διαβάσετε και να γράψετε SMS από την επιφάνεια εργασίας, χρειάζεται να δώσετε δικαιώματα στο SMS</string>
<string name="telephony_permission_explanation">Για να δείτε τηλεφωνικές κλήσεις στην επιφάνεια εργασίας απαιτείται να παραχωρήσετε δικαιώματα σε καταγραφές τηλεφωνικών κλήσεων και στην κατάσταση του τηλεφώνου</string>
<string name="telephony_optional_permission_explanation">Για να δείτε το όνομα επαφής αντί για τον αριθμό κλήσης χρειάζεται να παραχωρήσετε πρόσβαση στις επαφές στο τηλέφωνο</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">This plugin needs permissions to work</string>
<string name="optional_permission_explanation">You need to grant extra permissions to enable all functions</string>
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="share_optional_permission_explanation">To share files between your phone and your desktop you need to give access to the phone\'s storage</string>
<string name="share_optional_permission_explanation">To receive shared files you need to choose a destination directory</string>
<string name="telepathy_permission_explanation">To read and write SMS from your desktop you need to give permission to SMS</string>
<string name="telephony_permission_explanation">To see phone calls on the desktop you need to give permission to phone call logs and phone state</string>
<string name="telephony_optional_permission_explanation">To see a contact name instead of a phone number you need to give access to the phone\'s contacts</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Este complemento necesita permisos para funcionar</string>
<string name="optional_permission_explanation">Debe otorgar permisos extra para activar todas las funciones</string>
<string name="plugins_need_optional_permission">Algunos complementos tienen funcionalidades desactivadas por falta de permisos (pulse para más información):</string>
<string name="share_optional_permission_explanation">Para compartir archivos entre su teléfono y su escritorio, necesita dar acceso al almacenamiento de su teléfono</string>
<string name="share_optional_permission_explanation">Para recibir archivos compartidos debe elegir un directorio destino</string>
<string name="telepathy_permission_explanation">Para leer y escribir SMS desde su escritorio, necesita dar permisos para SMS</string>
<string name="telephony_permission_explanation">Para ver las llamadas telefónicas en el escritorio, necesita dar permisos al registro de llamadas telefónicas y al estado del teléfono</string>
<string name="telephony_optional_permission_explanation">Para ver el nombre de un contacto en lugar de un número telefónico, necesita dar acceso a los contactos de su teléfono</string>

View File

@@ -226,7 +226,6 @@
<string name="permission_explanation">See plugin vajab töötamiseks õigusi</string>
<string name="optional_permission_explanation">Kõigi funktsioonide lubamiseks tuleb sul anda lisaõigusi</string>
<string name="plugins_need_optional_permission">Mõne plugina osa omadusi on keelatud õiguste puudumise tõttu (koputa rohkema teabe saamiseks):</string>
<string name="share_optional_permission_explanation">Failide jagamiseks telefoni ja töölau vahel tuleb anda telefonile ligipääs</string>
<string name="telepathy_permission_explanation">SMS-ide lugemiseks ja kirjutamiseks töölaual tuleb anda õigused SMS-ile</string>
<string name="telephony_permission_explanation">Kõnede nägemiseks töölaual tuleb anda õigused telefonikõnede logile ja telefoni olekule</string>
<string name="telephony_optional_permission_explanation">Telefoninumbri asemel kontakti nime nägemiseks tuleb anda õigused telefoni kontaktidele</string>

View File

@@ -226,7 +226,6 @@
<string name="permission_explanation">Plugin honek baimena behar du funtzionatzeko</string>
<string name="optional_permission_explanation">Baimen gehiago eman behar dituzu funtzio guztiak gaitzeko</string>
<string name="plugins_need_optional_permission">Plugin batzuek desgaitutako eginbideak dituzte baimenak faltan dituztelako (tak egin informazio gehiagorako):</string>
<string name="share_optional_permission_explanation">Zure telefonoa eta mahaigainaren artean fitxategiak partekatzeko telefonoaren biltegiratzea atzitzeko baimena eman behar duzu</string>
<string name="telepathy_permission_explanation">SMSak zure mahaigainetik bidali ahal izateko, SMSak erabiltzeko baimena eman behar duzu</string>
<string name="telephony_permission_explanation">Telefono deiak zure mahaigainetik ikusteko, telefono deien egunkarira eta telefonoaren egoerara baimena eman behar duzu</string>
<string name="telephony_optional_permission_explanation">Telefono zenbakiaren ordez kontaktuaren izena ikusteko telefonoko kontaktuak atzitzeko baimena eman behar duzu</string>
@@ -251,6 +250,7 @@
<string name="notification_channel_media_control">Euskarrien kontrola</string>
<string name="notification_channel_filetransfer">Fitxategi transferentzia</string>
<string name="notification_channel_high_priority">Lehentasun handia</string>
<string name="notification_channel_sms_mms">Mezu berria</string>
<string name="mpris_stop">Gelditu uneko jotzailea</string>
<string name="copy_url_to_clipboard">Kopiatu URLa arbelera</string>
<string name="clipboard_toast">Arbelera kopiatua</string>
@@ -305,6 +305,9 @@
<string name="pref_plugin_bigscreen_desc">Erabili zure gailua Plasma Bigscreen-en urrutiko aginte gisa</string>
<string name="bigscreen_optional_permission_explanation">Zure telefonoko mikrofonoko sarrerakoa partekatzeko, telefonoko audio sarrerakora sarbidea eman behar diozu</string>
<string name="bigscreen_speech_extra_prompt">Hizketa</string>
<string name="message_reply_label">ERANTZUN</string>
<string name="mark_as_read_label">MARKATU IRAKURRITAKO GISA</string>
<string name="user_display_name">Zu</string>
<string name="set_default_sms_app_title">Bidali MMS</string>
<string name="set_group_message_as_mms_title">Bidali taldeko MMS</string>
<string name="set_long_text_as_mms_title">Bidali testu luzea MMS gisa</string>

View File

@@ -226,7 +226,6 @@
<string name="permission_explanation">Liitännäinen tarvitsee toimiakseen lisäkäyttöoikeuksia</string>
<string name="optional_permission_explanation">Kaikkien toimintojen käyttämiseksi sinun on annettava lisäkäyttöoikeuksia</string>
<string name="plugins_need_optional_permission">Jotkin liitännäisten ominaisuudet eivät ole käytössä puuttuvien käyttöoikeuksien takia (lisätietoa napsauttamalla):</string>
<string name="share_optional_permission_explanation">Jakaaksesi tiedostoja puhelimen ja työpöydän välillä sinun on annettava käyttöoikeudet puhelimen tallennustilaan</string>
<string name="telepathy_permission_explanation">Lukeaksesi ja lähettääksesi tekstiviestejä työpöydältä sinun on annettava käyttöoikeudet tekstiviesteihin</string>
<string name="telephony_permission_explanation">Puhelujen näyttäminen työpöydällä vaatii käyttöoikeuden puhelulokiin ja puhelimen tilaan</string>
<string name="telephony_optional_permission_explanation">Puhelimen yhteystietoihin on annettava käyttöoikeudet, jotta voit nähdä yhteystiedoissa nimet puhelinnumerojen sijaan</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Ce module externe nécessite des permissions pour fonctionner</string>
<string name="optional_permission_explanation">Vous devez accorder des permissions supplémentaires pour activer toutes les fonctionnalités</string>
<string name="plugins_need_optional_permission">Certaines fonctionnalités de modules externes sont désactivées faute de permissions suffisantes (tapez pour plus d\'informations) :</string>
<string name="share_optional_permission_explanation">Pour partager des fichiers entre votre téléphone et votre ordinateur, veuillez permettre l\'accès à la mémoire de stockage du téléphone</string>
<string name="share_optional_permission_explanation">Pour la réception de fichiers partagés, vous devez sélectionner un dossier de destination.</string>
<string name="telepathy_permission_explanation">Pour lire et écrire des SMS depuis votre ordinateur, veuillez permettre l\'accès aux SMS</string>
<string name="telephony_permission_explanation">Pour voir les appels depuis votre ordinateur, veuillez permettre l\'accès au journal des appels et à l\'état du téléphone</string>
<string name="telephony_optional_permission_explanation">Pour voir le nom du contact au lieu du numéro de téléphone, veuillez permettre l\'accès aux contacts du téléphone</string>

View File

@@ -218,7 +218,6 @@
<string name="permission_explanation">Este complemento necesita permisos para funcionar.</string>
<string name="optional_permission_explanation">Ten que conceder permisos adicionais para activar todas as funcións.</string>
<string name="plugins_need_optional_permission">Algúns complementos teñen funcionalidades desactivadas por mor dunha falta de permisos (toque para máis información):</string>
<string name="share_optional_permission_explanation">Para compartir ficheiros entre o teléfono e o escritorio ten que dar acceso ao almacenamento do teléfono.</string>
<string name="telepathy_permission_explanation">Para ler e escribir SMS desde o escritorio ten que dar permiso de SMS.</string>
<string name="telephony_permission_explanation">Para ver as chamadas de teléfono no escritorio ten que dar permiso aos rexistros de chamadas telefónicas e ao estado do teléfono</string>
<string name="telephony_optional_permission_explanation">Para ver o nome dun contacto en vez dun número de teléfono ten que dar acceso aos contactos do teléfono.</string>

View File

@@ -170,7 +170,6 @@
<string name="permission_explanation">Plugin ini perlu perizinan untuk kerja</string>
<string name="optional_permission_explanation">Kamu perlu mengabulkan perizinan extra untuk memfungsikan semua fungsian</string>
<string name="plugins_need_optional_permission">Beberapa plugin yang memiliki fitur dinonfungsikan karena kurangnya perizinan (ketuk untuk info selebihnya):</string>
<string name="share_optional_permission_explanation">Untuk membagikan file antara teleponmu dan desktopmu kamu harus memberikan akses ke penyimpanan teleponmu</string>
<string name="telepathy_permission_explanation">Untuk membaca dan menulis SMS dari desktopmu kamu harus memberikan perizinan untuk SMS</string>
<string name="telephony_permission_explanation">Untuk melihat panggilan ponsel pada desktop kamu harus memberikan perizinan untuk log panggilan ponsel dan keadaan ponsel</string>
<string name="telephony_optional_permission_explanation">Untuk melihat nama kontak alih-alih nomor ponsel, kamu harus memberikan akses ke kontak ponsel</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Questa estensione ha bisogno di permessi per funzionare</string>
<string name="optional_permission_explanation">Devi concedere permessi aggiuntivi per abilitare tutte le funzioni</string>
<string name="plugins_need_optional_permission">Alcune estensioni hanno funzioni disabilitate per una mancanza di permessi (tocca per maggiori informazioni):</string>
<string name="share_optional_permission_explanation">Per condividere i file tra il telefono e il tuo desktop devi dare accesso alla memoria del telefono</string>
<string name="share_optional_permission_explanation">Per ricevere file condivisi è necessario scegliere una cartella di destinazione</string>
<string name="telepathy_permission_explanation">Per leggere e scrivere SMS dal tuo desktop, devi concedere l\'autorizzazione per SMS</string>
<string name="telephony_permission_explanation">Per vedere le chiamate telefoniche dal desktop devi dare l\'autorizzazione per accedere al registro delle chiamate e allo stato del telefono</string>
<string name="telephony_optional_permission_explanation">Per vedere il nome di un contatto invece del numero di telefono devi dare accesso alla rubrica del telefono</string>

View File

@@ -210,7 +210,6 @@
<string name="permission_explanation">このプラグインが機能するには権限が必要です</string>
<string name="optional_permission_explanation">すべての機能を有効にするには、追加の権限を許可する必要があります</string>
<string name="plugins_need_optional_permission">権限が不足しているため、いくつかのプラグインの機能は無効化されています (タップして詳細情報を表示):</string>
<string name="share_optional_permission_explanation">スマートフォンとデスクトップ間でファイルを共有するには、端末のストレージへのアクセスを許可する必要があります</string>
<string name="telepathy_permission_explanation">デスクトップから SMS を送受信するには SMS の権限を付与する必要があります</string>
<string name="telephony_permission_explanation">デスクトップで通話を表示するために、電話の状態と通話履歴の権限を許可する必要があります</string>
<string name="telephony_optional_permission_explanation">電話番号ではなく連絡先名を見るには、端末の連絡先へのアクセスを許可する必要があります</string>

View File

@@ -218,7 +218,6 @@
<string name="permission_explanation">이 플러그인을 사용하려면 권한이 필요합니다</string>
<string name="optional_permission_explanation">모든 기능을 사용하려면 추가 권한이 필요합니다</string>
<string name="plugins_need_optional_permission">일부 플러그인은 권한이 없어서 비활성화되었습니다(정보를 보려면 누르기):</string>
<string name="share_optional_permission_explanation">휴대폰과 데스크톱간 파일을 공유하려면 휴대폰 저장소 접근 권한이 필요합니다</string>
<string name="telepathy_permission_explanation">데스크톱에서 문자 메시지를 읽고 보내려면 문자 메시지 접근 권한이 필요합니다</string>
<string name="telephony_permission_explanation">데스크톱에서 통화와 문자 메시지를 보려면 통화 기록 및 휴대폰 상태 접근 권한이 필요합니다</string>
<string name="telephony_optional_permission_explanation">전화번호 대신 연락처에 등록된 이름을 보려면 주소록 접근 권한이 필요합니다</string>

View File

@@ -240,7 +240,6 @@
<string name="permission_explanation">Šis papildinys tam, kad veiktų, reikalauja leidimų</string>
<string name="optional_permission_explanation">Norėdami įjungti visas funkcijas, turite suteikti papildomus leidimus</string>
<string name="plugins_need_optional_permission">Kai kurių papildinių ypatybės, dėl leidimų trūkumo, buvo išjungtos (bakstelėkite išsamesnei informacijai):</string>
<string name="share_optional_permission_explanation">Norėdami bendrinti failus tarp savo telefono ir savo darbalaukio, turite suteikti prieigą prie telefono saugyklos</string>
<string name="telepathy_permission_explanation">Norėdami skaityti ir rašyti SMS žinutes iš savo darbalaukio, turite suteikti prieigą prie SMS žinučių</string>
<string name="telephony_permission_explanation">Norėdami matyti telefono skambučius darbalaukyje, turite suteikti prieigą prie telefono skambučių žurnalo ir telefono būsenos</string>
<string name="telephony_optional_permission_explanation">Norėdami vietoj adresato numerio matyti adresato vardą, turite suteikti priegą prie telefono adresatų</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Deze plug-in heeft toestemming nodig om te werken</string>
<string name="optional_permission_explanation">U moet toestemming geven om alle functies in te schakelen</string>
<string name="plugins_need_optional_permission">Sommige plug-ins hebben functies uitgeschakeld vanwege ontbrekende toestemming (tik voor meer informatie):</string>
<string name="share_optional_permission_explanation">Om bestanden tussen uw telefoon en uw bureaublad te delen moet u toegang geven tot de opslag van uw telefoon</string>
<string name="share_optional_permission_explanation">Om gedeelde bestanden te ontvangen moet u een bestemmingsmap kiezen</string>
<string name="telepathy_permission_explanation">Om een SMS te lezen of te schrijven vanaf uw bureaublad moet u toestemming geven tot SMS</string>
<string name="telephony_permission_explanation">Om telefoonoproepen te zien op het bureaublad moet u toestemming geven tot telefoonoproeplogs en telefoonstatus</string>
<string name="telephony_optional_permission_explanation">Om een naam van een contactpersoon te zien in plaats van een telefoonnummer moet u toegang geven tot de contacten in uw telefoon</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Dette tillegget treng utvida løyve for å fungera</string>
<string name="optional_permission_explanation">Du må gje utvida løyve for at alle funksjonane skal fungera</string>
<string name="plugins_need_optional_permission">På grunn av manglande løyve vil desse funksjonane ikkje verka (trykk på dei for meir informasjon):</string>
<string name="share_optional_permission_explanation">For å kunna dela filer mellom telefonen og datamaskina må du gje appen lese- og skriveløyve til lagringsområdet på telefonen</string>
<string name="share_optional_permission_explanation">For å kunna ta imot delte filer må du velja ei målmappe</string>
<string name="telepathy_permission_explanation">For å kunna lesa og skriva tekstmeldingar frå datamaskina må du gje appen tilgang til SMS</string>
<string name="telephony_permission_explanation">For å kunna sjå telefonsamtalar på datamaskina må du gje appen tilgang til samtaleloggar og telefonstatus</string>
<string name="telephony_optional_permission_explanation">For å kunna sjå namn på kontaktar i staden for berre telefonnummeret må du gje appen tilgang til kontaktlista di</string>

View File

@@ -234,7 +234,6 @@
<string name="permission_explanation">Ta wtyczka wymaga uprawnień do działania</string>
<string name="optional_permission_explanation">Musisz przydzielić dodatkowe uprawnienia, aby włączyć wszystkie funkcje</string>
<string name="plugins_need_optional_permission">Niektóre wtyczki mają ograniczone możliwości ze względu na ograniczone uprawnienia (stuknij, aby dowiedzieć się więcej)</string>
<string name="share_optional_permission_explanation">Aby udostępniać pliki z twojego telefonu na twoim komputerze, należy zezwolić na dostęp do pamięci telefonu</string>
<string name="telepathy_permission_explanation">Aby odbierać i wysyłać esemesy z pulpitu, należy nadać uprawnienia do esemesów</string>
<string name="telephony_permission_explanation">Aby widzieć rozmowy telefoniczne na pulpicie, należy nadać uprawnienia do dziennika rozmów i stanu telefonu</string>
<string name="telephony_optional_permission_explanation">Aby widzieć nazwę kontaktu zamiast numeru telefonu, należy zezwolić na dostęp do kontaktów telefonu</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Este plugin precisa de permissões para funcionar</string>
<string name="optional_permission_explanation">Você precisa conceder permissões extras para ativar todas as funções</string>
<string name="plugins_need_optional_permission">Alguns plugins possuem recursos desativados devido à falta de permissões (toque para obter mais informações):</string>
<string name="share_optional_permission_explanation">Para compartilhar arquivos entre o seu celular e o seu ambiente de trabalho é necessário permissão para acessar o armazenamento do seu celular</string>
<string name="share_optional_permission_explanation">Para receber arquivos compartilhados você precisa escolher uma pasta de destino</string>
<string name="telepathy_permission_explanation">Para ler e gravar SMS a partir do seu ambiente de trabalho é necessário conceder permissão para SMS</string>
<string name="telephony_permission_explanation">Para ver as chamadas telefônicas no seu ambiente de trabalho é preciso dar permissões para registro de chamadas telefônicas e do estado do telefone</string>
<string name="telephony_optional_permission_explanation">Para ver o nome de um contato em vez do seu número de telefone é necessário conceder acesso aos contatos do celular</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Este \'plugin\' precisa de permissões para funcionar</string>
<string name="optional_permission_explanation">Precisa de dar permissões extra para activar todas as funcionalidades</string>
<string name="plugins_need_optional_permission">Alguns \'plugins\' têm funcionalidades desactivadas devido à falta de permissões (toque para obter mais informações):</string>
<string name="share_optional_permission_explanation">Para partilhar ficheiros entre o seu telemóvel e o seu ambiente de trabalho, precisa de permissão para aceder ao armazenamento do seu telemóvel</string>
<string name="share_optional_permission_explanation">Para receber os ficheiros partilhados, tem de escolher uma pasta de destino</string>
<string name="telepathy_permission_explanation">Para ler e escrever SMS\'s a partir do seu ambiente de trabalho, precisa de dar permissões para os SMS\'s</string>
<string name="telephony_permission_explanation">Para ver as chamadas telefónicas a partir do seu ambiente de trabalho, precisa de dar permissões para o registo de chamadas telefónicas e do estado do telemóvel</string>
<string name="telephony_optional_permission_explanation">Para ver o nome de um contacto em vez do seu número de telefone, precisa de dar acesso aos contactos do telemóvel</string>

View File

@@ -234,7 +234,6 @@
<string name="permission_explanation">Această extensie are nevoie de permisiuni ca să lucreze</string>
<string name="optional_permission_explanation">Trebuie să acordați permisiuni suplimentare pentru activarea tuturor funcțiilor</string>
<string name="plugins_need_optional_permission">Unele extensii au caracteristici dezactivate din cauza lipsei permisiunilor (atingeți pentru informații suplimentare):</string>
<string name="share_optional_permission_explanation">Pentru a partaja fișiere între telefon și calculator, trebuie să permiteți accesul la stocarea telefonului</string>
<string name="telepathy_permission_explanation">Pentru a citi și scrie SMS de pe calculatorul de birou, trebuie să acordați permisiuni asupra SMS-urilor</string>
<string name="telephony_permission_explanation">Pentru a vedea apelurile pe calculatorul de birou, trebuie să acordați permisiuni asupra jurnalului de apeluri și stării telefonului</string>
<string name="telephony_optional_permission_explanation">Pentru a vedea numele contactului în locul numărului de telefon, trebuie să permiteți accesul la contactele telefonului.</string>

View File

@@ -239,7 +239,6 @@
<string name="permission_explanation">Этому модулю нужны разрешения для работы</string>
<string name="optional_permission_explanation">Необходимо предоставить дополнительные разрешения для включения всех функций</string>
<string name="plugins_need_optional_permission">Некоторые функции модулей отключены из-за отсутствия необходимых разрешений (нажмите для просмотра подробностей):</string>
<string name="share_optional_permission_explanation">Чтобы обмениваться файлами между телефоном и компьютером, необходимо предоставить доступ к встроенной памяти телефона</string>
<string name="telepathy_permission_explanation">Чтобы читать и писать SMS с компьютера, вам необходимо дать разрешение на доступ к SMS</string>
<string name="telephony_permission_explanation">Чтобы видеть телефонные звонки на компьютере, необходимо дать разрешение на доступ к списку звонков и состоянию телефона</string>
<string name="telephony_optional_permission_explanation">Чтобы видеть имя контакта вместо номера телефона, необходимо предоставить доступ к контактам</string>

View File

@@ -242,7 +242,7 @@
<string name="permission_explanation">Tento zásuvný modul vyžaduje oprávnenie, aby mohol fungovať</string>
<string name="optional_permission_explanation">Musíte udeliť oprávnenia navyše, aby boli povolené všetky funkcie</string>
<string name="plugins_need_optional_permission">Niektoré zásuvné moduly majú zakázané funkcie, pretože im chýbajú oprávnenia (ťuknutím získate viac informácií):</string>
<string name="share_optional_permission_explanation">Na zdieľanie súborov medzi vašim telefónom a počítačom, musíte udeliť prístup k úložisku telefónu</string>
<string name="share_optional_permission_explanation">Ak chcete prijímať zdieľané súbory, musíte zvoliť cieľový adresár</string>
<string name="telepathy_permission_explanation">Na čítanie a písanie SMS z vášho počítača, musíte udeliť oprávnenie k správam SMS</string>
<string name="telephony_permission_explanation">Na prezeranie telefónnych hovorov z vášho počítača, musíte udeliť oprávnenie k záznamom hovorov a stavu telefónu</string>
<string name="telephony_optional_permission_explanation">Na prezeranie mien kontaktov namiesto telefónnych čísel, musíte udeliť prístup k telefónnym kontaktom</string>

View File

@@ -242,7 +242,7 @@
<string name="permission_explanation">Ti vtičniki potrebujejo dovoljenja za delovanje</string>
<string name="optional_permission_explanation">Če želite omogočiti vse funkcije, morate podeliti dodatna dovoljenja</string>
<string name="plugins_need_optional_permission">Nekateri vtičniki imajo funkcije onemogočene zaradi pomanjkanja dovoljenj (tapnite za več informacij):</string>
<string name="share_optional_permission_explanation">Za skupno rabo datotek med telefonom in namizjem morate dovoliti dostop v shrambo telefona</string>
<string name="share_optional_permission_explanation">Če želite prejemati datoteke v skupni rabi, morate izbrati ciljni imenik</string>
<string name="telepathy_permission_explanation">Če želite brati in pisati SMS z namizja, morate izdati dovoljenje za SMS</string>
<string name="telephony_permission_explanation">Če želite videti telefonske klice na namizju, morate dati dovoljenje za dnevnike klicev in stanje telefona</string>
<string name="telephony_optional_permission_explanation">Če želite namesto telefonske številke videti ime stika, morate omogočiti dostop do stikov telefona</string>

View File

@@ -226,7 +226,7 @@
<string name="permission_explanation">Insticksprogrammet behöver rättigheter för att fungera</string>
<string name="optional_permission_explanation">Du måste ge extra rättigheter för att aktivera alla funktioner</string>
<string name="plugins_need_optional_permission">Vissa insticksprogram har inaktiverade funktioner på grund av att rättigheter saknas (rör för mer information):</string>
<string name="share_optional_permission_explanation">För att dela filer mellan telefonen och skrivbordet behöver du ge tillgång till telefonens lagringsutrymme</string>
<string name="share_optional_permission_explanation">För att ta emot delade filer måste du välja en målkatalog</string>
<string name="telepathy_permission_explanation">För att läsa och skriva SMS från skrivbordet måste du ge rättigheter för SMS</string>
<string name="telephony_permission_explanation">För att se telefonsamtal på skrivbordet måste du ge rättigheter för telefonsamtalsloggar och telefontillstånd</string>
<string name="telephony_optional_permission_explanation">För att se ett kontaktnamn istället för ett telefonnummer måste du ge tillgång till telefonens kontakter</string>

View File

@@ -217,7 +217,6 @@
<string name="permission_explanation">Bu eklenti, çalışmak için izne ihtiyaç duyuyor</string>
<string name="optional_permission_explanation">Tüm işlevleri etkinleştirmek için daha fazla yetkiye ihtiyacınız var</string>
<string name="plugins_need_optional_permission">Bazı eklentilerin özellikleri, izin yetersizliğinden kapalı gelmektedir (daha fazla bilgi için dokunun):</string>
<string name="share_optional_permission_explanation">Telefon ve masaüstünüz arasında dosya paylaşılabilmesi için, telefonun depolama alanına erişim izni olmalıdır</string>
<string name="telepathy_permission_explanation">Masaüstünde SMS yazma ve okuma yapmak için SMS izni gereklidir</string>
<string name="telephony_permission_explanation">Masaüstünde telefon görüşmelerini görmek için telefon görüşmesi kayıtlarına ve telefon durumuna izin vermeniz gerekir</string>
<string name="telephony_optional_permission_explanation">Telefon numarası yerine kişi ismi görebilmek için telefonun kişilerine erişim gereklidir</string>

View File

@@ -242,7 +242,7 @@
<string name="permission_explanation">Для роботи цього додатка потрібні додаткові права доступу</string>
<string name="optional_permission_explanation">Щоб уможливити використання усіх функцій, вам слід надати програмі додаткові права доступу</string>
<string name="plugins_need_optional_permission">Можливості деяких додатків вимкнено, оскільки програмі не вистачає прав доступу (натисніть, щоб дізнатися більше):</string>
<string name="share_optional_permission_explanation">Щоб спільного використовувати файли на вашому телефоні і робочому комп’ютері, вам слід надати програмі доступ до сховища даних вашого телефону</string>
<string name="share_optional_permission_explanation">Для отримання файлів спільного користування вам слід вибрати каталог призначення</string>
<string name="telepathy_permission_explanation">Щоб читати і писати SMS з вашого робочого комп’ютера, вам слід надати програмі доступ до SMS</string>
<string name="telephony_permission_explanation">Щоб переглядати дзвінки з робочого комп’ютера, вам слід надати програмі доступ до журналу дзвінків та стану телефону</string>
<string name="telephony_optional_permission_explanation">Щоб бачити ім’я контакту замість номеру телефону, вам слід надати програмі доступ до записів контактів на телефоні</string>

View File

@@ -218,7 +218,6 @@
<string name="permission_explanation">这个插件需要权限才能工作</string>
<string name="optional_permission_explanation">您需要授予额外权限以启用全部功能</string>
<string name="plugins_need_optional_permission">因缺少权限,某些插件的一些功能已禁用(点击以查看更多信息):</string>
<string name="share_optional_permission_explanation">您需要给予访问手机存储的权限才能在手机和桌面计算机之间分享文件</string>
<string name="telepathy_permission_explanation">从计算机桌面读取、写入短消息需要向应用程序授予 SMS 权限</string>
<string name="telephony_permission_explanation">要桌面上查看手机通话记录,您需要授予访问通话记录和手机状态的权限</string>
<string name="telephony_optional_permission_explanation">要查看联系人姓名而非电话号码,您需要授予访问手机通讯录的权限</string>

View File

@@ -216,7 +216,6 @@
<string name="permission_explanation">這外掛程式需要權限以運作</string>
<string name="optional_permission_explanation">你需要授予延伸的權限以啟用所有的功能</string>
<string name="plugins_need_optional_permission">部份的外掛程式因為缺乏權限,而導致功能被停用。(按一下以了解更多資訊):</string>
<string name="share_optional_permission_explanation">為了要在您的手機與電腦之間分享檔案,你需要同意存取手機的儲存空間。</string>
<string name="telepathy_permission_explanation">為了要在您的個人電腦上讀取與撰寫簡訊,你需要提供簡訊的權限。</string>
<string name="telephony_permission_explanation">為了要在桌面上檢視手機通話,您需要提供手機通話記錄及手機狀態的權限。</string>
<string name="telephony_optional_permission_explanation">為了要讓聯絡人名稱取代手機號碼,您需要提供手機通訊錄的權限。</string>

View File

@@ -280,7 +280,7 @@
<string name="permission_explanation">This plugin needs permissions to work</string>
<string name="optional_permission_explanation">You need to grant extra permissions to enable all functions</string>
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="share_optional_permission_explanation">To receive shared files you need to chose a destination directory</string>
<string name="share_optional_permission_explanation">To receive shared files you need to choose a destination directory</string>
<string name="telepathy_permission_explanation">To read and write SMS from your desktop you need to give permission to SMS</string>
<string name="telephony_permission_explanation">To see phone calls on the desktop you need to give permission to phone call logs and phone state</string>
<string name="telephony_optional_permission_explanation">To see a contact name instead of a phone number you need to give access to the phone\'s contacts</string>

View File

@@ -26,7 +26,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.klinker.android.send_message.Utils;
import com.google.android.mms.pdu_alt.MultimediaMessagePdu;
import com.google.android.mms.pdu_alt.PduPersister;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
@@ -135,24 +136,26 @@ public class SMSHelper {
@NonNull ThreadID threadID,
@Nullable Long numberToGet
) {
return getMessagesInRange(context, threadID, Long.MAX_VALUE, numberToGet);
return getMessagesInRange(context, threadID, Long.MAX_VALUE, numberToGet, true);
}
/**
* Get some messages in the given thread which have timestamp equal to or after the given timestamp
* Get some messages in the given thread based on a start timestamp and an optional count
*
* @param context android.content.Context running the request
* @param threadID Thread to look up
* @param threadID Optional ThreadID to look up. If not included, this method will return the latest messages from all threads.
* @param startTimestamp Beginning of the range to return
* @param numberToGet Number of messages to return. Pass null for "all"
* @param getMessagesOlderStartTime If true, get messages with timestamps before the startTimestamp. If false, get newer messages
* @return Some messages in the requested conversation
*/
@SuppressLint("NewApi")
public static @NonNull List<Message> getMessagesInRange(
@NonNull Context context,
@NonNull ThreadID threadID,
@Nullable ThreadID threadID,
@NonNull Long startTimestamp,
@Nullable Long numberToGet
@Nullable Long numberToGet,
@NonNull Boolean getMessagesOlderStartTime
) {
// The stickiness with this is that Android's MMS database has its timestamp in epoch *seconds*
// while the SMS database uses epoch *milliseconds*.
@@ -172,15 +175,30 @@ public class SMSHelper {
allMmsColumns.addAll(Arrays.asList(Message.multiSIMColumns));
}
String selection = Message.THREAD_ID + " = ? AND ? >= " + Message.DATE;
String selection;
String[] smsSelectionArgs = new String[] { threadID.toString(), startTimestamp.toString() };
String[] mmsSelectionArgs = new String[] { threadID.toString(), Long.toString(startTimestamp / 1000) };
if (getMessagesOlderStartTime) {
selection = Message.DATE + " <= ?";
} else {
selection = Message.DATE + " >= ?";
}
List<String> smsSelectionArgs = new ArrayList<String>(2);
smsSelectionArgs.add(startTimestamp.toString());
List<String> mmsSelectionArgs = new ArrayList<String>(2);
mmsSelectionArgs.add(Long.toString(startTimestamp / 1000));
if (threadID != null) {
selection += " AND " + Message.THREAD_ID + " = ?";
smsSelectionArgs.add(threadID.toString());
mmsSelectionArgs.add(threadID.toString());
}
String sortOrder = Message.DATE + " DESC";
List<Message> allMessages = getMessages(smsUri, context, allSmsColumns, selection, smsSelectionArgs, sortOrder, numberToGet);
allMessages.addAll(getMessages(mmsUri, context, allMmsColumns, selection, mmsSelectionArgs, sortOrder, numberToGet));
List<Message> allMessages = getMessages(smsUri, context, allSmsColumns, selection, smsSelectionArgs.toArray(new String[0]), sortOrder, numberToGet);
allMessages.addAll(getMessages(mmsUri, context, allMmsColumns, selection, mmsSelectionArgs.toArray(new String[0]), sortOrder, numberToGet));
// Need to now only return the requested number of messages:
// Suppose we were requested to return N values and suppose a user sends only one MMS per
@@ -205,30 +223,6 @@ public class SMSHelper {
return toReturn;
}
/**
* Get the newest sent or received message
*
* This might have some potential for race conditions if many messages are received in a short
* timespan, but my target use-case is humans sending and receiving messages, so I don't think
* it will be an issue
*
* @return null if no matching message is found, otherwise return a Message
*/
public static @Nullable Message getNewestMessage(
@NonNull Context context
) {
List<Message> messages = getMessagesWithFilter(context, null, null, 1L);
if (messages.size() > 1) {
Log.w("SMSHelper", "getNewestMessage asked for one message but got " + messages.size());
}
if (messages.size() < 1) {
return null;
} else {
return messages.get(0);
}
}
/**
* Checks if device supports `Telephony.Sms.SUBSCRIPTION_ID` column in database with URI `uri`
*
@@ -282,14 +276,7 @@ public class SMSHelper {
// Get all the active phone numbers so we can filter the user out of the list of targets
// of any MMSes
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);
}
List<TelephonyHelper.LocalPhoneNumber> userPhoneNumbers = TelephonyHelper.getAllPhoneNumbers(context);
try (Cursor myCursor = context.getContentResolver().query(
uri,
@@ -608,7 +595,7 @@ public class SMSHelper {
private static @NonNull Message parseMMS(
@NonNull Context context,
@NonNull Map<String, String> messageInfo,
@NonNull List<String> userPhoneNumbers
@NonNull List<TelephonyHelper.LocalPhoneNumber> userPhoneNumbers
) {
int event = Message.EVENT_UNKNOWN;
@@ -712,21 +699,24 @@ public class SMSHelper {
}
// Get address(es) of the message
Uri uri = ContentUris.appendId(getMMSUri().buildUpon(), uID).build();
Address from = SmsMmsUtils.getMmsFrom(context, uri);
List<Address> to = SmsMmsUtils.getMmsTo(context, uri);
MultimediaMessagePdu msg = getMessagePdu(context, uID);
Address from = SmsMmsUtils.getMmsFrom(msg);
List<Address> to = SmsMmsUtils.getMmsTo(msg);
List<Address> addresses = new ArrayList<>();
if (from != null) {
if (!userPhoneNumbers.contains(from.toString()) && !from.toString().equals("insert-address-token")) {
boolean isLocalPhoneNumber = userPhoneNumbers.stream().anyMatch(localPhoneNumber -> localPhoneNumber.isMatchingPhoneNumber(from.address));
if (!isLocalPhoneNumber && !from.toString().equals("insert-address-token")) {
addresses.add(from);
}
}
if (to != null) {
for (Address address : to) {
if (!userPhoneNumbers.contains(address.toString()) && !address.toString().equals("insert-address-token")) {
boolean isLocalPhoneNumber = userPhoneNumbers.stream().anyMatch(localPhoneNumber -> localPhoneNumber.isMatchingPhoneNumber(address.address));
if (!isLocalPhoneNumber && !from.toString().equals("insert-address-token")) {
addresses.add(address);
}
}
@@ -760,6 +750,17 @@ public class SMSHelper {
);
}
private static MultimediaMessagePdu getMessagePdu(Context context, long uID) {
Uri uri = ContentUris.appendId(getMMSUri().buildUpon(), uID).build();
try {
return (MultimediaMessagePdu) PduPersister.getPduPersister(context).load(uri);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Get a text part of an MMS message
* Original implementation from https://stackoverflow.com/a/6446831/3723163
@@ -798,26 +799,26 @@ public class SMSHelper {
* Represent an ID used to uniquely identify a message thread
*/
public static class ThreadID {
final Long threadID;
final long threadID;
static final String lookupColumn = Telephony.Sms.THREAD_ID;
public ThreadID(Long threadID) {
public ThreadID(long threadID) {
this.threadID = threadID;
}
@NonNull
public String toString() {
return threadID.toString();
return Long.toString(threadID);
}
@Override
public int hashCode() {
return threadID.hashCode();
return Long.hashCode(threadID);
}
@Override
public boolean equals(Object other) {
return other.getClass().isAssignableFrom(ThreadID.class) && ((ThreadID) other).threadID.equals(this.threadID);
return other.getClass().isAssignableFrom(ThreadID.class) && ((ThreadID) other).threadID == this.threadID;
}
}
@@ -862,7 +863,7 @@ public class SMSHelper {
}
public static class Address {
final String address;
public final String address;
/**
* Address object field names

View File

@@ -25,6 +25,7 @@ import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class TelephonyHelper {
@@ -65,7 +66,7 @@ public class TelephonyHelper {
*
* Note that entries of the returned list might return null if the phone number is not known by the device
*/
public static @NonNull List<String> getAllPhoneNumbers(
public static @NonNull List<LocalPhoneNumber> getAllPhoneNumbers(
@NonNull Context context)
throws SecurityException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
@@ -82,7 +83,7 @@ public class TelephonyHelper {
Log.w(LOGGING_TAG, "Could not get TelephonyManager");
return Collections.emptyList();
}
String phoneNumber = getPhoneNumber(telephonyManager);
LocalPhoneNumber phoneNumber = getPhoneNumber(telephonyManager);
return Collections.singletonList(phoneNumber);
} else {
// Potentially multi-sim case
@@ -99,18 +100,19 @@ public class TelephonyHelper {
Log.w(LOGGING_TAG, "Could not get SubscriptionInfos");
return Collections.emptyList();
}
List<String> phoneNumbers = new ArrayList<>(subscriptionInfos.size());
List<LocalPhoneNumber> phoneNumbers = new ArrayList<>(subscriptionInfos.size());
for (SubscriptionInfo info : subscriptionInfos) {
phoneNumbers.add(info.getNumber());
LocalPhoneNumber thisPhoneNumber = new LocalPhoneNumber(info.getNumber(), info.getSubscriptionId());
phoneNumbers.add(thisPhoneNumber);
}
return phoneNumbers;
return phoneNumbers.stream().filter(localPhoneNumber -> localPhoneNumber.number != null).collect(Collectors.toList());
}
}
/**
* Try to get the phone number to which the TelephonyManager is pinned
*/
public static @Nullable String getPhoneNumber(
public static @Nullable LocalPhoneNumber getPhoneNumber(
@NonNull TelephonyManager telephonyManager)
throws SecurityException {
@SuppressLint("HardwareIds")
@@ -138,7 +140,7 @@ public class TelephonyHelper {
Log.d(LOGGING_TAG, "Discarding " + maybeNumber + " because it does not contain a high enough digit ratio to be a real phone number");
return null;
} else {
return maybeNumber;
return new LocalPhoneNumber(maybeNumber, -1);
}
}
@@ -261,6 +263,31 @@ public class TelephonyHelper {
return false;
}
/**
* Canonicalize a phone number by removing all (valid) non-digit characters
*
* Should be equivalent to SmsHelper::canonicalizePhoneNumber in the C++ implementation
*
* @param phoneNumber The phone number to canonicalize
* @return The canonicalized version of the input phone number
*/
public static String canonicalizePhoneNumber(String phoneNumber)
{
String toReturn = phoneNumber;
toReturn = toReturn.replace(" ", "");
toReturn = toReturn.replace("-", "");
toReturn = toReturn.replace("(", "");
toReturn = toReturn.replace(")", "");
toReturn = toReturn.replace("+", "");
toReturn = toReturn.replaceFirst("^0*", "");
if (toReturn.isEmpty()) {
// If we have stripped away everything, assume this is a special number (and already canonicalized)
return phoneNumber;
}
return toReturn;
}
/**
* Light copy of https://developer.android.com/reference/android/telephony/data/ApnSetting so
* that we can support older API versions. Delete this when API 28 becomes our supported version.
@@ -312,4 +339,63 @@ public class TelephonyHelper {
return mmsProxyPort;
}
}
/**
* Class representing a phone number which is assigned to the current device
*/
public static class LocalPhoneNumber {
/**
* The phone number
*/
public final String number;
/**
* The subscription ID to which this phone number belongs
*/
public final int subscriptionID;
public LocalPhoneNumber(String number, int subscriptionID) {
this.number = number;
this.subscriptionID = subscriptionID;
}
@Override
public String toString() {
return number;
}
/**
* Do some basic fuzzy matching on two phone numbers to determine whether they match
*
* This is roughly equivalent to SmsHelper::isPhoneNumberMatch, but might produce more false negatives
*
* @param potentialMatchingPhoneNumber The phone number to compare to this phone number
* @return True if the phone numbers appear to be the same, false otherwise
*/
public boolean isMatchingPhoneNumber(String potentialMatchingPhoneNumber) {
String mPhoneNumber = canonicalizePhoneNumber(this.number);
String oPhoneNumber = canonicalizePhoneNumber(potentialMatchingPhoneNumber);
if (mPhoneNumber.isEmpty() || oPhoneNumber.isEmpty()) {
// The empty string is not a valid phone number so does not match anything
return false;
}
// To decide if a phone number matches:
// 1. Are they similar lengths? If two numbers are very different, probably one is junk data and should be ignored
// 2. Is one a superset of the other? Phone number digits get more specific the further towards the end of the string,
// so if one phone number ends with the other, it is probably just a more-complete version of the same thing
String longerNumber = mPhoneNumber.length() >= oPhoneNumber.length() ? mPhoneNumber : oPhoneNumber;
String shorterNumber = mPhoneNumber.length() < oPhoneNumber.length() ? mPhoneNumber : oPhoneNumber;
// If the numbers are vastly different in length, assume they are not the same
if (shorterNumber.length() < 0.75 * longerNumber.length()) {
return false;
}
boolean matchingPhoneNumber = longerNumber.endsWith(shorterNumber);
return matchingPhoneNumber;
}
}
}

View File

@@ -27,6 +27,7 @@ import android.widget.ListView;
import org.kde.kdeconnect.UserInterface.ThemeUtil;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.widget.TextViewCompat;
import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect_tp.databinding.ActivityNotificationFilterBinding;
@@ -74,10 +75,10 @@ public class NotificationFilterActivity extends AppCompatActivity {
CheckedTextView checkedTextView = (CheckedTextView) view;
if (position == 0) {
checkedTextView.setText(R.string.all);
checkedTextView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(checkedTextView, null, null, null, null);
} else {
checkedTextView.setText(apps[position - 1].name);
checkedTextView.setCompoundDrawablesWithIntrinsicBounds(apps[position - 1].icon, null, null, null);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(checkedTextView, apps[position - 1].icon, null, null, null);
checkedTextView.setCompoundDrawablePadding((int) (8 * getResources().getDisplayMetrics().density));
}

View File

@@ -44,7 +44,6 @@ 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;
@@ -213,10 +212,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
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

@@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
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.getPreferredApn(context, intent);
delegate.onReceive(context, intent);
}
}

View File

@@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
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;
}
}

View File

@@ -1,163 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
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 it will also show a notification in the status bar.
*/
public class MmsReceivedReceiver extends com.klinker.android.send_message.MmsReceivedReceiver {
private TelephonyHelper.ApnSetting apnSetting = 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);
// 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) {
int subscriptionId = intent.getIntExtra(SUBSCRIPTION_ID, Utils.getDefaultSubscriptionId());
apnSetting = TelephonyHelper.getPreferredApn(context, subscriptionId);
}
/**
* 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 (apnSetting != null) {
String mmscUrl = apnSetting.getMmsc().toString();
String mmsProxy = apnSetting.getMmsProxyAddressAsString();
int mmsPort = apnSetting.getMmsProxyPort();
try {
return new MmscInformation(mmscUrl, mmsProxy, mmsPort);
} catch (Exception e) {
Log.e("MmsReceivedReceiver", "Exception", e);
}
}
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

@@ -1,53 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
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;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
public class MmsSentReceiverImpl 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) {
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

@@ -1,289 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
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

@@ -40,7 +40,6 @@ import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
@@ -240,12 +239,6 @@ public class SMSPlugin extends Plugin {
*/
@Override
public void onChange(boolean selfChange) {
// If the KDE Connect is set as default Sms app
// prevent from reading the latest message in the database before the sentReceivers mark it as sent
if (Utils.isDefaultSmsApp(context)) {
return;
}
sendLatestMessage();
}
@@ -283,20 +276,21 @@ public class SMSPlugin extends Plugin {
mostRecentTimestampLock.unlock();
return;
}
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
List<SMSHelper.Message> messages = SMSHelper.getMessagesInRange(context, null, mostRecentTimestamp, null, false);
if (message == null || message.date <= mostRecentTimestamp) {
// onChange can trigger many times for a single message. Don't make unnecessary noise
mostRecentTimestampLock.unlock();
return;
long newMostRecentTimestamp = mostRecentTimestamp;
for (SMSHelper.Message message : messages) {
if (message == null || message.date <= newMostRecentTimestamp) {
newMostRecentTimestamp = message.date;
}
}
// Update the most recent counter
mostRecentTimestamp = message.date;
mostRecentTimestamp = newMostRecentTimestamp;
mostRecentTimestampLock.unlock();
// Send the alert about the update
device.sendPacket(constructBulkMessagePacket(Collections.singleton(message)));
device.sendPacket(constructBulkMessagePacket(messages));
}
/**
@@ -529,7 +523,7 @@ public class SMSPlugin extends Plugin {
if (rangeStartTimestamp < 0) {
conversation = SMSHelper.getMessagesInThread(this.context, threadID, numberToGet);
} else {
conversation = SMSHelper.getMessagesInRange(this.context, threadID, rangeStartTimestamp, numberToGet);
conversation = SMSHelper.getMessagesInRange(this.context, threadID, rangeStartTimestamp, numberToGet, true);
}
// Sometimes when desktop app is kept open while android app is restarted for any reason

View File

@@ -6,16 +6,27 @@
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import com.android.mms.dom.smil.parser.SmilXmlSerializer;
import com.google.android.mms.ContentType;
import com.google.android.mms.InvalidHeaderValueException;
import com.google.android.mms.MMSPart;
import com.google.android.mms.pdu_alt.CharacterSets;
import com.google.android.mms.pdu_alt.EncodedStringValue;
import com.google.android.mms.pdu_alt.MultimediaMessagePdu;
import com.google.android.mms.pdu_alt.PduPersister;
import com.google.android.mms.pdu_alt.PduBody;
import com.google.android.mms.pdu_alt.PduComposer;
import com.google.android.mms.pdu_alt.PduHeaders;
import com.google.android.mms.pdu_alt.PduPart;
import com.google.android.mms.pdu_alt.RetrieveConf;
import com.google.android.mms.pdu_alt.SendReq;
import com.google.android.mms.smil.SmilHelper;
import com.klinker.android.send_message.Message;
import com.klinker.android.send_message.Settings;
import com.klinker.android.send_message.Transaction;
@@ -27,6 +38,9 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.provider.Telephony;
import android.net.Uri;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
@@ -41,11 +55,15 @@ import org.kde.kdeconnect_tp.R;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Random;
public class SmsMmsUtils {
@@ -68,6 +86,29 @@ public class SmsMmsUtils {
prefs.getString(context.getString(R.string.convert_to_mms_after),
context.getString(R.string.convert_to_mms_after_default)));
TelephonyHelper.LocalPhoneNumber sendingPhoneNumber;
List<TelephonyHelper.LocalPhoneNumber> allPhoneNumbers = TelephonyHelper.getAllPhoneNumbers(context);
Optional<TelephonyHelper.LocalPhoneNumber> maybeSendingPhoneNumber = allPhoneNumbers.stream()
.filter(localPhoneNumber -> localPhoneNumber.subscriptionID == subID)
.findAny();
if (maybeSendingPhoneNumber.isPresent()) {
sendingPhoneNumber = maybeSendingPhoneNumber.get();
} else {
if (allPhoneNumbers.isEmpty()) {
sendingPhoneNumber = null;
} else {
sendingPhoneNumber = allPhoneNumbers.get(0);
}
Log.w(SENDING_MESSAGE, "Unable to get outgoing address for sub ID " + subID + " using " + sendingPhoneNumber);
}
if (sendingPhoneNumber != null) {
// Remove the user's phone number if present in the list of recipients
addressList.removeIf(address -> sendingPhoneNumber.isMatchingPhoneNumber(address.address));
}
try {
Settings settings = new Settings();
TelephonyHelper.ApnSetting apnSettings = TelephonyHelper.getPreferredApn(context, subID);
@@ -80,10 +121,8 @@ public class SmsMmsUtils {
settings.setUseSystemSending(true);
}
if (Utils.isDefaultSmsApp(context)) {
settings.setSendLongAsMms(longTextAsMms);
settings.setSendLongAsMmsAfter(sendLongAsMmsAfter);
}
settings.setSendLongAsMms(longTextAsMms);
settings.setSendLongAsMmsAfter(sendLongAsMmsAfter);
settings.setGroup(groupMessageAsMms);
@@ -92,8 +131,6 @@ public class SmsMmsUtils {
}
Transaction transaction = new Transaction(context, settings);
transaction.setExplicitBroadcastForSentSms(new Intent(context, SmsSentReceiver.class));
transaction.setExplicitBroadcastForSentMms(new Intent(context, MmsSentReceiverImpl.class));
List<String> addresses = new ArrayList<>();
for (SMSHelper.Address address : addressList) {
@@ -101,47 +138,188 @@ public class SmsMmsUtils {
}
Message message = new Message(textMessage, addresses.toArray(ArrayUtils.EMPTY_STRING_ARRAY));
message.setFromAddress(sendingPhoneNumber.number);
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);
}
Log.v("", "Sending new MMS");
//transaction.sendNewMessage(message, Transaction.NO_THREAD_ID);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
sendMmsMessageNative(context, message, settings);
} 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
// Cross fingers and hope Klinker's library works for this case
transaction.sendNewMessage(message, Transaction.NO_THREAD_ID);
}
} else {
com.klinker.android.logger.Log.v(SENDING_MESSAGE, "Sending new SMS");
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);
Log.e(SENDING_MESSAGE, "Exception", e);
}
}
/**
* Returns the Address of the sender of the MMS message.
* @param uri content://mms/msgId/addr
* @param context context in which the method is called.
* @return sender's Address
* Send an MMS message using SmsManager.sendMultimediaMessage
*
* @param context
* @param message
* @param klinkerSettings
*/
public static SMSHelper.Address getMmsFrom(Context context, Uri uri) {
MultimediaMessagePdu msg;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
protected static void sendMmsMessageNative(Context context, Message message, Settings klinkerSettings) {
ArrayList<MMSPart> data = new ArrayList<>();
try {
msg = (MultimediaMessagePdu) PduPersister.getPduPersister(context).load(uri);
} catch (Exception e) {
return null;
for (Message.Part p : message.getParts()) {
MMSPart part = new MMSPart();
if (p.getName() != null) {
part.Name = p.getName();
} else {
part.Name = p.getContentType().split("/")[0];
}
part.MimeType = p.getContentType();
part.Data = p.getMedia();
data.add(part);
}
if (message.getText() != null && !message.getText().equals("")) {
// add text to the end of the part and send
MMSPart part = new MMSPart();
part.Name = "text";
part.MimeType = "text/plain";
part.Data = message.getText().getBytes();
data.add(part);
}
SendReq sendReq = buildPdu(context, message.getFromAddress(), message.getAddresses(), message.getSubject(), data, klinkerSettings);
Bundle configOverrides = new Bundle();
configOverrides.putBoolean(SmsManager.MMS_CONFIG_GROUP_MMS_ENABLED, klinkerSettings.getGroup());
// Write the PDUs to disk so that we can pass them to the SmsManager
final String fileName = "send." + String.valueOf(Math.abs(new Random().nextLong())) + ".dat";
File mSendFile = new File(context.getCacheDir(), fileName);
Uri contentUri = (new Uri.Builder())
.authority(context.getPackageName() + ".MmsFileProvider")
.path(fileName)
.scheme(ContentResolver.SCHEME_CONTENT)
.build();
try (FileOutputStream writer = new FileOutputStream(mSendFile)) {
writer.write(new PduComposer(context, sendReq).make());
} catch (final IOException e)
{
android.util.Log.e(SENDING_MESSAGE, "Error while writing temporary PDU file: ", e);
}
SmsManager mSmsManager;
if (klinkerSettings.getSubscriptionId() < 0)
{
mSmsManager = SmsManager.getDefault();
} else {
mSmsManager = SmsManager.getSmsManagerForSubscriptionId(klinkerSettings.getSubscriptionId());
}
mSmsManager.sendMultimediaMessage(context, contentUri, null, null, null);
}
public static final long DEFAULT_EXPIRY_TIME = 7 * 24 * 60 * 60;
public static final int DEFAULT_PRIORITY = PduHeaders.PRIORITY_NORMAL;
/**
* Copy of the same-name method from https://github.com/klinker41/android-smsmms
*/
private static SendReq buildPdu(Context context, String fromAddress, String[] recipients, String subject,
List<MMSPart> parts, Settings settings) {
final SendReq req = new SendReq();
// From, per spec
req.prepareFromAddress(context, fromAddress, settings.getSubscriptionId());
// To
for (String recipient : recipients) {
req.addTo(new EncodedStringValue(recipient));
}
// Subject
if (!TextUtils.isEmpty(subject)) {
req.setSubject(new EncodedStringValue(subject));
}
// Date
req.setDate(System.currentTimeMillis() / 1000);
// Body
PduBody body = new PduBody();
// Add text part. Always add a smil part for compatibility, without it there
// may be issues on some carriers/client apps
int size = 0;
for (int i = 0; i < parts.size(); i++) {
MMSPart part = parts.get(i);
size += addTextPart(body, part, i);
}
// add a SMIL document for compatibility
ByteArrayOutputStream out = new ByteArrayOutputStream();
SmilXmlSerializer.serialize(SmilHelper.createSmilDocument(body), out);
PduPart smilPart = new PduPart();
smilPart.setContentId("smil".getBytes());
smilPart.setContentLocation("smil.xml".getBytes());
smilPart.setContentType(ContentType.APP_SMIL.getBytes());
smilPart.setData(out.toByteArray());
body.addPart(0, smilPart);
req.setBody(body);
// Message size
req.setMessageSize(size);
// Message class
req.setMessageClass(PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes());
// Expiry
req.setExpiry(DEFAULT_EXPIRY_TIME);
try {
// Priority
req.setPriority(DEFAULT_PRIORITY);
// Delivery report
req.setDeliveryReport(PduHeaders.VALUE_NO);
// Read report
req.setReadReport(PduHeaders.VALUE_NO);
} catch (InvalidHeaderValueException e) {}
return req;
}
/**
* Copy of the same-name method from https://github.com/klinker41/android-smsmms
*/
private static int addTextPart(PduBody pb, MMSPart p, int id) {
String filename = p.Name;
final PduPart part = new PduPart();
// Set Charset if it's a text media.
if (p.MimeType.startsWith("text")) {
part.setCharset(CharacterSets.UTF_8);
}
// Set Content-Type.
part.setContentType(p.MimeType.getBytes());
// Set Content-Location.
part.setContentLocation(filename.getBytes());
int index = filename.lastIndexOf(".");
String contentId = (index == -1) ? filename
: filename.substring(0, index);
part.setContentId(contentId.getBytes());
part.setData(p.Data);
pb.addPart(part);
return part.getData().length;
}
/**
* Returns the Address of the sender of the MMS message.
* @return sender's Address
*/
public static SMSHelper.Address getMmsFrom(MultimediaMessagePdu msg) {
if (msg == null) { return null; }
EncodedStringValue encodedStringValue = msg.getFrom();
SMSHelper.Address from = new SMSHelper.Address(encodedStringValue.getString());
return from;
@@ -149,20 +327,10 @@ public class SmsMmsUtils {
/**
* returns a List of Addresses of all the recipients of a MMS message.
* @param uri content://mms/part_id
* @param context Context in which the method is called.
* @return List of Addresses of all recipients of an MMS message
*/
public static List<SMSHelper.Address> getMmsTo(Context context, Uri uri) {
MultimediaMessagePdu msg;
try {
msg = (MultimediaMessagePdu) PduPersister.getPduPersister(context).load(uri);
} catch (Exception e) {
e.printStackTrace();
return null;
}
public static List<SMSHelper.Address> getMmsTo(MultimediaMessagePdu msg) {
if (msg == null) { return null; }
StringBuilder toBuilder = new StringBuilder();
EncodedStringValue to[] = msg.getTo();

View File

@@ -1,150 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
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.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.provider.Telephony.Sms;
import android.net.Uri;
import android.content.ContentValues;
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";
@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);
}
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

@@ -1,54 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
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;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
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) {
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

@@ -31,6 +31,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.widget.TextViewCompat;
import androidx.preference.PreferenceDialogFragmentCompat;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -130,7 +131,7 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
boolean isClickToSelect = TextUtils.equals(storageLocation.getText(),
getString(R.string.sftp_storage_preference_click_to_select));
storageLocation.setCompoundDrawables(null, null, isClickToSelect ? arrowDropDownDrawable : null, null);
TextViewCompat.setCompoundDrawablesRelative(storageLocation, null, null, isClickToSelect ? arrowDropDownDrawable : null, null);
storageLocation.setEnabled(isClickToSelect);
storageLocation.setFocusable(false);
storageLocation.setFocusableInTouchMode(false);
@@ -156,7 +157,7 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
storageDisplayName.setText(storageInfo.displayName);
}
storageLocation.setCompoundDrawables(null, null, null, null);
TextViewCompat.setCompoundDrawablesRelative(storageLocation, null, null, null, null);
storageLocation.setEnabled(false);
storageLocation.setFocusable(false);
storageLocation.setFocusableInTouchMode(false);
@@ -207,7 +208,7 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
storageInfo = new SftpPlugin.StorageInfo(displayName, uri);
storageLocation.setText(documentId);
storageLocation.setCompoundDrawables(null, null, null, null);
TextViewCompat.setCompoundDrawablesRelative(storageLocation, null, null, null, null);
storageLocation.setError(null);
storageLocation.setEnabled(false);

View File

@@ -22,6 +22,7 @@ import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.widget.TextViewCompat;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
@@ -82,7 +83,7 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Drawable deleteDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.ic_delete);
deviceNameOrIPBackdrop.setCompoundDrawablesWithIntrinsicBounds(deleteDrawable, null, deleteDrawable, null);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(deviceNameOrIPBackdrop, deleteDrawable, null, deleteDrawable, null);
}
deviceNameOrIP.setOnClickListener(v -> callback.onCustomDeviceClicked(customDevices.get(getAdapterPosition())));

View File

@@ -296,44 +296,6 @@ public class DeviceFragment extends Fragment {
dialog.show(getChildFragmentManager(), null);
}
});
// Add a button to the pluginList for setting KDE Connect as default sms app for allowing it to send mms
if (!Utils.isDefaultSmsApp(mActivity)) {
// Check if there are any preferred APN settings available on the device, if not then disable the MMS support
boolean hasApnSettings = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
List<Integer> subIds = TelephonyHelper.getActiveSubscriptionIDs(mActivity);
for (final int subId : subIds) {
if (TelephonyHelper.getPreferredApn(mActivity, subId) != null) {
hasApnSettings = true;
break;
}
}
} else {
if (TelephonyHelper.getPreferredApn(mActivity, 0) != null) {
hasApnSettings = true;
}
}
for (Plugin p : plugins) {
if (p.getPluginKey().equals("SMSPlugin") && hasApnSettings) {
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);