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

Compare commits

..

49 Commits

Author SHA1 Message Date
Albert Vaca Cintora
d6a284cb25 Bump all the versions 2021-01-06 12:47:33 +01:00
Albert Vaca Cintora
fc0320e2b3 Release 1.16.0 2021-01-06 12:38:07 +01:00
Térence Clastres
d914c8763f StoragePreference: Use findViewbyId() instead of trying to bind
Since commit f3ada9738d, the app crashes with a ClassCastException when going into the "expose filesystem" menu.

This is because view binding checks if the root view is the one it expects but the one passed here isn't directly the checkbox layout but instead the whole StoragePreference layout.

Because there is one view to bind, view binding here doesn't make much sense so replace it with a findViewbyId().
Also  add viewBindingIgnore="true" to the view so its binding class is
not generated.
2021-01-05 09:17:49 +00:00
l10n daemon script
e95e66b1cb GIT_SILENT made messages (after extraction) 2021-01-05 02:28:04 +01:00
l10n daemon script
d1d451be46 GIT_SILENT made messages (after extraction) 2020-12-22 02:27:52 +01:00
l10n daemon script
d2cd898d6a GIT_SILENT made messages (after extraction) 2020-12-21 02:28:42 +01:00
l10n daemon script
bc9032119a GIT_SILENT made messages (after extraction) 2020-12-20 02:27:31 +01:00
l10n daemon script
ccc1e74e88 GIT_SILENT made messages (after extraction) 2020-12-13 02:29:10 +01:00
Nicolas Fella
f6ca187fb2 Fix not sending our own notification
This got broken during some MMS work.

BUG: 428754
BUG: 428715
2020-12-07 19:51:49 +00:00
l10n daemon script
edc2de7a33 GIT_SILENT made messages (after extraction) 2020-12-07 02:26:56 +01:00
l10n daemon script
b107c3d37c GIT_SILENT made messages (after extraction) 2020-12-04 02:25:40 +01:00
l10n daemon script
66b6c2c533 GIT_SILENT made messages (after extraction) 2020-12-02 02:29:20 +01:00
l10n daemon script
4691123cfe GIT_SILENT made messages (after extraction) 2020-11-30 02:26:02 +01:00
l10n daemon script
15a18fa5b8 GIT_SILENT made messages (after extraction) 2020-11-29 02:23:59 +01:00
l10n daemon script
af791fa676 GIT_SILENT made messages (after extraction) 2020-11-28 02:25:52 +01:00
Albert Vaca Cintora
d09ad45e11 Add a verification key that's displayed when pairing
The key is a sha256 of both devices' certificates. Both should generate the
same key, so hey user can check they are pairing against the right device.

Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
2020-11-26 11:30:53 +01:00
l10n daemon script
dd210259b9 GIT_SILENT made messages (after extraction) 2020-11-22 02:22:34 +01:00
l10n daemon script
19f4cd8c42 GIT_SILENT made messages (after extraction) 2020-11-14 02:25:02 +01:00
l10n daemon script
f6482155c4 GIT_SILENT made messages (after extraction) 2020-11-13 02:24:18 +01:00
l10n daemon script
e26ff862a9 GIT_SILENT made messages (after extraction) 2020-11-11 02:42:17 +01:00
l10n daemon script
43800903a2 GIT_SILENT made messages (after extraction) 2020-11-08 02:24:09 +01:00
l10n daemon script
3f829681a2 GIT_SILENT made messages (after extraction) 2020-11-07 02:23:40 +01:00
Isira Seneviratne
9aeb49ad49 Remove Butterknife. 2020-11-03 11:31:20 +00:00
Isira Seneviratne
b52ba2e29d Use view binding in EditTextAlertDialogFragment. 2020-11-03 11:31:20 +00:00
Isira Seneviratne
8d71354aea Use view binding in CustomDevicesAdapter. 2020-11-03 11:31:20 +00:00
Isira Seneviratne
10b2d561f0 Use view binding in StoragePreferenceDialogFragment. 2020-11-03 11:31:20 +00:00
Isira Seneviratne
f3ada9738d Use view binding in StoragePreference. 2020-11-03 11:31:19 +00:00
Isira Seneviratne
df1a9fb557 Use view binding in MprisActivity. 2020-11-03 11:31:19 +00:00
Albert Vaca Cintora
baa7032e9d Release 1.15.1 2020-11-02 10:18:05 +01:00
Simon Redman
d206b737d3 [SMS App] Allow sending MMS while not the default messaging app
## Summary

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

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

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

This is a collaboration between myself and Albert Vaca

## Test Plan

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

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.kde.kdeconnect_tp"
android:versionCode="11501"
android:versionName="1.15.0">
android:versionCode="11600"
android:versionName="1.16.0">
<supports-screens
android:anyDensity="true"
@@ -37,7 +37,6 @@
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
@@ -54,22 +53,6 @@
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/KdeConnectTheme"
android:name="org.kde.kdeconnect.MyApplication">
<receiver
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.SmsReceiver"
android:exported="true"
android:permission="android.permission.BROADCAST_SMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_DELIVER" />
</intent-filter>
<intent-filter android:priority="999">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<receiver
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.SmsSentReceiver"
android:exported="true"
android:taskAffinity="${applicationId}.SMS_SENT" />
<receiver
android:name="com.android.mms.transaction.PushReceiver"
@@ -82,39 +65,6 @@
</intent-filter>
</receiver>
<receiver
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.DelegatingMmsReceivedReceiver"
android:enabled="true"
android:exported="true"
android:taskAffinity="com.klinker.android.messaging.MMS_RECEIVED" />
<receiver
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.MmsSentReceiverImpl"
android:exported="true"
android:taskAffinity="com.klinker.android.messaging.MMS_SENT" />
<receiver
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.NotificationReplyReceiver"
android:enabled="true"
android:exported="true">
</receiver>
<service
android:name="org.kde.kdeconnect.Plugins.SMSPlugin.HeadlessSmsSendService"
android:exported="true"
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE">
<intent-filter>
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
</service>
<service
android:name="com.android.mms.transaction.TransactionService"
android:enabled="true"
@@ -140,23 +90,10 @@
android:name="org.kde.kdeconnect.UserInterface.MainActivity"
android:label="KDE Connect"
android:theme="@style/KdeConnectTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sms" />
<data android:scheme="smsto" />
<data android:scheme="mms" />
<data android:scheme="mmsto" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.app.role.SMS"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />

View File

