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

Compare commits

..

20 Commits

Author SHA1 Message Date
Albert Vaca Cintora
2190c9cdaa Missed updated changelog 2025-04-06 10:32:38 +02:00
Albert Vaca Cintora
864d44cb5b Release 1.33.3 2025-04-06 10:30:22 +02:00
Albert Vaca Cintora
72e958a891 Allow exporting the logs form the app 2025-04-06 10:24:28 +02:00
Albert Vaca Cintora
d4ab2ca6cf Bump deps 2025-04-06 10:24:14 +02:00
Albert Vaca Cintora
fd51ec7c14 Fix linter warnings 2025-04-05 00:44:08 +02:00
Albert Vaca Cintora
28070954a6 Regenerate device ID if the stored ID is not valid 2025-04-05 00:05:56 +02:00
Albert Vaca Cintora
e10f2496de Simplify running git in gradle 2025-04-04 14:06:20 +02:00
Albert Vaca Cintora
95b4c08605 Bump deps 2025-04-04 12:15:11 +02:00
l10n daemon script
51d4de34c4 GIT_SILENT made messages (after extraction) 2025-04-04 02:01:41 +00:00
l10n daemon script
de2001bbe1 GIT_SILENT made messages (after extraction) 2025-03-31 01:55:34 +00:00
Albert Vaca Cintora
9c80cb9a40 Remove old code that used Android IDs as device IDs 2025-03-30 21:01:32 +02:00
Albert Vaca Cintora
0b03a66c37 Generate IDs with only alphanumeric values
As per https://invent.kde.org/network/kdeconnect-meta/-/merge_requests/13
2025-03-30 20:59:38 +02:00
l10n daemon script
6d66d69820 GIT_SILENT made messages (after extraction) 2025-03-27 01:58:41 +00:00
Albert Vaca Cintora
c0fc19baaa Bump version of classindexksp 2025-03-18 12:56:45 +01:00
Albert Vaca Cintora
03ea5eae4c Fix NPE 2025-03-17 14:03:15 +01:00
l10n daemon script
b373c28cdd GIT_SILENT made messages (after extraction) 2025-03-16 01:58:31 +00:00
Albert Vaca Cintora
6c8d22b1ed Release 1.33.2 2025-03-11 17:17:44 +01:00
Albert Vaca Cintora
69adfbfbc2 Do the kdeconnect handshake in a new thread
Some Android versions seem to hang if calling sslSocket.getOutputStream()
from within the HandshakeCompleted callback (maybe because calling it in
on a socket that hasn't finished the SSL handshake is supposed to trigger
the SSL handshake).

BUG: 501241
2025-03-11 17:14:36 +01:00
Albert Vaca Cintora
f80e29538a Do not use BufferedReader to read from socket
Reading the docs, BufferedReader maybe could read and cache more than one
line from the socket, and since we discarded the BufferedReader and
created a new one (up to three times), data could be lost.
2025-03-11 12:44:33 +01:00
l10n daemon script
56dda889d1 GIT_SILENT made messages (after extraction) 2025-03-08 02:05:24 +00:00
64 changed files with 335 additions and 261 deletions

View File