@@ -6,13 +6,15 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
buildscript {
ext.kotlin_version = '1.4.10'
ext.kotlin_version = '1.4.21'
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.2'
// Pinned 4.0.2 due to this bug on 4.1: https://issuetracker.google.com/issues/172096891
//noinspection GradleDependency
classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
@@ -150,10 +152,10 @@ dependencies {
*/
}
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.media:media:1.2.0'
implementation 'androidx.media:media:1.2.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.preference:preference:1.1.1'
@@ -174,10 +176,6 @@ dependencies {
//implementation('com.github.bright:slf4android:0.1.6') { transitive = true } // For org.apache.sshd debugging
implementation 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0' //For SSL certificate generation
//noinspection AnnotationProcessorOnCompilePath - c.f. https://issuetracker.google.com/issues/140881211
implementation 'com.jakewharton:butterknife:10.2.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.0.0'
implementation 'org.atteo.classindex:classindex:3.6'
annotationProcessor 'org.atteo.classindex:classindex:3.6'
@@ -185,7 +183,7 @@ dependencies {
implementation 'commons-io:commons-io:2.7'
implementation 'org.apache.commons:commons-collections4:4.4'
implementation 'org.apache.commons:commons-lang3:3.10'
implementation 'org.apache.commons:commons-lang3:3.11'
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

View File

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

10
res/drawable/ic_key.xml Normal file
View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
</vector>

View File

@@ -30,6 +30,18 @@
android:text="@string/device_not_paired"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<TextView
android:id="@+id/pair_verification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:drawableLeft="@drawable/ic_key"
android:drawableStart="@drawable/ic_key"
android:layout_marginBottom="8dip"
android:visibility="gone"
android:text=""
android:textAppearance="?android:attr/textAppearanceMedium"/>
<Button
android:id="@+id/pair_button"
android:layout_width="match_parent"

View File

@@ -19,6 +19,7 @@
<include
layout="@layout/mpris_control"
android:id="@+id/mpris_control"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

View File

@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<CheckBox
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:clickable="false"
tools:viewBindingIgnore="true"
android:background="@null"/>

View File

@@ -69,7 +69,6 @@
<string name="encryption_info_title">معلومات التّعمية</string>
<string name="encryption_info_msg_no_ssl">لا يستخدم الجهاز الآخر إصدارة حديثة من «كدي المتّصل»، ستُستخدم طريقة التّعمية القديمة.</string>
<string name="my_device_fingerprint">بصمة SHA1 لشهادة جهازك هي:</string>
<string name="remote_device_fingerprint">بصمة SHA1 لشهادة الجهاز البعيد هي:</string>
<string name="pair_requested">طُلب الاقتران</string>
<string name="pairing_request_from">طلب اقتران من %1s</string>
<string name="received_file_text">المس لفتح \'%1s\'</string>

View File

@@ -12,7 +12,7 @@
<string name="pref_plugin_clipboard">Sincronización del cartafueyu</string>
<string name="pref_plugin_clipboard_desc">Comparte\'l conteníu del cartafueyu</string>
<string name="pref_plugin_mousepad">Entrada remota</string>
<string name="pref_plugin_mousepad_desc">Usa\'l preséu como panel táutil y tecláu</string>
<string name="pref_plugin_mousepad_desc">Usa\'l preséu como panel táctil y tecláu</string>
<string name="pref_plugin_presenter">Mandu pa presentaciones de diapositives</string>
<string name="pref_plugin_presenter_desc">Usa esti preséu pa cambiar de diapositives nuna presentación</string>
<string name="pref_plugin_remotekeyboard">Receición de calcos de tecles remotos</string>
@@ -41,14 +41,14 @@
<string name="remotekeyboard_editing_only_title">Remanar les tecles remotes namás al editar</string>
<string name="remotekeyboard_connected">La conexón col tecláu remotu ta activa</string>
<string name="open_mousepad">Entrada remota</string>
<string name="mousepad_info">Movi un deu pela pantalla pa mover el mur. Con un toque faes un clic esquierdu y teniendo primío faes l\'aición d\'arrastrar y soltar, col toque de dos deos simules un clic drechu y col toque de tres simules un clic col botón d\'en mediu del mur. Esliza dos deos pela pantalla pa desplazate.</string>
<string name="mousepad_info">Movi un deu pela pantalla pa mover el mur. Con un toque faes un clic esquierdu y teniendo primío faes l\'aición d\'arrastrar y soltar, col toque de dos deos simules un clic derechu y col toque de tres simules un clic col botón d\'en mediu del mur. Esliza dos deos pela pantalla pa desplazate.</string>
<string name="mousepad_double_tap_settings_title">Aición al tocar con dos deos</string>
<string name="mousepad_triple_tap_settings_title">Aición al tocar con tres deos</string>
<string name="mousepad_sensitivity_settings_title">Sensibilidá del panel táutil</string>
<string name="mousepad_sensitivity_settings_title">Sensibilidá del panel táctil</string>
<string name="mousepad_acceleration_profile_settings_title">Aceleración del punteru</string>
<string name="mousepad_scroll_direction_title">Direición de desplazamientu inversa</string>
<string-array name="mousepad_tap_entries">
<item>Clic drechu</item>
<item>Clic derechu</item>
<item>Clic d\'en mediu</item>
<item>Nada</item>
</string-array>
@@ -78,7 +78,7 @@
<string name="encryption_info_title">Información del cifráu</string>
<string name="encryption_info_msg_no_ssl">L\'otru preséu nun executa una versión recién de KDE Connect y usa\'l métodu heredáu de cifráu.</string>
<string name="my_device_fingerprint">La buelga SHA1 d\'esti preséu ye:</string>
<string name="remote_device_fingerprint">La buelga SHA1 del preséu remotu ye:</string>
<string name="remote_device_fingerprint">La buelga SHA256 del certificáu del preséu remotu:</string>
<string name="pair_requested">Solicitóse l\'empareyamientu</string>
<string name="pairing_request_from">Solicitú d\'empareyamientu de %1s</string>
<plurals name="incoming_file_title">
@@ -116,7 +116,7 @@
<string name="received_file_text">Toca p\'abrir «%1s»</string>
<string name="cannot_create_file">Nun pue crease\'l ficheru %s</string>
<string name="tap_to_answer">Toca pa responder</string>
<string name="right_click">Unviar un clic drechu</string>
<string name="right_click">Unviar un clic derechu</string>
<string name="middle_click">Unviar un clic d\'en mediu</string>
<string name="show_keyboard">Amosar el tecláu</string>
<string name="device_not_paired">Nun s\'empareyó\'l preséu</string>
@@ -142,7 +142,7 @@
<string name="custom_devices_settings">Llista de preseos personalizada</string>
<string name="custom_device_list">Amestar preseos pola IP</string>
<string name="custom_device_deleted">Desanicióse\'l preséu personalizáu</string>
<string name="custom_device_list_help">Si\'l preséu nun se deteuta automáticamente, pues amestar la so direición IP o nome d\'agospiu calcando nel botón flotante d\'aición</string>
<string name="custom_device_list_help">Si\'l preséu nun se detecta automáticamente, pues amestar la so direición IP o nome d\'agospiu calcando nel botón flotante d\'aición</string>
<string name="undo">Desfacer</string>
<string name="share_notification_preference">Avisos sonoros</string>
<string name="share_notification_preference_summary">Fai que\'l teléfonu vibre y reproduza un soníu al recibir un ficheru</string>
@@ -189,7 +189,7 @@
<string name="plugins_need_optional_permission">Dalgunos plugins tienen carauterísiques desactivaes pola falta de permisos (tócalos pa más información):</string>
<string name="select_ringtone">Timbre</string>
<string name="telephony_pref_blocked_title">Númberos bloquiaos</string>
<string name="telephony_pref_blocked_dialog_desc">Nun amuesa les llamaes nin SMS de los númberos que configures. Especifica un númberu per llinia.</string>
<string name="telephony_pref_blocked_dialog_desc">Nun s\'amuesen les llamaes nin SMS de los númberos que configures. Especifica un númberu per llinia.</string>
<string name="presenter_fullscreen">Pantalla completa</string>
<string name="presenter_exit">Colar de la presentación</string>
<string name="presenter_lock_tip">Pues bloquiar esti preséu y usar les tecles de volume pa dir a la diapositiva anterior/siguiente</string>
@@ -204,8 +204,11 @@
<string name="notification_channel_media_control">Mandu multimedia</string>
<string name="notification_channel_filetransfer">Tresferencia de ficheros</string>
<string name="notification_channel_high_priority">Prioridá alta</string>
<string name="notification_channel_sms_mms">Mensaxes nuevos</string>
<string name="copy_url_to_clipboard">Copiar la URL al cartafueyu</string>
<string name="clipboard_toast">Copióse al cartafueyu</string>
<string name="runcommand_notreachable">El preséu nun ye algamable</string>
<string name="runcommand_notpaired">El preséu nun ta empareyáu</string>
<string name="runcommand_noruncommandplugin">Esti preséu nun tien activáu\'l plugin Execución de comandos</string>
<string name="pref_plugin_findremotedevice_desc">Fai que\'l preséu remotu suene</string>
<string name="ring">Facer sonar</string>
@@ -237,21 +240,28 @@
<string name="location_permission_needed_title">Ríquese un permisu</string>
<string name="location_permission_needed_desc">Android rique\'l permisu Allugamientu pa indentificar redes Wi-Fi</string>
<string name="clipboard_android_x_incompat">Android 10 quitó l\'accesu al cartafueyu en toles aplicaciones. Esti plugin va desactivase.</string>
<string name="cant_open_url">Nun pue abrise la URL pa siguir cola reproducción</string>
<string name="bigscreen_optional_permission_explanation">Pa compartir la entrada del micrófonu del preséu precises dar accesu a la entrada d\'audiu del preséu</string>
<string name="message_reply_label">RESPONDER</string>
<string name="user_display_name">Tu</string>
<string name="set_long_text_as_mms_title">Unviar testos llargos como MMS</string>
<string name="convert_to_mms_after_title">Conversión a MMS</string>
<string-array name="convert_to_mms_after_entries">
<item>After one message</item>
<item>After two messages</item>
<item>After three messages</item>
<item>After four messages</item>
<item>After five messages</item>
<item>Dempués d\'un mensaxes</item>
<item>Dempués de dos mensaxes</item>
<item>Dempués de tres mensaxes</item>
<item>Dempués de cuatro mensaxes</item>
<item>Dempués de cinco mensaxes</item>
</string-array>
<string name="theme_dialog_title">Estilu de l\'aplicación</string>
<string-array name="theme_list">
<item>Set by Battery Saver</item>
<item>Light</item>
<item>Dark</item>
<item>Claridá</item>
<item>Escuridá</item>
</string-array>
<string-array name="theme_list_v28">
<item/>
<item>Light</item>
<item>Dark</item>
<item>System Default</item>
<item>Claridá</item>
<item>Escuridá</item>
</string-array>
</resources>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_clipboard">Mübadilə Buferini eyniləşdirmək</string>
<string name="pref_plugin_clipboard_desc">Mübadilə buferi tərkiblərini paylaşmaq</string>
<string name="pref_plugin_clipboard_sent">Mübadilə buferini göndərmək</string>
<string name="pref_plugin_mousepad">Məsafədən yazmaq</string>
<string name="pref_plugin_mousepad">Məsafədən giriş</string>
<string name="pref_plugin_mousepad_desc">Telefon və Planşetinizi toxunma paneli və klaviatura kimi istifadə edin</string>
<string name="pref_plugin_presenter">Məsafədən slayd göstərişi</string>
<string name="pref_plugin_presenter_desc">Cihazınızı təqdimatda slaydları dəyişmək üçün istifadə edin</string>
@@ -47,7 +47,7 @@
<string name="remotekeyboard_not_connected">Aktiv bir uzaq klaviatura bağlantısı yoxdur, onu kdeconnect-də yaradın</string>
<string name="remotekeyboard_connected">Uzaq klaviatura bağlantısını aktiv edin</string>
<string name="remotekeyboard_multiple_connections">Birdən çox uzaq klaviatura bağlantısı var, tənzimləmək üçün cihazı seçin</string>
<string name="open_mousepad">Məsafədən yazmaq</string>
<string name="open_mousepad">Məsafədən giriş</string>
<string name="mousepad_info">Siçan kursorunu hərəkət etdirmək üçün barmağı ekranda sürüşdürün. Klik üçün ekrana vurun, sağ və orta siçan düymələri üçün iki/üç barmaqla toxunuş edin. Sürüşdürmək üçün iki barmaqdan istifadə edin. Tutub saxlamaq üçün basın saxlayın.</string>
<string name="mousepad_double_tap_settings_title">İki barmaq toxunuşu əməlini təyin edin</string>
<string name="mousepad_triple_tap_settings_title">Üç barmaq toxunuşu əməlini təyin edin</string>
@@ -90,7 +90,7 @@
<string name="encryption_info_title">Şifrələmə məlumatı</string>
<string name="encryption_info_msg_no_ssl">Digər cihaz köhnə şifrələmə metodu istifadə edən KDE Connect\'in sonuncu versiyasını istifadə etmir.</string>
<string name="my_device_fingerprint">Cihazınızın SHA1 şifrə izi:</string>
<string name="remote_device_fingerprint">Uzaq cihazın SHA1 şifrə izi:</string>
<string name="remote_device_fingerprint">Uzaq cihaz sertifikatının SHA256 şifrə izi:</string>
<string name="pair_requested">Qoşulma soruşuldu</string>
<string name="pairing_request_from">%1s tərəfindən qoşulma sorğusu</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Bu qoşmanın işləməsi üçün icazələr lazımdır</string>
<string name="optional_permission_explanation">Bütün funksiyaların işləməsi üçün əlavə icazələr verməlisiniz</string>
<string name="plugins_need_optional_permission">Bəzi qoşmalarda icazə çatışmamazlığı səbəbindən bir sıra imkanlar söndürülmüşdür (daha çox məlumat üçün toxunun)</string>
<string name="share_optional_permission_explanation">Telefonunuz və İş Masanız arasında fayllar mübadiləsi üçün telefon yaddaşına girişə icazə verməlisiniz</string>
<string name="share_optional_permission_explanation">Paylaşılan faylları qəbul etmək üçün saxlama qovluğu seçməlisiniz</string>
<string name="telepathy_permission_explanation">İş Masanızdan telefonunuzdakı SMS\'ləri oxumaq və SMS göndərmək üçün SMS\'ə girişə icazə verməlisiniz</string>
<string name="telephony_permission_explanation">İş Masanızda telefon zənglərini görmək üçün Zəng Tarixşəsinə və Zəng yığımı vəziyyətinə icazə verməlisiniz</string>
<string name="telephony_optional_permission_explanation">Telefon nömrəsi əvəzinə kontaktın adını görmək üçün Kontakt Kitabçasına girişə icazə verməlisiniz</string>

View File

@@ -16,7 +16,7 @@
<string name="pref_plugin_mousepad">Entrada remota</string>
<string name="pref_plugin_mousepad_desc">Usa el vostre telèfon o tauleta com un ratolí i un teclat</string>
<string name="pref_plugin_presenter">Presentació de diapositives remota</string>
<string name="pref_plugin_presenter_desc">Usa el dispositiu per a canviar les dispositives d\'una presentació</string>
<string name="pref_plugin_presenter_desc">Usa el dispositiu per a canviar les diapositives d\'una presentació</string>
<string name="pref_plugin_remotekeyboard">Rep les pulsacions remotes de tecla</string>
<string name="pref_plugin_remotekeyboard_desc">Rep els esdeveniments de pulsacions de tecla des de dispositius remots</string>
<string name="pref_plugin_mpris">Controls multimèdia</string>
@@ -30,7 +30,7 @@
<string name="pref_plugin_notifications">Sincronitza les notificacions</string>
<string name="pref_plugin_notifications_desc">Accedeix a les vostres notificacions des d\'altres dispositius</string>
<string name="pref_plugin_receive_notifications">Rep les notificacions</string>
<string name="pref_plugin_receive_notifications_desc">Rep notificacions des d\'altres dispositius i mostrar-los a l\'Android</string>
<string name="pref_plugin_receive_notifications_desc">Rep notificacions des d\'altres dispositius i mostrar-les a l\'Android</string>
<string name="pref_plugin_sharereceiver">Comparteix i rep</string>
<string name="pref_plugin_sharereceiver_desc">Comparteix els fitxers i URL entre els dispositius</string>
<string name="device_list_empty">No hi ha cap dispositiu</string>
@@ -77,8 +77,8 @@
<string name="category_connected_devices">Dispositius connectats</string>
<string name="category_not_paired_devices">Dispositius disponibles</string>
<string name="category_remembered_devices">Dispositius recordats</string>
<string name="device_menu_plugins">Arranjament del connector</string>
<string name="device_menu_unpair">Desparella</string>
<string name="device_menu_plugins">Arranjament dels connectors</string>
<string name="device_menu_unpair">Desaparella</string>
<string name="pair_new_device">Aparella amb un dispositiu nou</string>
<string name="unknown_device">Dispositiu desconegut</string>
<string name="error_not_reachable">No es pot accedir al dispositiu</string>
@@ -90,7 +90,7 @@
<string name="encryption_info_title">Informació de l\'encriptatge</string>
<string name="encryption_info_msg_no_ssl">L\'altre dispositiu no usa una versió recent del KDE Connect, s\'utilitzarà el mètode d\'encriptatge antic.</string>
<string name="my_device_fingerprint">L\'empremta digital SHA1 del certificat del vostre dispositiu és:</string>
<string name="remote_device_fingerprint">L\'empremta digital SHA1 del certificat del dispositiu remot és:</string>
<string name="remote_device_fingerprint">L\'empremta digital SHA256 del certificat del dispositiu remot és:</string>
<string name="pair_requested">S\'ha demanat aparellar</string>
<string name="pairing_request_from">S\'ha demanat aparellar des de %1s</string>
<plurals name="incoming_file_title">
@@ -157,7 +157,7 @@
<string name="mpris_notification_settings_summary">Permet controlar els reproductors multimèdia sense obrir el KDE Connect</string>
<string name="share_to">Comparteix amb...</string>
<string name="protocol_version_newer">Aquest dispositiu usa una versió nova del protocol</string>
<string name="plugin_settings_with_name">Ajustaments del %s</string>
<string name="plugin_settings_with_name">Configuració del %s</string>
<string name="invalid_device_name">El nom del dispositiu no és vàlid</string>
<string name="shareplugin_text_saved">S\'ha rebut text i s\'ha desat al porta-retalls</string>
<string name="custom_devices_settings">Llista personalitzada de dispositius</string>
@@ -166,7 +166,7 @@
<string name="custom_device_list_help">Si el dispositiu no es detecta automàticament, podeu afegir la seva adreça IP o el nom de la màquina fent clic al botó flotant d\'acció</string>
<string name="custom_device_fab_hint">Afegeix un dispositiu</string>
<string name="undo">Desfés</string>
<string name="share_notification_preference">Rebombori de les notificacions</string>
<string name="share_notification_preference">Notificacions sonores</string>
<string name="share_notification_preference_summary">Vibra i reprodueix un so en rebre un fitxer</string>
<string name="share_destination_customize">Personalitza el directori de destinació</string>
<string name="share_destination_customize_summary_disabled">Els fitxers rebuts apareixeran a Baixades</string>
@@ -226,7 +226,7 @@
<string name="permission_explanation">Aquest connector necessita permisos per a funcionar</string>
<string name="optional_permission_explanation">Us caldrà concedir permisos extres per a accedir a totes les característiques</string>
<string name="plugins_need_optional_permission">Alguns connectors tenen característiques desactivades per la falta de permís (toqueu per a més informació):</string>
<string name="share_optional_permission_explanation">Per a compartir fitxers entre el telèfon i l\'escriptori, haureu de donar accés a l\'emmagatzematge del telèfon</string>
<string name="share_optional_permission_explanation">Per a rebre fitxers compartits cal triar un directori de destinació</string>
<string name="telepathy_permission_explanation">Per a llegir i escriure SMS des de l\'escriptori, haureu de donar permís als SMS</string>
<string name="telephony_permission_explanation">Per a veure les trucades telefòniques des de l\'escriptori, haureu de donar permís d\'accés al registre de trucades telefòniques i a l\'estat del telèfon</string>
<string name="telephony_optional_permission_explanation">Per a veure un nom de contacte en comptes d\'un número de telèfon, haureu de donar permís als contactes del telèfon</string>
@@ -269,11 +269,11 @@
<string name="devices">Dispositius</string>
<string name="settings_rename">Nom del dispositiu</string>
<string name="settings_dark_mode">Tema fosc</string>
<string name="settings_more_settings_title">Més ajustaments</string>
<string name="settings_more_settings_text">Els ajustaments per dispositiu es poden trobar a «Arranjament del connector» des d\'un dispositiu.</string>
<string name="settings_more_settings_title">Més opcions</string>
<string name="settings_more_settings_text">La configuració per dispositiu es pot trobar a «Arranjament dels connectors» des d\'un dispositiu.</string>
<string name="setting_persistent_notification">Mostra les notificacions persistents</string>
<string name="setting_persistent_notification_oreo">Notificacions persistents</string>
<string name="setting_persistent_notification_description">Tocar per a des/activar els ajustaments de les notificacions</string>
<string name="setting_persistent_notification_description">Tocar per a des/activar la configuració de les notificacions</string>
<string name="extra_options">Opcions extres</string>
<string name="privacy_options">Opcions de privadesa</string>
<string name="set_privacy_options">Establiu les opcions de privadesa</string>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">Informace o šifrování</string>
<string name="encryption_info_msg_no_ssl">Druhé zařízení nepoužívá poslední verzi KDE connect. Bude použita stará metoda šifrování.</string>
<string name="my_device_fingerprint">Otisk SHA1 certifikátu vašeho zařízení je:</string>
<string name="remote_device_fingerprint">Otisk SHA1 certifikátu vzdáleného zařízení je:</string>
<string name="pair_requested">Bylo vyžádáno párování</string>
<string name="pairing_request_from">Požadavek o párování z %1s</string>
<plurals name="incoming_file_title">
@@ -242,7 +241,6 @@
<string name="permission_explanation">Tento modul potřebuje pro práci povolení</string>
<string name="optional_permission_explanation">Pro zpřístupnění všech funkcí potřebujete další oprávnění</string>
<string name="plugins_need_optional_permission">Některé moduly mají vypnuté vlastnosti, kvůli nedostatečným oprávněním (ťukněte pro více informací):</string>
<string name="share_optional_permission_explanation">Pro sdílení souborů mezi telefonem a počítačem potřebujete udělit oprávnění k úložišti telefonu</string>
<string name="telepathy_permission_explanation">Pro čtení a psaní SMS z počítače musíte udělit oprávnění k SMS</string>
<string name="telephony_permission_explanation">Pro zobrazení telefonátů v počítači musíte udělit oprávnění k záznamům telefonování a stavu telefonu</string>
<string name="telephony_optional_permission_explanation">Pro zobrazení jména kontaktu u telefonního čísla je potřeba udělit oprávnění ke kontaktům v telefonu</string>

View File

@@ -75,7 +75,6 @@
<string name="encryption_info_title">Krypteringsinfo</string>
<string name="encryption_info_msg_no_ssl">Den anden enhed bruger ikke en nylig version af KDE Connect, og bruger dermed den forældede krypteringsmetode.</string>
<string name="my_device_fingerprint">SHA1-fingeraftrykket for dit enhedscertifikat er:</string>
<string name="remote_device_fingerprint">SHA1-fingeraftrykket for det eksterne enhedscertifikat er:</string>
<string name="pair_requested">Anmodet om parring</string>
<string name="pairing_request_from">Parringsanmodning fra %1s</string>
<string name="received_file_text">Tap for at åbne \"%1s\"</string>
@@ -139,7 +138,6 @@
<string name="permission_explanation">Dette plugin kræver tilladelser for at virke</string>
<string name="optional_permission_explanation">Du skal give ekstra tilladelser for at aktivere alle funktioner</string>
<string name="plugins_need_optional_permission">Nogle plugins har deaktiverede funktioner pga. manglende tilladelser (tap for mere info):</string>
<string name="share_optional_permission_explanation">For at dele filer mellem din telefon og din desktop skal du give adgang til telefonens datalager.</string>
<string name="telepathy_permission_explanation">For at læse og skrive sms\'er fra din desktop, skal du give tilladelse til sms</string>
<string name="telephony_optional_permission_explanation">For at se et kontaktnavn i stedet for et telefonnummer, skal du give adgang til telefonens kontakter</string>
<string-array name="convert_to_mms_after_entries">

View File

@@ -87,7 +87,6 @@
<string name="encryption_info_title">Verschlüsselungsinformationen</string>
<string name="encryption_info_msg_no_ssl">Das andere Gerät verwendet eine ältere Version von KDE Connect. Daher muss eine veraltete Verschlüsselungsmethode verwendet werden</string>
<string name="my_device_fingerprint">Der SHA1-Fingerabdruck Ihres Gerätezertifikats lautet:</string>
<string name="remote_device_fingerprint">Der SHA1-Fingerabdruck des Gerätezertifikats der Gegenstelle lautet:</string>
<string name="pair_requested">Verbindung angefordert</string>
<string name="pairing_request_from">Kopplungsanfrage von %1s</string>
<plurals name="incoming_file_title">
@@ -122,6 +121,7 @@
<item quantity="one">Die Datei kann nicht an %1$s gesendet werden</item>
<item quantity="other">%2$d der %3$d Dateien können nicht an %1$s gesendet werden</item>
</plurals>
<string name="tap_to_open">Tippen um zu öffnen</string>
<string name="received_file_text">Tippen um „%1s“ zu öffnen</string>
<string name="cannot_create_file">Die Datei %s kann nicht erstellt werden</string>
<string name="tap_to_answer">Zum Antworten tippen</string>
@@ -208,6 +208,7 @@
<string name="no_file_browser">Es sind keine Dateiverwaltungsprogramme installiert</string>
<string name="pref_plugin_telepathy">SMS senden</string>
<string name="pref_plugin_telepathy_desc">SMS von Ihrer Arbeitsfläche senden</string>
<string name="pref_plugin_telepathy_mms">MMS senden</string>
<string name="findmyphone_title">Mein Telefon suchen</string>
<string name="findmyphone_title_tablet">Mein Tablet suchen</string>
<string name="findmyphone_title_tv">Meinen Fernseher suchen</string>
@@ -219,7 +220,6 @@
<string name="permission_explanation">Dieses Modul benötigt zusätzliche Berechtigungen</string>
<string name="optional_permission_explanation">Es müssen weitere Berechtigungen erteilt werden, um alle Funktionen nutzen zu können</string>
<string name="plugins_need_optional_permission">Einige Module haben eingeschränkte Funktionen wegen fehlender Berechtigungen, tippen Sie für weitere Informationen:</string>
<string name="share_optional_permission_explanation">Um Dateien zwischen Rechner und Telefon auszutauschen, muss der Zugriff auf den Telefonspeicher gewährt werden</string>
<string name="telepathy_permission_explanation">Um SMS vom Rechner aus zu lesen und zu versenden, muss der Zugriff auf die SMS-Funktion gewährt werden</string>
<string name="telephony_permission_explanation">Um eingehende Anrufe auf der Arbeitsfläche anzuzeigen, muss der Zugriff auf die Anrufliste und den Telefonstatus gewährt werden</string>
<string name="telephony_optional_permission_explanation">Um einen Namen anstelle der Telefonnummer anzuzeigen, muss der Zugriff auf das Adressbuch gewährt werden</string>
@@ -295,7 +295,7 @@
<item>Dark</item>
</string-array>
<string-array name="theme_list_v28">
<item/>
<item>Systemvoreinstellung</item>
<item/>
<item>Dark</item>
</string-array>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">Πληροφορίες κρυπτογράφησης</string>
<string name="encryption_info_msg_no_ssl">Η άλλη συσκευή δεν χρησιμοποιεί μια πρόσφατη έκδοση του KDE Connect, θα χρησιμοποιηθεί η παλαιά μέθοδος κρυπτογράφησης.</string>
<string name="my_device_fingerprint">Το ίχνος SHA1 του πιστοποιητικού της συσκευής σας είναι:</string>
<string name="remote_device_fingerprint">Το ίχνος SHA1 του πιστοποιητικού της απομακρυσμένης συσκευής είναι:</string>
<string name="pair_requested">Ζητήθηκε σύζευξη</string>
<string name="pairing_request_from">Αίτημα σύζευξης από %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +225,7 @@
<string name="permission_explanation">Αυτό το πρόσθετο χρειάζεται δικαιώματα για να λειτουργήσει</string>
<string name="optional_permission_explanation">Απαιτείται παραχώρηση επιπλέον δικαιωμάτων για την ενεργοποίηση όλων των λειτουργιών</string>
<string name="plugins_need_optional_permission">Κάποια πρόσθετα έχουν λειτουργίες ανενεργές εξαιτίας της απουσίας δικαιωμάτων (χτυπήστε για περισσότερες πληροφορίες):</string>
<string name="share_optional_permission_explanation">Για το διαμοιρασμό αρχείων ανάμεσα στο τηλέφωνο και τον υπολογιστή σας χρειάζεται να παραχωρήσετε πρόσβαση στον αποθηκευτικό χώρο του τηλεφώνου σας</string>
<string name="share_optional_permission_explanation">Για να λάβετε κοινόχρηστα αρχεία πρέπει να επιλέξετε έναν κατάλογο προορισμού</string>
<string name="telepathy_permission_explanation">Για να διαβάσετε και να γράψετε SMS από την επιφάνεια εργασίας, χρειάζεται να δώσετε δικαιώματα στο SMS</string>
<string name="telephony_permission_explanation">Για να δείτε τηλεφωνικές κλήσεις στην επιφάνεια εργασίας απαιτείται να παραχωρήσετε δικαιώματα σε καταγραφές τηλεφωνικών κλήσεων και στην κατάσταση του τηλεφώνου</string>
<string name="telephony_optional_permission_explanation">Για να δείτε το όνομα επαφής αντί για τον αριθμό κλήσης χρειάζεται να παραχωρήσετε πρόσβαση στις επαφές στο τηλέφωνο</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Encryption Info</string>
<string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string>
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is:</string>
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is:</string>
<string name="remote_device_fingerprint">SHA256 fingerprint of remote device certificate is:</string>
<string name="pair_requested">Pair requested</string>
<string name="pairing_request_from">Pairing request from %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">This plugin needs permissions to work</string>
<string name="optional_permission_explanation">You need to grant extra permissions to enable all functions</string>
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="share_optional_permission_explanation">To share files between your phone and your desktop you need to give access to the phone\'s storage</string>
<string name="share_optional_permission_explanation">To receive shared files you need to choose a destination directory</string>
<string name="telepathy_permission_explanation">To read and write SMS from your desktop you need to give permission to SMS</string>
<string name="telephony_permission_explanation">To see phone calls on the desktop you need to give permission to phone call logs and phone state</string>
<string name="telephony_optional_permission_explanation">To see a contact name instead of a phone number you need to give access to the phone\'s contacts</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Información de cifrado</string>
<string name="encryption_info_msg_no_ssl">El otro dispositivo no dispone de una versión reciente de KDE Connect, se usará un método de cifrado antiguo.</string>
<string name="my_device_fingerprint">La huella digital SHA1 del certificado de su dispositivo es:</string>
<string name="remote_device_fingerprint">La huella digital SHA1 del certificado del dispositivo remoto es:</string>
<string name="remote_device_fingerprint">La huella digital SHA256 del certificado del dispositivo remoto es:</string>
<string name="pair_requested">Vinculación solicitada</string>
<string name="pairing_request_from">Solicitud de vinculación de %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Este complemento necesita permisos para funcionar</string>
<string name="optional_permission_explanation">Debe otorgar permisos extra para activar todas las funciones</string>
<string name="plugins_need_optional_permission">Algunos complementos tienen funcionalidades desactivadas por falta de permisos (pulse para más información):</string>
<string name="share_optional_permission_explanation">Para compartir archivos entre su teléfono y su escritorio, necesita dar acceso al almacenamiento de su teléfono</string>
<string name="share_optional_permission_explanation">Para recibir archivos compartidos debe elegir un directorio destino</string>
<string name="telepathy_permission_explanation">Para leer y escribir SMS desde su escritorio, necesita dar permisos para SMS</string>
<string name="telephony_permission_explanation">Para ver las llamadas telefónicas en el escritorio, necesita dar permisos al registro de llamadas telefónicas y al estado del teléfono</string>
<string name="telephony_optional_permission_explanation">Para ver el nombre de un contacto en lugar de un número telefónico, necesita dar acceso a los contactos de su teléfono</string>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">Krüptimise teave</string>
<string name="encryption_info_msg_no_ssl">Teine seade ei kasuta KDE Connecti uusimat versiooni ja tarvitab krüptimisel pärandmeetodit.</string>
<string name="my_device_fingerprint">Sinu seadme sertifikaadi SHA1 sõrmejälg on:</string>
<string name="remote_device_fingerprint">Kaugseadme sertifikaadi SHA1 sõrmejälg on:</string>
<string name="pair_requested">Paardumise soov</string>
<string name="pairing_request_from">Paardumise soov seadmest %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +225,6 @@
<string name="permission_explanation">See plugin vajab töötamiseks õigusi</string>
<string name="optional_permission_explanation">Kõigi funktsioonide lubamiseks tuleb sul anda lisaõigusi</string>
<string name="plugins_need_optional_permission">Mõne plugina osa omadusi on keelatud õiguste puudumise tõttu (koputa rohkema teabe saamiseks):</string>
<string name="share_optional_permission_explanation">Failide jagamiseks telefoni ja töölau vahel tuleb anda telefonile ligipääs</string>
<string name="telepathy_permission_explanation">SMS-ide lugemiseks ja kirjutamiseks töölaual tuleb anda õigused SMS-ile</string>
<string name="telephony_permission_explanation">Kõnede nägemiseks töölaual tuleb anda õigused telefonikõnede logile ja telefoni olekule</string>
<string name="telephony_optional_permission_explanation">Telefoninumbri asemel kontakti nime nägemiseks tuleb anda õigused telefoni kontaktidele</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Zifratze informazioa</string>
<string name="encryption_info_msg_no_ssl">Beste gailuak ez du oraintsuko KDE Connect bertsio bat erabiltzen, erabili aurreko bertsioetako metodoa.</string>
<string name="my_device_fingerprint">Zure gailuaren ziurtagiriaren SHA1 hatz-marka hau da:</string>
<string name="remote_device_fingerprint">Urruneko gailuaren ziurtagiriaren SHA1 hatz-marka hau da:</string>
<string name="remote_device_fingerprint">Urruneko gailuaren ziurtagiriaren SHA256 hatz-marka hau da:</string>
<string name="pair_requested">Parekatzea eskatu da</string>
<string name="pairing_request_from">Parekatzeko eskaria %1s-tik</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Plugin honek baimena behar du funtzionatzeko</string>
<string name="optional_permission_explanation">Baimen gehiago eman behar dituzu funtzio guztiak gaitzeko</string>
<string name="plugins_need_optional_permission">Plugin batzuek desgaitutako eginbideak dituzte baimenak faltan dituztelako (tak egin informazio gehiagorako):</string>
<string name="share_optional_permission_explanation">Zure telefonoa eta mahaigainaren artean fitxategiak partekatzeko telefonoaren biltegiratzea atzitzeko baimena eman behar duzu</string>
<string name="share_optional_permission_explanation">Partekatutako fitxategiak jasotzeko jomugako direktorio bat aukeratu behar duzu</string>
<string name="telepathy_permission_explanation">SMSak zure mahaigainetik bidali ahal izateko, SMSak erabiltzeko baimena eman behar duzu</string>
<string name="telephony_permission_explanation">Telefono deiak zure mahaigainetik ikusteko, telefono deien egunkarira eta telefonoaren egoerara baimena eman behar duzu</string>
<string name="telephony_optional_permission_explanation">Telefono zenbakiaren ordez kontaktuaren izena ikusteko telefonoko kontaktuak atzitzeko baimena eman behar duzu</string>
@@ -251,6 +251,7 @@
<string name="notification_channel_media_control">Euskarrien kontrola</string>
<string name="notification_channel_filetransfer">Fitxategi transferentzia</string>
<string name="notification_channel_high_priority">Lehentasun handia</string>
<string name="notification_channel_sms_mms">Mezu berria</string>
<string name="mpris_stop">Gelditu uneko jotzailea</string>
<string name="copy_url_to_clipboard">Kopiatu URLa arbelera</string>
<string name="clipboard_toast">Arbelera kopiatua</string>
@@ -305,6 +306,9 @@
<string name="pref_plugin_bigscreen_desc">Erabili zure gailua Plasma Bigscreen-en urrutiko aginte gisa</string>
<string name="bigscreen_optional_permission_explanation">Zure telefonoko mikrofonoko sarrerakoa partekatzeko, telefonoko audio sarrerakora sarbidea eman behar diozu</string>
<string name="bigscreen_speech_extra_prompt">Hizketa</string>
<string name="message_reply_label">ERANTZUN</string>
<string name="mark_as_read_label">MARKATU IRAKURRITAKO GISA</string>
<string name="user_display_name">Zu</string>
<string name="set_default_sms_app_title">Bidali MMS</string>
<string name="set_group_message_as_mms_title">Bidali taldeko MMS</string>
<string name="set_long_text_as_mms_title">Bidali testu luzea MMS gisa</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Salaustiedot</string>
<string name="encryption_info_msg_no_ssl">Toinen laite ei käytä KDE Connectin uudehkoa versiota, joten käytetään vanhaa salausmenetelmää.</string>
<string name="my_device_fingerprint">Laitteen varmenteen SHA1-sormenjälki on:</string>
<string name="remote_device_fingerprint">Etälaitteen varmenteen SHA1-sormenjälki on:</string>
<string name="remote_device_fingerprint">Etälaitteen varmenteen SHA256-sormenjälki on:</string>
<string name="pair_requested">Paripyyntö</string>
<string name="pairing_request_from">Paripyyntö laitteesta %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Liitännäinen tarvitsee toimiakseen lisäkäyttöoikeuksia</string>
<string name="optional_permission_explanation">Kaikkien toimintojen käyttämiseksi sinun on annettava lisäkäyttöoikeuksia</string>
<string name="plugins_need_optional_permission">Jotkin liitännäisten ominaisuudet eivät ole käytössä puuttuvien käyttöoikeuksien takia (lisätietoa napsauttamalla):</string>
<string name="share_optional_permission_explanation">Jakaaksesi tiedostoja puhelimen ja työpöydän välillä sinun on annettava käyttöoikeudet puhelimen tallennustilaan</string>
<string name="share_optional_permission_explanation">Jaettujen tiedostojen vastaanottamiseksi on määritettävä kohdekansio</string>
<string name="telepathy_permission_explanation">Lukeaksesi ja lähettääksesi tekstiviestejä työpöydältä sinun on annettava käyttöoikeudet tekstiviesteihin</string>
<string name="telephony_permission_explanation">Puhelujen näyttäminen työpöydällä vaatii käyttöoikeuden puhelulokiin ja puhelimen tilaan</string>
<string name="telephony_optional_permission_explanation">Puhelimen yhteystietoihin on annettava käyttöoikeudet, jotta voit nähdä yhteystiedoissa nimet puhelinnumerojen sijaan</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Informations de chiffrement</string>
<string name="encryption_info_msg_no_ssl">Ce périphérique n\'utilise pas une version récente de KDE Connect qui utilise l\'ancienne méthode de chiffrement.</string>
<string name="my_device_fingerprint">L\'empreinte SHA1 du certificat de votre appareil est :</string>
<string name="remote_device_fingerprint">L\'empreinte SHA1 du certificat du périphérique distant est :</string>
<string name="remote_device_fingerprint">L\'empreinte « SHA256 » du certificat du périphérique distant est :</string>
<string name="pair_requested">Paire demandée</string>
<string name="pairing_request_from">Demande d\'association provenant de %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Ce module externe nécessite des permissions pour fonctionner</string>
<string name="optional_permission_explanation">Vous devez accorder des permissions supplémentaires pour activer toutes les fonctionnalités</string>
<string name="plugins_need_optional_permission">Certaines fonctionnalités de modules externes sont désactivées faute de permissions suffisantes (tapez pour plus d\'informations) :</string>
<string name="share_optional_permission_explanation">Pour partager des fichiers entre votre téléphone et votre ordinateur, veuillez permettre l\'accès à la mémoire de stockage du téléphone</string>
<string name="share_optional_permission_explanation">Pour la réception de fichiers partagés, vous devez sélectionner un dossier de destination.</string>
<string name="telepathy_permission_explanation">Pour lire et écrire des SMS depuis votre ordinateur, veuillez permettre l\'accès aux SMS</string>
<string name="telephony_permission_explanation">Pour voir les appels depuis votre ordinateur, veuillez permettre l\'accès au journal des appels et à l\'état du téléphone</string>
<string name="telephony_optional_permission_explanation">Pour voir le nom du contact au lieu du numéro de téléphone, veuillez permettre l\'accès aux contacts du téléphone</string>

View File

@@ -87,7 +87,6 @@
<string name="encryption_info_title">Información do cifrado</string>
<string name="encryption_info_msg_no_ssl">O outro dispositivo non usa unha versión recente de KDE Connect, usarase un método obsoleto de cifrado.</string>
<string name="my_device_fingerprint">A pegada SHA1 do certificado do seu dispositivo é:</string>
<string name="remote_device_fingerprint">A pegada SHA1 do certificado do dispositivo remoto é:</string>
<string name="pair_requested">Solicitude de emparellamento</string>
<string name="pairing_request_from">Solicitude de emparellamento de %1s.</string>
<plurals name="incoming_file_title">
@@ -218,7 +217,6 @@
<string name="permission_explanation">Este complemento necesita permisos para funcionar.</string>
<string name="optional_permission_explanation">Ten que conceder permisos adicionais para activar todas as funcións.</string>
<string name="plugins_need_optional_permission">Algúns complementos teñen funcionalidades desactivadas por mor dunha falta de permisos (toque para máis información):</string>
<string name="share_optional_permission_explanation">Para compartir ficheiros entre o teléfono e o escritorio ten que dar acceso ao almacenamento do teléfono.</string>
<string name="telepathy_permission_explanation">Para ler e escribir SMS desde o escritorio ten que dar permiso de SMS.</string>
<string name="telephony_permission_explanation">Para ver as chamadas de teléfono no escritorio ten que dar permiso aos rexistros de chamadas telefónicas e ao estado do teléfono</string>
<string name="telephony_optional_permission_explanation">Para ver o nome dun contacto en vez dun número de teléfono ten que dar acceso aos contactos do teléfono.</string>

View File

@@ -84,7 +84,6 @@
<string name="encryption_info_title">Info Enkripsi</string>
<string name="encryption_info_msg_no_ssl">Perangkat lain tidaklah menggunakan KDE Connect yang berversi saat ini, menggunakan metode enkripsi kuno.</string>
<string name="my_device_fingerprint">Sidik jari SHA1 pada sertifikat perangkatmu adalah:</string>
<string name="remote_device_fingerprint">Sidik jari SHA1 pada sertifikat perangkat jarak jauh adalah:</string>
<string name="pair_requested">Meminta sanding</string>
<string name="pairing_request_from">Minta penyandingan dari %1s</string>
<plurals name="incoming_file_title">
@@ -170,7 +169,6 @@
<string name="permission_explanation">Plugin ini perlu perizinan untuk kerja</string>
<string name="optional_permission_explanation">Kamu perlu mengabulkan perizinan extra untuk memfungsikan semua fungsian</string>
<string name="plugins_need_optional_permission">Beberapa plugin yang memiliki fitur dinonfungsikan karena kurangnya perizinan (ketuk untuk info selebihnya):</string>
<string name="share_optional_permission_explanation">Untuk membagikan file antara teleponmu dan desktopmu kamu harus memberikan akses ke penyimpanan teleponmu</string>
<string name="telepathy_permission_explanation">Untuk membaca dan menulis SMS dari desktopmu kamu harus memberikan perizinan untuk SMS</string>
<string name="telephony_permission_explanation">Untuk melihat panggilan ponsel pada desktop kamu harus memberikan perizinan untuk log panggilan ponsel dan keadaan ponsel</string>
<string name="telephony_optional_permission_explanation">Untuk melihat nama kontak alih-alih nomor ponsel, kamu harus memberikan akses ke kontak ponsel</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Informazioni di cifratura</string>
<string name="encryption_info_msg_no_ssl">L\'altro dispositivo non utilizza una versione recente di KDE Connect, utilizzando il metodo di cifratura precedente.</string>
<string name="my_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo è:</string>
<string name="remote_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo remoto è:</string>
<string name="remote_device_fingerprint">L\'impronta digitale SHA256 del certificato del dispositivo remoto è:</string>
<string name="pair_requested">Richiesta di associazione</string>
<string name="pairing_request_from">Richiesta associazione da %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Questa estensione ha bisogno di permessi per funzionare</string>
<string name="optional_permission_explanation">Devi concedere permessi aggiuntivi per abilitare tutte le funzioni</string>
<string name="plugins_need_optional_permission">Alcune estensioni hanno funzioni disabilitate per una mancanza di permessi (tocca per maggiori informazioni):</string>
<string name="share_optional_permission_explanation">Per condividere i file tra il telefono e il tuo desktop devi dare accesso alla memoria del telefono</string>
<string name="share_optional_permission_explanation">Per ricevere file condivisi è necessario scegliere una cartella di destinazione</string>
<string name="telepathy_permission_explanation">Per leggere e scrivere SMS dal tuo desktop, devi concedere l\'autorizzazione per SMS</string>
<string name="telephony_permission_explanation">Per vedere le chiamate telefoniche dal desktop devi dare l\'autorizzazione per accedere al registro delle chiamate e allo stato del telefono</string>
<string name="telephony_optional_permission_explanation">Per vedere il nome di un contatto invece del numero di telefono devi dare accesso alla rubrica del telefono</string>

View File

@@ -75,7 +75,6 @@
<string name="encryption_info_title">פרטי הצפנה</string>
<string name="encryption_info_msg_no_ssl">ההתקן השני אינו משתמש בגרסה האחרונה של KDE Connect, משתמש בשיטת ההצפנה הישנה.</string>
<string name="my_device_fingerprint">טביעת האצבע SHA1 של ההתקן היא:</string>
<string name="remote_device_fingerprint">"טביעת האצבע SHA1 של ההתקן המרוחק היא: "</string>
<string name="pair_requested">בקשת התאמה</string>
<string name="pairing_request_from">בוקשה התאמה מ־%1s</string>
<string name="received_file_text">לחץ כדי לפתוח את \"%1s\"</string>

View File

@@ -87,7 +87,6 @@
<string name="encryption_info_title">暗号化情報</string>
<string name="encryption_info_msg_no_ssl">他のデバイスは最近のバージョンの KDE Connect を利用していません。古い暗号化方式を使用しています</string>
<string name="my_device_fingerprint">このデバイスの証明書の SHA1 フィンガープリント:</string>
<string name="remote_device_fingerprint">リモートデバイスの証明書の SHA1 フィンガープリント:</string>
<string name="pair_requested">ペアリング要求済み</string>
<string name="pairing_request_from">%1s からペアリングを要求されました</string>
<plurals name="incoming_file_title">
@@ -210,7 +209,6 @@
<string name="permission_explanation">このプラグインが機能するには権限が必要です</string>
<string name="optional_permission_explanation">すべての機能を有効にするには、追加の権限を許可する必要があります</string>
<string name="plugins_need_optional_permission">権限が不足しているため、いくつかのプラグインの機能は無効化されています (タップして詳細情報を表示):</string>
<string name="share_optional_permission_explanation">スマートフォンとデスクトップ間でファイルを共有するには、端末のストレージへのアクセスを許可する必要があります</string>
<string name="telepathy_permission_explanation">デスクトップから SMS を送受信するには SMS の権限を付与する必要があります</string>
<string name="telephony_permission_explanation">デスクトップで通話を表示するために、電話の状態と通話履歴の権限を許可する必要があります</string>
<string name="telephony_optional_permission_explanation">電話番号ではなく連絡先名を見るには、端末の連絡先へのアクセスを許可する必要があります</string>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">암호화 정보</string>
<string name="encryption_info_msg_no_ssl">다른 장치에서 KDE Connect의 최근 버전을 실행하고 있지 않아서 레거시 암호화를 사용합니다.</string>
<string name="my_device_fingerprint">내 장치 인증서의 SHA1 지문:</string>
<string name="remote_device_fingerprint">원격 장치 인증서의 SHA1 지문:</string>
<string name="pair_requested">연결 요청됨</string>
<string name="pairing_request_from">%1s에서 연결 요청</string>
<plurals name="incoming_file_title">
@@ -218,7 +217,6 @@
<string name="permission_explanation">이 플러그인을 사용하려면 권한이 필요합니다</string>
<string name="optional_permission_explanation">모든 기능을 사용하려면 추가 권한이 필요합니다</string>
<string name="plugins_need_optional_permission">일부 플러그인은 권한이 없어서 비활성화되었습니다(정보를 보려면 누르기):</string>
<string name="share_optional_permission_explanation">휴대폰과 데스크톱간 파일을 공유하려면 휴대폰 저장소 접근 권한이 필요합니다</string>
<string name="telepathy_permission_explanation">데스크톱에서 문자 메시지를 읽고 보내려면 문자 메시지 접근 권한이 필요합니다</string>
<string name="telephony_permission_explanation">데스크톱에서 통화와 문자 메시지를 보려면 통화 기록 및 휴대폰 상태 접근 권한이 필요합니다</string>
<string name="telephony_optional_permission_explanation">전화번호 대신 연락처에 등록된 이름을 보려면 주소록 접근 권한이 필요합니다</string>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">Šifravimo informacija</string>
<string name="encryption_info_msg_no_ssl">Kitas įrenginys nenaudoja paskiausios KDE Connect versijos, naudojamas pasenęs šifravimo metodas.</string>
<string name="my_device_fingerprint">Jūsų įrenginio liudijimo SHA1 kontrolinis kodas yra:</string>
<string name="remote_device_fingerprint">Nuotolinio įrenginio liudijimo SHA1 kontrolinis kodas yra:</string>
<string name="pair_requested">Užklaustas suporavimas</string>
<string name="pairing_request_from">Suporavimo užklausa iš %1s</string>
<plurals name="incoming_file_title">
@@ -240,7 +239,6 @@
<string name="permission_explanation">Šis papildinys tam, kad veiktų, reikalauja leidimų</string>
<string name="optional_permission_explanation">Norėdami įjungti visas funkcijas, turite suteikti papildomus leidimus</string>
<string name="plugins_need_optional_permission">Kai kurių papildinių ypatybės, dėl leidimų trūkumo, buvo išjungtos (bakstelėkite išsamesnei informacijai):</string>
<string name="share_optional_permission_explanation">Norėdami bendrinti failus tarp savo telefono ir savo darbalaukio, turite suteikti prieigą prie telefono saugyklos</string>
<string name="telepathy_permission_explanation">Norėdami skaityti ir rašyti SMS žinutes iš savo darbalaukio, turite suteikti prieigą prie SMS žinučių</string>
<string name="telephony_permission_explanation">Norėdami matyti telefono skambučius darbalaukyje, turite suteikti prieigą prie telefono skambučių žurnalo ir telefono būsenos</string>
<string name="telephony_optional_permission_explanation">Norėdami vietoj adresato numerio matyti adresato vardą, turite suteikti priegą prie telefono adresatų</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Versleutelde informatie</string>
<string name="encryption_info_msg_no_ssl">Het andere apparaat gebruikt geen recente versie van KDE Connect, de verouderde versleutelingsmethode zal worden gebruikt.</string>
<string name="my_device_fingerprint">De SHA1 vingerafdruk van het certificaat van uw apparaat is:</string>
<string name="remote_device_fingerprint">De SHA1 vingerafdruk van het certificaat van het apparaat op afstand is:</string>
<string name="remote_device_fingerprint">De SHA256 vingerafdruk van het certificaat van het apparaat op afstand is:</string>
<string name="pair_requested">Paarvorming gevraagd</string>
<string name="pairing_request_from">Verzoek om een paar te maken van %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Deze plug-in heeft toestemming nodig om te werken</string>
<string name="optional_permission_explanation">U moet toestemming geven om alle functies in te schakelen</string>
<string name="plugins_need_optional_permission">Sommige plug-ins hebben functies uitgeschakeld vanwege ontbrekende toestemming (tik voor meer informatie):</string>
<string name="share_optional_permission_explanation">Om bestanden tussen uw telefoon en uw bureaublad te delen moet u toegang geven tot de opslag van uw telefoon</string>
<string name="share_optional_permission_explanation">Om gedeelde bestanden te ontvangen moet u een bestemmingsmap kiezen</string>
<string name="telepathy_permission_explanation">Om een SMS te lezen of te schrijven vanaf uw bureaublad moet u toestemming geven tot SMS</string>
<string name="telephony_permission_explanation">Om telefoonoproepen te zien op het bureaublad moet u toestemming geven tot telefoonoproeplogs en telefoonstatus</string>
<string name="telephony_optional_permission_explanation">Om een naam van een contactpersoon te zien in plaats van een telefoonnummer moet u toegang geven tot de contacten in uw telefoon</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Krypteringsinfo</string>
<string name="encryption_info_msg_no_ssl">Den andre eininga brukar ein gammal versjon av KDE Connect, med ein utdatert krypteringsmetode.</string>
<string name="my_device_fingerprint">SHA-fingeravtrykket til einingssertifikatet er:</string>
<string name="remote_device_fingerprint">SHA-fingeravtrykket til fjerneiningssertifikatet er:</string>
<string name="remote_device_fingerprint">SHA-256-fingeravtrykket til fjerneiningssertifikatet er:</string>
<string name="pair_requested">Paringsførespurnad</string>
<string name="pairing_request_from">Paringsførespurnad frå %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Dette tillegget treng utvida løyve for å fungera</string>
<string name="optional_permission_explanation">Du må gje utvida løyve for at alle funksjonane skal fungera</string>
<string name="plugins_need_optional_permission">På grunn av manglande løyve vil desse funksjonane ikkje verka (trykk på dei for meir informasjon):</string>
<string name="share_optional_permission_explanation">For å kunna dela filer mellom telefonen og datamaskina må du gje appen lese- og skriveløyve til lagringsområdet på telefonen</string>
<string name="share_optional_permission_explanation">For å kunna ta imot delte filer må du velja ei målmappe</string>
<string name="telepathy_permission_explanation">For å kunna lesa og skriva tekstmeldingar frå datamaskina må du gje appen tilgang til SMS</string>
<string name="telephony_permission_explanation">For å kunna sjå telefonsamtalar på datamaskina må du gje appen tilgang til samtaleloggar og telefonstatus</string>
<string name="telephony_optional_permission_explanation">For å kunna sjå namn på kontaktar i staden for berre telefonnummeret må du gje appen tilgang til kontaktlista di</string>

View File

@@ -87,7 +87,6 @@
<string name="encryption_info_title">Dane o szyfrowaniu</string>
<string name="encryption_info_msg_no_ssl">Drugie urządzenie nie używa ostatniej wersji KDE Connect, użyto przestarzałego szyfrowania.</string>
<string name="my_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego urządzenia to:</string>
<string name="remote_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego zdalnego urządzenia to:</string>
<string name="pair_requested">Zażądano parowania</string>
<string name="pairing_request_from">Żądanie parowania z %1s</string>
<plurals name="incoming_file_title">
@@ -234,7 +233,6 @@
<string name="permission_explanation">Ta wtyczka wymaga uprawnień do działania</string>
<string name="optional_permission_explanation">Musisz przydzielić dodatkowe uprawnienia, aby włączyć wszystkie funkcje</string>
<string name="plugins_need_optional_permission">Niektóre wtyczki mają ograniczone możliwości ze względu na ograniczone uprawnienia (stuknij, aby dowiedzieć się więcej)</string>
<string name="share_optional_permission_explanation">Aby udostępniać pliki z twojego telefonu na twoim komputerze, należy zezwolić na dostęp do pamięci telefonu</string>
<string name="telepathy_permission_explanation">Aby odbierać i wysyłać esemesy z pulpitu, należy nadać uprawnienia do esemesów</string>
<string name="telephony_permission_explanation">Aby widzieć rozmowy telefoniczne na pulpicie, należy nadać uprawnienia do dziennika rozmów i stanu telefonu</string>
<string name="telephony_optional_permission_explanation">Aby widzieć nazwę kontaktu zamiast numeru telefonu, należy zezwolić na dostęp do kontaktów telefonu</string>

View File

@@ -89,8 +89,8 @@
<string name="error_canceled_by_other_peer">Cancelado pelo outro dispositivo</string>
<string name="encryption_info_title">Informação de criptografia</string>
<string name="encryption_info_msg_no_ssl">O outro dispositivo não usa uma versão recente do KDE Connect. Será utilizado o método antigo de criptografia.</string>
<string name="my_device_fingerprint">Código SHA1 do certificado do seu dispositivo:</string>
<string name="remote_device_fingerprint">Código SHA1 do certificado do dispositivo remoto:</string>
<string name="my_device_fingerprint">A impressão digital SHA1 do certificado do seu dispositivo é:</string>
<string name="remote_device_fingerprint">A impressão digital SHA256 do certificado do dispositivo remoto é:</string>
<string name="pair_requested">Solicitação de emparelhamento</string>
<string name="pairing_request_from">Emparelhamento solicitado por %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Este plugin precisa de permissões para funcionar</string>
<string name="optional_permission_explanation">Você precisa conceder permissões extras para ativar todas as funções</string>
<string name="plugins_need_optional_permission">Alguns plugins possuem recursos desativados devido à falta de permissões (toque para obter mais informações):</string>
<string name="share_optional_permission_explanation">Para compartilhar arquivos entre o seu celular e o seu ambiente de trabalho é necessário permissão para acessar o armazenamento do seu celular</string>
<string name="share_optional_permission_explanation">Para receber arquivos compartilhados você precisa escolher uma pasta de destino</string>
<string name="telepathy_permission_explanation">Para ler e gravar SMS a partir do seu ambiente de trabalho é necessário conceder permissão para SMS</string>
<string name="telephony_permission_explanation">Para ver as chamadas telefônicas no seu ambiente de trabalho é preciso dar permissões para registro de chamadas telefônicas e do estado do telefone</string>
<string name="telephony_optional_permission_explanation">Para ver o nome de um contato em vez do seu número de telefone é necessário conceder acesso aos contatos do celular</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Dados de Encriptação</string>
<string name="encryption_info_msg_no_ssl">O outro dispositivo não usa uma versão recente do KDE Connect; será usado o método antigo de encriptação.</string>
<string name="my_device_fingerprint">A impressão digital SHA1 do certificado do seu dispositivo é:</string>
<string name="remote_device_fingerprint">A impressão digital SHA1 do certificado do dispositivo remoto é:</string>
<string name="remote_device_fingerprint">A impressão digital SHA256 do certificado do dispositivo remoto é:</string>
<string name="pair_requested">Emparelhamento pedido</string>
<string name="pairing_request_from">Pedido de emparelhamento de %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Este \'plugin\' precisa de permissões para funcionar</string>
<string name="optional_permission_explanation">Precisa de dar permissões extra para activar todas as funcionalidades</string>
<string name="plugins_need_optional_permission">Alguns \'plugins\' têm funcionalidades desactivadas devido à falta de permissões (toque para obter mais informações):</string>
<string name="share_optional_permission_explanation">Para partilhar ficheiros entre o seu telemóvel e o seu ambiente de trabalho, precisa de permissão para aceder ao armazenamento do seu telemóvel</string>
<string name="share_optional_permission_explanation">Para receber os ficheiros partilhados, tem de escolher uma pasta de destino</string>
<string name="telepathy_permission_explanation">Para ler e escrever SMS\'s a partir do seu ambiente de trabalho, precisa de dar permissões para os SMS\'s</string>
<string name="telephony_permission_explanation">Para ver as chamadas telefónicas a partir do seu ambiente de trabalho, precisa de dar permissões para o registo de chamadas telefónicas e do estado do telemóvel</string>
<string name="telephony_optional_permission_explanation">Para ver o nome de um contacto em vez do seu número de telefone, precisa de dar acesso aos contactos do telemóvel</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Informații despre criptare</string>
<string name="encryption_info_msg_no_ssl">Celălalt dispozitiv nu folosește o versiune recentă de KDE Connect, se va folosi metoda de criptare moștenită.</string>
<string name="my_device_fingerprint">Amprenta SHA1 a certificatului acestui dispozitiv e:</string>
<string name="remote_device_fingerprint">Amprenta SHA1 a certificatului dispozitivului distant e:</string>
<string name="remote_device_fingerprint">Amprenta SHA256 a certificatului dispozitivului distant e:</string>
<string name="pair_requested">Asociere cerută</string>
<string name="pairing_request_from">Cerere de asociere de la %1s</string>
<plurals name="incoming_file_title">
@@ -234,7 +234,7 @@
<string name="permission_explanation">Această extensie are nevoie de permisiuni ca să lucreze</string>
<string name="optional_permission_explanation">Trebuie să acordați permisiuni suplimentare pentru activarea tuturor funcțiilor</string>
<string name="plugins_need_optional_permission">Unele extensii au caracteristici dezactivate din cauza lipsei permisiunilor (atingeți pentru informații suplimentare):</string>
<string name="share_optional_permission_explanation">Pentru a partaja fișiere între telefon și calculator, trebuie să permiteți accesul la stocarea telefonului</string>
<string name="share_optional_permission_explanation">Trebuie să alegeți un dosar destinație pentru a primi fișiere partajate</string>
<string name="telepathy_permission_explanation">Pentru a citi și scrie SMS de pe calculatorul de birou, trebuie să acordați permisiuni asupra SMS-urilor</string>
<string name="telephony_permission_explanation">Pentru a vedea apelurile pe calculatorul de birou, trebuie să acordați permisiuni asupra jurnalului de apeluri și stării telefonului</string>
<string name="telephony_optional_permission_explanation">Pentru a vedea numele contactului în locul numărului de telefon, trebuie să permiteți accesul la contactele telefonului.</string>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">Информация о шифровании</string>
<string name="encryption_info_msg_no_ssl">На другом устройстве используется старая версия KDE Connect, используется старый метод шифрования.</string>
<string name="my_device_fingerprint">Отпечаток SHA-1 сертификата вашего устройства:</string>
<string name="remote_device_fingerprint">Отпечаток SHA-1 сертификата удалённого устройства:</string>
<string name="pair_requested">Запрошено сопряжение</string>
<string name="pairing_request_from">Запрос на сопряжение от %1s</string>
<plurals name="incoming_file_title">
@@ -239,7 +238,6 @@
<string name="permission_explanation">Этому модулю нужны разрешения для работы</string>
<string name="optional_permission_explanation">Необходимо предоставить дополнительные разрешения для включения всех функций</string>
<string name="plugins_need_optional_permission">Некоторые функции модулей отключены из-за отсутствия необходимых разрешений (нажмите для просмотра подробностей):</string>
<string name="share_optional_permission_explanation">Чтобы обмениваться файлами между телефоном и компьютером, необходимо предоставить доступ к встроенной памяти телефона</string>
<string name="telepathy_permission_explanation">Чтобы читать и писать SMS с компьютера, вам необходимо дать разрешение на доступ к SMS</string>
<string name="telephony_permission_explanation">Чтобы видеть телефонные звонки на компьютере, необходимо дать разрешение на доступ к списку звонков и состоянию телефона</string>
<string name="telephony_optional_permission_explanation">Чтобы видеть имя контакта вместо номера телефона, необходимо предоставить доступ к контактам</string>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">Informácie o šifrovaní</string>
<string name="encryption_info_msg_no_ssl">Druhé zariadenie nepoužíva najnovšiu verziu aplikácie KDE Connect. Použije sa zastaralý spôsob šifrovania.</string>
<string name="my_device_fingerprint">Odtlačok SHA1 certifikátu vášho zariadenia je:</string>
<string name="remote_device_fingerprint">Odtlačok SHA1 certifikátu vzdialeného zariadenia je:</string>
<string name="pair_requested">Spárovanie vyžiadané</string>
<string name="pairing_request_from">Požiadavka na spárovanie zo zariadenia %1s</string>
<plurals name="incoming_file_title">
@@ -242,7 +241,7 @@
<string name="permission_explanation">Tento zásuvný modul vyžaduje oprávnenie, aby mohol fungovať</string>
<string name="optional_permission_explanation">Musíte udeliť oprávnenia navyše, aby boli povolené všetky funkcie</string>
<string name="plugins_need_optional_permission">Niektoré zásuvné moduly majú zakázané funkcie, pretože im chýbajú oprávnenia (ťuknutím získate viac informácií):</string>
<string name="share_optional_permission_explanation">Na zdieľanie súborov medzi vašim telefónom a počítačom, musíte udeliť prístup k úložisku telefónu</string>
<string name="share_optional_permission_explanation">Ak chcete prijímať zdieľané súbory, musíte zvoliť cieľový adresár</string>
<string name="telepathy_permission_explanation">Na čítanie a písanie SMS z vášho počítača, musíte udeliť oprávnenie k správam SMS</string>
<string name="telephony_permission_explanation">Na prezeranie telefónnych hovorov z vášho počítača, musíte udeliť oprávnenie k záznamom hovorov a stavu telefónu</string>
<string name="telephony_optional_permission_explanation">Na prezeranie mien kontaktov namiesto telefónnych čísel, musíte udeliť prístup k telefónnym kontaktom</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Informacija o šifriranju</string>
<string name="encryption_info_msg_no_ssl">Druga naprava ne uporablja najnovejše različice KDE Connect, uporablja podedovano metodo šifriranja.</string>
<string name="my_device_fingerprint">Prstni odtis SHA1 certifikata vaše naprave je:</string>
<string name="remote_device_fingerprint">Prstni odtis SHA1 certifikata oddaljene naprave je:</string>
<string name="remote_device_fingerprint">Prstni odtis SHA256 certifikata oddaljene naprave je:</string>
<string name="pair_requested">Zahtevan je par</string>
<string name="pairing_request_from">Zahtevek za uparjanje od %1s</string>
<plurals name="incoming_file_title">
@@ -242,7 +242,7 @@
<string name="permission_explanation">Ti vtičniki potrebujejo dovoljenja za delovanje</string>
<string name="optional_permission_explanation">Če želite omogočiti vse funkcije, morate podeliti dodatna dovoljenja</string>
<string name="plugins_need_optional_permission">Nekateri vtičniki imajo funkcije onemogočene zaradi pomanjkanja dovoljenj (tapnite za več informacij):</string>
<string name="share_optional_permission_explanation">Za skupno rabo datotek med telefonom in namizjem morate dovoliti dostop v shrambo telefona</string>
<string name="share_optional_permission_explanation">Če želite prejemati datoteke v skupni rabi, morate izbrati ciljni imenik</string>
<string name="telepathy_permission_explanation">Če želite brati in pisati SMS z namizja, morate izdati dovoljenje za SMS</string>
<string name="telephony_permission_explanation">Če želite videti telefonske klice na namizju, morate dati dovoljenje za dnevnike klicev in stanje telefona</string>
<string name="telephony_optional_permission_explanation">Če želite namesto telefonske številke videti ime stika, morate omogočiti dostop do stikov telefona</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Krypteringsinformation</string>
<string name="encryption_info_msg_no_ssl">Den andra apparaten använder inte en aktuell version av KDE-anslut. Använder den föråldrade krypteringsmetoden.</string>
<string name="my_device_fingerprint">SHA1-fingeravtryck för din apparats certifikat är:</string>
<string name="remote_device_fingerprint">SHA1-fingeravtryck för den andra apparatens certifikat är:</string>
<string name="remote_device_fingerprint">SHA256-fingeravtryck för den andra apparatens certifikat är:</string>
<string name="pair_requested">Ihopparning begärd</string>
<string name="pairing_request_from">Begäran om ihopparning från %1s</string>
<plurals name="incoming_file_title">
@@ -226,7 +226,7 @@
<string name="permission_explanation">Insticksprogrammet behöver rättigheter för att fungera</string>
<string name="optional_permission_explanation">Du måste ge extra rättigheter för att aktivera alla funktioner</string>
<string name="plugins_need_optional_permission">Vissa insticksprogram har inaktiverade funktioner på grund av att rättigheter saknas (rör för mer information):</string>
<string name="share_optional_permission_explanation">För att dela filer mellan telefonen och skrivbordet behöver du ge tillgång till telefonens lagringsutrymme</string>
<string name="share_optional_permission_explanation">För att ta emot delade filer måste du välja en målkatalog</string>
<string name="telepathy_permission_explanation">För att läsa och skriva SMS från skrivbordet måste du ge rättigheter för SMS</string>
<string name="telephony_permission_explanation">För att se telefonsamtal på skrivbordet måste du ge rättigheter för telefonsamtalsloggar och telefontillstånd</string>
<string name="telephony_optional_permission_explanation">För att se ett kontaktnamn istället för ett telefonnummer måste du ge tillgång till telefonens kontakter</string>

View File

@@ -87,7 +87,6 @@
<string name="encryption_info_title">Şifreleme Bilgisi</string>
<string name="encryption_info_msg_no_ssl">Diğer aygıt, KDE Connect\'in son sürümünü kullanmıyor, eski şifreleme yöntemini kullanıyor.</string>
<string name="my_device_fingerprint">Aygıt sertifikanızın SHA1 parmak izi:</string>
<string name="remote_device_fingerprint">Uzak aygıt sertifikanızın SHA1 parmak izi:</string>
<string name="pair_requested">Eşleşme talep edildi</string>
<string name="pairing_request_from">%1s için eşleşme talebi</string>
<plurals name="incoming_file_title">
@@ -217,7 +216,6 @@
<string name="permission_explanation">Bu eklenti, çalışmak için izne ihtiyaç duyuyor</string>
<string name="optional_permission_explanation">Tüm işlevleri etkinleştirmek için daha fazla yetkiye ihtiyacınız var</string>
<string name="plugins_need_optional_permission">Bazı eklentilerin özellikleri, izin yetersizliğinden kapalı gelmektedir (daha fazla bilgi için dokunun):</string>
<string name="share_optional_permission_explanation">Telefon ve masaüstünüz arasında dosya paylaşılabilmesi için, telefonun depolama alanına erişim izni olmalıdır</string>
<string name="telepathy_permission_explanation">Masaüstünde SMS yazma ve okuma yapmak için SMS izni gereklidir</string>
<string name="telephony_permission_explanation">Masaüstünde telefon görüşmelerini görmek için telefon görüşmesi kayıtlarına ve telefon durumuna izin vermeniz gerekir</string>
<string name="telephony_optional_permission_explanation">Telefon numarası yerine kişi ismi görebilmek için telefonun kişilerine erişim gereklidir</string>

View File

@@ -90,7 +90,7 @@
<string name="encryption_info_title">Дані щодо шифрування</string>
<string name="encryption_info_msg_no_ssl">На сторонньому пристрої не використовується нова версія KDE Connect, у якій використовується застарілий спосіб шифрування.</string>
<string name="my_device_fingerprint">Відбиток SHA1 сертифіката пристрою:</string>
<string name="remote_device_fingerprint">Відбиток SHA1 сертифіката віддаленого пристрою:</string>
<string name="remote_device_fingerprint">Відбиток SHA256 сертифіката віддаленого пристрою:</string>
<string name="pair_requested">Запит щодо пов’язування</string>
<string name="pairing_request_from">Запит щодо пов’язування від %1s</string>
<plurals name="incoming_file_title">
@@ -242,7 +242,7 @@
<string name="permission_explanation">Для роботи цього додатка потрібні додаткові права доступу</string>
<string name="optional_permission_explanation">Щоб уможливити використання усіх функцій, вам слід надати програмі додаткові права доступу</string>
<string name="plugins_need_optional_permission">Можливості деяких додатків вимкнено, оскільки програмі не вистачає прав доступу (натисніть, щоб дізнатися більше):</string>
<string name="share_optional_permission_explanation">Щоб спільного використовувати файли на вашому телефоні і робочому комп’ютері, вам слід надати програмі доступ до сховища даних вашого телефону</string>
<string name="share_optional_permission_explanation">Для отримання файлів спільного користування вам слід вибрати каталог призначення</string>
<string name="telepathy_permission_explanation">Щоб читати і писати SMS з вашого робочого комп’ютера, вам слід надати програмі доступ до SMS</string>
<string name="telephony_permission_explanation">Щоб переглядати дзвінки з робочого комп’ютера, вам слід надати програмі доступ до журналу дзвінків та стану телефону</string>
<string name="telephony_optional_permission_explanation">Щоб бачити ім’я контакту замість номеру телефону, вам слід надати програмі доступ до записів контактів на телефоні</string>

View File

@@ -1,10 +1,10 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="kde_connect">KDE Connect</string>
<string name="foreground_notification_no_devices">没有连接到任何设备</string>
<string name="kde_connect">KDE 连接</string>
<string name="foreground_notification_no_devices">连接到任何设备</string>
<string name="foreground_notification_devices">已连接到:%s</string>
<string name="foreground_notification_send_clipboard">发送剪</string>
<string name="pref_plugin_telephony">通知</string>
<string name="foreground_notification_send_clipboard">发送剪</string>
<string name="pref_plugin_telephony">电通知</string>
<string name="pref_plugin_telephony_desc">发送来电通知</string>
<string name="pref_plugin_battery">电池报告</string>
<string name="pref_plugin_battery_desc">定期报告电池状态</string>
@@ -20,40 +20,40 @@
<string name="pref_plugin_remotekeyboard">接收远程按键</string>
<string name="pref_plugin_remotekeyboard_desc">从远程设备接收按键事件</string>
<string name="pref_plugin_mpris">多媒体控制</string>
<string name="pref_plugin_mpris_desc">媒体播放器的遥控器</string>
<string name="pref_plugin_runcommand">行命令</string>
<string name="pref_plugin_mpris_desc">遥控媒体播放器</string>
<string name="pref_plugin_runcommand">行命令</string>
<string name="pref_plugin_runcommand_desc">触发您的手机或平板电脑的远程命令</string>
<string name="pref_plugin_contacts">联系人同步</string>
<string name="pref_plugin_contacts">联系人同步</string>
<string name="pref_plugin_contacts_desc">允许同步设备的通讯录</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">发送和接受ping</string>
<string name="pref_plugin_ping_desc">发送和接受 Ping</string>
<string name="pref_plugin_notifications">通知同步</string>
<string name="pref_plugin_notifications_desc">从其他设备访问您的通知</string>
<string name="pref_plugin_receive_notifications">接收通知</string>
<string name="pref_plugin_receive_notifications_desc">从其他设备接收通知并显示在 Android 上</string>
<string name="pref_plugin_sharereceiver">分享和接收</string>
<string name="pref_plugin_sharereceiver_desc">在设备间共享文件和 URL</string>
<string name="pref_plugin_sharereceiver">发送和接收</string>
<string name="pref_plugin_sharereceiver_desc">在设备间共享文件和链接</string>
<string name="device_list_empty">无设备</string>
<string name="ok"></string>
<string name="ok"></string>
<string name="sad_ok">确定 :(</string>
<string name="cancel">取消</string>
<string name="open_settings">设置</string>
<string name="no_permissions">您需要授予权限以便访问通知</string>
<string name="no_permission_mprisreceiver">为了能够控制您的媒体播放器,您需要授予访问通知消息的权限</string>
<string name="no_permissions_remotekeyboard">要接收按键,您需要激活 KDE Connect 远程键盘</string>
<string name="send_ping">发送ping</string>
<string name="open_settings">开设置</string>
<string name="no_permissions">您需要授予权限以访问通知</string>
<string name="no_permission_mprisreceiver">为了控制您的媒体播放器,您需要授予访问通知的权限</string>
<string name="no_permissions_remotekeyboard">要接收按键,您需要激活 K 易连远程键盘</string>
<string name="send_ping">发送 Ping</string>
<string name="open_mpris_controls">多媒体控制</string>
<string name="remotekeyboard_editing_only_title">只有在编辑时才接受远程按键</string>
<string name="remotekeyboard_not_connected">没有活动的远程键盘链接,请在 KDE Connect 中连接</string>
<string name="remotekeyboard_not_connected">没有活动的远程键盘链接,请在 K 易连中连接</string>
<string name="remotekeyboard_connected">远程键盘连接已启用</string>
<string name="remotekeyboard_multiple_connections">发现多个远程键盘连接,请选择设备进行配置</string>
<string name="remotekeyboard_multiple_connections">发现多个远程键盘连接,请选择要配置的设备</string>
<string name="open_mousepad">远程输入</string>
<string name="mousepad_info">在屏幕上移动手指来移动光标。轻击代表左键,双指或三指点击代表右键或中键。用双指滚动。用长按来拖放。</string>
<string name="mousepad_double_tap_settings_title">设置双指点击动作</string>
<string name="mousepad_triple_tap_settings_title">设置三指点击动作</string>
<string name="mousepad_sensitivity_settings_title">设置触摸板灵敏度</string>
<string name="mousepad_acceleration_profile_settings_title">设置指针加速</string>
<string name="mousepad_scroll_direction_title">滚动方向反向</string>
<string name="mousepad_acceleration_profile_settings_title">设置指针加速</string>
<string name="mousepad_scroll_direction_title">反转滚动方向</string>
<string-array name="mousepad_tap_entries">
<item>右键点击</item>
<item>中键点击</item>
@@ -74,25 +74,25 @@
<item>更强</item>
<item>最强</item>
</string-array>
<string name="category_connected_devices">连接设备</string>
<string name="category_not_paired_devices">可用设备</string>
<string name="category_remembered_devices">记住设备</string>
<string name="category_connected_devices">连接设备</string>
<string name="category_not_paired_devices">可用设备</string>
<string name="category_remembered_devices">记住设备</string>
<string name="device_menu_plugins">插件设置</string>
<string name="device_menu_unpair">取消配对</string>
<string name="pair_new_device">配对新设备</string>
<string name="unknown_device">未知设备</string>
<string name="error_not_reachable">设备不可及</string>
<string name="error_already_paired">设备已配对</string>
<string name="error_could_not_send_package">数据包无法发送</string>
<string name="error_could_not_send_package">无法发送数据包</string>
<string name="error_timed_out">超时</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_msg_no_ssl">另一设备没有使用最新的 KDE Connect,使用旧版加密方法。</string>
<string name="encryption_info_msg_no_ssl">另一设备没有使用最新的 K 易连,使用旧版加密方法。</string>
<string name="my_device_fingerprint">您的设备证书的 SHA1 指纹是:</string>
<string name="remote_device_fingerprint">远程设备证书的 SHA1 指纹是:</string>
<string name="remote_device_fingerprint">远程设备证书的 SHA256 指纹是:</string>
<string name="pair_requested">已请求配对</string>
<string name="pairing_request_from">来自%1s的配对请求</string>
<string name="pairing_request_from">来自 %1s 的配对请求</string>
<plurals name="incoming_file_title">
<item quantity="other">正在从 %2$s 接收 %1$d 个文件</item>
</plurals>
@@ -100,7 +100,7 @@
<item quantity="other">(%3$d 个文件中的第 %2$d 个)%1$s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="other">正在发送 %1$d 个文件 %2$s</item>
<item quantity="other">正在发送 %1$d 个文件 %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="other">(%3$d 个文件中的 %2$d 个)%1$s</item>
@@ -117,10 +117,10 @@
<plurals name="send_files_fail_title">
<item quantity="other">发送给 %1$s 的 %3$d 个文件中的 %2$d 个失败</item>
</plurals>
<string name="tap_to_open">点击打开</string>
<string name="received_file_text">点击打开“%1s”</string>
<string name="tap_to_open">点击打开</string>
<string name="received_file_text">点击打开“%1s”</string>
<string name="cannot_create_file">无法创建文件 %s</string>
<string name="tap_to_answer">点击应答</string>
<string name="tap_to_answer">点击应答</string>
<string name="right_click">发送右键点击</string>
<string name="middle_click">发送中键点击</string>
<string name="show_keyboard">显示键盘</string>
@@ -194,8 +194,8 @@
<string name="sftp_saf_permission_explanation">要远程访问文件,您需要配置存储位置</string>
<string name="no_players_connected">未找到播放器</string>
<string name="send_files">发送文件</string>
<string name="pairing_title">KDE Connect 设备</string>
<string name="pairing_description">其他在同一网络运行 KDE Connect 的设备会在此处显示。</string>
<string name="pairing_title">K 易连设备</string>
<string name="pairing_description">其他在同一网络运行 K 易连的设备会在此处显示。</string>
<string name="device_rename_title">重命名设备</string>
<string name="device_rename_confirm">重命名</string>
<string name="refresh">刷新</string>
@@ -206,7 +206,7 @@
<string name="pref_plugin_telepathy">发送短消息</string>
<string name="pref_plugin_telepathy_desc">从桌面发送短消息</string>
<string name="pref_plugin_telepathy_mms">发送彩信</string>
<string name="pref_plugin_telepathy_mms_desc">您需要将 KDE Connect 设置为默认短信应用程序才能通过它发送彩信。</string>
<string name="pref_plugin_telepathy_mms_desc">您需要将 K 易连设置为默认短信应用程序才能通过它发送彩信。</string>
<string name="findmyphone_title">找到我的手机</string>
<string name="findmyphone_title_tablet">找到我的平板电脑</string>
<string name="findmyphone_title_tv">查找我的电视</string>
@@ -218,7 +218,7 @@
<string name="permission_explanation">这个插件需要权限才能工作</string>
<string name="optional_permission_explanation">您需要授予额外权限以启用全部功能</string>
<string name="plugins_need_optional_permission">因缺少权限,某些插件的一些功能已禁用(点击以查看更多信息):</string>
<string name="share_optional_permission_explanation">您需要给予访问手机存储的权限才能在手机和桌面计算机之间分享文件</string>
<string name="share_optional_permission_explanation">要接收共享的文件,您需要选择一个目标目录</string>
<string name="telepathy_permission_explanation">从计算机桌面读取、写入短消息需要向应用程序授予 SMS 权限</string>
<string name="telephony_permission_explanation">要桌面上查看手机通话记录,您需要授予访问通话记录和手机状态的权限</string>
<string name="telephony_optional_permission_explanation">要查看联系人姓名而非电话号码,您需要授予访问手机通讯录的权限</string>
@@ -234,7 +234,7 @@
<string name="presenter_lock_tip">您可以锁定设备,用音量键作为上一页/下一页按钮</string>
<string name="add_command">添加命令</string>
<string name="addcommand_explanation">没有已注册的命令</string>
<string name="addcommand_explanation2">您可以在 KDE Connect 系统设置中添加新命令</string>
<string name="addcommand_explanation2">您可以在 K 易连系统设置中添加新命令</string>
<string name="add_command_description">您可以在桌面添加新命令</string>
<string name="pref_plugin_mprisreceiver">控制媒体播放器</string>
<string name="pref_plugin_mprisreceiver_desc">从其他设备控制您的手机的播放器</string>
@@ -275,7 +275,7 @@
<string name="take_picture">启动相机</string>
<string name="plugin_photo_desc">启动相机应用程序以方便拍摄和转移图片</string>
<string name="no_app_for_opening">没有找到合适的应用程序打开此文件</string>
<string name="remote_keyboard_service">KDE Connect 远程键盘</string>
<string name="remote_keyboard_service">K 易连远程键盘</string>
<string name="presenter_pointer">指针</string>
<string name="trusted_networks">信任的网络</string>
<string name="trusted_networks_desc">限制只在已知网络中启用自动发现</string>

View File

@@ -90,7 +90,6 @@
<string name="encryption_info_title">加密資訊</string>
<string name="encryption_info_msg_no_ssl">其他的裝置沒有使用新版本的KDE連線使用傳統的加密模式。</string>
<string name="my_device_fingerprint">您裝置憑證的 SHA1 指紋是:</string>
<string name="remote_device_fingerprint">您遠端裝置憑證的 SHA1 指紋是:</string>
<string name="pair_requested">已請求配對</string>
<string name="pairing_request_from">來自 %1s 的配對請求</string>
<plurals name="incoming_file_title">
@@ -216,7 +215,6 @@
<string name="permission_explanation">這外掛程式需要權限以運作</string>
<string name="optional_permission_explanation">你需要授予延伸的權限以啟用所有的功能</string>
<string name="plugins_need_optional_permission">部份的外掛程式因為缺乏權限,而導致功能被停用。(按一下以了解更多資訊):</string>
<string name="share_optional_permission_explanation">為了要在您的手機與電腦之間分享檔案,你需要同意存取手機的儲存空間。</string>
<string name="telepathy_permission_explanation">為了要在您的個人電腦上讀取與撰寫簡訊,你需要提供簡訊的權限。</string>
<string name="telephony_permission_explanation">為了要在桌面上檢視手機通話,您需要提供手機通話記錄及手機狀態的權限。</string>
<string name="telephony_optional_permission_explanation">為了要讓聯絡人名稱取代手機號碼,您需要提供手機通訊錄的權限。</string>

View File

@@ -126,9 +126,10 @@
<string name="encryption_info_title">Encryption Info</string>
<string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string>
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is:</string>
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is:</string>
<string name="remote_device_fingerprint">SHA256 fingerprint of remote device certificate is:</string>
<string name="pair_requested">Pair requested</string>
<string name="pairing_request_from">Pairing request from %1s</string>
<string name="pairing_verification_code" translatable="false">🔑%1s...</string>
<plurals name="incoming_file_title">Receiving file from %1s>
<item quantity="one">Receiving %1$d file from %2$s</item>
<item quantity="other">Receiving %1$d files from %2$s</item>
@@ -280,7 +281,7 @@
<string name="permission_explanation">This plugin needs permissions to work</string>
<string name="optional_permission_explanation">You need to grant extra permissions to enable all functions</string>
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="share_optional_permission_explanation">To receive shared files you need to chose a destination directory</string>
<string name="share_optional_permission_explanation">To receive shared files you need to choose a destination directory</string>
<string name="telepathy_permission_explanation">To read and write SMS from your desktop you need to give permission to SMS</string>
<string name="telephony_permission_explanation">To see phone calls on the desktop you need to give permission to phone call logs and phone state</string>
<string name="telephony_optional_permission_explanation">To see a contact name instead of a phone number you need to give access to the phone\'s contacts</string>

View File

@@ -391,10 +391,11 @@ public class Device implements BaseLink.PacketReceiver {
final NotificationManager notificationManager = ContextCompat.getSystemService(getContext(), NotificationManager.class);
String verificationKeyShort = SslHelper.getVerificationKey(SslHelper.certificate, certificate).substring(8);
Notification noti = new NotificationCompat.Builder(getContext(), NotificationHelper.Channels.DEFAULT)
.setContentTitle(res.getString(R.string.pairing_request_from, getName()))
.setContentText(res.getString(R.string.tap_to_answer))
.setContentIntent(pendingIntent)
.setContentText(res.getString(R.string.pairing_verification_code, verificationKeyShort))
.setTicker(res.getString(R.string.pair_requested))
.setSmallIcon(R.drawable.ic_notification)
.addAction(R.drawable.ic_accept_pairing_24dp, res.getString(R.string.pairing_accept), acceptedPendingIntent)

View File

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

View File

@@ -30,6 +30,7 @@ import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import org.spongycastle.util.Arrays;
import java.io.IOException;
import java.math.BigInteger;
@@ -254,7 +255,7 @@ public class SslHelper {
public static String getCertificateHash(Certificate certificate) {
try {
byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded());
byte[] hash = MessageDigest.getInstance("SHA-256").digest(certificate.getEncoded());
Formatter formatter = new Formatter();
int i;
for (i = 0; i < hash.length - 1; i++) {
@@ -279,4 +280,31 @@ public class SslHelper {
return IETFUtils.valueToString(rdn.getFirst().getValue());
}
public static String getVerificationKey(X509Certificate certificateA, Certificate certificateB) {
try {
byte[] a = certificateA.getPublicKey().getEncoded();
byte[] b = certificateB.getPublicKey().getEncoded();
if (Arrays.compareUnsigned(a, b) < 0) {
// Swap them so on both devices they are in the same order
byte[] aux = a;
a = b;
b = aux;
}
byte[] concat = new byte[a.length + b.length];
System.arraycopy(a, 0, concat, 0, a.length);
System.arraycopy(b, 0, concat, a.length, b.length);
byte[] hash = MessageDigest.getInstance("SHA-256").digest(concat);
Formatter formatter = new Formatter();
for (int i = 0; i < hash.length - 1; i++) {
formatter.format("%02x", hash[i]);
}
return formatter.toString();
} catch(Exception e) {
e.printStackTrace();
return "error";
}
}
}

View File

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

View File

@@ -23,12 +23,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -46,13 +41,12 @@ import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.SystemvolumePlugin.SystemvolumeFragment;
import org.kde.kdeconnect.UserInterface.ThemeUtil;
import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect_tp.databinding.ActivityMprisBinding;
import org.kde.kdeconnect_tp.databinding.MprisControlBinding;
import java.net.MalformedURLException;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MprisActivity extends AppCompatActivity {
private String deviceId;
@@ -60,54 +54,8 @@ public class MprisActivity extends AppCompatActivity {
private Runnable positionSeekUpdateRunnable = null;
private MprisPlugin.MprisPlayer targetPlayer = null;
@BindView(R.id.play_button)
ImageButton playButton;
@BindView(R.id.prev_button)
ImageButton prevButton;
@BindView(R.id.next_button)
ImageButton nextButton;
@BindView(R.id.rew_button)
ImageButton rewButton;
@BindView(R.id.ff_button)
ImageButton ffButton;
@BindView(R.id.time_textview)
TextView timeText;
@BindView(R.id.album_art)
ImageView albumArtView;
@BindView(R.id.player_spinner)
Spinner playerSpinner;
@BindView(R.id.no_players)
TextView noPlayers;
@BindView(R.id.now_playing_textview)
TextView nowPlayingText;
@BindView(R.id.positionSeek)
SeekBar positionBar;
@BindView(R.id.progress_slider)
LinearLayout progressSlider;
@BindView(R.id.volume_seek)
SeekBar volumeSeek;
@BindView(R.id.volume_layout)
LinearLayout volumeLayout;
@BindView(R.id.stop_button)
ImageButton stopButton;
@BindView(R.id.progress_textview)
TextView progressText;
private ActivityMprisBinding activityMprisBinding;
private MprisControlBinding mprisControlBinding;
private static String milisToProgress(long milis) {
int length = (int) (milis / 1000); //From milis to seconds
@@ -128,7 +76,6 @@ public class MprisActivity extends AppCompatActivity {
}
private void connectToPlugin(final String targetPlayerName) {
BackgroundService.RunWithPlugin(this, deviceId, MprisPlugin.class, mpris -> {
targetPlayer = mpris.getPlayerStatus(targetPlayerName);
@@ -152,19 +99,18 @@ public class MprisActivity extends AppCompatActivity {
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
runOnUiThread(() -> {
playerSpinner.setAdapter(adapter);
mprisControlBinding.playerSpinner.setAdapter(adapter);
if (playerList.isEmpty()) {
noPlayers.setVisibility(View.VISIBLE);
playerSpinner.setVisibility(View.GONE);
nowPlayingText.setText("");
mprisControlBinding.noPlayers.setVisibility(View.VISIBLE);
mprisControlBinding.playerSpinner.setVisibility(View.GONE);
mprisControlBinding.nowPlayingTextview.setText("");
} else {
noPlayers.setVisibility(View.GONE);
playerSpinner.setVisibility(View.VISIBLE);
mprisControlBinding.noPlayers.setVisibility(View.GONE);
mprisControlBinding.playerSpinner.setVisibility(View.VISIBLE);
}
playerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
mprisControlBinding.playerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int pos, long id) {
@@ -196,7 +142,7 @@ public class MprisActivity extends AppCompatActivity {
if (targetPlayer != null) {
int targetIndex = adapter.getPosition(targetPlayer.getPlayer());
if (targetIndex >= 0) {
playerSpinner.setSelection(targetIndex);
mprisControlBinding.playerSpinner.setSelection(targetIndex);
} else {
targetPlayer = null;
}
@@ -204,18 +150,16 @@ public class MprisActivity extends AppCompatActivity {
//If no player selected, select the first one (if any)
if (targetPlayer == null && !playerList.isEmpty()) {
targetPlayer = mpris.getPlayerStatus(playerList.get(0));
playerSpinner.setSelection(0);
mprisControlBinding.playerSpinner.setSelection(0);
}
updatePlayerStatus(mpris);
});
}
});
});
}
private void addSystemVolumeFragment() {
if (findViewById(R.id.systemvolume_fragment) == null)
return;
@@ -249,8 +193,8 @@ public class MprisActivity extends AppCompatActivity {
}
String song = playerStatus.getCurrentSong();
if (!nowPlayingText.getText().toString().equals(song)) {
nowPlayingText.setText(song);
if (!mprisControlBinding.nowPlayingTextview.getText().toString().equals(song)) {
mprisControlBinding.nowPlayingTextview.setText(song);
}
Bitmap albumArt = playerStatus.getAlbumArt();
@@ -259,47 +203,47 @@ public class MprisActivity extends AppCompatActivity {
assert drawable != null;
Drawable placeholder_art = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(placeholder_art, ContextCompat.getColor(this, R.color.primary));
albumArtView.setImageDrawable(placeholder_art);
activityMprisBinding.albumArt.setImageDrawable(placeholder_art);
} else {
albumArtView.setImageBitmap(albumArt);
activityMprisBinding.albumArt.setImageBitmap(albumArt);
}
if (playerStatus.isSeekAllowed()) {
timeText.setText(milisToProgress(playerStatus.getLength()));
positionBar.setMax((int) (playerStatus.getLength()));
positionBar.setProgress((int) (playerStatus.getPosition()));
progressSlider.setVisibility(View.VISIBLE);
mprisControlBinding.timeTextview.setText(milisToProgress(playerStatus.getLength()));
mprisControlBinding.positionSeek.setMax((int) (playerStatus.getLength()));
mprisControlBinding.positionSeek.setProgress((int) (playerStatus.getPosition()));
mprisControlBinding.progressSlider.setVisibility(View.VISIBLE);
} else {
progressSlider.setVisibility(View.GONE);
mprisControlBinding.progressSlider.setVisibility(View.GONE);
}
int volume = playerStatus.getVolume();
volumeSeek.setProgress(volume);
mprisControlBinding.volumeSeek.setProgress(volume);
boolean isPlaying = playerStatus.isPlaying();
if (isPlaying) {
playButton.setImageResource(R.drawable.ic_pause_black);
playButton.setEnabled(playerStatus.isPauseAllowed());
mprisControlBinding.playButton.setImageResource(R.drawable.ic_pause_black);
mprisControlBinding.playButton.setEnabled(playerStatus.isPauseAllowed());
} else {
playButton.setImageResource(R.drawable.ic_play_black);
playButton.setEnabled(playerStatus.isPlayAllowed());
mprisControlBinding.playButton.setImageResource(R.drawable.ic_play_black);
mprisControlBinding.playButton.setEnabled(playerStatus.isPlayAllowed());
}
volumeLayout.setVisibility(playerStatus.isSetVolumeAllowed() ? View.VISIBLE : View.GONE);
rewButton.setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
ffButton.setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
mprisControlBinding.volumeLayout.setVisibility(playerStatus.isSetVolumeAllowed() ? View.VISIBLE : View.GONE);
mprisControlBinding.rewButton.setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
mprisControlBinding.ffButton.setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
invalidateOptionsMenu();
//Show and hide previous/next buttons simultaneously
if (playerStatus.isGoPreviousAllowed() || playerStatus.isGoNextAllowed()) {
prevButton.setVisibility(View.VISIBLE);
prevButton.setEnabled(playerStatus.isGoPreviousAllowed());
nextButton.setVisibility(View.VISIBLE);
nextButton.setEnabled(playerStatus.isGoNextAllowed());
mprisControlBinding.prevButton.setVisibility(View.VISIBLE);
mprisControlBinding.prevButton.setEnabled(playerStatus.isGoPreviousAllowed());
mprisControlBinding.nextButton.setVisibility(View.VISIBLE);
mprisControlBinding.nextButton.setEnabled(playerStatus.isGoNextAllowed());
} else {
prevButton.setVisibility(View.GONE);
nextButton.setVisibility(View.GONE);
mprisControlBinding.prevButton.setVisibility(View.GONE);
mprisControlBinding.nextButton.setVisibility(View.GONE);
}
}
@@ -366,8 +310,11 @@ public class MprisActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeUtil.setUserPreferredTheme(this);
setContentView(R.layout.activity_mpris);
ButterKnife.bind(this);
activityMprisBinding = ActivityMprisBinding.inflate(getLayoutInflater());
mprisControlBinding = activityMprisBinding.mprisControl;
setContentView(activityMprisBinding.getRoot());
String targetPlayerName = getIntent().getStringExtra("player");
getIntent().removeExtra("player");
@@ -388,19 +335,19 @@ public class MprisActivity extends AppCompatActivity {
BackgroundService.RunCommand(MprisActivity.this, service -> service.addConnectionListener(connectionReceiver));
connectToPlugin(targetPlayerName);
performActionOnClick(playButton, MprisPlugin.MprisPlayer::playPause);
performActionOnClick(mprisControlBinding.playButton, MprisPlugin.MprisPlayer::playPause);
performActionOnClick(prevButton, MprisPlugin.MprisPlayer::previous);
performActionOnClick(mprisControlBinding.prevButton, MprisPlugin.MprisPlayer::previous);
performActionOnClick(rewButton, p -> targetPlayer.seek(interval_time * -1));
performActionOnClick(mprisControlBinding.rewButton, p -> targetPlayer.seek(interval_time * -1));
performActionOnClick(ffButton, p -> p.seek(interval_time));
performActionOnClick(mprisControlBinding.ffButton, p -> p.seek(interval_time));
performActionOnClick(nextButton, MprisPlugin.MprisPlayer::next);
performActionOnClick(mprisControlBinding.nextButton, MprisPlugin.MprisPlayer::next);
performActionOnClick(stopButton, MprisPlugin.MprisPlayer::stop);
performActionOnClick(mprisControlBinding.stopButton, MprisPlugin.MprisPlayer::stop);
volumeSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
mprisControlBinding.volumeSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
@@ -416,22 +363,21 @@ public class MprisActivity extends AppCompatActivity {
targetPlayer.setVolume(seekBar.getProgress());
});
}
});
positionSeekUpdateRunnable = () -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer != null) {
positionBar.setProgress((int) (targetPlayer.getPosition()));
mprisControlBinding.positionSeek.setProgress((int) (targetPlayer.getPosition()));
}
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable);
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 1000);
});
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200);
positionBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
mprisControlBinding.positionSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean byUser) {
progressText.setText(milisToProgress(progress));
mprisControlBinding.progressTextview.setText(milisToProgress(progress));
}
@Override
@@ -448,13 +394,11 @@ public class MprisActivity extends AppCompatActivity {
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200);
});
}
});
nowPlayingText.setSelected(true);
mprisControlBinding.nowPlayingTextview.setSelected(true);
}
final static int MENU_OPEN_URL = Menu.FIRST;
public boolean onPrepareOptionsMenu(Menu menu) {

View File

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

View File

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

View File

@@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* A small BroadcastReceiver wrapper for MMSReceivedReceiver to load user preferences
*/
public class DelegatingMmsReceivedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
MmsReceivedReceiver delegate = new MmsReceivedReceiver();
delegate.getPreferredApn(context, intent);
delegate.onReceive(context, intent);
}
}

View File

@@ -1,24 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.content.Intent;
import android.app.Service;
import android.os.IBinder;
/**
* Service for sending messages to a conversation without a UI present. These messages could come
* from something like Phone, needed to make default sms app
*/
public class HeadlessSmsSendService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View File

@@ -1,163 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.app.NotificationManager;
import android.app.PendingIntent;
import androidx.core.app.NotificationCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.TelephonyHelper;
import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
import java.util.HashSet;
/**
* Receiver for notifying user when a new MMS has been received by the device. By default it will
* persist the message to the internal database and it will also show a notification in the status bar.
*/
public class MmsReceivedReceiver extends com.klinker.android.send_message.MmsReceivedReceiver {
private TelephonyHelper.ApnSetting apnSetting = null;
@Override
public void onMessageReceived(Context context, Uri messageUri) {
Log.v("MmsReceived", "message received: " + messageUri.toString());
// Notify messageUpdateReceiver about the arrival of the new MMS message
Intent refreshIntent = new Intent(Transaction.REFRESH);
context.sendBroadcast(refreshIntent);
// Fetch the latest message from the database
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
// Notify the user about the received mms message
createMmsNotification(context, message);
}
@Override
public void onError(Context context, String error) {
Log.v("MmsReceived", "error: " + error);
// Fetch the latest message from the database
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
// Notify the user about the received mms message
createMmsNotification(context, message);
}
public void getPreferredApn(Context context, Intent intent) {
int subscriptionId = intent.getIntExtra(SUBSCRIPTION_ID, Utils.getDefaultSubscriptionId());
apnSetting = TelephonyHelper.getPreferredApn(context, subscriptionId);
}
/**
* some carriers will download duplicate MMS messages without this ACK. When using the
* system sending method, apparently Android does not do this for us. Not sure why.
* We might have to have users manually enter their APN settings if we cannot get them
* from the system somehow.
*/
@Override
public MmscInformation getMmscInfoForReceptionAck() {
if (apnSetting != null) {
String mmscUrl = apnSetting.getMmsc().toString();
String mmsProxy = apnSetting.getMmsProxyAddressAsString();
int mmsPort = apnSetting.getMmsProxyPort();
try {
return new MmscInformation(mmscUrl, mmsProxy, mmsPort);
} catch (Exception e) {
Log.e("MmsReceivedReceiver", "Exception", e);
}
}
return null;
}
private void createMmsNotification(Context context, SMSHelper.Message mmsMessage) {
ArrayList<String> addressList = new ArrayList<>();
for (SMSHelper.Address address : mmsMessage.addresses) {
addressList.add(address.toString());
}
Person sender = NotificationReplyReceiver.getMessageSender(context, addressList.get(0));
int notificationId;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationId = (int) Utils.getOrCreateThreadId(context, new HashSet<>(addressList));
} else {
notificationId = (int) System.currentTimeMillis();
}
// Todo: When SMSHelper.Message class will be modified to contain thumbnail of the image or video attachment, add them here to display.
// Create pending intent for reply action through notification
PendingIntent replyPendingIntent = NotificationReplyReceiver.createReplyPendingIntent(
context,
addressList,
notificationId,
true
);
RemoteInput remoteReplyInput = new RemoteInput.Builder(NotificationReplyReceiver.KEY_TEXT_REPLY)
.setLabel(context.getString(R.string.message_reply_label))
.build();
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.message_reply_label), replyPendingIntent)
.addRemoteInput(remoteReplyInput)
.setAllowGeneratedReplies(true)
.build();
// Create pending intent for marking the message as read in database through mark as read action
PendingIntent markAsReadPendingIntent = NotificationReplyReceiver.createMarkAsReadPendingIntent(
context,
addressList,
notificationId
);
NotificationCompat.Action markAsReadAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.mark_as_read_label), markAsReadPendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.MessagingStyle messagingStyle = NotificationReplyReceiver.createMessagingStyle(
context,
notificationId,
mmsMessage.body,
TextUtils.join(",", addressList),
mmsMessage.date,
sender,
notificationManager
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationReplyReceiver.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_sms_24)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(messagingStyle)
.setAutoCancel(true)
.addAction(replyAction)
.addAction(markAsReadAction)
.setGroup(NotificationReplyReceiver.SMS_NOTIFICATION_GROUP_KEY);
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
}