@@ -28,23 +28,10 @@ plugins {
val licenseResDir = File("$projectDir/build/dependency-license-res") val licenseResDir = File("$projectDir/build/dependency-license-res")
fun String.runCommand( val hashProvider = project.providers.exec {
workingDir: File = File("."), workingDir = rootDir
timeoutAmount: Long = 60, commandLine("git", "rev-parse", "--short", "HEAD")
timeoutUnit: TimeUnit = TimeUnit.SECONDS }.standardOutput.asText.map { it.trim() }
): String = ProcessBuilder(split("\\s(?=(?:[^'\"`]*(['\"`])[^'\"`]*\\1)*[^'\"`]*$)".toRegex()))
.directory(workingDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
.apply { waitFor(timeoutAmount, timeoutUnit) }
.run {
val error = errorStream.bufferedReader().readText().trim()
if (error.isNotEmpty()) {
throw Exception(error)
}
inputStream.bufferedReader().readText().trim()
}
android { android {
namespace = "org.kde.kdeconnect_tp" namespace = "org.kde.kdeconnect_tp"
@@ -53,8 +40,8 @@ android {
applicationId = "org.kde.kdeconnect_tp" applicationId = "org.kde.kdeconnect_tp"
minSdk = 21 minSdk = 21
targetSdk = 35 targetSdk = 35
versionCode = 13301 versionCode = 13303
versionName = "1.33.1" versionName = "1.33.3"
proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro")
} }
buildFeatures { buildFeatures {
@@ -134,8 +121,7 @@ android {
// Default output filename is "${project.name}-${v.name}.apk". We want // Default output filename is "${project.name}-${v.name}.apk". We want
// the Git commit short-hash to be added onto that default filename. // the Git commit short-hash to be added onto that default filename.
try { try {
val hash = "git rev-parse --short HEAD".runCommand(workingDir = rootDir) val newName = "${project.name}-${variant.name}-${hashProvider.get()}.apk"
val newName = "${project.name}-${variant.name}-${hash}.apk"
logger.quiet(" Found an output file ${output.outputFile.name}, renaming to $newName") logger.quiet(" Found an output file ${output.outputFile.name}, renaming to $newName")
output.outputFileName = newName output.outputFileName = newName
} catch (ignored: Exception) { } catch (ignored: Exception) {

View File

@@ -0,0 +1,12 @@
1.33.2
* Fix connection issues on some devices
1.33.1
* Fix compatibility with GSConnect
1.33.0
* Add support for PeerTube links
* Allow filtering notifications from work profile
* Fix bug where devices would unpair without user interaction
* Verification key now changes every second (only if both devices support it)
* Fix crashes

View File

@@ -0,0 +1,16 @@
1.33.3
* Fix more connection issues. Pairing again might be needed in some setups.
* Add a setting to export the application logs.
1.33.2
* Fix connection issues on some devices.
1.33.1
* Fix compatibility with GSConnect.
1.33.0
* Add support for PeerTube links.
* Allow filtering notifications from work profile.
* Fix bug where devices would unpair without user interaction.
* Verification key now changes every second (only if both devices support it).
* Fix crashes.

View File

@@ -1,24 +1,24 @@
[versions] [versions]
activityCompose = "1.10.1" activityCompose = "1.10.1"
androidDesugarJdkLibs = "2.1.5" androidDesugarJdkLibs = "2.1.5"
androidGradlePlugin = "8.9.0" androidGradlePlugin = "8.9.1"
androidSmsmms = "kdeconnect-1-21-0" androidSmsmms = "kdeconnect-1-21-0"
appcompat = "1.7.0" appcompat = "1.7.0"
bcpkixJdk15on = "1.70" bcpkixJdk15on = "1.70"
classindexksp = "1.1" classindexksp = "1.2"
commonsCollections4 = "4.4" commonsCollections4 = "4.4"
commonsIo = "2.18.0" commonsIo = "2.18.0"
commonsLang3 = "3.17.0" commonsLang3 = "3.17.0"
constraintlayoutCompose = "1.1.1" constraintlayoutCompose = "1.1.1"
coreKtx = "1.15.0" coreKtx = "1.15.0"
dependencyLicenseReport = "2.7" dependencyLicenseReport = "2.9"
disklrucache = "2.0.2" disklrucache = "2.0.2"
documentfile = "1.0.1" documentfile = "1.0.1"
gridlayout = "1.0.0" gridlayout = "1.0.0"
jsonassert = "1.5.3" jsonassert = "1.5.3"
junit = "4.13.2" junit = "4.13.2"
kotlin = "2.1.10" kotlin = "2.1.20"
kspPlugin = "2.1.10-1.0.30" kspPlugin = "2.1.20-1.0.32"
kotlinxCoroutinesCore = "1.10.1" kotlinxCoroutinesCore = "1.10.1"
lifecycleExtensions = "2.2.0" lifecycleExtensions = "2.2.0"
lifecycleRuntimeKtx = "2.8.7" lifecycleRuntimeKtx = "2.8.7"
@@ -27,7 +27,7 @@ material = "1.12.0"
material3 = "1.3.1" material3 = "1.3.1"
media = "1.7.0" media = "1.7.0"
minaCore = "2.2.4" minaCore = "2.2.4"
mockitoCore = "5.16.0" mockitoCore = "5.17.0"
preferenceKtx = "1.2.1" preferenceKtx = "1.2.1"
reactiveStreams = "1.0.4" reactiveStreams = "1.0.4"
recyclerview = "1.4.0" recyclerview = "1.4.0"

View File

@@ -17,7 +17,7 @@
<string name="pref_plugin_clipboard_desc">Kunhavigi la enhavon de la tondujo</string> <string name="pref_plugin_clipboard_desc">Kunhavigi la enhavon de la tondujo</string>
<string name="pref_plugin_clipboard_sent">Clipboard Sendita</string> <string name="pref_plugin_clipboard_sent">Clipboard Sendita</string>
<string name="pref_plugin_mousepad">Fora enigo</string> <string name="pref_plugin_mousepad">Fora enigo</string>
<string name="pref_plugin_mousepad_desc">Uzu vian telefonon aŭ tablojdon kiel tuŝplaton kaj klavaron</string> <string name="pref_plugin_mousepad_desc">Uzu vian telefonon aŭ tabuleton kiel tuŝplaton kaj klavaron</string>
<string name="pref_plugin_presenter">Prezento fora</string> <string name="pref_plugin_presenter">Prezento fora</string>
<string name="pref_plugin_presenter_desc">Uzu vian aparaton por ŝanĝi lumbildojn en prezento</string> <string name="pref_plugin_presenter_desc">Uzu vian aparaton por ŝanĝi lumbildojn en prezento</string>
<string name="pref_plugin_remotekeyboard">Ricevi forajn klavojn</string> <string name="pref_plugin_remotekeyboard">Ricevi forajn klavojn</string>
@@ -25,7 +25,7 @@
<string name="pref_plugin_mpris">Plurmedia regiloj</string> <string name="pref_plugin_mpris">Plurmedia regiloj</string>
<string name="pref_plugin_mpris_desc">Provizas teleregilon por via plurmedia ludilo</string> <string name="pref_plugin_mpris_desc">Provizas teleregilon por via plurmedia ludilo</string>
<string name="pref_plugin_runcommand">Lanĉi Komandon</string> <string name="pref_plugin_runcommand">Lanĉi Komandon</string>
<string name="pref_plugin_runcommand_desc">Ekigi forajn komandojn de via telefono aŭ tablojdo</string> <string name="pref_plugin_runcommand_desc">Ekigi forajn komandojn de via telefono aŭ tabuleto</string>
<string name="pref_plugin_contacts">Sinkronigilo de Kontaktoj</string> <string name="pref_plugin_contacts">Sinkronigilo de Kontaktoj</string>
<string name="pref_plugin_contacts_desc">Permesi sinkronigi la kontaktlibron de la aparato</string> <string name="pref_plugin_contacts_desc">Permesi sinkronigi la kontaktlibron de la aparato</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
@@ -115,6 +115,7 @@
<string name="error_not_reachable">Aparato ne atingebla</string> <string name="error_not_reachable">Aparato ne atingebla</string>
<string name="error_already_paired">Aparato jam parigita</string> <string name="error_already_paired">Aparato jam parigita</string>
<string name="error_timed_out">Tempo elĉerpita</string> <string name="error_timed_out">Tempo elĉerpita</string>
<string name="error_clocks_not_match">Aparataj horloĝoj estas malsinkronaj</string>
<string name="error_canceled_by_user">Nuligite de uzanto</string> <string name="error_canceled_by_user">Nuligite de uzanto</string>
<string name="error_canceled_by_other_peer">Nuligite de alia kunulo</string> <string name="error_canceled_by_other_peer">Nuligite de alia kunulo</string>
<string name="encryption_info_title">Ĉifrada Informo</string> <string name="encryption_info_title">Ĉifrada Informo</string>
@@ -192,11 +193,13 @@
<string name="share_to">Kunhavigi al…</string> <string name="share_to">Kunhavigi al…</string>
<string name="unreachable_device">%s (Neatingebla)</string> <string name="unreachable_device">%s (Neatingebla)</string>
<string name="unreachable_device_url_share_text">URL-oj kundividitaj al neatingebla aparato estos liveritaj al ĝi post kiam ĝi fariĝos atingebla.\n\n</string> <string name="unreachable_device_url_share_text">URL-oj kundividitaj al neatingebla aparato estos liveritaj al ĝi post kiam ĝi fariĝos atingebla.\n\n</string>
<string name="protocol_version">Protokolversio:</string>
<string name="protocol_version_newer">Ĉi tiu aparato uzas pli novan protokolversion</string> <string name="protocol_version_newer">Ĉi tiu aparato uzas pli novan protokolversion</string>
<string name="plugin_settings_with_name">%s agordoj</string> <string name="plugin_settings_with_name">%s agordoj</string>
<string name="invalid_device_name">Nevalida aparato nomo</string> <string name="invalid_device_name">Nevalida aparato nomo</string>
<string name="shareplugin_text_saved">Ricevita teksto, konservita en tondujo</string> <string name="shareplugin_text_saved">Ricevita teksto, konservita en tondujo</string>
<string name="custom_devices_settings">Propra aparato listo</string> <string name="custom_devices_settings">Propra aparato listo</string>
<string name="custom_devices_settings_summary">%d aparatoj aldoniĝis permane</string>
<string name="custom_device_list">Aldoni aparatojn per IP</string> <string name="custom_device_list">Aldoni aparatojn per IP</string>
<string name="custom_device_deleted">Propra aparato forigita</string> <string name="custom_device_deleted">Propra aparato forigita</string>
<string name="custom_device_list_help">Se via aparato ne estas aŭtomate detektita, vi povas aldoni ĝian IP-adreson aŭ gastigan nomon alklakante la Ŝveban Ago-Butonon</string> <string name="custom_device_list_help">Se via aparato ne estas aŭtomate detektita, vi povas aldoni ĝian IP-adreson aŭ gastigan nomon alklakante la Ŝveban Ago-Butonon</string>
@@ -326,6 +329,7 @@
<string name="empty_trusted_networks_list_text">Vi ankoraŭ ne aldonis neniun fidindan reton</string> <string name="empty_trusted_networks_list_text">Vi ankoraŭ ne aldonis neniun fidindan reton</string>
<string name="allow_all_networks_text">Permesi ĉion</string> <string name="allow_all_networks_text">Permesi ĉion</string>
<string name="location_permission_needed_title">Permeso bezonata</string> <string name="location_permission_needed_title">Permeso bezonata</string>
<string name="bluetooth_permission_needed_desc">KDE Connect bezonas permeson por konekti al proksimaj aparatoj por fari aparatojn parigitaj per Bluetooth disponebla en KDE Connect.</string>
<string name="location_permission_needed_desc">KDE Connect bezonas la fonlokan permeson por scii la WiFi-reton al kiu vi estas konektita eĉ kiam la programo estas en la fono. Ĉi tio estas ĉar la nomo de la WiFi-retoj ĉirkaŭ vi povus esti uzata por trovi vian lokon, eĉ kiam tion ne faras KDE Connect.</string> <string name="location_permission_needed_desc">KDE Connect bezonas la fonlokan permeson por scii la WiFi-reton al kiu vi estas konektita eĉ kiam la programo estas en la fono. Ĉi tio estas ĉar la nomo de la WiFi-retoj ĉirkaŭ vi povus esti uzata por trovi vian lokon, eĉ kiam tion ne faras KDE Connect.</string>
<string name="clipboard_android_x_incompat">Android 10 forigis aliron al tondujo al ĉiuj aplikaĵoj. Ĉi tiu kromaĵo estos malŝaltita.</string> <string name="clipboard_android_x_incompat">Android 10 forigis aliron al tondujo al ĉiuj aplikaĵoj. Ĉi tiu kromaĵo estos malŝaltita.</string>
<string name="mpris_open_url">Daŭre ludi ĉi tie</string> <string name="mpris_open_url">Daŭre ludi ĉi tie</string>
@@ -411,6 +415,7 @@
<string name="tap_to_execute">Frapi por plenumi</string> <string name="tap_to_execute">Frapi por plenumi</string>
<string name="plugin_stats">Statistiko de kromprogramoj</string> <string name="plugin_stats">Statistiko de kromprogramoj</string>
<string name="enable_udp_broadcast">Ebligi UDP-aparatan malkovron</string> <string name="enable_udp_broadcast">Ebligi UDP-aparatan malkovron</string>
<string name="enable_bluetooth">Ŝalti bluetooth (beta)</string>
<string name="receive_notifications_permission_explanation">Sciigoj devas esti permesitaj ricevi ilin de aliaj aparatoj</string> <string name="receive_notifications_permission_explanation">Sciigoj devas esti permesitaj ricevi ilin de aliaj aparatoj</string>
<string name="findmyphone_notifications_explanation">La sciiga permeso estas necesa por ke la telefono povu sonori kiam la app estas en la fono</string> <string name="findmyphone_notifications_explanation">La sciiga permeso estas necesa por ke la telefono povu sonori kiam la app estas en la fono</string>
<string name="no_notifications">Sciigoj estas malŝaltitaj, vi ne ricevos alvenantajn parajn sciigojn.</string> <string name="no_notifications">Sciigoj estas malŝaltitaj, vi ne ricevos alvenantajn parajn sciigojn.</string>

View File

@@ -115,6 +115,7 @@
<string name="error_not_reachable">Laite tavoittamattomissa</string> <string name="error_not_reachable">Laite tavoittamattomissa</string>
<string name="error_already_paired">Laite on jo kytketty pariksi</string> <string name="error_already_paired">Laite on jo kytketty pariksi</string>
<string name="error_timed_out">Aikakatkaisu</string> <string name="error_timed_out">Aikakatkaisu</string>
<string name="error_clocks_not_match">Laitteiden kelloja ei ole tahdistettu</string>
<string name="error_canceled_by_user">Käyttäjä perui</string> <string name="error_canceled_by_user">Käyttäjä perui</string>
<string name="error_canceled_by_other_peer">Vertaiskäyttäjä perui</string> <string name="error_canceled_by_other_peer">Vertaiskäyttäjä perui</string>
<string name="encryption_info_title">Salaustiedot</string> <string name="encryption_info_title">Salaustiedot</string>
@@ -192,6 +193,7 @@
<string name="share_to">Jaa…</string> <string name="share_to">Jaa…</string>
<string name="unreachable_device">%s (tavoittamattomissa)</string> <string name="unreachable_device">%s (tavoittamattomissa)</string>
<string name="unreachable_device_url_share_text">Tavoittamattomissa olevalle laitteelle jaetut verkko-osoitteet välitetään heti kun laite tavoitetaan.\n\n</string> <string name="unreachable_device_url_share_text">Tavoittamattomissa olevalle laitteelle jaetut verkko-osoitteet välitetään heti kun laite tavoitetaan.\n\n</string>
<string name="protocol_version">Yhteyskäytäntöversio:</string>
<string name="protocol_version_newer">Laite käyttää uudempaa yhteyskäytäntöversiota</string> <string name="protocol_version_newer">Laite käyttää uudempaa yhteyskäytäntöversiota</string>
<string name="plugin_settings_with_name">%s-asetukset</string> <string name="plugin_settings_with_name">%s-asetukset</string>
<string name="invalid_device_name">Virheellinen laitenimi</string> <string name="invalid_device_name">Virheellinen laitenimi</string>

View File

@@ -115,6 +115,7 @@
<string name="error_not_reachable">Urządzenie nieosiągalne</string> <string name="error_not_reachable">Urządzenie nieosiągalne</string>
<string name="error_already_paired">Urządzenie już sparowano</string> <string name="error_already_paired">Urządzenie już sparowano</string>
<string name="error_timed_out">Upłynął czas na odpowiedź</string> <string name="error_timed_out">Upłynął czas na odpowiedź</string>
<string name="error_clocks_not_match">Zegary urządzenia nie są zsynchronizowane</string>
<string name="error_canceled_by_user">Użytkownik zaniechał</string> <string name="error_canceled_by_user">Użytkownik zaniechał</string>
<string name="error_canceled_by_other_peer">Inny uczestnik zaniechał</string> <string name="error_canceled_by_other_peer">Inny uczestnik zaniechał</string>
<string name="encryption_info_title">Dane o szyfrowaniu</string> <string name="encryption_info_title">Dane o szyfrowaniu</string>
@@ -208,6 +209,7 @@
<string name="share_to">Udostępnij urządzeniu...</string> <string name="share_to">Udostępnij urządzeniu...</string>
<string name="unreachable_device">%s (nieosiągalne)</string> <string name="unreachable_device">%s (nieosiągalne)</string>
<string name="unreachable_device_url_share_text">Adres URL udostępniony nieosiągalnemu urządzeniu zostanie do niego dostarczony zaraz po tym jak stanie się osiągalne.\n\n</string> <string name="unreachable_device_url_share_text">Adres URL udostępniony nieosiągalnemu urządzeniu zostanie do niego dostarczony zaraz po tym jak stanie się osiągalne.\n\n</string>
<string name="protocol_version">Wersja protokołu:</string>
<string name="protocol_version_newer">Urządzenie to używa nowszej wersji protokołu</string> <string name="protocol_version_newer">Urządzenie to używa nowszej wersji protokołu</string>
<string name="plugin_settings_with_name">Ustawienia %s</string> <string name="plugin_settings_with_name">Ustawienia %s</string>
<string name="invalid_device_name">Nieprawidłowa nazwa urządzenia</string> <string name="invalid_device_name">Nieprawidłowa nazwa urządzenia</string>

View File

@@ -399,7 +399,7 @@
<string name="about_kde_report_bugs_or_wishes">&lt;h1&gt;Hataları veya İsteklerinizi Bildirin&lt;/h1&gt; &lt;p&gt;Yazılımlar her zaman iyileştirilebilir ve KDE takımın bunu yapmaya hazırdır. Ancak siz de bir şey beklendiği gibi gitmezse veya hata verirse bize bildirin.&lt;/p&gt; &lt;p&gt;KDEnin bir hata takip sistemi vardır. &lt;a href=https://bugs.kde.org/&gt;https://bugs.kde.org/&lt;/a&gt; adresini ziyaret edin veya hakkında ekranının Hata Bildir düğmesini kullanarak hataları bildirin.&lt;/p&gt; Bir iyileştirme için öneriniz varsa bunu bildirmek için hata takip sistemini kullanabilirsiniz; yalnızca “Wishlist” önceliğini kullandığınızdan emin olun.</string> <string name="about_kde_report_bugs_or_wishes">&lt;h1&gt;Hataları veya İsteklerinizi Bildirin&lt;/h1&gt; &lt;p&gt;Yazılımlar her zaman iyileştirilebilir ve KDE takımın bunu yapmaya hazırdır. Ancak siz de bir şey beklendiği gibi gitmezse veya hata verirse bize bildirin.&lt;/p&gt; &lt;p&gt;KDEnin bir hata takip sistemi vardır. &lt;a href=https://bugs.kde.org/&gt;https://bugs.kde.org/&lt;/a&gt; adresini ziyaret edin veya hakkında ekranının Hata Bildir düğmesini kullanarak hataları bildirin.&lt;/p&gt; Bir iyileştirme için öneriniz varsa bunu bildirmek için hata takip sistemini kullanabilirsiniz; yalnızca “Wishlist” önceliğini kullandığınızdan emin olun.</string>
<string name="about_kde_join_kde">"&lt;h1&gt;KDEye Katılın&lt;/h1&gt; &lt;p&gt;KDE takımının bir üyesi olmak için yazılım geliştirici olmanıza gerek yoktur. Program arayüzlerini çeviren dil takımlarına katılabilirsiniz. Grafikler, temalar, sesler ve iyileştirilmiş belgelendirme sağlayabilirsiniz. Karar sizin!&lt;/p&gt; &lt;p&gt;Katılabileceğiniz bazı projeler hakkında bilgi almak için &lt;a href=https://community.kde.org/Get_Involved&gt;https://community.kde.org/Get_Involved&lt;/a&gt; sayfasını ziyaret edin.&lt;/p&gt; Daha fazla bilgiye veya belgeye gereksiniminiz varsa &lt;a href=https://techbase.kde.org/&gt;https://techbase.kde.org/&lt;/a&gt; sayfasında aradığınızı bulabilirsiniz."</string> <string name="about_kde_join_kde">"&lt;h1&gt;KDEye Katılın&lt;/h1&gt; &lt;p&gt;KDE takımının bir üyesi olmak için yazılım geliştirici olmanıza gerek yoktur. Program arayüzlerini çeviren dil takımlarına katılabilirsiniz. Grafikler, temalar, sesler ve iyileştirilmiş belgelendirme sağlayabilirsiniz. Karar sizin!&lt;/p&gt; &lt;p&gt;Katılabileceğiniz bazı projeler hakkında bilgi almak için &lt;a href=https://community.kde.org/Get_Involved&gt;https://community.kde.org/Get_Involved&lt;/a&gt; sayfasını ziyaret edin.&lt;/p&gt; Daha fazla bilgiye veya belgeye gereksiniminiz varsa &lt;a href=https://techbase.kde.org/&gt;https://techbase.kde.org/&lt;/a&gt; sayfasında aradığınızı bulabilirsiniz."</string>
<string name="about_kde_support_kde">"&lt;h1&gt;KDEyi Destekleyin&lt;/h1&gt; &lt;p&gt;KDE yazılımları her zaman ücretsiz kalmayı sürdürecektir; ancak bunu oluşturmak bedava değildir. &lt;/p&gt; &lt;p&gt;Geliştirmeyi desteklemek için KDE topluluğu, kar amacı gütmeyen bir kuruluş olan KDE e.V.yi kurmuştur, bu topluluk KDE topluğunu yasal ve finansal konularda temsil eder. KDE e.V. hakkında daha fazla bilgi için &lt;a href=https://ev.kde.org/&gt;https://ev.kde.org/&lt;/a&gt; adresini ziyaret edin.&lt;/p&gt; &lt;p&gt;KDE, finansal da dahil olmak üzere her türlü katkıdan yarar sağlar. Maddi kaynaklarımızla, geliştiricilerimizin ve diğerlerinin katkıda bulunurken oluşan masraflarını karşılıyoruz. Ayrıca yasal destek ve konferanslar ve toplantılar için de kullanılmaktadır.&lt;/p&gt; &lt;p&gt;Emeklerimizi, finansal destekle desteklemeniz için &lt;a href=https://www.kde.org/community/donations/&gt;https://www.kde.org/community/donations/&lt;/a&gt; adresinde bulunan yollardan birini kullanabilirsiniz.&lt;/p&gt; Desteğiniz için şimdiden teşekkürler."</string> <string name="about_kde_support_kde">"&lt;h1&gt;KDEyi Destekleyin&lt;/h1&gt; &lt;p&gt;KDE yazılımları her zaman ücretsiz kalmayı sürdürecektir; ancak bunu oluşturmak bedava değildir. &lt;/p&gt; &lt;p&gt;Geliştirmeyi desteklemek için KDE topluluğu, kar amacı gütmeyen bir kuruluş olan KDE e.V.yi kurmuştur, bu topluluk KDE topluğunu yasal ve finansal konularda temsil eder. KDE e.V. hakkında daha fazla bilgi için &lt;a href=https://ev.kde.org/&gt;https://ev.kde.org/&lt;/a&gt; adresini ziyaret edin.&lt;/p&gt; &lt;p&gt;KDE, finansal da dahil olmak üzere her türlü katkıdan yarar sağlar. Maddi kaynaklarımızla, geliştiricilerimizin ve diğerlerinin katkıda bulunurken oluşan masraflarını karşılıyoruz. Ayrıca yasal destek ve konferanslar ve toplantılar için de kullanılmaktadır.&lt;/p&gt; &lt;p&gt;Emeklerimizi, finansal destekle desteklemeniz için &lt;a href=https://www.kde.org/community/donations/&gt;https://www.kde.org/community/donations/&lt;/a&gt; adresinde bulunan yollardan birini kullanabilirsiniz.&lt;/p&gt; Desteğiniz için şimdiden teşekkürler."</string>
<string name="maintainer_and_developer">Projeyi sürdüren ve geliştirici</string> <string name="maintainer_and_developer">Bakımcı ve geliştirici</string>
<string name="developer">Geliştirici</string> <string name="developer">Geliştirici</string>
<string name="apple_support">macOS ve iOS desteği üzerinde çalışılmaktadır.</string> <string name="apple_support">macOS ve iOS desteği üzerinde çalışılmaktadır.</string>
<string name="bug_fixes_and_general_improvements">Hata düzeltmeleri ve genel iyileştirmeler</string> <string name="bug_fixes_and_general_improvements">Hata düzeltmeleri ve genel iyileştirmeler</string>

View File

@@ -115,6 +115,7 @@
<string name="error_not_reachable">裝置無法存取</string> <string name="error_not_reachable">裝置無法存取</string>
<string name="error_already_paired">裝置已經配對</string> <string name="error_already_paired">裝置已經配對</string>
<string name="error_timed_out">逾時</string> <string name="error_timed_out">逾時</string>
<string name="error_clocks_not_match">裝置時鐘不同步</string>
<string name="error_canceled_by_user">使用者中斷</string> <string name="error_canceled_by_user">使用者中斷</string>
<string name="error_canceled_by_other_peer">被其他同等功能應用中斷</string> <string name="error_canceled_by_other_peer">被其他同等功能應用中斷</string>
<string name="encryption_info_title">加密資訊</string> <string name="encryption_info_title">加密資訊</string>
@@ -184,6 +185,7 @@
<string name="share_to">分享給…</string> <string name="share_to">分享給…</string>
<string name="unreachable_device">%s無法存取</string> <string name="unreachable_device">%s無法存取</string>
<string name="unreachable_device_url_share_text">若分享網址 (URL) 到無法存取的裝置,將在該裝置變得可存取後再傳送過去。\n\n</string> <string name="unreachable_device_url_share_text">若分享網址 (URL) 到無法存取的裝置,將在該裝置變得可存取後再傳送過去。\n\n</string>
<string name="protocol_version">協定版本:</string>
<string name="protocol_version_newer">此裝置使用較新的通訊協定版本</string> <string name="protocol_version_newer">此裝置使用較新的通訊協定版本</string>
<string name="plugin_settings_with_name">%s的設定</string> <string name="plugin_settings_with_name">%s的設定</string>
<string name="invalid_device_name">無效的裝置名稱</string> <string name="invalid_device_name">無效的裝置名稱</string>

View File

@@ -401,6 +401,9 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
<string name="settings_rename">Device name</string> <string name="settings_rename">Device name</string>
<string name="settings_dark_mode">Dark theme</string> <string name="settings_dark_mode">Dark theme</string>
<string name="settings_export_logs">Export KDE Connect logs</string>
<string name="settings_export_logs_text">Generate a file with execution information that can help troubleshoot issues.</string>
<string name="settings_more_settings_title">More settings</string> <string name="settings_more_settings_title">More settings</string>
<string name="settings_more_settings_text">Per-device settings can be found under \'Plugin settings\' from within a device.</string> <string name="settings_more_settings_text">Per-device settings can be found under \'Plugin settings\' from within a device.</string>
<string name="setting_persistent_notification">Show persistent notification</string> <string name="setting_persistent_notification">Show persistent notification</string>

View File

@@ -24,13 +24,16 @@ import java.io.Reader
import java.util.UUID import java.util.UUID
import kotlin.text.Charsets.UTF_8 import kotlin.text.Charsets.UTF_8
class BluetoothLink(context: Context?, connection: ConnectionMultiplexer, input: InputStream, output: OutputStream, remoteAddress: BluetoothDevice, deviceInfo: DeviceInfo, linkProvider: BluetoothLinkProvider) : BaseLink(context!!, linkProvider) { class BluetoothLink(
private val connection: ConnectionMultiplexer? context: Context?,
private val input: InputStream connection: ConnectionMultiplexer,
private val output: OutputStream val input: InputStream,
private val remoteAddress: BluetoothDevice val output: OutputStream,
private val linkProvider: BluetoothLinkProvider val remoteAddress: BluetoothDevice,
private val deviceInfo: DeviceInfo val theDeviceInfo: DeviceInfo,
val linkProvider: BluetoothLinkProvider
) : BaseLink(context!!, linkProvider) {
private val connection: ConnectionMultiplexer? = connection
private var continueAccepting = true private var continueAccepting = true
private val receivingThread = Thread(object : Runnable { private val receivingThread = Thread(object : Runnable {
override fun run() { override fun run() {
@@ -64,8 +67,7 @@ class BluetoothLink(context: Context?, connection: ConnectionMultiplexer, input:
} }
private fun processMessage(message: String) { private fun processMessage(message: String) {
val np: NetworkPacket val np = try {
np = try {
NetworkPacket.unserialize(message) NetworkPacket.unserialize(message)
} catch (e: JSONException) { } catch (e: JSONException) {
Log.e("BluetoothLink/receiving", "Unable to parse message.", e) Log.e("BluetoothLink/receiving", "Unable to parse message.", e)
@@ -84,15 +86,6 @@ class BluetoothLink(context: Context?, connection: ConnectionMultiplexer, input:
} }
}) })
init {
this.connection = connection
this.input = input
this.output = output
this.deviceInfo = deviceInfo
this.remoteAddress = remoteAddress
this.linkProvider = linkProvider
}
fun startListening() { fun startListening() {
receivingThread.start() receivingThread.start()
} }
@@ -102,7 +95,7 @@ class BluetoothLink(context: Context?, connection: ConnectionMultiplexer, input:
} }
override fun getDeviceInfo(): DeviceInfo { override fun getDeviceInfo(): DeviceInfo {
return deviceInfo return theDeviceInfo
} }
override fun disconnect() { override fun disconnect() {

View File

@@ -35,6 +35,7 @@ import java.io.Reader
import java.security.cert.CertificateException import java.security.cert.CertificateException
import java.util.UUID import java.util.UUID
import kotlin.text.Charsets.UTF_8 import kotlin.text.Charsets.UTF_8
import androidx.core.content.edit
class BluetoothLinkProvider(private val context: Context) : BaseLinkProvider() { class BluetoothLinkProvider(private val context: Context) : BaseLinkProvider() {
private val visibleDevices: MutableMap<String, BluetoothLink> = HashMap() private val visibleDevices: MutableMap<String, BluetoothLink> = HashMap()
@@ -138,9 +139,9 @@ class BluetoothLinkProvider(private val context: Context) : BaseLinkProvider() {
} catch (e: SecurityException) { } catch (e: SecurityException) {
Log.e("KDEConnect", "Security Exception for CONNECT", e) Log.e("KDEConnect", "Security Exception for CONNECT", e)
val prefenceEditor = PreferenceManager.getDefaultSharedPreferences(context).edit() PreferenceManager.getDefaultSharedPreferences(context).edit {
prefenceEditor.putBoolean(SettingsFragment.KEY_BLUETOOTH_ENABLED, false) putBoolean(SettingsFragment.KEY_BLUETOOTH_ENABLED, false)
prefenceEditor.apply() }
return return
} }

View File

@@ -21,7 +21,7 @@ import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
class ConnectionMultiplexer(socket: BluetoothSocket) : Closeable { class ConnectionMultiplexer(socket: BluetoothSocket) : Closeable {
private class ChannelInputStream constructor(val channel: Channel) : InputStream(), Closeable { private class ChannelInputStream(val channel: Channel) : InputStream(), Closeable {
override fun available(): Int { override fun available(): Int {
return channel.available() return channel.available()
} }
@@ -49,7 +49,7 @@ class ConnectionMultiplexer(socket: BluetoothSocket) : Closeable {
} }
} }
private class ChannelOutputStream constructor(val channel: Channel) : OutputStream(), Closeable { private class ChannelOutputStream(val channel: Channel) : OutputStream(), Closeable {
@Throws(IOException::class) @Throws(IOException::class)
override fun close() { override fun close() {
channel.close() channel.close()
@@ -78,7 +78,7 @@ class ConnectionMultiplexer(socket: BluetoothSocket) : Closeable {
} }
} }
private class Channel constructor(val multiplexer: ConnectionMultiplexer, val id: UUID) : Closeable { private class Channel(val multiplexer: ConnectionMultiplexer, val id: UUID) : Closeable {
val readBuffer: ByteBuffer = ByteBuffer.allocate(BUFFER_SIZE) val readBuffer: ByteBuffer = ByteBuffer.allocate(BUFFER_SIZE)
val lock = ReentrantLock() val lock = ReentrantLock()
var lockCondition: Condition = lock.newCondition() var lockCondition: Condition = lock.newCondition()
@@ -371,14 +371,9 @@ class ConnectionMultiplexer(socket: BluetoothSocket) : Closeable {
} }
} }
private inner class ListenRunnable constructor(socket: BluetoothSocket) : Runnable { private inner class ListenRunnable(socket: BluetoothSocket) : Runnable {
var input: InputStream var input: InputStream = socket.inputStream
var output: OutputStream var output: OutputStream = socket.outputStream
init {
input = socket.inputStream
output = socket.outputStream
}
@Throws(IOException::class) @Throws(IOException::class)
private fun readBuffer(buffer: ByteArray, len: Int) { private fun readBuffer(buffer: ByteArray, len: Int) {

View File

@@ -19,21 +19,18 @@ import androidx.annotation.WorkerThread;
import org.json.JSONException; import org.json.JSONException;
import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider; import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.DeviceHost; import org.kde.kdeconnect.DeviceHost;
import org.kde.kdeconnect.DeviceInfo; import org.kde.kdeconnect.DeviceInfo;
import org.kde.kdeconnect.Helpers.DeviceHelper; import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.ThreadHelper; import org.kde.kdeconnect.Helpers.ThreadHelper;
import org.kde.kdeconnect.Helpers.TrustedNetworkHelper; import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
import org.kde.kdeconnect.KdeConnect;
import org.kde.kdeconnect.NetworkPacket; import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity; import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
import org.kde.kdeconnect.UserInterface.SettingsFragment; import org.kde.kdeconnect.UserInterface.SettingsFragment;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
@@ -68,6 +65,7 @@ public class LanLinkProvider extends BaseLinkProvider {
final static int MAX_PORT = 1764; final static int MAX_PORT = 1764;
final static int PAYLOAD_TRANSFER_MIN_PORT = 1739; final static int PAYLOAD_TRANSFER_MIN_PORT = 1739;
final static int MAX_IDENTITY_PACKET_SIZE = 1024 * 512;
final static int MAX_UDP_PACKET_SIZE = 1024 * 512; final static int MAX_UDP_PACKET_SIZE = 1024 * 512;
final static long MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE = 500L; final static long MILLIS_DELAY_BETWEEN_CONNECTIONS_TO_SAME_DEVICE = 500L;
@@ -100,8 +98,7 @@ public class LanLinkProvider extends BaseLinkProvider {
NetworkPacket networkPacket; NetworkPacket networkPacket;
try { try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String message = readSingleLine(socket);
String message = reader.readLine();
networkPacket = NetworkPacket.unserialize(message); networkPacket = NetworkPacket.unserialize(message);
//Log.e("TcpListener", "Received TCP packet: " + networkPacket.serialize()); //Log.e("TcpListener", "Received TCP packet: " + networkPacket.serialize());
} catch (Exception e) { } catch (Exception e) {
@@ -120,6 +117,25 @@ public class LanLinkProvider extends BaseLinkProvider {
identityPacketReceived(networkPacket, socket, LanLink.ConnectionStarted.Locally, deviceTrusted); identityPacketReceived(networkPacket, socket, LanLink.ConnectionStarted.Locally, deviceTrusted);
} }
/**
* Read a single line from a socket without consuming anything else from the input.
*/
private String readSingleLine(Socket socket) throws IOException {
InputStream stream = socket.getInputStream();
StringBuilder line = new StringBuilder(MAX_IDENTITY_PACKET_SIZE);
int ch;
while ((ch = stream.read()) != -1) {
line.append((char) ch);
if (ch == '\n') {
return line.toString();
}
if (line.length() >= MAX_IDENTITY_PACKET_SIZE) {
break;
}
}
throw new IOException("Couldn't read a line from the socket");
}
//I've received their broadcast and should connect to their TCP socket and send my identity. //I've received their broadcast and should connect to their TCP socket and send my identity.
@WorkerThread @WorkerThread
private void udpPacketReceived(DatagramPacket packet) throws JSONException, IOException { private void udpPacketReceived(DatagramPacket packet) throws JSONException, IOException {
@@ -239,41 +255,40 @@ public class LanLinkProvider extends BaseLinkProvider {
final boolean clientMode = (connectionStarted == LanLink.ConnectionStarted.Locally); final boolean clientMode = (connectionStarted == LanLink.ConnectionStarted.Locally);
final SSLSocket sslSocket = SslHelper.convertToSslSocket(context, socket, deviceId, deviceTrusted, clientMode); final SSLSocket sslSocket = SslHelper.convertToSslSocket(context, socket, deviceId, deviceTrusted, clientMode);
sslSocket.addHandshakeCompletedListener(event -> { sslSocket.addHandshakeCompletedListener(event -> {
String mode = clientMode ? "client" : "server"; // Start a new thread because some Android versions don't allow calling sslSocket.getOutputStream() from the callback
try { ThreadHelper.execute(() -> {
NetworkPacket secureIdentityPacket; String mode = clientMode ? "client" : "server";
if (protocolVersion >= 8) { try {
DeviceInfo myDeviceInfo = DeviceHelper.getDeviceInfo(context); NetworkPacket secureIdentityPacket;
NetworkPacket myIdentity = myDeviceInfo.toIdentityPacket(); if (protocolVersion >= 8) {
OutputStream writer = sslSocket.getOutputStream(); DeviceInfo myDeviceInfo = DeviceHelper.getDeviceInfo(context);
writer.write(myIdentity.serialize().getBytes(Charsets.UTF_8)); NetworkPacket myIdentity = myDeviceInfo.toIdentityPacket();
writer.flush(); OutputStream writer = sslSocket.getOutputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream())); writer.write(myIdentity.serialize().getBytes(Charsets.UTF_8));
String line = reader.readLine(); writer.flush();
if (line == null) { String line = readSingleLine(sslSocket);
throw new JSONException("Can't read line"); // Do not trust the identity packet we received unencrypted
secureIdentityPacket = NetworkPacket.unserialize(line);
if (!DeviceInfo.isValidIdentityPacket(secureIdentityPacket)) {
throw new JSONException("Invalid identity packet");
}
int newProtocolVersion = secureIdentityPacket.getInt("protocolVersion");
if (newProtocolVersion != protocolVersion) {
Log.w("KDE/LanLinkProvider", "Protocol version changed half-way through the handshake: " + protocolVersion + " ->" + newProtocolVersion);
}
} else {
secureIdentityPacket = identityPacket;
} }
// Do not trust the identity packet we received unencrypted Certificate certificate = event.getPeerCertificates()[0];
secureIdentityPacket = NetworkPacket.unserialize(line); DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(secureIdentityPacket, certificate);
if (!DeviceInfo.isValidIdentityPacket(secureIdentityPacket)) { Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + deviceName + " secured with " + event.getCipherSuite());
throw new JSONException("Invalid identity packet"); addOrUpdateLink(sslSocket, deviceInfo);
} } catch (JSONException e) {
int newProtocolVersion = secureIdentityPacket.getInt("protocolVersion"); Log.e("KDE/LanLinkProvider", "Remote device doesn't correctly implement protocol version 8", e);
if (newProtocolVersion != protocolVersion) { } catch (IOException e) {
Log.w("KDE/LanLinkProvider", "Protocol version changed half-way through the handshake: " + protocolVersion + " ->" + newProtocolVersion); Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + deviceName, e);
}
} else {
secureIdentityPacket = identityPacket;
} }
Certificate certificate = event.getPeerCertificates()[0]; });
DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(secureIdentityPacket, certificate);
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + deviceName + " secured with " + event.getCipherSuite());
addOrUpdateLink(sslSocket, deviceInfo);
} catch (JSONException e) {
Log.e("KDE/LanLinkProvider", "Remote device doesn't correctly implement protocol version 8", e);
} catch (IOException e) {
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + deviceName, e);
}
}); });
//Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection //Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection

View File

@@ -41,7 +41,7 @@ public class MdnsDiscovery {
this.lanLinkProvider = lanLinkProvider; this.lanLinkProvider = lanLinkProvider;
this.mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE); this.mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
this.mNsdResolveQueue = new NsdResolveQueue(this.mNsdManager); this.mNsdResolveQueue = new NsdResolveQueue(this.mNsdManager);
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
multicastLock = wifiManager.createMulticastLock("kdeConnectMdnsMulticastLock"); multicastLock = wifiManager.createMulticastLock("kdeConnectMdnsMulticastLock");
} }
@@ -132,7 +132,7 @@ public class MdnsDiscovery {
// Also, on Android Lollipop those fields aren't resolved. // Also, on Android Lollipop those fields aren't resolved.
String deviceName = DeviceHelper.getDeviceName(context); String deviceName = DeviceHelper.getDeviceName(context);
String deviceType = DeviceHelper.getDeviceType().toString(); String deviceType = DeviceHelper.getDeviceType().toString();
String protocolVersion = Integer.toString(DeviceHelper.ProtocolVersion); String protocolVersion = Integer.toString(DeviceHelper.PROTOCOL_VERSION);
serviceInfo.setAttribute("id", deviceId); serviceInfo.setAttribute("id", deviceId);
serviceInfo.setAttribute("name", deviceName); serviceInfo.setAttribute("name", deviceName);
serviceInfo.setAttribute("type", deviceType); serviceInfo.setAttribute("type", deviceType);

View File

@@ -32,7 +32,6 @@ import org.kde.kdeconnect.Backends.BaseLinkProvider
import org.kde.kdeconnect.Backends.BaseLinkProvider.ConnectionReceiver import org.kde.kdeconnect.Backends.BaseLinkProvider.ConnectionReceiver
import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider
import org.kde.kdeconnect.Backends.LoopbackBackend.LoopbackLinkProvider
import org.kde.kdeconnect.Helpers.NotificationHelper import org.kde.kdeconnect.Helpers.NotificationHelper
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity

View File

@@ -33,7 +33,6 @@ import org.kde.kdeconnect.DeviceStats.countReceived
import org.kde.kdeconnect.DeviceStats.countSent import org.kde.kdeconnect.DeviceStats.countSent
import org.kde.kdeconnect.Helpers.DeviceHelper import org.kde.kdeconnect.Helpers.DeviceHelper
import org.kde.kdeconnect.Helpers.NotificationHelper import org.kde.kdeconnect.Helpers.NotificationHelper
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
import org.kde.kdeconnect.PairingHandler.PairingCallback import org.kde.kdeconnect.PairingHandler.PairingCallback
import org.kde.kdeconnect.Plugins.Plugin import org.kde.kdeconnect.Plugins.Plugin
import org.kde.kdeconnect.Plugins.Plugin.Companion.getPluginKey import org.kde.kdeconnect.Plugins.Plugin.Companion.getPluginKey
@@ -46,6 +45,7 @@ import java.util.Vector
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap import java.util.concurrent.ConcurrentMap
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import androidx.core.content.edit
class Device : PacketReceiver { class Device : PacketReceiver {
@@ -164,7 +164,7 @@ class Device : PacketReceiver {
// Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer // Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer
fun compareProtocolVersion(): Int = fun compareProtocolVersion(): Int =
deviceInfo.protocolVersion - DeviceHelper.ProtocolVersion deviceInfo.protocolVersion - DeviceHelper.PROTOCOL_VERSION
val isPaired: Boolean val isPaired: Boolean
get() = pairingHandler.state == PairingHandler.PairState.Paired get() = pairingHandler.state == PairingHandler.PairState.Paired
@@ -209,7 +209,7 @@ class Device : PacketReceiver {
// Store as trusted device // Store as trusted device
val preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE) val preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE)
preferences.edit().putBoolean(deviceInfo.id, true).apply() preferences.edit { putBoolean(deviceInfo.id, true) }
try { try {
reloadPluginsFromSettings() reloadPluginsFromSettings()
@@ -228,10 +228,10 @@ class Device : PacketReceiver {
override fun unpaired() { override fun unpaired() {
Log.i("Device", "unpaired, removing from trusted devices list") Log.i("Device", "unpaired, removing from trusted devices list")
val preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE) val preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE)
preferences.edit().remove(deviceInfo.id).apply() preferences.edit { remove(deviceInfo.id) }
val devicePreferences = context.getSharedPreferences(deviceInfo.id, Context.MODE_PRIVATE) val devicePreferences = context.getSharedPreferences(deviceInfo.id, Context.MODE_PRIVATE)
devicePreferences.edit().clear().apply() devicePreferences.edit { clear() }
pairingCallbacks.forEach(PairingCallback::unpaired) pairingCallbacks.forEach(PairingCallback::unpaired)
@@ -596,7 +596,7 @@ class Device : PacketReceiver {
} }
fun setPluginEnabled(pluginKey: String, value: Boolean) { fun setPluginEnabled(pluginKey: String, value: Boolean) {
settings.edit().putBoolean(pluginKey, value).apply() settings.edit { putBoolean(pluginKey, value) }
reloadPluginsFromSettings() reloadPluginsFromSettings()
} }

View File

@@ -59,12 +59,12 @@ class DeviceInfo(
*/ */
fun toIdentityPacket(): NetworkPacket = fun toIdentityPacket(): NetworkPacket =
NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY).also { np -> NetworkPacket(NetworkPacket.PACKET_TYPE_IDENTITY).also { np ->
np.set("deviceId", id) np["deviceId"] = id
np.set("deviceName", name) np["deviceName"] = name
np.set("protocolVersion", protocolVersion) np["protocolVersion"] = protocolVersion
np.set("deviceType", type.toString()) np["deviceType"] = type.toString()
np.set("incomingCapabilities", incomingCapabilities!!) np["incomingCapabilities"] = incomingCapabilities!!
np.set("outgoingCapabilities", outgoingCapabilities!!) np["outgoingCapabilities"] = outgoingCapabilities!!
} }
companion object { companion object {
@@ -107,10 +107,10 @@ class DeviceInfo(
fun isValidIdentityPacket(identityPacket: NetworkPacket): Boolean = with(identityPacket) { fun isValidIdentityPacket(identityPacket: NetworkPacket): Boolean = with(identityPacket) {
type == NetworkPacket.PACKET_TYPE_IDENTITY && type == NetworkPacket.PACKET_TYPE_IDENTITY &&
DeviceHelper.filterName(getString("deviceName", "")).isNotBlank() && DeviceHelper.filterName(getString("deviceName", "")).isNotBlank() &&
isValidDeviceId(getString("deviceId", "")); isValidDeviceId(getString("deviceId", ""))
} }
private val DEVICE_ID_REGEX = "^[a-zA-Z0-9_-]{32,38}\$".toRegex() private val DEVICE_ID_REGEX = "^[a-zA-Z0-9_-]{32,38}$".toRegex()
@JvmStatic @JvmStatic
fun isValidDeviceId(deviceId: String): Boolean = deviceId.matches(DEVICE_ID_REGEX) fun isValidDeviceId(deviceId: String): Boolean = deviceId.matches(DEVICE_ID_REGEX)

View File

@@ -115,7 +115,7 @@ object DeviceStats {
val entry = iterator.next() val entry = iterator.next()
val events = entry.value val events = entry.value
var index = Collections.binarySearch(events, cutoutTimestamp) var index = events.binarySearch(cutoutTimestamp)
if (index < 0) { if (index < 0) {
index = -(index + 1) // Convert the negative index to insertion point index = -(index + 1) // Convert the negative index to insertion point
} }

View File

@@ -91,7 +91,7 @@ public final class CollectionsBackport {
} }
static boolean eq(Object o1, Object o2) { static boolean eq(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2); return Objects.equals(o1, o2);
} }
static class UnmodifiableNavigableSetBackport<E> static class UnmodifiableNavigableSetBackport<E>

View File

@@ -214,7 +214,7 @@ public class ContactsHelper {
Map<uID, Map<String, String>> databaseValue = accessContactsDatabase(context, projection, selection, selectionArgs, null); Map<uID, Map<String, String>> databaseValue = accessContactsDatabase(context, projection, selection, selectionArgs, null);
if (databaseValue.size() == 0) { if (databaseValue.isEmpty()) {
throw new ContactNotFoundException("Querying for contact with id " + contactID + " returned no results."); throw new ContactNotFoundException("Querying for contact with id " + contactID + " returned no results.");
} }

View File

@@ -0,0 +1,27 @@
package org.kde.kdeconnect.Helpers
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.activity.result.contract.ActivityResultContract
data class CreateFileParams(
val fileMimeType: String,
val suggestedFileName: String,
)
class CreateFileResultContract : ActivityResultContract<CreateFileParams, Uri?>() {
override fun createIntent(context: Context, input: CreateFileParams): Intent =
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
setTypeAndNormalize(input.fileMimeType)
putExtra(Intent.EXTRA_TITLE, input.suggestedFileName)
}
override fun parseResult(resultCode: Int, intent: Intent?): Uri? = when (resultCode) {
Activity.RESULT_OK -> intent?.data
else -> null
}
}

View File

@@ -11,7 +11,6 @@ import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import android.os.Build import android.os.Build
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.provider.Settings
import android.util.Log import android.util.Log
import com.univocity.parsers.common.TextParsingException import com.univocity.parsers.common.TextParsingException
import com.univocity.parsers.csv.CsvParser import com.univocity.parsers.csv.CsvParser
@@ -26,9 +25,10 @@ import java.io.InputStreamReader
import java.net.URL import java.net.URL
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.UUID import java.util.UUID
import androidx.core.content.edit
object DeviceHelper { object DeviceHelper {
const val ProtocolVersion = 8 const val PROTOCOL_VERSION = 8
const val KEY_DEVICE_NAME_PREFERENCE = "device_name_preference" const val KEY_DEVICE_NAME_PREFERENCE = "device_name_preference"
private const val KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET = "device_name_downloaded_preference" private const val KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET = "device_name_downloaded_preference"
@@ -85,7 +85,7 @@ object DeviceHelper {
// If we get here we managed to download the file. Mark that as done so we don't try again even if we don't end up finding a name. // If we get here we managed to download the file. Mark that as done so we don't try again even if we don't end up finding a name.
val preferences = PreferenceManager.getDefaultSharedPreferences(context) val preferences = PreferenceManager.getDefaultSharedPreferences(context)
preferences.edit().putBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, true).apply() preferences.edit { putBoolean(KEY_DEVICE_NAME_FETCHED_FROM_THE_INTERNET, true) }
BufferedReader( BufferedReader(
InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_16) InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_16)
@@ -124,26 +124,17 @@ object DeviceHelper {
fun setDeviceName(context: Context, name: String) { fun setDeviceName(context: Context, name: String) {
val filteredName = filterName(name) val filteredName = filterName(name)
val preferences = PreferenceManager.getDefaultSharedPreferences(context) val preferences = PreferenceManager.getDefaultSharedPreferences(context)
preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, filteredName).apply() preferences.edit { putString(KEY_DEVICE_NAME_PREFERENCE, filteredName) }
} }
fun initializeDeviceId(context: Context) { fun initializeDeviceId(context: Context) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context) val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val preferenceKeys: Set<String> = preferences.all.keys val deviceId = preferences.getString(KEY_DEVICE_ID_PREFERENCE, "")!!
if (preferenceKeys.contains(KEY_DEVICE_ID_PREFERENCE)) { if (DeviceInfo.isValidDeviceId(deviceId)) {
return // We already have an ID return // We already have an ID
} }
@SuppressLint("HardwareIds") val deviceName = UUID.randomUUID().toString().replace("-", "")
val deviceName = if (preferenceKeys.isEmpty()) { preferences.edit { putString(KEY_DEVICE_ID_PREFERENCE, deviceName) }
// For new installations, use random IDs
Log.i("DeviceHelper","No device ID found and this looks like a new installation, creating a random ID")
UUID.randomUUID().toString().replace('-', '_')
} else {
// Use the ANDROID_ID as device ID for existing installations, for backwards compatibility
Log.i("DeviceHelper", "No device ID found but this seems an existing installation, using the Android ID")
Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
}
preferences.edit().putString(KEY_DEVICE_ID_PREFERENCE, deviceName).apply()
} }
@JvmStatic @JvmStatic
@@ -159,7 +150,7 @@ object DeviceHelper {
SslHelper.certificate, SslHelper.certificate,
getDeviceName(context), getDeviceName(context),
deviceType, deviceType,
ProtocolVersion, PROTOCOL_VERSION,
PluginFactory.incomingCapabilities, PluginFactory.incomingCapabilities,
PluginFactory.outgoingCapabilities PluginFactory.outgoingCapabilities
) )

View File

@@ -97,7 +97,7 @@ object FilesHelper {
fun contentResolverExtract(): Triple<String?, Long, Long?> { fun contentResolverExtract(): Triple<String?, Long, Long?> {
// Since we used Intent.CATEGORY_OPENABLE, these two columns are the only ones we are guaranteed to have: https://developer.android.com/reference/android/provider/OpenableColumns // Since we used Intent.CATEGORY_OPENABLE, these two columns are the only ones we are guaranteed to have: https://developer.android.com/reference/android/provider/OpenableColumns
val proj = arrayOf(OpenableColumns.SIZE, OpenableColumns.DISPLAY_NAME,) val proj = arrayOf(OpenableColumns.SIZE, OpenableColumns.DISPLAY_NAME)
try { try {
contentResolver.query(uri, proj, null, null, null).use { cursor -> contentResolver.query(uri, proj, null, null, null).use { cursor ->

View File

@@ -11,7 +11,6 @@ import android.content.ContentUris
import android.content.Context import android.content.Context
import android.database.ContentObserver import android.database.ContentObserver
import android.database.sqlite.SQLiteException import android.database.sqlite.SQLiteException
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever import android.media.MediaMetadataRetriever
import android.media.ThumbnailUtils import android.media.ThumbnailUtils
import android.net.Uri import android.net.Uri
@@ -23,6 +22,8 @@ import android.telephony.TelephonyManager
import android.util.Log import android.util.Log
import android.util.Pair import android.util.Pair
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.graphics.scale
import androidx.core.net.toUri
import com.google.android.mms.pdu_alt.MultimediaMessagePdu import com.google.android.mms.pdu_alt.MultimediaMessagePdu
import com.google.android.mms.pdu_alt.PduPersister import com.google.android.mms.pdu_alt.PduPersister
import com.google.android.mms.util_alt.PduCache import com.google.android.mms.util_alt.PduCache
@@ -37,13 +38,11 @@ import org.kde.kdeconnect.Helpers.TelephonyHelper.LocalPhoneNumber
import org.kde.kdeconnect.Plugins.SMSPlugin.MimeType import org.kde.kdeconnect.Plugins.SMSPlugin.MimeType
import org.kde.kdeconnect.Plugins.SMSPlugin.SmsMmsUtils import org.kde.kdeconnect.Plugins.SMSPlugin.SmsMmsUtils
import java.io.IOException import java.io.IOException
import java.util.Arrays
import java.util.Objects import java.util.Objects
import java.util.SortedMap import java.util.SortedMap
import java.util.TreeMap import java.util.TreeMap
import java.util.concurrent.locks.Lock import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import java.util.stream.Collectors
import kotlin.text.Charsets.UTF_8 import kotlin.text.Charsets.UTF_8
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
@@ -52,7 +51,7 @@ object SMSHelper {
private const val THUMBNAIL_WIDTH = 100 private const val THUMBNAIL_WIDTH = 100
// The constant Telephony.Mms.Part.CONTENT_URI was added in API 29 // The constant Telephony.Mms.Part.CONTENT_URI was added in API 29
val mMSPartUri : Uri = Uri.parse("content://mms/part/") val mMSPartUri : Uri = "content://mms/part/".toUri()
/** /**
* Get the base address for all message conversations * Get the base address for all message conversations
@@ -67,14 +66,14 @@ object SMSHelper {
if ("Samsung".equals(Build.MANUFACTURER, ignoreCase = true)) { if ("Samsung".equals(Build.MANUFACTURER, ignoreCase = true)) {
Log.i("SMSHelper", "This appears to be a Samsung device. This may cause some features to not work properly.") Log.i("SMSHelper", "This appears to be a Samsung device. This may cause some features to not work properly.")
} }
return Uri.parse("content://mms-sms/conversations?simple=true") return "content://mms-sms/conversations?simple=true".toUri()
} }
private fun getCompleteConversationsUri(): Uri { private fun getCompleteConversationsUri(): Uri {
// This glorious - but completely undocumented - content URI gives us all messages, both MMS and SMS, // This glorious - but completely undocumented - content URI gives us all messages, both MMS and SMS,
// in all conversations // in all conversations
// See https://stackoverflow.com/a/36439630/3723163 // See https://stackoverflow.com/a/36439630/3723163
return Uri.parse("content://mms-sms/complete-conversations") return "content://mms-sms/complete-conversations".toUri()
} }
/** /**
@@ -651,12 +650,7 @@ object SMSHelper {
) )
val videoThumbnail = retriever.frameAtTime val videoThumbnail = retriever.frameAtTime
val encodedThumbnail = SmsMmsUtils.bitMapToBase64( val encodedThumbnail = SmsMmsUtils.bitMapToBase64(
Bitmap.createScaledBitmap( videoThumbnail!!.scale(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)
videoThumbnail!!,
THUMBNAIL_WIDTH,
THUMBNAIL_HEIGHT,
true
)
) )
attachments.add( attachments.add(
Attachment( Attachment(

View File

@@ -24,7 +24,6 @@ import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.Arrays;
import org.kde.kdeconnect.Helpers.DeviceHelper; import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.RandomHelper; import org.kde.kdeconnect.Helpers.RandomHelper;
import org.kde.kdeconnect.KdeConnect; import org.kde.kdeconnect.KdeConnect;

View File

@@ -6,7 +6,6 @@
package org.kde.kdeconnect.Helpers package org.kde.kdeconnect.Helpers
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract
object StorageHelper { object StorageHelper {
fun getDisplayName(treeUri: Uri): String { fun getDisplayName(treeUri: Uri): String {

View File

@@ -13,6 +13,7 @@ import android.net.wifi.WifiManager
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.util.Log import android.util.Log
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.edit
class TrustedNetworkHelper(private val context: Context) { class TrustedNetworkHelper(private val context: Context) {
@@ -22,19 +23,19 @@ class TrustedNetworkHelper(private val context: Context) {
return serializedNetworks.split(NETWORK_SSID_DELIMITER, "#_#" /* TODO remove old delimiter in 2025 */).filter { it.isNotEmpty() } return serializedNetworks.split(NETWORK_SSID_DELIMITER, "#_#" /* TODO remove old delimiter in 2025 */).filter { it.isNotEmpty() }
} }
set(value) { set(value) {
PreferenceManager.getDefaultSharedPreferences(context) PreferenceManager.getDefaultSharedPreferences(context).edit {
.edit() putString(
.putString(KEY_CUSTOM_TRUSTED_NETWORKS, value.joinToString(NETWORK_SSID_DELIMITER)) KEY_CUSTOM_TRUSTED_NETWORKS,
.apply() value.joinToString(NETWORK_SSID_DELIMITER)
)
}
} }
var allNetworksAllowed: Boolean var allNetworksAllowed: Boolean
get() = !hasPermissions || PreferenceManager.getDefaultSharedPreferences(context).getBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, true) get() = !hasPermissions || PreferenceManager.getDefaultSharedPreferences(context).getBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, true)
set(value) = PreferenceManager set(value) = PreferenceManager.getDefaultSharedPreferences(context).edit {
.getDefaultSharedPreferences(context) putBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, value)
.edit() }
.putBoolean(KEY_CUSTOM_TRUST_ALL_NETWORKS, value)
.apply()
val hasPermissions: Boolean val hasPermissions: Boolean
get() = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED get() = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED

View File

@@ -25,6 +25,7 @@ import java.security.cert.CertificateException
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.Date import java.util.Date
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import androidx.core.content.edit
/* /*
* This class holds all the active devices and makes them accessible from every other class. * This class holds all the active devices and makes them accessible from every other class.
@@ -116,7 +117,7 @@ class KdeConnect : Application() {
"KdeConnect", "KdeConnect",
"Couldn't load the certificate for a remembered device. Removing from trusted list.", e "Couldn't load the certificate for a remembered device. Removing from trusted list.", e
) )
preferences.edit().remove(it).apply() preferences.edit { remove(it) }
} }
} }
} }
@@ -127,7 +128,7 @@ class KdeConnect : Application() {
trustedDevices.filter { preferences.getBoolean(it, false) } trustedDevices.filter { preferences.getBoolean(it, false) }
.forEach { .forEach {
Log.d("KdeConnect", "Removing devices: $it") Log.d("KdeConnect", "Removing devices: $it")
preferences.edit().remove(it).apply() preferences.edit { remove(it) }
} }
} }

View File

@@ -11,9 +11,7 @@ import org.json.JSONObject
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.lang.RuntimeException
import java.net.Socket import java.net.Socket
import kotlin.concurrent.Volatile
class NetworkPacket private constructor( class NetworkPacket private constructor(
val type: String, val type: String,

View File

@@ -25,8 +25,6 @@ import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.util.Objects;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin
public class BigscreenPlugin extends Plugin { public class BigscreenPlugin extends Plugin {

View File

@@ -25,8 +25,6 @@ import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.util.Objects;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin
public class ClipboardPlugin extends Plugin { public class ClipboardPlugin extends Plugin {

View File

@@ -37,7 +37,6 @@ import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin
public class FindMyPhonePlugin extends Plugin { public class FindMyPhonePlugin extends Plugin {

View File

@@ -19,8 +19,6 @@ import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.PluginSettingsFragment; import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.util.Objects;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin
public class MousePadPlugin extends Plugin { public class MousePadPlugin extends Plugin {

View File

@@ -157,9 +157,6 @@ public class PointerAccelerationProfileFactory {
public static PointerAccelerationProfile getProfileWithName(String name) { public static PointerAccelerationProfile getProfileWithName(String name) {
switch (name) { switch (name) {
case "noacceleration":
default:
return new DefaultProfile();
case "weaker": case "weaker":
return new PolynomialProfile(0.25f); return new PolynomialProfile(0.25f);
case "weak": case "weak":
@@ -170,6 +167,9 @@ public class PointerAccelerationProfileFactory {
return new PolynomialProfile(1.5f); return new PolynomialProfile(1.5f);
case "stronger": case "stronger":
return new PolynomialProfile(2.0f); return new PolynomialProfile(2.0f);
case "noacceleration":
default:
return new DefaultProfile();
} }
} }
} }

View File

@@ -29,6 +29,7 @@ import java.net.URL
import java.net.URLDecoder import java.net.URLDecoder
import java.security.MessageDigest import java.security.MessageDigest
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import androidx.core.net.toUri
/** /**
* Handles the cache for album art * Handles the cache for album art
@@ -130,7 +131,7 @@ internal object AlbumArtCache {
if (albumUrl.isNullOrEmpty()) { if (albumUrl.isNullOrEmpty()) {
return null return null
} }
val url = Uri.parse(albumUrl) val url = albumUrl.toUri()
//We currently only support http(s), file, and kdeconnect urls //We currently only support http(s), file, and kdeconnect urls
if (url.scheme !in ALLOWED_SCHEMES) { if (url.scheme !in ALLOWED_SCHEMES) {
@@ -221,7 +222,7 @@ internal object AlbumArtCache {
* Does the actual fetching and makes sure only not too many fetches are running at the same time * Does the actual fetching and makes sure only not too many fetches are running at the same time
*/ */
private fun initiateFetch() { private fun initiateFetch() {
var url : Uri; var url : Uri
synchronized(fetchUrlList) { synchronized(fetchUrlList) {
if (numFetching >= 2 || fetchUrlList.isEmpty()) return if (numFetching >= 2 || fetchUrlList.isEmpty()) return
//Fetch the last-requested url first, it will probably be needed first //Fetch the last-requested url first, it will probably be needed first
@@ -283,7 +284,7 @@ internal object AlbumArtCache {
payload.close() payload.close()
return return
} }
val url = Uri.parse(albumUrl) val url = albumUrl.toUri()
if (url.scheme !in REMOTE_FETCH_SCHEMES) { if (url.scheme !in REMOTE_FETCH_SCHEMES) {
//Shouldn't happen (checked on receival of the url), but just to be sure //Shouldn't happen (checked on receival of the url), but just to be sure
Log.e("KDE/Mpris/AlbumArtCache", "Got invalid art url with payload: $albumUrl") Log.e("KDE/Mpris/AlbumArtCache", "Got invalid art url with payload: $albumUrl")

View File

@@ -34,6 +34,7 @@ import org.kde.kdeconnect_tp.databinding.MprisNowPlayingBinding
import java.net.MalformedURLException import java.net.MalformedURLException
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import androidx.core.net.toUri
private typealias MprisPlayerCallback = (MprisPlayer) -> Unit private typealias MprisPlayerCallback = (MprisPlayer) -> Unit
@@ -370,7 +371,7 @@ class MprisNowPlayingFragment : Fragment(), VolumeKeyListener {
if (targetPlayer != null && item.itemId == MENU_OPEN_URL) { if (targetPlayer != null && item.itemId == MENU_OPEN_URL) {
try { try {
val url = VideoUrlsHelper.formatUriWithSeek(targetPlayer.url, targetPlayer.position).toString() val url = VideoUrlsHelper.formatUriWithSeek(targetPlayer.url, targetPlayer.position).toString()
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) val browserIntent = Intent(Intent.ACTION_VIEW, url.toUri())
startActivity(browserIntent) startActivity(browserIntent)
targetPlayer.sendPause() targetPlayer.sendPause()
return true return true

View File

@@ -33,6 +33,7 @@ import org.kde.kdeconnect.UserInterface.PluginSettingsFragment
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import java.net.MalformedURLException import java.net.MalformedURLException
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import androidx.core.net.toUri
@LoadablePlugin @LoadablePlugin
class MprisPlugin : Plugin() { class MprisPlugin : Plugin() {
@@ -273,7 +274,7 @@ class MprisPlugin : Plugin() {
playerStatus.isGoPreviousAllowed = np.getBoolean("canGoPrevious", playerStatus.isGoPreviousAllowed) playerStatus.isGoPreviousAllowed = np.getBoolean("canGoPrevious", playerStatus.isGoPreviousAllowed)
playerStatus.seekAllowed = np.getBoolean("canSeek", playerStatus.seekAllowed) playerStatus.seekAllowed = np.getBoolean("canSeek", playerStatus.seekAllowed)
val newAlbumArtUrlString = np.getString("albumArtUrl", playerStatus.albumArtUrl) val newAlbumArtUrlString = np.getString("albumArtUrl", playerStatus.albumArtUrl)
val newAlbumArtUrl = Uri.parse(newAlbumArtUrlString) val newAlbumArtUrl = newAlbumArtUrlString.toUri()
if (newAlbumArtUrl.scheme in AlbumArtCache.ALLOWED_SCHEMES) { if (newAlbumArtUrl.scheme in AlbumArtCache.ALLOWED_SCHEMES) {
playerStatus.albumArtUrl = newAlbumArtUrl.toString() playerStatus.albumArtUrl = newAlbumArtUrl.toString()
} else { } else {
@@ -334,7 +335,7 @@ class MprisPlugin : Plugin() {
) { ) {
try { try {
val url = VideoUrlsHelper.formatUriWithSeek(playerStatus.url, playerStatus.position).toString() val url = VideoUrlsHelper.formatUriWithSeek(playerStatus.url, playerStatus.position).toString()
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) val browserIntent = Intent(Intent.ACTION_VIEW, url.toUri())
val pendingIntent = PendingIntent.getActivity(context, 0, browserIntent, PendingIntent.FLAG_IMMUTABLE) val pendingIntent = PendingIntent.getActivity(context, 0, browserIntent, PendingIntent.FLAG_IMMUTABLE)
val notificationManager = ContextCompat.getSystemService(context, NotificationManager::class.java) val notificationManager = ContextCompat.getSystemService(context, NotificationManager::class.java)
@@ -350,7 +351,7 @@ class MprisPlugin : Plugin() {
builder.build() builder.build()
) )
} catch (e: MalformedURLException) { } catch (e: MalformedURLException) {
e.printStackTrace(); e.printStackTrace()
} }
} }
} }

View File

@@ -19,7 +19,6 @@ import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import org.kde.kdeconnect.Helpers.NotificationHelper import org.kde.kdeconnect.Helpers.NotificationHelper
import org.kde.kdeconnect.NetworkPacket import org.kde.kdeconnect.NetworkPacket

View File

@@ -24,8 +24,6 @@ import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.util.Objects;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin
public class PresenterPlugin extends Plugin { public class PresenterPlugin extends Plugin {

View File

@@ -29,7 +29,6 @@ import org.kde.kdeconnect.UserInterface.MainActivity;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.io.InputStream; import java.io.InputStream;
import java.util.Objects;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin
public class ReceiveNotificationsPlugin extends Plugin { public class ReceiveNotificationsPlugin extends Plugin {

View File

@@ -67,7 +67,7 @@ public class RemoteKeyboardPlugin extends Plugin implements SharedPreferences.On
} }
public static boolean isConnected() { public static boolean isConnected() {
return instances.size() > 0; return !instances.isEmpty();
} }
private static final SparseIntArray specialKeyMap = new SparseIntArray(); private static final SparseIntArray specialKeyMap = new SparseIntArray();
@@ -135,7 +135,7 @@ public class RemoteKeyboardPlugin extends Plugin implements SharedPreferences.On
try { try {
if (instances.contains(this)) { if (instances.contains(this)) {
instances.remove(this); instances.remove(this);
if (instances.size() < 1 && RemoteKeyboardService.instance != null) if (instances.isEmpty() && RemoteKeyboardService.instance != null)
RemoteKeyboardService.instance.handler.post(() -> RemoteKeyboardService.instance.updateInputView()); RemoteKeyboardService.instance.handler.post(() -> RemoteKeyboardService.instance.updateInputView());
} }
} finally { } finally {
@@ -348,7 +348,7 @@ public class RemoteKeyboardPlugin extends Plugin implements SharedPreferences.On
public enum MousePadPacketType { public enum MousePadPacketType {
Keyboard, Keyboard,
Mouse, Mouse,
}; }
public static MousePadPacketType getMousePadPacketType(NetworkPacket np) { public static MousePadPacketType getMousePadPacketType(NetworkPacket np) {
if (np.has("key") || np.has("specialKey")) { if (np.has("key") || np.has("specialKey")) {

View File

@@ -165,7 +165,7 @@ public class RemoteKeyboardService
intent.putExtra(MainActivity.FLAG_FORCE_OVERVIEW, true); intent.putExtra(MainActivity.FLAG_FORCE_OVERVIEW, true);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); startActivity(intent);
if (instances.size() < 1) if (instances.isEmpty())
Toast.makeText(this, R.string.remotekeyboard_not_connected, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.remotekeyboard_not_connected, Toast.LENGTH_SHORT).show();
else // instances.size() > 1 else // instances.size() > 1
Toast.makeText(this, R.string.remotekeyboard_multiple_connections, Toast.LENGTH_SHORT).show(); Toast.makeText(this, R.string.remotekeyboard_multiple_connections, Toast.LENGTH_SHORT).show();

View File

@@ -130,7 +130,7 @@ public class RunCommandActivity extends BaseActivity<ActivityRunCommandBinding>
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
if (item.getItemId() == R.id.copy_url_to_clipboard) { if (item.getItemId() == R.id.copy_url_to_clipboard) {
CommandEntry entry = (CommandEntry) commandItems.get(info.position); CommandEntry entry = commandItems.get(info.position);
String url = "kdeconnect://runcommand/" + deviceId + "/" + entry.getKey(); String url = "kdeconnect://runcommand/" + deviceId + "/" + entry.getKey();
ClipboardManager cm = ContextCompat.getSystemService(this, ClipboardManager.class); ClipboardManager cm = ContextCompat.getSystemService(this, ClipboardManager.class);
cm.setText(url); cm.setText(url);

View File

@@ -34,7 +34,6 @@ import org.kde.kdeconnect_tp.R;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Objects;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin
public class RunCommandPlugin extends Plugin { public class RunCommandPlugin extends Plugin {

View File

@@ -18,12 +18,12 @@ import android.widget.ArrayAdapter
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import org.kde.kdeconnect.Device import org.kde.kdeconnect.Device
import org.kde.kdeconnect.KdeConnect import org.kde.kdeconnect.KdeConnect
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import org.kde.kdeconnect_tp.databinding.WidgetRemoteCommandPluginDialogBinding import org.kde.kdeconnect_tp.databinding.WidgetRemoteCommandPluginDialogBinding
import java.util.stream.Collectors import java.util.stream.Collectors
import kotlin.streams.toList
class RunCommandWidgetConfigActivity : AppCompatActivity() { class RunCommandWidgetConfigActivity : AppCompatActivity() {
@@ -45,7 +45,7 @@ class RunCommandWidgetConfigActivity : AppCompatActivity() {
val binding = WidgetRemoteCommandPluginDialogBinding.inflate(layoutInflater) val binding = WidgetRemoteCommandPluginDialogBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val pairedDevices = KdeConnect.getInstance().devices.values.stream().filter(Device::isPaired).collect(Collectors.toList()); val pairedDevices = KdeConnect.getInstance().devices.values.stream().filter(Device::isPaired).collect(Collectors.toList())
binding.runCommandsDeviceList.adapter = object : ArrayAdapter<Device>(this, 0, pairedDevices) { binding.runCommandsDeviceList.adapter = object : ArrayAdapter<Device>(this, 0, pairedDevices) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
@@ -76,9 +76,9 @@ private const val PREFS_NAME = "org.kde.kdeconnect_tp.WidgetProvider"
private const val PREF_PREFIX_KEY = "appwidget_" private const val PREF_PREFIX_KEY = "appwidget_"
internal fun saveWidgetDeviceIdPref(context: Context, appWidgetId: Int, deviceName: String) { internal fun saveWidgetDeviceIdPref(context: Context, appWidgetId: Int, deviceName: String) {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit() context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit {
prefs.putString(PREF_PREFIX_KEY + appWidgetId, deviceName) putString(PREF_PREFIX_KEY + appWidgetId, deviceName)
prefs.apply() }
} }
internal fun loadWidgetDeviceIdPref(context: Context, appWidgetId: Int): String? { internal fun loadWidgetDeviceIdPref(context: Context, appWidgetId: Int): String? {
@@ -87,7 +87,7 @@ internal fun loadWidgetDeviceIdPref(context: Context, appWidgetId: Int): String?
} }
internal fun deleteWidgetDeviceIdPref(context: Context, appWidgetId: Int) { internal fun deleteWidgetDeviceIdPref(context: Context, appWidgetId: Int) {
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit() context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit {
prefs.remove(PREF_PREFIX_KEY + appWidgetId) remove(PREF_PREFIX_KEY + appWidgetId)
prefs.apply() }
} }

View File

@@ -20,6 +20,7 @@ import org.kde.kdeconnect.Device
import org.kde.kdeconnect.KdeConnect import org.kde.kdeconnect.KdeConnect
import org.kde.kdeconnect_tp.BuildConfig import org.kde.kdeconnect_tp.BuildConfig
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import androidx.core.net.toUri
const val RUN_COMMAND_ACTION = "RUN_COMMAND_ACTION" const val RUN_COMMAND_ACTION = "RUN_COMMAND_ACTION"
const val TARGET_COMMAND = "TARGET_COMMAND" const val TARGET_COMMAND = "TARGET_COMMAND"
@@ -65,10 +66,10 @@ class RunCommandWidgetProvider : AppWidgetProvider() {
Log.e("RunCommandWidget", "Error running command", ex) Log.e("RunCommandWidget", "Error running command", ex)
} }
} else { } else {
Log.w("RunCommandWidget", "Device not available or runcommand plugin disabled"); Log.w("RunCommandWidget", "Device not available or runcommand plugin disabled")
} }
} else { } else {
super.onReceive(context, intent); super.onReceive(context, intent)
} }
} }
} }
@@ -114,7 +115,7 @@ internal fun updateAppWidget(
val views = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_remotecommandplugin) val views = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.widget_remotecommandplugin)
assignTitleIntent(context, appWidgetId, views) assignTitleIntent(context, appWidgetId, views)
Log.d("WidgetProvider", "updateAppWidget device: " + if (device == null) "null" else device.name) Log.d("WidgetProvider", "updateAppWidget device: " + (device?.name ?: "null"))
// Android should automatically toggle between the command list and the error text // Android should automatically toggle between the command list and the error text
views.setEmptyView(R.id.widget_command_list, R.id.widget_error_text) views.setEmptyView(R.id.widget_command_list, R.id.widget_error_text)
@@ -174,7 +175,7 @@ private fun assignTitleIntent(context: Context, appWidgetId: Int, views: RemoteV
private fun assignListAdapter(context: Context, appWidgetId: Int, views: RemoteViews) { private fun assignListAdapter(context: Context, appWidgetId: Int, views: RemoteViews) {
val dataProviderIntent = Intent(context, CommandsRemoteViewsService::class.java) val dataProviderIntent = Intent(context, CommandsRemoteViewsService::class.java)
dataProviderIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId) dataProviderIntent.putExtra(EXTRA_APPWIDGET_ID, appWidgetId)
dataProviderIntent.data = Uri.parse(dataProviderIntent.toUri(Intent.URI_INTENT_SCHEME)) dataProviderIntent.data = dataProviderIntent.toUri(Intent.URI_INTENT_SCHEME).toUri()
views.setRemoteAdapter(R.id.widget_command_list, dataProviderIntent) views.setRemoteAdapter(R.id.widget_command_list, dataProviderIntent)
} }

View File

@@ -181,7 +181,7 @@ class SMSPlugin : Plugin() {
@Deprecated("") @Deprecated("")
private fun smsBroadcastReceivedDeprecated(messages: MutableList<SmsMessage>) { private fun smsBroadcastReceivedDeprecated(messages: MutableList<SmsMessage>) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
if (messages.size <= 0) { if (messages.isEmpty()) {
throw AssertionError("This method requires at least one message") throw AssertionError("This method requires at least one message")
} }
} }
@@ -224,11 +224,11 @@ class SMSPlugin : Plugin() {
get() = R.string.telepathy_permission_explanation get() = R.string.telepathy_permission_explanation
override fun onCreate(): Boolean { override fun onCreate(): Boolean {
val filter: IntentFilter = IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION) val filter = IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)
filter.priority = 500 filter.priority = 500
context.registerReceiver(receiver, filter) context.registerReceiver(receiver, filter)
val refreshFilter: IntentFilter = IntentFilter(Transaction.REFRESH) val refreshFilter = IntentFilter(Transaction.REFRESH)
refreshFilter.priority = 500 refreshFilter.priority = 500
context.registerReceiver(messagesUpdateReceiver, refreshFilter, ContextCompat.RECEIVER_EXPORTED) context.registerReceiver(messagesUpdateReceiver, refreshFilter, ContextCompat.RECEIVER_EXPORTED)
@@ -269,13 +269,11 @@ class SMSPlugin : Plugin() {
val subID = np.getLong("subID", -1) val subID = np.getLong("subID", -1)
val jsonAddressList = np.getJSONArray("addresses") val jsonAddressList = np.getJSONArray("addresses")
val addressList: List<SMSHelper.Address> val addressList = if (jsonAddressList == null) {
if (jsonAddressList == null) {
// If jsonAddressList is null, then the SMS_REQUEST packet is most probably from the older version of the desktop app. // If jsonAddressList is null, then the SMS_REQUEST packet is most probably from the older version of the desktop app.
addressList = listOf(SMSHelper.Address(context, np.getString("phoneNumber"))) listOf(SMSHelper.Address(context, np.getString("phoneNumber")))
} } else {
else { jsonArrayToAddressList(context, jsonAddressList)
addressList = jsonArrayToAddressList(context, jsonAddressList)
} }
val attachedFiles: List<SMSHelper.Attachment> = jsonArrayToAttachmentsList(np.getJSONArray("attachments")) val attachedFiles: List<SMSHelper.Attachment> = jsonArrayToAttachmentsList(np.getJSONArray("attachments"))
@@ -350,11 +348,10 @@ class SMSPlugin : Plugin() {
numberToGet = null numberToGet = null
} }
val conversation: List<SMSHelper.Message> val conversation = if (rangeStartTimestamp < 0) {
if (rangeStartTimestamp < 0) { getMessagesInThread(this.context, threadID, numberToGet)
conversation = getMessagesInThread(this.context, threadID, numberToGet)
} else { } else {
conversation = getMessagesInRange(this.context, threadID, rangeStartTimestamp, numberToGet, true) getMessagesInRange(this.context, threadID, rangeStartTimestamp, numberToGet, true)
} }
val reply: NetworkPacket = constructBulkMessagePacket(conversation) val reply: NetworkPacket = constructBulkMessagePacket(conversation)

View File

@@ -11,10 +11,10 @@ import android.content.ContentResolver
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.storage.StorageManager import android.os.storage.StorageManager
import android.provider.Settings import android.provider.Settings
import androidx.core.net.toUri
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import org.kde.kdeconnect.Helpers.NetworkHelper.localIpAddress import org.kde.kdeconnect.Helpers.NetworkHelper.localIpAddress
@@ -44,7 +44,7 @@ class SftpPlugin : Plugin(), OnSharedPreferenceChangeListener {
return if (SimpleSftpServer.SUPPORTS_NATIVEFS) { return if (SimpleSftpServer.SUPPORTS_NATIVEFS) {
Environment.isExternalStorageManager() Environment.isExternalStorageManager()
} else { } else {
SftpSettingsFragment.getStorageInfoList(context, this).size != 0 SftpSettingsFragment.getStorageInfoList(context, this).isNotEmpty()
} }
} }
@@ -101,7 +101,7 @@ class SftpPlugin : Plugin(), OnSharedPreferenceChangeListener {
} else { } else {
val storageInfoList = SftpSettingsFragment.getStorageInfoList(context, this) val storageInfoList = SftpSettingsFragment.getStorageInfoList(context, this)
storageInfoList.sortBy { it.uri } storageInfoList.sortBy { it.uri }
if (storageInfoList.size <= 0) { if (storageInfoList.isEmpty()) {
device.sendPacket(NetworkPacket(PACKET_TYPE_SFTP).apply { device.sendPacket(NetworkPacket(PACKET_TYPE_SFTP).apply {
this["errorMessage"] = context.getString(R.string.sftp_no_storage_locations_configured) this["errorMessage"] = context.getString(R.string.sftp_no_storage_locations_configured)
}) })
@@ -127,7 +127,7 @@ class SftpPlugin : Plugin(), OnSharedPreferenceChangeListener {
this["password"] = server.regeneratePassword() this["password"] = server.regeneratePassword()
// Kept for compatibility, in case "multiPaths" is not possible or the other end does not support it // Kept for compatibility, in case "multiPaths" is not possible or the other end does not support it
this["path"] = if (paths.size == 1) paths[0] else "/" this["path"] = if (paths.size == 1) paths[0] else "/"
if (paths.size > 0) { if (paths.isNotEmpty()) {
this["multiPaths"] = paths this["multiPaths"] = paths
this["pathNames"] = pathNames this["pathNames"] = pathNames
} }
@@ -240,7 +240,7 @@ class SftpPlugin : Plugin(), OnSharedPreferenceChangeListener {
@Throws(JSONException::class) @Throws(JSONException::class)
fun fromJSON(jsonObject: JSONObject): StorageInfo { // TODO: Use Result after migrate callee to Kotlin fun fromJSON(jsonObject: JSONObject): StorageInfo { // TODO: Use Result after migrate callee to Kotlin
val displayName = jsonObject.getString(KEY_DISPLAY_NAME) val displayName = jsonObject.getString(KEY_DISPLAY_NAME)
val uri = Uri.parse(jsonObject.getString(KEY_URI)) val uri = jsonObject.getString(KEY_URI).toUri()
return StorageInfo(displayName, uri) return StorageInfo(displayName, uri)
} }

View File

@@ -48,6 +48,7 @@ import java.security.KeyPair
import java.security.MessageDigest import java.security.MessageDigest
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.security.PublicKey import java.security.PublicKey
import androidx.core.net.toUri
internal class SimpleSftpServer { internal class SimpleSftpServer {
private lateinit var sshd: SshServer private lateinit var sshd: SshServer
@@ -110,7 +111,7 @@ internal class SimpleSftpServer {
withFileSystemAccessor(object : SftpFileSystemAccessor { withFileSystemAccessor(object : SftpFileSystemAccessor {
fun notifyMediaStore(path: Path) { fun notifyMediaStore(path: Path) {
kotlin.runCatching { kotlin.runCatching {
val uri = Uri.parse(path.toUri().toString()) val uri = path.toUri().toString().toUri()
MediaStoreHelper.indexFile(context, uri) MediaStoreHelper.indexFile(context, uri)
uri uri
}.fold( }.fold(

View File

@@ -5,14 +5,12 @@
*/ */
package org.kde.kdeconnect.Plugins.SftpPlugin.saf package org.kde.kdeconnect.Plugins.SftpPlugin.saf
import android.content.ContentValues
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.Log import android.util.Log
import org.kde.kdeconnect.Helpers.MediaStoreHelper
import java.io.FileNotFoundException import java.io.FileNotFoundException
import java.io.IOException import java.io.IOException
import java.lang.reflect.Method import java.lang.reflect.Method

View File

@@ -17,7 +17,6 @@ import android.webkit.URLUtil;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.BackgroundService;

View File

@@ -20,7 +20,6 @@ import org.kde.kdeconnect_tp.R;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@PluginFactory.LoadablePlugin @PluginFactory.LoadablePlugin

View File

@@ -34,7 +34,6 @@ import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;

View File

@@ -21,6 +21,7 @@ import org.kde.kdeconnect.UserInterface.MainActivity
import org.kde.kdeconnect.extensions.setupBottomPadding import org.kde.kdeconnect.extensions.setupBottomPadding
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import org.kde.kdeconnect_tp.databinding.FragmentAboutBinding import org.kde.kdeconnect_tp.databinding.FragmentAboutBinding
import androidx.core.net.toUri
class AboutFragment : Fragment() { class AboutFragment : Fragment() {
private var _binding: FragmentAboutBinding? = null private var _binding: FragmentAboutBinding? = null
@@ -113,7 +114,7 @@ class AboutFragment : Fragment() {
button.visibility = View.GONE button.visibility = View.GONE
} else { } else {
button.setOnClickListener { button.setOnClickListener {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) startActivity(Intent(Intent.ACTION_VIEW, url.toUri()))
} }
} }
} }

View File

@@ -15,6 +15,7 @@ import androidx.appcompat.widget.TooltipCompat
import org.kde.kdeconnect.UserInterface.List.ListAdapter import org.kde.kdeconnect.UserInterface.List.ListAdapter
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import org.kde.kdeconnect_tp.databinding.AboutPersonListItemEntryBinding import org.kde.kdeconnect_tp.databinding.AboutPersonListItemEntryBinding
import androidx.core.net.toUri
class AboutPersonEntryItem(val person: AboutPerson) : ListAdapter.Item { class AboutPersonEntryItem(val person: AboutPerson) : ListAdapter.Item {
override fun inflateView(layoutInflater: LayoutInflater): View { override fun inflateView(layoutInflater: LayoutInflater): View {
@@ -31,7 +32,8 @@ class AboutPersonEntryItem(val person: AboutPerson) : ListAdapter.Item {
binding.aboutPersonListItemEntryVisitHomepageButton.visibility = View.VISIBLE binding.aboutPersonListItemEntryVisitHomepageButton.visibility = View.VISIBLE
TooltipCompat.setTooltipText(binding.aboutPersonListItemEntryVisitHomepageButton, layoutInflater.context.resources.getString(R.string.visit_contributors_homepage, person.webAddress)) TooltipCompat.setTooltipText(binding.aboutPersonListItemEntryVisitHomepageButton, layoutInflater.context.resources.getString(R.string.visit_contributors_homepage, person.webAddress))
binding.aboutPersonListItemEntryVisitHomepageButton.setOnClickListener { binding.aboutPersonListItemEntryVisitHomepageButton.setOnClickListener {
layoutInflater.context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(person.webAddress))) layoutInflater.context.startActivity(Intent(Intent.ACTION_VIEW,
person.webAddress.toUri()))
} }
} }

View File

@@ -194,7 +194,7 @@ public class CustomDevicesActivity extends BaseActivity<ActivityCustomDevicesBin
saveList(); saveList();
showEmptyListMessageIfRequired(); showEmptyListMessageIfRequired();
}) })
.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>() { .addCallback(new BaseTransientBottomBar.BaseCallback<>() {
@Override @Override
public void onDismissed(Snackbar transientBottomBar, int event) { public void onDismissed(Snackbar transientBottomBar, int event) {
switch (event) { switch (event) {
@@ -250,11 +250,17 @@ public class CustomDevicesActivity extends BaseActivity<ActivityCustomDevicesBin
showEmptyListMessageIfRequired(); showEmptyListMessageIfRequired();
} }
else { else {
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_duplicate, Toast.LENGTH_SHORT).show(); Context context = addDeviceDialog.getContext();
if (context != null) {
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_duplicate, Toast.LENGTH_SHORT).show();
}
} }
} }
else { else {
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_invalid, Toast.LENGTH_SHORT).show(); Context context = addDeviceDialog.getContext();
if (context != null) {
Toast.makeText(addDeviceDialog.getContext(), R.string.device_host_invalid, Toast.LENGTH_SHORT).show();
}
} }
} }
} }
@@ -266,8 +272,8 @@ public class CustomDevicesActivity extends BaseActivity<ActivityCustomDevicesBin
} }
private static class DeletedCustomDevice { private static class DeletedCustomDevice {
@NonNull DeviceHost hostnameOrIP; @NonNull final DeviceHost hostnameOrIP;
int position; final int position;
DeletedCustomDevice(@NonNull DeviceHost hostnameOrIP, int position) { DeletedCustomDevice(@NonNull DeviceHost hostnameOrIP, int position) {
this.hostnameOrIP = hostnameOrIP; this.hostnameOrIP = hostnameOrIP;

View File

@@ -39,7 +39,9 @@ import org.kde.kdeconnect.UserInterface.About.AboutFragment
import org.kde.kdeconnect.UserInterface.About.getApplicationAboutData import org.kde.kdeconnect.UserInterface.About.getApplicationAboutData
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import org.kde.kdeconnect_tp.databinding.ActivityMainBinding import org.kde.kdeconnect_tp.databinding.ActivityMainBinding
import java.util.LinkedList import androidx.core.content.edit
import androidx.core.view.size
import androidx.core.view.get
private const val MENU_ENTRY_ADD_DEVICE = 1 //0 means no-selection private const val MENU_ENTRY_ADD_DEVICE = 1 //0 means no-selection
private const val MENU_ENTRY_SETTINGS = 2 private const val MENU_ENTRY_SETTINGS = 2
@@ -59,7 +61,7 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
private var mCurrentDevice: String? = null private var mCurrentDevice: String? = null
private var mCurrentMenuEntry = 0 private var mCurrentMenuEntry = 0
private set(value) { set(value) {
field = value field = value
//Enabling "go to default fragment on back" callback when user in settings or "about" fragment //Enabling "go to default fragment on back" callback when user in settings or "about" fragment
mainFragmentCallback.isEnabled = value == MENU_ENTRY_SETTINGS || value == MENU_ENTRY_ABOUT mainFragmentCallback.isEnabled = value == MENU_ENTRY_SETTINGS || value == MENU_ENTRY_ABOUT
@@ -87,7 +89,7 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
val root = binding.root val root = binding.root
setContentView(root) setContentView(root)
mDrawerLayout = if (root is DrawerLayout) root else null mDrawerLayout = root as? DrawerLayout
val mDrawerHeader = mNavigationView.getHeaderView(0) val mDrawerHeader = mNavigationView.getHeaderView(0)
mNavViewDeviceName = mDrawerHeader.findViewById(R.id.device_name) mNavViewDeviceName = mDrawerHeader.findViewById(R.id.device_name)
@@ -115,17 +117,17 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
when (mCurrentMenuEntry) { when (mCurrentMenuEntry) {
MENU_ENTRY_ADD_DEVICE -> { MENU_ENTRY_ADD_DEVICE -> {
mCurrentDevice = null mCurrentDevice = null
preferences.edit().putString(STATE_SELECTED_DEVICE, null).apply() preferences.edit { putString(STATE_SELECTED_DEVICE, null) }
setContentFragment(PairingFragment()) setContentFragment(PairingFragment())
} }
MENU_ENTRY_SETTINGS -> { MENU_ENTRY_SETTINGS -> {
preferences.edit().putString(STATE_SELECTED_DEVICE, null).apply() preferences.edit { putString(STATE_SELECTED_DEVICE, null) }
setContentFragment(SettingsFragment()) setContentFragment(SettingsFragment())
} }
MENU_ENTRY_ABOUT -> { MENU_ENTRY_ABOUT -> {
preferences.edit().putString(STATE_SELECTED_DEVICE, null).apply() preferences.edit { putString(STATE_SELECTED_DEVICE, null) }
setContentFragment(AboutFragment.newInstance(getApplicationAboutData(this))) setContentFragment(AboutFragment.newInstance(getApplicationAboutData(this)))
} }
@@ -207,7 +209,7 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
} }
} }
if(missingPermissions.size > 0){ if(missingPermissions.isNotEmpty()){
ActivityCompat.requestPermissions(this, missingPermissions.toTypedArray(), RESULT_NOTIFICATIONS_ENABLED) ActivityCompat.requestPermissions(this, missingPermissions.toTypedArray(), RESULT_NOTIFICATIONS_ENABLED)
} }
} }
@@ -303,7 +305,7 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
@JvmOverloads @JvmOverloads
fun onDeviceSelected(deviceId: String?, fromDeviceList: Boolean = false) { fun onDeviceSelected(deviceId: String?, fromDeviceList: Boolean = false) {
mCurrentDevice = deviceId mCurrentDevice = deviceId
preferences.edit().putString(STATE_SELECTED_DEVICE, deviceId).apply() preferences.edit { putString(STATE_SELECTED_DEVICE, deviceId) }
if (mCurrentDevice != null) { if (mCurrentDevice != null) {
mCurrentMenuEntry = deviceIdToMenuEntryId(deviceId) mCurrentMenuEntry = deviceIdToMenuEntryId(deviceId)
if (mCurrentMenuEntry == MENU_ENTRY_DEVICE_UNKNOWN) { if (mCurrentMenuEntry == MENU_ENTRY_DEVICE_UNKNOWN) {
@@ -364,9 +366,9 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
if (isPermissionGranted(permissions, grantResults, Manifest.permission.BLUETOOTH_CONNECT) && if (isPermissionGranted(permissions, grantResults, Manifest.permission.BLUETOOTH_CONNECT) &&
isPermissionGranted(permissions, grantResults, Manifest.permission.BLUETOOTH_SCAN)) { isPermissionGranted(permissions, grantResults, Manifest.permission.BLUETOOTH_SCAN)) {
val preferenceEditor = PreferenceManager.getDefaultSharedPreferences(this).edit() PreferenceManager.getDefaultSharedPreferences(this).edit {
preferenceEditor.putBoolean(SettingsFragment.KEY_BLUETOOTH_ENABLED, true) putBoolean(SettingsFragment.KEY_BLUETOOTH_ENABLED, true)
preferenceEditor.apply() }
setContentFragment(SettingsFragment()) setContentFragment(SettingsFragment())
} }
@@ -390,9 +392,9 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
} }
private fun uncheckAllMenuItems(menu: Menu) { private fun uncheckAllMenuItems(menu: Menu) {
val size = menu.size() val size = menu.size
for (i in 0 until size) { for (i in 0 until size) {
val item = menu.getItem(i) val item = menu[i]
item.subMenu?.let { uncheckAllMenuItems(it) } ?: item.setChecked(false) item.subMenu?.let { uncheckAllMenuItems(it) } ?: item.setChecked(false)
} }
} }

View File

@@ -6,12 +6,12 @@
package org.kde.kdeconnect.UserInterface package org.kde.kdeconnect.UserInterface
import android.Manifest import android.Manifest
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.InputFilter import android.text.InputFilter
@@ -21,6 +21,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import androidx.activity.result.ActivityResultLauncher
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
@@ -28,14 +29,22 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.apache.commons.io.IOUtils
import org.kde.kdeconnect.BackgroundService import org.kde.kdeconnect.BackgroundService
import org.kde.kdeconnect.Helpers.CreateFileParams
import org.kde.kdeconnect.Helpers.CreateFileResultContract
import org.kde.kdeconnect.Helpers.DeviceHelper import org.kde.kdeconnect.Helpers.DeviceHelper
import org.kde.kdeconnect.Helpers.DeviceHelper.filterName import org.kde.kdeconnect.Helpers.DeviceHelper.filterName
import org.kde.kdeconnect.Helpers.DeviceHelper.getDeviceName import org.kde.kdeconnect.Helpers.DeviceHelper.getDeviceName
import org.kde.kdeconnect.Helpers.NotificationHelper import org.kde.kdeconnect.Helpers.NotificationHelper
import org.kde.kdeconnect.UserInterface.ThemeUtil.applyTheme import org.kde.kdeconnect.UserInterface.ThemeUtil.applyTheme
import org.kde.kdeconnect.extensions.setupBottomPadding import org.kde.kdeconnect.extensions.setupBottomPadding
import org.kde.kdeconnect_tp.BuildConfig
import org.kde.kdeconnect_tp.R import org.kde.kdeconnect_tp.R
import java.io.InputStreamReader
class SettingsFragment : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@@ -57,6 +66,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
devicesByIpPref(context), devicesByIpPref(context),
udpBroadcastPref(context), udpBroadcastPref(context),
bluetoothSupportPref(context), bluetoothSupportPref(context),
exportLogsPref(context),
moreSettingsPref(context), moreSettingsPref(context),
).forEach(screen::addPreference) ).forEach(screen::addPreference)
@@ -218,6 +228,32 @@ class SettingsFragment : PreferenceFragmentCompat() {
} }
} }
private fun exportLogsPref(context: Context) = Preference(context).apply {
isPersistent = false
setTitle(R.string.settings_export_logs)
setSummary(R.string.settings_export_logs_text)
onPreferenceClickListener = Preference.OnPreferenceClickListener {
exportLogs.launch(CreateFileParams("text/plain", "kdeconnect-log.txt"))
true
}
}
private val exportLogs: ActivityResultLauncher<CreateFileParams> = registerForActivityResult(
CreateFileResultContract()
) { uri: Uri? ->
val output = uri?.let { context?.contentResolver?.openOutputStream(uri) } ?: return@registerForActivityResult
CoroutineScope(Dispatchers.IO).launch {
val pid = android.os.Process.myPid()
val process = Runtime.getRuntime().exec(arrayOf("logcat", "-d", "--pid=$pid"))
val reader = InputStreamReader(process.inputStream)
output.use {
it.write("KDE Connect ${BuildConfig.VERSION_NAME}\n".toByteArray(Charsets.UTF_8))
it.write("Android ${Build.VERSION.RELEASE} (${Build.MANUFACTURER} ${Build.MODEL})\n".toByteArray(Charsets.UTF_8))
IOUtils.copy(reader, it, Charsets.UTF_8)
}
}
}
private fun moreSettingsPref(context: Context) = Preference(context).apply { private fun moreSettingsPref(context: Context) = Preference(context).apply {
isPersistent = false isPersistent = false
isSelectable = false isSelectable = false

View File

@@ -6,9 +6,8 @@
package org.kde.kdeconnect.UserInterface package org.kde.kdeconnect.UserInterface
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import org.apache.commons.lang3.StringUtils import androidx.core.net.toUri
class StartActivityAlertDialogFragment : AlertDialogFragment() { class StartActivityAlertDialogFragment : AlertDialogFragment() {
private var intentAction: String? = null private var intentAction: String? = null
@@ -36,8 +35,9 @@ class StartActivityAlertDialogFragment : AlertDialogFragment() {
setCallback(object : Callback() { setCallback(object : Callback() {
override fun onPositiveButtonClicked() { override fun onPositiveButtonClicked() {
val intentUrl = intentUrl
val intent = if (!intentUrl.isNullOrEmpty()) { val intent = if (!intentUrl.isNullOrEmpty()) {
Intent(intentAction, Uri.parse(intentUrl)) Intent(intentAction, intentUrl.toUri())
} else { } else {
Intent(intentAction) Intent(intentAction)
} }

View File

@@ -9,8 +9,11 @@ package org.kde.kdeconnect.UserInterface.compose
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.* import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource

View File

@@ -6,7 +6,6 @@
package org.kde.kdeconnect.async package org.kde.kdeconnect.async
import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.atomic.AtomicLong
import kotlin.concurrent.Volatile
abstract class BackgroundJob<I, R> : Runnable { abstract class BackgroundJob<I, R> : Runnable {
private val callback: Callback<R> private val callback: Callback<R>

View File

@@ -129,7 +129,7 @@ class DeviceTest {
val deviceId = "testDevice" val deviceId = "testDevice"
val settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE) val settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE)
val deviceInfo = loadFromSettings(context, deviceId, settings) val deviceInfo = loadFromSettings(context, deviceId, settings)
deviceInfo.protocolVersion = DeviceHelper.ProtocolVersion deviceInfo.protocolVersion = DeviceHelper.PROTOCOL_VERSION
deviceInfo.incomingCapabilities = hashSetOf("kdeconnect.plugin1State", "kdeconnect.plugin2State") deviceInfo.incomingCapabilities = hashSetOf("kdeconnect.plugin1State", "kdeconnect.plugin2State")
deviceInfo.outgoingCapabilities = hashSetOf("kdeconnect.plugin1State.request", "kdeconnect.plugin2State.request") deviceInfo.outgoingCapabilities = hashSetOf("kdeconnect.plugin1State.request", "kdeconnect.plugin2State.request")
@@ -208,7 +208,7 @@ class DeviceTest {
val deviceId = "unpairedTestDevice" val deviceId = "unpairedTestDevice"
fakeNetworkPacket["deviceId"] = deviceId fakeNetworkPacket["deviceId"] = deviceId
fakeNetworkPacket["deviceName"] = "Unpaired Test Device" fakeNetworkPacket["deviceName"] = "Unpaired Test Device"
fakeNetworkPacket["protocolVersion"] = DeviceHelper.ProtocolVersion fakeNetworkPacket["protocolVersion"] = DeviceHelper.PROTOCOL_VERSION
fakeNetworkPacket["deviceType"] = DeviceType.PHONE.toString() fakeNetworkPacket["deviceType"] = DeviceType.PHONE.toString()
val certificateString = val certificateString =
""" """

View File

@@ -12,7 +12,6 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
import org.kde.kdeconnect.MockSharedPreference import org.kde.kdeconnect.MockSharedPreference
import org.kde.kdeconnect.PairingHandler
import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers
import org.mockito.MockedStatic import org.mockito.MockedStatic
import org.mockito.Mockito import org.mockito.Mockito