View File

@@ -1,53 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.content.Context;
import android.content.Intent;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
public class MmsSentReceiverImpl extends com.klinker.android.send_message.MmsSentReceiver {
@Override
public void updateInInternalDatabase(Context context, Intent intent, int resultCode) {
super.updateInInternalDatabase(context, intent, resultCode);
if (Utils.isDefaultSmsApp(context)) {
// Notify messageUpdateReceiver about the successful sending of the mms message
Intent refreshIntent = new Intent(Transaction.REFRESH);
context.sendBroadcast(refreshIntent);
}
}
@Override
public void onMessageStatusUpdated(Context context, Intent intent, int resultCode) {
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
ArrayList<String> addressList = new ArrayList<>();
for (SMSHelper.Address address : message.addresses) {
addressList.add(address.toString());
}
Intent repliedNotification = new Intent(context, NotificationReplyReceiver.class);
repliedNotification.setAction(NotificationReplyReceiver.SMS_MMS_REPLY_ACTION);
repliedNotification.putExtra(NotificationReplyReceiver.TEXT_BODY, message.body);
repliedNotification.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, Integer.parseInt(message.threadID.toString()));
repliedNotification.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
// SEND_ACTION value is required to differentiate between the intents sent from reply action or
// SentReceivers inorder to avoid posting duplicate notifications
repliedNotification.putExtra(NotificationReplyReceiver.SEND_ACTION, false);
context.sendBroadcast(repliedNotification);
}
}

View File

@@ -1,289 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.IconCompat;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.ContactsHelper;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Helpers.SMSHelper;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
public class NotificationReplyReceiver extends BroadcastReceiver {
public static final String SMS_MMS_REPLY_ACTION = "org.kde.kdeconnect.Plugins.SMSPlugin.sms_mms_reply_action";
public static final String SMS_MMS_MARK_ACTION = "org.kde.kdeconnect.Plugins.SMSPlugin.sms_mms_mark_action";
public static final String ADDRESS_LIST = "address_list";
public static final String CHANNEL_ID = NotificationHelper.Channels.SMS_MMS;
public static final String KEY_TEXT_REPLY = "key_text_reply";
public static final String NOTIFICATION_ID = "notification_id";
public static final String SEND_ACTION = "send_action";
public static final String TEXT_BODY = "text_body";
public static final String SMS_NOTIFICATION_GROUP_KEY = "Plugins.SMSPlugin.sms_notification_group_key";
@Override
public void onReceive(Context context, Intent intent) {
if (!Utils.isDefaultSmsApp(context) || intent == null) {
return;
}
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
final int notificationId = intent.getIntExtra(NotificationReplyReceiver.NOTIFICATION_ID, 0);
final ArrayList<String> addressList = intent.getStringArrayListExtra(ADDRESS_LIST);
final boolean sentUsingReplyButton = intent.getBooleanExtra(SEND_ACTION, false);
if (intent.getAction().equals(SMS_MMS_REPLY_ACTION)) {
String inputString = null;
if (sentUsingReplyButton) {
inputString = remoteInput.getCharSequence(NotificationReplyReceiver.KEY_TEXT_REPLY).toString();
ArrayList<SMSHelper.Address> addresses = new ArrayList<>();
for (String address : addressList) {
addresses.add(new SMSHelper.Address(address));
}
SmsMmsUtils.sendMessage(context, inputString, addresses, -1);
} else {
inputString = intent.getStringExtra(TEXT_BODY);
repliedMessageNotification(context, notificationId, inputString, addressList);
}
}
// Mark the conversation as read
if (intent.getAction().equals(SMS_MMS_MARK_ACTION)) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
SmsMmsUtils.markConversationRead(context, new HashSet<>(addressList));
}
}
/**
* Updates the active notification with the newly replied message
*/
private void repliedMessageNotification(Context context, int notificationId, String inputString, ArrayList<String> addressList) {
Person sender = new Person.Builder()
.setName(context.getString(R.string.user_display_name))
.build();
// Create pending intent for reply action through notification
PendingIntent replyPendingIntent = createReplyPendingIntent(context, addressList, notificationId, true);
RemoteInput remoteReplyInput = new RemoteInput.Builder(NotificationReplyReceiver.KEY_TEXT_REPLY)
.setLabel(context.getString(R.string.message_reply_label))
.build();
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.message_reply_label), replyPendingIntent)
.addRemoteInput(remoteReplyInput)
.setAllowGeneratedReplies(true)
.build();
// Create pending intent for marking the message as read in database through mark as read action
PendingIntent markAsReadPendingIntent = createMarkAsReadPendingIntent(context, addressList, notificationId);
NotificationCompat.Action markAsReadAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.mark_as_read_label), markAsReadPendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.MessagingStyle.Message message = new NotificationCompat.MessagingStyle.Message(
inputString,
System.currentTimeMillis(),
sender
);
NotificationCompat.MessagingStyle messagingStyle = restoreActiveMessagingStyle(notificationId, notificationManager);
if (messagingStyle == null) {
// Return when there is no active notification in the statusBar with the above notificationId
return;
}
messagingStyle.addMessage(message);
NotificationCompat.Builder repliedNotification = new NotificationCompat.Builder(context, NotificationReplyReceiver.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_sms_24)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setOnlyAlertOnce(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(messagingStyle)
.setAutoCancel(true)
.addAction(replyAction)
.addAction(markAsReadAction)
.setGroup(NotificationReplyReceiver.SMS_NOTIFICATION_GROUP_KEY);
NotificationHelper.notifyCompat(notificationManager, notificationId, repliedNotification.build());
}
/**
* This method creates a new messaging style for newer conversations and if there is already an active notification
* of the same id, it just adds to the previous and returns the modified messagingStyle object.
*/
public static NotificationCompat.MessagingStyle createMessagingStyle(
Context context,
int notificationId,
String textMessage,
String phoneNumbers,
long date,
Person sender,
NotificationManager notificationManager
) {
NotificationCompat.MessagingStyle messageStyle = NotificationReplyReceiver.restoreActiveMessagingStyle(
notificationId,
notificationManager
);
NotificationCompat.MessagingStyle.Message message = new NotificationCompat.MessagingStyle.Message(
textMessage,
date,
sender
);
if (messageStyle == null) {
// When no active notification is found for matching conversation create a new one
String senderName = phoneNumbers;
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, phoneNumbers);
if (contactInfo.containsKey("name")) {
senderName = contactInfo.get("name");
}
messageStyle = new NotificationCompat.MessagingStyle(sender)
.setConversationTitle(senderName);
}
messageStyle.addMessage(message);
return messageStyle;
}
/**
* This method is responsible for searching the notification for same conversation ID and if there is an active notification
* of save ID found in the status menu it extracts and returns the messagingStyle object of that notification
*/
public static NotificationCompat.MessagingStyle restoreActiveMessagingStyle(
int notificationId,
NotificationManager notificationManager
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
StatusBarNotification notifications[] = notificationManager.getActiveNotifications();
for (StatusBarNotification notification : notifications) {
if (notification.getId() == notificationId) {
return NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(notification.getNotification());
}
}
}
return null;
}
/**
* returns the sender of the message as a Person object
*/
public static Person getMessageSender(Context context, String address) {
Map<String, String> contactInfo = ContactsHelper.phoneNumberLookup(context, address);
String senderName = address;
if (contactInfo.containsKey("name")) {
senderName = contactInfo.get("name");
}
Bitmap contactPhoto = null;
if (contactInfo.containsKey("photoID")) {
String photoUri = contactInfo.get("photoID");
if (photoUri != null) {
try {
String base64photo = ContactsHelper.photoId64Encoded(context, photoUri);
if (!TextUtils.isEmpty(base64photo)) {
byte[] decodedString = Base64.decode(base64photo, Base64.DEFAULT);
contactPhoto = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
}
} catch (Exception e) {
Log.e("SMS Notification", "Failed to get contact photo");
}
}
}
Person.Builder personBuilder = new Person.Builder()
.setName(senderName);
if (contactPhoto != null) {
personBuilder.setIcon(IconCompat.createWithBitmap(contactPhoto));
}
return personBuilder.build();
}
/**
* Create pending intent for reply action through notification
*/
public static PendingIntent createReplyPendingIntent(
Context context,
ArrayList<String> addressList,
int notificationId,
boolean isFromSendAction
) {
Intent replyIntent = new Intent(context, NotificationReplyReceiver.class);
replyIntent.setAction(NotificationReplyReceiver.SMS_MMS_REPLY_ACTION);
replyIntent.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, notificationId);
replyIntent.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
replyIntent.putExtra(NotificationReplyReceiver.SEND_ACTION, isFromSendAction);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(
context,
notificationId,
replyIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
return replyPendingIntent;
}
/**
* Create pending intent for marking the message as read in database through mark as read action
*/
public static PendingIntent createMarkAsReadPendingIntent(
Context context,
ArrayList<String> addressList,
int notificationId
) {
Intent markAsReadIntent = new Intent(context, NotificationReplyReceiver.class);
markAsReadIntent.setAction(NotificationReplyReceiver.SMS_MMS_MARK_ACTION);
markAsReadIntent.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, notificationId);
markAsReadIntent.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
PendingIntent markAsReadPendingIntent = PendingIntent.getBroadcast(
context,
notificationId,
markAsReadIntent,
PendingIntent.FLAG_CANCEL_CURRENT
);
return markAsReadPendingIntent;
}
}

View File

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

View File

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

View File

@@ -1,150 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.provider.Telephony.Sms;
import android.net.Uri;
import android.content.ContentValues;
import androidx.core.app.NotificationCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.content.ContextCompat;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Arrays;
public class SmsReceiver extends BroadcastReceiver {
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
if (!Utils.isDefaultSmsApp(context)) {
return;
}
if (intent != null && intent.getAction().equals(SMS_RECEIVED)) {
Bundle dataBundle = intent.getExtras();
if (dataBundle != null) {
Object[] smsExtra = (Object[]) dataBundle.get("pdus");
final SmsMessage[] message = new SmsMessage[smsExtra.length];
for (int i = 0; i < smsExtra.length; ++i) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String format = dataBundle.getString("format");
message[i] = SmsMessage.createFromPdu((byte[]) smsExtra[i], format);
} else {
message[i] = SmsMessage.createFromPdu((byte[]) smsExtra[i]);
}
// Write the received sms to the sms provider
for (SmsMessage msg : message) {
ContentValues values = new ContentValues();
values.put(Sms.ADDRESS, msg.getDisplayOriginatingAddress());
values.put(Sms.BODY, msg.getMessageBody());
values.put(Sms.DATE, System.currentTimeMillis()+"");
values.put(Sms.TYPE, Sms.MESSAGE_TYPE_INBOX);
values.put(Sms.STATUS, msg.getStatus());
values.put(Sms.READ, 0);
values.put(Sms.SEEN, 0);
context.getApplicationContext().getContentResolver().insert(Uri.parse("content://sms/"), values);
// Notify messageUpdateReceiver about the arrival of the new sms message
Intent refreshIntent = new Intent(Transaction.REFRESH);
context.sendBroadcast(refreshIntent);
}
String body = message[i].getMessageBody();
String phoneNo = message[i].getOriginatingAddress();
long date = message[i].getTimestampMillis();
createSmsNotification(context, body, phoneNo, date);
}
}
}
}
private void createSmsNotification(Context context, String body, String phoneNo, long date) {
int notificationId;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationId = (int) Utils.getOrCreateThreadId(context, phoneNo);
} else {
notificationId = (int) System.currentTimeMillis();
}
Person sender = NotificationReplyReceiver.getMessageSender(context, phoneNo);
ArrayList<String> addressList = new ArrayList<>(Arrays.asList(phoneNo));
// Create pending intent for reply action through notification
PendingIntent replyPendingIntent = NotificationReplyReceiver.createReplyPendingIntent(
context,
addressList,
notificationId,
true
);
RemoteInput remoteReplyInput = new RemoteInput.Builder(NotificationReplyReceiver.KEY_TEXT_REPLY)
.setLabel(context.getString(R.string.message_reply_label))
.build();
NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.message_reply_label), replyPendingIntent)
.addRemoteInput(remoteReplyInput)
.setAllowGeneratedReplies(true)
.build();
// Create pending intent for marking the message as read in database through mark as read action
PendingIntent markAsReadPendingIntent = NotificationReplyReceiver.createMarkAsReadPendingIntent(
context,
addressList,
notificationId
);
NotificationCompat.Action markAsReadAction = new NotificationCompat.Action.Builder(0, context.getString(R.string.mark_as_read_label), markAsReadPendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationCompat.MessagingStyle messagingStyle = NotificationReplyReceiver.createMessagingStyle(
context,
notificationId,
body,
phoneNo,
date,
sender,
notificationManager
);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationReplyReceiver.CHANNEL_ID)
.setSmallIcon(R.drawable.ic_baseline_sms_24)
.setColor(ContextCompat.getColor(context, R.color.primary))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(messagingStyle)
.setAutoCancel(true)
.addAction(replyAction)
.addAction(markAsReadAction)
.setGroup(NotificationReplyReceiver.SMS_NOTIFICATION_GROUP_KEY);
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
}

View File

@@ -1,54 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020 Aniket Kumar <anikketkumar786@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.content.Context;
import android.content.Intent;
import com.klinker.android.send_message.SentReceiver;
import com.klinker.android.send_message.Transaction;
import com.klinker.android.send_message.Utils;
import org.kde.kdeconnect.Helpers.SMSHelper;
import java.util.ArrayList;
public class SmsSentReceiver extends SentReceiver {
@Override
public void updateInInternalDatabase(Context context, Intent intent, int receiverResultCode) {
super.updateInInternalDatabase(context, intent, receiverResultCode);
if (Utils.isDefaultSmsApp(context)) {
// Notify messageUpdateReceiver about the successful sending of the sms message
Intent refreshIntent = new Intent(Transaction.REFRESH);
context.sendBroadcast(refreshIntent);
}
}
@Override
public void onMessageStatusUpdated(Context context, Intent intent, int receiverResultCode) {
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
ArrayList<String> addressList = new ArrayList<>();
for (SMSHelper.Address address : message.addresses) {
addressList.add(address.toString());
}
Intent repliedNotification = new Intent(context, NotificationReplyReceiver.class);
repliedNotification.setAction(NotificationReplyReceiver.SMS_MMS_REPLY_ACTION);
repliedNotification.putExtra(NotificationReplyReceiver.TEXT_BODY, message.body);
repliedNotification.putExtra(NotificationReplyReceiver.NOTIFICATION_ID, Integer.parseInt(message.threadID.toString()));
repliedNotification.putExtra(NotificationReplyReceiver.ADDRESS_LIST, addressList);
// SEND_ACTION value is required to differentiate between the intents sent from reply action or
// SentReceivers inorder to avoid posting duplicate notifications
repliedNotification.putExtra(NotificationReplyReceiver.SEND_ACTION, false);
context.sendBroadcast(repliedNotification);
}
}

View File

@@ -13,14 +13,12 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;
import org.kde.kdeconnect_tp.R;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.DialogPreference;
import androidx.preference.PreferenceViewHolder;
import butterknife.BindView;
import butterknife.ButterKnife;
import org.kde.kdeconnect_tp.R;
public class StoragePreference extends DialogPreference {
@Nullable
@@ -28,7 +26,7 @@ public class StoragePreference extends DialogPreference {
@Nullable
private OnLongClickListener onLongClickListener;
@BindView(R.id.checkbox) CheckBox checkbox;
CheckBox checkbox;
public boolean inSelectionMode;
public void setInSelectionMode(boolean inSelectionMode) {
@@ -101,7 +99,7 @@ public class StoragePreference extends DialogPreference {
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
ButterKnife.bind(this, holder.itemView);
checkbox = (CheckBox) holder.itemView.findViewById(R.id.checkbox);
checkbox.setVisibility(inSelectionMode ? View.VISIBLE : View.INVISIBLE);

View File

@@ -18,24 +18,19 @@ import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import org.json.JSONException;
import org.json.JSONObject;
import org.kde.kdeconnect.Helpers.StorageHelper;
import org.kde.kdeconnect_tp.R;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.widget.TextViewCompat;
import androidx.preference.PreferenceDialogFragmentCompat;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
import org.json.JSONException;
import org.json.JSONObject;
import org.kde.kdeconnect.Helpers.StorageHelper;
import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect_tp.databinding.FragmentStoragePreferenceDialogBinding;
public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCompat implements TextWatcher {
private static final int REQUEST_CODE_DOCUMENT_TREE = 1001;
@@ -45,11 +40,8 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
private static final String KEY_STORAGE_INFO = "StorageInfo";
private static final String KEY_TAKE_FLAGS = "TakeFlags";
@BindView(R.id.storageLocation) TextInputEditText storageLocation;
@BindView(R.id.storageDisplayName) TextInputEditText storageDisplayName;
@BindView(R.id.storageDisplayNameInputLayout) TextInputLayout storageDisplayInputLayout;
private FragmentStoragePreferenceDialogBinding binding;
private Unbinder unbinder;
private Callback callback;
private Drawable arrowDropDownDrawable;
private Button positiveButton;
@@ -116,26 +108,33 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
unbinder = ButterKnife.bind(this, view);
binding = FragmentStoragePreferenceDialogBinding.bind(view);
storageDisplayName.setFilters(new InputFilter[]{new FileSeparatorCharFilter()});
storageDisplayName.addTextChangedListener(this);
binding.storageLocation.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
//For API >= 26 we can also set Extra: DocumentsContract.EXTRA_INITIAL_URI
startActivityForResult(intent, REQUEST_CODE_DOCUMENT_TREE);
});
binding.storageDisplayName.setFilters(new InputFilter[]{new FileSeparatorCharFilter()});
binding.storageDisplayName.addTextChangedListener(this);
if (getPreference().getKey().equals(getString(R.string.sftp_preference_key_add_storage))) {
if (!stateRestored) {
enablePositiveButton = false;
storageLocation.setText(requireContext().getString(R.string.sftp_storage_preference_click_to_select));
binding.storageLocation.setText(requireContext().getString(R.string.sftp_storage_preference_click_to_select));
}
boolean isClickToSelect = TextUtils.equals(storageLocation.getText(),
boolean isClickToSelect = TextUtils.equals(binding.storageLocation.getText(),
getString(R.string.sftp_storage_preference_click_to_select));
storageLocation.setCompoundDrawables(null, null, isClickToSelect ? arrowDropDownDrawable : null, null);
storageLocation.setEnabled(isClickToSelect);
storageLocation.setFocusable(false);
storageLocation.setFocusableInTouchMode(false);
TextViewCompat.setCompoundDrawablesRelative(binding.storageLocation, null, null,
isClickToSelect ? arrowDropDownDrawable : null, null);
binding.storageLocation.setEnabled(isClickToSelect);
binding.storageLocation.setFocusable(false);
binding.storageLocation.setFocusableInTouchMode(false);
storageDisplayName.setEnabled(!isClickToSelect);
binding.storageDisplayName.setEnabled(!isClickToSelect);
} else {
if (!stateRestored) {
StoragePreference preference = (StoragePreference) getPreference();
@@ -148,20 +147,20 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
storageInfo = SftpPlugin.StorageInfo.copy(info);
if (Build.VERSION.SDK_INT < 21) {
storageLocation.setText(storageInfo.uri.getPath());
binding.storageLocation.setText(storageInfo.uri.getPath());
} else {
storageLocation.setText(DocumentsContract.getTreeDocumentId(storageInfo.uri));
binding.storageLocation.setText(DocumentsContract.getTreeDocumentId(storageInfo.uri));
}
storageDisplayName.setText(storageInfo.displayName);
binding.storageDisplayName.setText(storageInfo.displayName);
}
storageLocation.setCompoundDrawables(null, null, null, null);
storageLocation.setEnabled(false);
storageLocation.setFocusable(false);
storageLocation.setFocusableInTouchMode(false);
TextViewCompat.setCompoundDrawablesRelative(binding.storageLocation, null, null, null, null);
binding.storageLocation.setEnabled(false);
binding.storageLocation.setFocusable(false);
binding.storageLocation.setFocusableInTouchMode(false);
storageDisplayName.setEnabled(true);
binding.storageDisplayName.setEnabled(true);
}
}
@@ -169,15 +168,7 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@OnClick(R.id.storageLocation)
void onSelectStorageClicked() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
//For API >= 26 we can also set Extra: DocumentsContract.EXTRA_INITIAL_URI
startActivityForResult(intent, REQUEST_CODE_DOCUMENT_TREE);
binding = null;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -206,16 +197,16 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
storageInfo = new SftpPlugin.StorageInfo(displayName, uri);
storageLocation.setText(documentId);
storageLocation.setCompoundDrawables(null, null, null, null);
storageLocation.setError(null);
storageLocation.setEnabled(false);
binding.storageLocation.setText(documentId);
TextViewCompat.setCompoundDrawablesRelative(binding.storageLocation, null, null, null, null);
binding.storageLocation.setError(null);
binding.storageLocation.setEnabled(false);
// TODO: Show name as used in android's picker app but I don't think it's possible to get that, everything I tried throws PermissionDeniedException
storageDisplayName.setText(displayName);
storageDisplayName.setEnabled(true);
binding.storageDisplayName.setText(displayName);
binding.storageDisplayName.setEnabled(true);
} else {
storageLocation.setError(result.errorMessage);
binding.storageLocation.setError(result.errorMessage);
setPositiveButtonEnabled(false);
}
break;
@@ -239,7 +230,7 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
@Override
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
storageInfo.displayName = storageDisplayName.getText().toString();
storageInfo.displayName = binding.storageDisplayName.getText().toString();
if (getPreference().getKey().equals(getString(R.string.sftp_preference_key_add_storage))) {
callback.addNewStoragePreference(storageInfo, takeFlags);
@@ -273,7 +264,7 @@ public class StoragePreferenceDialogFragment extends PreferenceDialogFragmentCom
setPositiveButtonEnabled(true);
} else {
setPositiveButtonEnabled(false);
storageDisplayName.setError(result.errorMessage);
binding.storageDisplayName.setError(result.errorMessage);
}
}
}

View File

@@ -12,20 +12,18 @@ import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.widget.TextViewCompat;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect_tp.databinding.CustomDeviceItemBinding;
import java.util.ArrayList;
public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdapter.ViewHolder> {
private ArrayList<String> customDevices;
@@ -55,9 +53,10 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_device_item, parent, false);
CustomDeviceItemBinding itemBinding =
CustomDeviceItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
return new ViewHolder(v);
return new ViewHolder(itemBinding);
}
@Override
@@ -71,30 +70,29 @@ public class CustomDevicesAdapter extends RecyclerView.Adapter<CustomDevicesAdap
}
class ViewHolder extends RecyclerView.ViewHolder implements SwipeableViewHolder {
@BindView(R.id.deviceNameOrIPBackdrop) TextView deviceNameOrIPBackdrop;
@BindView(R.id.swipeableView) FrameLayout swipeableView;
@BindView(R.id.deviceNameOrIP) TextView deviceNameOrIP;
private final CustomDeviceItemBinding itemBinding;
ViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
ViewHolder(@NonNull CustomDeviceItemBinding itemBinding) {
super(itemBinding.getRoot());
this.itemBinding = itemBinding;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Drawable deleteDrawable = AppCompatResources.getDrawable(itemView.getContext(), R.drawable.ic_delete);
deviceNameOrIPBackdrop.setCompoundDrawablesWithIntrinsicBounds(deleteDrawable, null, deleteDrawable, null);
Drawable deleteDrawable = AppCompatResources.getDrawable(itemBinding.getRoot().getContext(),
R.drawable.ic_delete);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(itemBinding.deviceNameOrIPBackdrop,
deleteDrawable, null, deleteDrawable, null);
}
deviceNameOrIP.setOnClickListener(v -> callback.onCustomDeviceClicked(customDevices.get(getAdapterPosition())));
itemBinding.deviceNameOrIP.setOnClickListener(v -> callback.onCustomDeviceClicked(customDevices.get(getAdapterPosition())));
}
void bind(String customDevice) {
deviceNameOrIP.setText(customDevice);
itemBinding.deviceNameOrIP.setText(customDevice);
}
@Override
public View getSwipeableView() {
return swipeableView;
return itemBinding.swipeableView;
}
}

View File

@@ -104,6 +104,8 @@ public class DeviceFragment extends Fragment {
binding.pairButton.setOnClickListener(v -> {
binding.pairButton.setVisibility(View.GONE);
binding.pairMessage.setText("");
binding.pairVerification.setVisibility(View.VISIBLE);
binding.pairVerification.setText(SslHelper.getVerificationKey(SslHelper.certificate, device.certificate));
binding.pairProgress.setVisibility(View.VISIBLE);
BackgroundService.RunCommand(mActivity, service -> {
device = service.getDevice(mDeviceId);
@@ -259,6 +261,8 @@ public class DeviceFragment extends Fragment {
public void run() {
if (device.isPairRequestedByPeer()) {
binding.pairMessage.setText(R.string.pair_requested);
binding.pairVerification.setVisibility(View.VISIBLE);
binding.pairVerification.setText(SslHelper.getVerificationKey(SslHelper.certificate, device.certificate));
binding.pairingButtons.setVisibility(View.VISIBLE);
binding.pairProgress.setVisibility(View.GONE);
binding.pairButton.setVisibility(View.GONE);
@@ -296,44 +300,6 @@ public class DeviceFragment extends Fragment {
dialog.show(getChildFragmentManager(), null);
}
});
// Add a button to the pluginList for setting KDE Connect as default sms app for allowing it to send mms
if (!Utils.isDefaultSmsApp(mActivity)) {
// Check if there are any preferred APN settings available on the device, if not then disable the MMS support
boolean hasApnSettings = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
List<Integer> subIds = TelephonyHelper.getActiveSubscriptionIDs(mActivity);
for (final int subId : subIds) {
if (TelephonyHelper.getPreferredApn(mActivity, subId) != null) {
hasApnSettings = true;
break;
}
}
} else {
if (TelephonyHelper.getPreferredApn(mActivity, 0) != null) {
hasApnSettings = true;
}
}
for (Plugin p : plugins) {
if (p.getPluginKey().equals("SMSPlugin") && hasApnSettings) {
pluginListItems.add(new SetDefaultAppPluginListItem(p, mActivity.getResources().getString(R.string.pref_plugin_telepathy_mms), (action) -> {
DialogFragment dialog = new DefaultSmsAppAlertDialogFragment.Builder()
.setTitle(R.string.set_default_sms_app_title)
.setMessage(R.string.pref_plugin_telepathy_mms_desc)
.setPositiveButton(R.string.ok)
.setNegativeButton(R.string.cancel)
.setPermissions(SMSPlugin.getMmsPermissions())
.setRequestCode(MainActivity.RESULT_NEEDS_RELOAD)
.create();
if (dialog != null) {
dialog.show(getChildFragmentManager(), null);
}
}));
}
}
}
}
ListAdapter adapter = new ListAdapter(mActivity, pluginListItems);
@@ -371,6 +337,8 @@ public class DeviceFragment extends Fragment {
mActivity.runOnUiThread(() -> {
if (binding.getRoot() == null) return;
binding.pairMessage.setText(error);
binding.pairVerification.setText("");
binding.pairVerification.setVisibility(View.GONE);
binding.pairProgress.setVisibility(View.GONE);
binding.pairButton.setVisibility(View.VISIBLE);
binding.pairRequestButtons.setVisibility(View.GONE);
@@ -383,6 +351,7 @@ public class DeviceFragment extends Fragment {
mActivity.runOnUiThread(() -> {
if (binding.getRoot() == null) return;
binding.pairMessage.setText(R.string.device_not_paired);
binding.pairVerification.setVisibility(View.GONE);
binding.pairProgress.setVisibility(View.GONE);
binding.pairButton.setVisibility(View.VISIBLE);
binding.pairRequestButtons.setVisibility(View.GONE);

View File

@@ -9,23 +9,22 @@ package org.kde.kdeconnect.UserInterface;
import android.app.Dialog;
import android.os.Bundle;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import org.kde.kdeconnect_tp.R;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.textfield.TextInputEditText;
import org.kde.kdeconnect_tp.R;
import org.kde.kdeconnect_tp.databinding.EditTextAlertDialogViewBinding;
public class EditTextAlertDialogFragment extends AlertDialogFragment {
private static final String KEY_HINT_RES_ID = "HintResId";
private static final String KEY_TEXT = "Text";
@BindView(R.id.textInputLayout) TextInputLayout textInputLayout;
@BindView(R.id.textInputEditText) TextInputEditText editText;
private EditTextAlertDialogViewBinding binding;
TextInputEditText editText;
private @StringRes int hintResId;
private String text;
@@ -35,10 +34,12 @@ public class EditTextAlertDialogFragment extends AlertDialogFragment {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setOnShowListener(dialogInterface -> {
dialog.setOnShowListener(null);
ButterKnife.bind(EditTextAlertDialogFragment.this, dialog);
textInputLayout.setHintEnabled(true);
textInputLayout.setHint(getString(hintResId));
binding = EditTextAlertDialogViewBinding.bind(dialog.getWindow().getDecorView());
editText = binding.textInputEditText;
binding.textInputLayout.setHintEnabled(true);
binding.textInputLayout.setHint(getString(hintResId));
editText.setText(text);
});