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

Compare commits

...

65 Commits

Author SHA1 Message Date
Albert Vaca Cintora
044e56184a Bump version to release 2019-07-05 21:34:47 +02:00
Albert Vaca
f923ce198f Merge branch 'albertvaka/sms-lock-fix' 2019-07-05 14:59:40 +02:00
Albert Vaca
a9508a7f46 Lock before first access as per CR 2019-07-05 14:57:50 +02:00
Dmitriy Bogdanov
cbbec32beb Fix text extraction from notifications
Summary:
Fixes an error of extracting SpannableString as String (which resulted in notification text being null).
Fixes group conversation text extraction on API <28.
Also includes some minor refactoring.

Reviewers: nicolasfella

Reviewed By: nicolasfella

Subscribers: kdeconnect

Tags: #kde_connect

Differential Revision: https://phabricator.kde.org/D22140
2019-07-05 11:09:19 +02:00
l10n daemon script
cf247b4d18 GIT_SILENT made messages (after extraction) 2019-07-05 02:41:14 +02:00
Albert Vaca Cintora
bfadb84b9f Fix potential race condition caused by early unlocking
The lock should be kept between the point we read and the point we write

Also, no need to pass SMSPlugin as a parameter, since enclossed classes
already have access to its parent by default.
2019-07-04 21:01:12 +02:00
Albert Vaca Cintora
7db40ee20a Fix potential race condition
lastState was updated long after it was read
2019-07-04 20:57:04 +02:00
Pino Toscano
5609c8ebcc i18n: use https for bugs.kde.org
bump also the date of the template, to indicate "it was changed"
2019-06-30 11:39:47 +02:00
l10n daemon script
1d1618d8ed GIT_SILENT made messages (after extraction) 2019-06-28 02:44:15 +02:00
Albert Vaca
47c2483d53 Remove gui counters logic
It had bugs and was never used. I've left in the Discovery Mode logic
because we might want to expose that somehow like GSConnect does.
2019-06-27 10:48:33 -04:00
l10n daemon script
5659812428 GIT_SILENT made messages (after extraction) 2019-06-24 02:43:41 +02:00
Simon Redman
418e1a813e Potentially fix SMS/MMS access on Samsung devices
According to some information I stumbled across while working on a different issue, Samsung devices do not support the content provider I was using to populate the list of SMS conversations. This is very annoying, but nevertheless there appears to be a workaround.

BUG: 401677

Many users with Samsung devices have reported problems using the SMS plugin.

Hopefully this will fix the SMS plugin on Samsung devices.
2019-06-21 18:49:13 -06:00
Simon Redman
50f395bdef Potentially fix SMS/MMS access on Samsung devices 2019-06-21 18:48:11 -06:00
l10n daemon script
9695e5a49e GIT_SILENT made messages (after extraction) 2019-06-22 02:45:07 +02:00
l10n daemon script
e36b80aa0c GIT_SILENT made messages (after extraction) 2019-06-19 02:51:31 +02:00
Albert Vaca
08230950b6 Bump version to release 2019-06-17 22:21:07 +02:00
Albert Vaca
63e9e7f522 Fix crash on Android < 7.0
getOrDefault is not supported until java 8, introduced on Android 7
2019-06-17 22:12:01 +02:00
Albert Vaca Cintora
7e5df06972 1.12.9.1 2019-06-16 01:12:16 +02:00
Nicolas Fella
8dd4297a0f Check if current player is null 2019-06-15 18:41:27 +02:00
Albert Vaca Cintora
1e58559584 Release 1.12.9 2019-06-15 13:40:16 +02:00
Albert Vaca
503eaa7ca8 Remove overly-complicated code that isn't working well
For some users, it was giving false positives or even crashing.

Detecting this across all Androids without an actual API doesn't seem
practical.
2019-06-15 13:27:18 +02:00
Albert Vaca
46cd99ba85 Upgrade gradle plugin for AS 3.4.1 2019-06-15 13:22:56 +02:00
Albert Vaca
a7d6b9a805 Fix crash if icon can't be found 2019-06-15 13:22:36 +02:00
Matthijs Tijink
f688aad3e1 Close the MPRIS media notification when the player disappears
The code now checks if the player still exists.
2019-06-14 23:19:51 +02:00
Albert Vaca Cintora
906c04ac1a Bump version number to release 2019-06-14 20:54:54 +02:00
Nicolas Fella
a61cb875f1 Only open file if open is actually true 2019-06-14 15:47:16 +02:00
Nicolas Fella
6e053a7e95 Hide keyboard display action if remote keyboard input is not supported 2019-06-13 19:28:45 +00:00
Nicolas Fella
71b034a025 Fix packet loss after connection 2019-06-13 17:33:21 +00:00
Matthijs Tijink
0e9dd25172 Enable the MPRIS server plugin - allows control of android media players
The changes allow loading the plugin on older Android versions
2019-06-10 15:59:37 +00:00
Simon Redman
51e957d822 [SMSApp] Support plain-text MMS
## Summary

Not having support for MMS caused some minor problems, like in https://bugs.kde.org/show_bug.cgi?id=398889 . This patch adds basic MMS support for plain-text MMS, including multi-target messages.

Android companion to https://invent.kde.org/kde/kdeconnect-kde/merge_requests/97

Currently there are several rough areas:
  - Multi-target messages do not have the full list of recipients (I am planning to work on this in another patch, because this one is already quite large enough)
  - Parsing MMS is significantly slower than parsing SMS. This makes sense, since we need to make significantly many more content:// calls for MMS. The only solution I can think of here is to add the ability to request a range of messages, which I need to do anyway, but which should not be part of this patch.
  - The desktop app is totally busted with regard to multi-target MMS, but that will also be fixed in another MR

BUG: 398889

## Test Plan

### Before:
Open SMS app on desktop, scroll through conversations, notice:
  - Any single-target message which had the most-recent message as an MMS does not appear
  - Any multi-target MMS conversations do not appear

### After:
Open SMS app on desktop, notice:
  - Conversations which have an MMS as their most-recent message appear
  - MMS which consisted of only text are rendered correctly
  - Multi-target conversations are shown (though pretty busted, as said before. Do not attempt to reply to one!)
2019-06-10 05:48:28 +00:00
l10n daemon script
ec43336153 GIT_SILENT made messages (after extraction) 2019-06-09 02:48:56 +02:00
Simon Redman
49295c0de9 Make lint suppression less aggressive for SMSHelper 2019-06-05 22:17:02 -06:00
Matthijs Tijink
56d01ed082 Fix pausing a mpris player when another is still playing 2019-06-04 13:12:51 +00:00
Nicolas Fella
dbd9ece110 refactor getTickerText 2019-06-04 13:01:34 +00:00
Nicolas Fella
132e4e7e0f Refactor extractRepliableNotification 2019-06-04 13:00:32 +00:00
Nicolas Fella
4cdda3f31b Refactor action extraction 2019-06-04 14:57:10 +02:00
Nicolas Fella
7c723eea8c Refactor icon extraction 2019-06-04 12:52:23 +00:00
Nicolas Fella
22e7b91bb3 Cleanup ReceiveNotification 2019-06-04 12:51:24 +00:00
Nicolas Fella
4aa365e4ff Cleanup upload notification 2019-06-04 12:50:35 +00:00
l10n daemon script
c50642e587 GIT_SILENT made messages (after extraction) 2019-06-02 02:48:49 +02:00
Matthijs Tijink
e52f418dad Properly close the media session when closing the media notification
BUG: 407812
2019-05-29 20:55:15 +00:00
l10n daemon script
6a7fbecc97 GIT_SILENT made messages (after extraction) 2019-05-26 02:47:22 +02:00
Nicolas Fella
db0c48cc6b Extract bigtext from notifications 2019-05-24 11:27:08 +00:00
Nicolas Fella
35635a0b0b remove unused parameter 2019-05-22 23:48:31 +02:00
Nicolas Fella
8a2cc4a841 remove unused ImagesHelper 2019-05-22 00:49:36 +02:00
Nicolas Fella
a53bf9b191 Simplify condition 2019-05-22 00:22:00 +02:00
Nicolas Fella
2d3b0e7641 Extract conversations from conversation notification style 2019-05-21 21:32:33 +00:00
l10n daemon script
274621e79d GIT_SILENT made messages (after extraction) 2019-05-21 02:47:16 +02:00
Albert Vaca Cintora
ea136498b4 Re-generate cert if it's for a different device ID
This would happen to people who transferred their KDE Connect config from
one phone to another (mostly with backup apps that only work on rooted
phones). This led to a state where other devices would always reject the
connection because the certificate CN didn't match the device ID.

On the PC side this is not a problem because the certificate is the source
of truth for the device ID.
2019-05-21 00:25:11 +02:00
Nicolas Fella
1e82c653d6 [DeviceFragment] Unify logging tags 2019-05-21 00:21:01 +02:00
Nicolas Fella
565be4a42a [plugins/notifications] Unify log tags 2019-05-21 00:06:15 +02:00
Nicolas Fella
de4e203d8c Fix typo 2019-05-20 22:25:26 +02:00
l10n daemon script
86e698df08 GIT_SILENT made messages (after extraction) 2019-05-20 02:49:40 +02:00
Nicolas Fella
3d5dcdacae Revert "Revert "Upgrade gradle plugin for AS 3.4""
This reverts commit 52486ed2ec.
2019-05-19 22:44:56 +02:00
Nicolas Fella
8b145b4c6a Simplify notification title/text extraction 2019-05-19 20:05:58 +02:00
Nicolas Fella
52486ed2ec Revert "Upgrade gradle plugin for AS 3.4"
This reverts commit 87ddf47999.

Seems to break running the app
2019-05-19 19:41:02 +02:00
Mitja Cotic
b8d327c2d9 [pretty please] allow fetching of cover art in mpris plugin from connected computer
Summary:
So from android 8 onwards it appears that fetching content from "cleartext" urls is disabled by default

https://stackoverflow.com/questions/45940861/android-8-cleartext-http-traffic-not-permitted

I have a mpris service running on my local computer which is connected to the android, that service is also serving cover art for currently playing song. Obviously I can not have a domain set for my computer on a local machine, so cover art urls look like
`http://<ip>:<port>/<songid>.ext` . Since this restriction was introduced into android 8, kde connect is not able to fetch the art from this url.

This is patch allows connections to raw IPs addresses as well. If there is any security issues regarding this change, I'm prepared to do more work, my suggestion would be to check if IP address in this case matches paired computer's IP address, although this would still be a bit annoying (especially since whatever malicious file can be served from a "cleartext" link as well).

Test Plan:
- start some sort of server which can serve an image on a paired computer
- create mpris Player instance on a paired computer and set the `mpris:artUrl` to the served address where this image is available
- open kde connect android app, select prepared player and see default cover art instead of the served image
- change artUrl to some image from the web
- check in kde connect again and see it correctly show a remote image

Reviewers: #kde_connect, nicolasfella, albertvaka

Reviewed By: #kde_connect, nicolasfella, albertvaka

Subscribers: albertvaka, nicolasfella, andyholmes, kdeconnect

Tags: #kde_connect

Differential Revision: https://phabricator.kde.org/D21247
2019-05-18 10:50:14 +02:00
l10n daemon script
630044e702 GIT_SILENT made messages (after extraction) 2019-05-16 02:51:33 +02:00
Nicolas Fella
720421554a Rework plugin list header 2019-05-13 22:34:10 +00:00
Nicolas Fella
e045964592 Declare permission for installing packages 2019-05-13 21:33:25 +00:00
Nicolas Fella
98931c7bcf Rename DeviceSettigsActivity->PluginSettingsActivity 2019-05-13 21:32:47 +00:00
Nicolas Fella
a616d5afbd Remove unused method 2019-05-13 21:31:58 +00:00
Nicolas Fella
1ffcaba71c Simplify code in createPluginList 2019-05-13 21:31:03 +00:00
l10n daemon script
260abb192c GIT_SILENT made messages (after extraction) 2019-05-06 03:07:52 +02:00
l10n daemon script
ea57aec40f GIT_SILENT made messages (after extraction) 2019-05-03 03:13:56 +02:00
49 changed files with 1855 additions and 721 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.kde.kdeconnect_tp"
android:versionCode="11270"
android:versionName="1.12.7">
android:versionCode="11293"
android:versionName="1.12.93">
<supports-screens
android:anyDensity="true"
@@ -32,11 +32,13 @@
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:icon="@drawable/icon"
android:label="KDE Connect"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/KdeConnectTheme">
<service
android:name="org.kde.kdeconnect.BackgroundService"
@@ -65,7 +67,7 @@
</intent-filter>
</activity>
<activity
android:name="org.kde.kdeconnect.UserInterface.DeviceSettingsActivity"
android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
android:label="@string/device_menu_plugins"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity">
<meta-data
@@ -259,10 +261,10 @@
<activity
android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationFilterActivity"
android:label="@string/title_activity_notification_filter"
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceSettingsActivity">
android:parentActivityName="org.kde.kdeconnect.UserInterface.PluginSettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.DeviceSettingsActivity" />
android:value="org.kde.kdeconnect.UserInterface.PluginSettingsActivity" />
</activity>
<activity android:name="org.kde.kdeconnect.Plugins.PhotoPlugin.PhotoActivity" />

View File

@@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
classpath 'com.android.tools.build:gradle:3.4.1'
}
}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingTop="28dp"
android:paddingRight="16dp"
android:paddingBottom="8dp" />

View File

@@ -1,64 +1,136 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="kde_connect">KDE Connect</string>
<string name="pref_plugin_telephony">Avisador telefónicu</string>
<string name="pref_plugin_battery">Informe de batería</string>
<string name="pref_plugin_battery_desc">Informe periódicu del estáu de la batería</string>
<string name="pref_plugin_sftp_desc">Permite restolar remotamente a esti preséu</string>
<string name="pref_plugin_telephony_desc">Unvia avisos de les llamaes entrantes</string>
<string name="pref_plugin_battery">Informe de la batería</string>
<string name="pref_plugin_battery_desc">Informa davezu del estáu de la batería</string>
<string name="pref_plugin_sftp_desc">Permite restolar remotamente\'l sistema de ficheros d\'esti preséu</string>
<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 to teléfonu o tableta como panel táutil y tecláu</string>
<string name="pref_plugin_remotekeyboard">Pulsaciones remotes</string>
<string name="pref_plugin_mousepad_desc">Usa\'l preséu como panel táutil y tecláu</string>
<string name="pref_plugin_mpris">Controles multimedia</string>
<string name="pref_plugin_mpris_desc">Forne un control remotu pal to reproductor multimedia</string>
<string name="pref_plugin_runcommand">Execución de comandos</string>
<string name="pref_plugin_runcommand_desc">Aiciona comandos remotos del to teléfonu o tableta</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Unvia y recibe pings</string>
<string name="pref_plugin_notifications">Sincronización d\'avisos</string>
<string name="pref_plugin_notifications_desc">Accede a los tos avisos d\'otros preseos</string>
<string name="pref_plugin_receive_notifications">Recibir avisos</string>
<string name="pref_plugin_receive_notifications_desc">Recibe avisos d\'otros preseos y amuésalos n\'Android</string>
<string name="pref_plugin_sharereceiver">Compartir y recibir</string>
<string name="pref_plugin_sharereceiver_desc">Comparte ficheros y URLs ente preseos</string>
<string name="plugin_not_available">Esta carauterística nun ta disponible na to versión d\'Android</string>
<string name="device_list_empty">Ensin preseos</string>
<string name="pref_plugin_runcommand_desc">Aiciona comandos remotos dende\'l preséu</string>
<string name="pref_plugin_contacts">Sincronizador de contautos</string>
<string name="pref_plugin_notifications_desc">Accede a los avisos n\'otros preseos</string>
<string name="device_list_empty">Nun hai preseos</string>
<string name="ok">Aceutar</string>
<string name="cancel">Encaboxar</string>
<string name="open_settings">Abrir axustes</string>
<string name="no_permissions">¡</string>
<string name="send_ping">Unviar ping</string>
<string name="open_mpris_controls">Control multimedia</string>
<string name="open_mousepad">Entrada remota</string>
<string name="mousepad_scroll_direction_title">Direición de desplazamientu inversa</string>
<string-array name="mousepad_tap_entries">
<item>Right click</item>
<item>Middle click</item>
<item>Nothing</item>
<item>Clic drechu</item>
<item>Clic d\'en mediu</item>
<item>Nada</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Perlenta</item>
<item>Lenta</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
<item>Rápida</item>
<item>Perrápida</item>
</string-array>
<string-array name="mousepad_acceleration_profile_entries">
<item>No Acceleration</item>
<item>Ensin aceleración</item>
<item>Weakest</item>
<item>Weaker</item>
<item>Medium</item>
<item>Stronger</item>
<item>Strongest</item>
<item>Normal</item>
<item>Fuerte</item>
<item>Perfuerte</item>
</string-array>
<string name="error_timed_out">Escosó\'l tiempu</string>
<string name="category_connected_devices">Preseos coneutaos</string>
<string name="category_not_paired_devices">Preseos disponibles</string>
<string name="category_remembered_devices">Preseos recordaos</string>
<string name="device_menu_plugins">Axustes de plugins</string>
<string name="device_menu_unpair">Desempareyar</string>
<string name="pair_new_device">Empareya un preséu nuevu</string>
<plurals name="incoming_file_title">
<item quantity="one">Recibiendo %1$d ficheru de %2$s</item>
<item quantity="other">Recibiendo %1$d ficheros de %2$s</item>
</plurals>
<plurals name="incoming_files_text">
<item quantity="one">Ficheru: %1s</item>
<item quantity="other">(Ficheru %2$d de %3$d): %1$s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="one">Unviando %1$d ficheru a %2$s</item>
<item quantity="other">Unviando %1$d ficheros a %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Ficheru: %1$s</item>
<item quantity="other">(Ficheru %2$d de %3$d): %1$s</item>
</plurals>
<plurals name="received_files_fail_title">
<item quantity="one">Fallu al recibir el ficheru de %1$s</item>
<item quantity="other">Fallu al recibir %2$d de los %3$d ficheros de %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Fallu al unviar el ficheru de %1$s</item>
<item quantity="other">Fallu al unviar %2$d de los %3$d ficheros de %1$s</item>
</plurals>
<string name="cannot_create_file">Nun pue crease\'l ficheru %s</string>
<string name="right_click">Unviar un clic drechu</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>
<string name="request_pairing">Solicitar l\'empareyamientu</string>
<string name="device">Preséu</string>
<string name="settings">Axustes</string>
<string name="mpris_volume">Volume</string>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
<item>20 seconds</item>
<item>30 seconds</item>
<item>1 minute</item>
<item>2 minutes</item>
<item>10 segundos</item>
<item>20 segundos</item>
<item>30 segundos</item>
<item>1 minutu</item>
<item>2 minutos</item>
</string-array>
<string name="no_file_browser">Nun hai restoladores de ficheros instalaos</string>
<string name="pref_plugin_telepathy_desc">Unvia mensaxes de testu dende\'l to escritoriu</string>
<string name="plugin_not_supported">Esti complementu nun lu sofita\'l preséu</string>
<string name="findmyphone_description">Fai sonar el teléfonu pa qu\'asina pueas alcontralu</string>
<string name="protocol_version_older">Esti preséu una una versión vieya del protocolu</string>
<string name="protocol_version_newer">Esti preséu una una versión nueva del protocolu</string>
<string name="general_settings">Axustes xenerales</string>
<string name="plugin_settings">Axustes</string>
<string name="device_name_preference_summary">%s</string>
<string name="custom_devices_settings">Llista de preseos personalizaos</string>
<string name="custom_device_list">Amestar preseos pola IP</string>
<string name="share_notification_preference">Avisos sonoros</string>
<string name="share_notification_preference_summary">Vibra y reproduz un soníu al recibir un ficheru</string>
<string name="share_destination_customize_summary_disabled">Los ficheros recibíos van apaecer en Descargues</string>
<string name="share_destination_folder_preference">Direutoriu de destín</string>
<string name="title_activity_notification_filter">Peñera d\'avisos</string>
<string name="filter_apps_info">Van sincronizase los avisos de les aplicaciones esbillaes.</string>
<string name="sftp_sdcard">Tarxeta SD</string>
<string name="add_device_dialog_title">Amiestu d\'un preséu</string>
<string name="add_device_hint">Nome d\'agospiu o direición IP</string>
<string name="no_players_connected">Nun s\'alcontraron reproductores</string>
<string name="send_files">Unviar ficheros</string>
<string name="pairing_description">Equí deberíen apaecer los demás preseos que tean na mesma rede y executando KDE Connect.</string>
<string name="device_rename_title">Renomáu del preséu</string>
<string name="device_rename_confirm">Renomar</string>
<string name="refresh">Refrescar</string>
<string name="pref_plugin_telepathy_desc">Unvia SMS dende l\'ordenador</string>
<string name="findmyphone_description">Fai qu\'esti preséu suene pa que pueas alcontralu</string>
<string name="findmyphone_found">Alcontrélu</string>
<string name="telephony_pref_blocked_title">Númberos bloquiaos</string>
<string name="presenter_fullscreen">Pantalla completa</string>
<string name="presenter_exit">Colar de la presentación</string>
<string name="addcommand_explanation">Nun hai comandos rexistraos.</string>
<string name="add_command_description">Pues amestar más comandos nel escritoriu</string>
<string name="dark_theme">Estilu escuru</string>
<string name="notification_channel_persistent">Indicador permanente</string>
<string name="clipboard_toast">Copióse al cartafueyu</string>
<string name="pref_plugin_findremotedevice_desc">Fai que\'l preséu remotu suene</string>
<string name="ring">Facer sonar</string>
<string name="pref_plugin_systemvolume">Volume del sistema</string>
<string name="pref_plugin_systemvolume_desc">Controla\'l volume del sistema del preséu remotu</string>
<string name="devices">Preseos</string>
<string name="settings_rename">Nome del preséu</string>
<string name="settings_dark_mode">Estilu escuru</string>
<string name="settings_more_settings_title">Más axustes</string>
<string name="setting_persistent_notification_oreo">Avisu permanente</string>
<string name="extra_options">Opciones adicionales</string>
<string name="new_notification">Avisu nuevu</string>
<string name="notification_channel_receivenotification">Avisos d\'otros preseos</string>
<string name="plugin_photo_desc">Llanza l\'aplicación de la cámarra p\'acenciellar la fechura y tresferencia de semeyes</string>
</resources>

View File

@@ -10,7 +10,7 @@
<string name="pref_plugin_sftp">Menyingkap sistem file</string>
<string name="pref_plugin_sftp_desc">Membolehkan menelusuri sistem file perangkat ini secara jarak jauh</string>
<string name="pref_plugin_clipboard">Sinkron clipboard</string>
<string name="pref_plugin_clipboard_desc">Berbagi konten clipboard</string>
<string name="pref_plugin_clipboard_desc">Berbagi konten papan-klip</string>
<string name="pref_plugin_mousepad">Input jarak jauh</string>
<string name="pref_plugin_mousepad_desc">Gunakan telepon atau tabletmu sebagai touchpad dan keyboard</string>
<string name="pref_plugin_presenter_desc">Gunakan perangkatmu untuk mengubah slide dalam sebuah presentasi</string>
@@ -147,7 +147,7 @@
<string name="device_name">Nama perangkat</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Nama perangkat tak absah</string>
<string name="shareplugin_text_saved">Teks diterima, tersimpan ke clipboard</string>
<string name="shareplugin_text_saved">Teks diterima, tersimpan ke papan-klip</string>
<string name="custom_devices_settings">Kustom daftar perangkat</string>
<string name="pair_device_action">Sandingkan sebuah perangkat baru</string>
<string name="unpair_device_action">Leraikan %s</string>

View File

@@ -104,6 +104,14 @@
<item quantity="one">File: %1s</item>
<item quantity="other">(File %2$d di %3$d) : %1$s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="one">Invio di %1$d file a %2$s</item>
<item quantity="other">Invio di %1$d file a %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">File: %1$s</item>
<item quantity="other">(File %2$d di %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">File ricevuto da %1$s</item>
<item quantity="other">Ricevuti %2$d file da %1$s</item>
@@ -112,6 +120,14 @@
<item quantity="one">Ricezione file da di %1$s non riuscita</item>
<item quantity="other">Ricezione di %2$d di %3$d file da %1$s non riuscita</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">File inviato a %1$s</item>
<item quantity="other">Inviati %2$d file a %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Invio del file a %1$s non riuscito</item>
<item quantity="other">Invio di %2$d di %3$d file a %1$s non riuscito</item>
</plurals>
<string name="received_file_text">Tocca per aprire «%1s»</string>
<string name="cannot_create_file">Impossibile creare il file %s</string>
<string name="tap_to_answer">Tocca per rispondere</string>
@@ -230,6 +246,7 @@
<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="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>
<string name="contacts_permission_explanation">Per condividere la tua rubrica con il desktop, devi concedere l\'autorizzazione per i contatti</string>
<string name="select_ringtone">Seleziona una suoneria</string>

View File

@@ -1,30 +1,259 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="foreground_notification_no_devices">デバイスに接続されていません</string>
<string name="foreground_notification_devices">接続済み: %s</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>
<string name="pref_plugin_sftp">ファイルシステムの参照</string>
<string name="pref_plugin_sftp_desc">リモートからこのデバイスのファイルシステムへの閲覧を可能にします</string>
<string name="pref_plugin_clipboard">クリップボードの同期</string>
<string name="pref_plugin_clipboard_desc">クリップボードの内容を共有</string>
<string name="pref_plugin_mousepad">リモート入力</string>
<string name="pref_plugin_mousepad_desc">あなたのスマートフォンやタブレットをタッチパッドやキーボードとして利用します</string>
<string name="pref_plugin_presenter">リモートスライドショー</string>
<string name="pref_plugin_presenter_desc">あなたのデバイスを使ってプレゼンテーションのスライドを変更</string>
<string name="pref_plugin_remotekeyboard">リモートからキー入力を受信</string>
<string name="pref_plugin_mpris">マルチメディアの操作</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_desc">デバイスの連絡先の同期を許可</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="plugin_not_available">この機能はあなたの Android バージョンでは利用できません</string>
<string name="device_list_empty">デバイスなし</string>
<string name="cancel">キャンセル</string>
<string name="open_settings">設定を開く</string>
<string name="no_permissions">通知にアクセスするには権限を許可する必要があります</string>
<string name="no_permissions_remotekeyboard">キー入力を受信するには KDE Connect リモートキーボードをアクティブ化する必要があります</string>
<string name="send_ping">Ping を送信</string>
<string name="open_mpris_controls">マルチメディアの操作</string>
<string name="remotekeyboard_connected">リモートキーボード接続はアクティブです</string>
<string name="open_mousepad">リモート入力</string>
<string name="mousepad_info">スクリーン上で指を動かしてマウスカーソルを移動します。タップはクリックとなり、2/3本指で右クリック、中クリックとなります。2本指を使ってスクロールが可能です。長押しすることでドラッグ&amp;ドロップできます。</string>
<string name="mousepad_double_tap_settings_title">2本指タップのアクションを設定</string>
<string name="mousepad_triple_tap_settings_title">3本指タップのアクションを設定</string>
<string name="mousepad_sensitivity_settings_title">タッチパッドの感度を設定</string>
<string name="mousepad_acceleration_profile_settings_title">ポインタの速度を設定</string>
<string name="mousepad_scroll_direction_title">スクロールの方向を反転</string>
<string-array name="mousepad_tap_entries">
<item>Right click</item>
<item>Middle click</item>
<item>Nothing</item>
<item>右クリック</item>
<item>中クリック</item>
<item>なし</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>デフォルト</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string-array name="mousepad_acceleration_profile_entries">
<item>No Acceleration</item>
<item>Weakest</item>
<item>Weaker</item>
<item>Medium</item>
<item>Stronger</item>
<item>Strongest</item>
<item>加速なし</item>
<item>最弱</item>
<item></item>
<item></item>
<item></item>
<item>最強</item>
</string-array>
<string name="category_connected_devices">接続済みのデバイス</string>
<string name="category_not_paired_devices">利用可能なデバイス</string>
<string name="device_menu_plugins">プラグイン設定</string>
<string name="device_menu_unpair">ペアリング解除</string>
<string name="device_not_reachable">ペアリング済みのデバイスに到達できません</string>
<string name="pair_new_device">新しいデバイスをペアリング</string>
<string name="unknown_device">不明なデバイス</string>
<string name="error_not_reachable">デバイスは到達不可です</string>
<string name="error_already_requested">ペアリングは既に要求済みです</string>
<string name="error_already_paired">デバイスは既にペアリング済みです</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_invalid_key">不正なキーを受信しました</string>
<string name="encryption_info_title">暗号化情報</string>
<string name="encryption_info_msg_no_ssl">他のデバイスは最近のバージョンの KDE Connect を利用していません。古い暗号化方式を使用しています</string>
<string name="pair_requested">ペアリング要求済み</string>
<string name="pairing_request_from">%1s からペアリングを要求されました</string>
<string name="received_url_title">%1s からリンクを受信</string>
<string name="received_url_text">タップして \'%1s\' を開く</string>
<plurals name="incoming_file_title">
<item quantity="other">%1$d ファイルを %2$s から受信しています</item>
</plurals>
<plurals name="incoming_files_text">
<item quantity="other">ファイル: %1s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="other">%1$d ファイルを %2$s へ送信中</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="other">ファイル: %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="other">%1$s からファイルを受信</item>
</plurals>
<plurals name="received_files_fail_title">
<item quantity="other">%1$s からのファイルの受信に失敗</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="other">%1$s にファイルを送信済み</item>
</plurals>
<string name="received_file_text">タップして \'%1s\' を開く</string>
<string name="tap_to_answer">タップして応答</string>
<string name="reconnect">再接続</string>
<string name="right_click">右クリックを送信</string>
<string name="middle_click">中クリックを送信</string>
<string name="show_keyboard">キーボードを表示</string>
<string name="device_not_paired">デバイスはペアリングされていません</string>
<string name="request_pairing">ペアリングを要求</string>
<string name="pairing_accept">許可</string>
<string name="pairing_reject">拒否</string>
<string name="device">デバイス</string>
<string name="pair_device">デバイスをペアリング</string>
<string name="settings">設定</string>
<string name="mpris_play">再生</string>
<string name="mpris_pause">一時停止</string>
<string name="mpris_previous">前へ</string>
<string name="mpris_rew">巻き戻し</string>
<string name="mpris_next">次へ</string>
<string name="mpris_volume">音量</string>
<string name="mpris_settings">マルチメディアの設定</string>
<string name="mpris_time_settings_title">早送り/巻き戻しボタン</string>
<string name="mpris_time_settings_summary">押した時に早送り/巻き戻しする時間を調整</string>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
<item>20 seconds</item>
<item>30 seconds</item>
<item>1 minute</item>
<item>2 minutes</item>
<item>10 </item>
<item>20 </item>
<item>30 </item>
<item>1 </item>
<item>2 </item>
</string-array>
<string name="mpris_notification_settings_title">メディア操作の通知を表示</string>
<string name="mpris_notification_settings_summary">KDE Connect を開かずにメディアプレーヤーをコントロール可能にします</string>
<string name="share_to">共有先...</string>
<string name="protocol_version_older">このデバイスは古いプロトコルバージョンを使用しています</string>
<string name="protocol_version_newer">このデバイスはより新しいプロトコルバージョンを使用しています</string>
<string name="general_settings">全般設定</string>
<string name="plugin_settings">設定</string>
<string name="plugin_settings_with_name">%s 設定</string>
<string name="device_name">デバイス名</string>
<string name="invalid_device_name">不正なデバイス名</string>
<string name="pair_device_action">新しいデバイスをペアリング</string>
<string name="unpair_device_action">%s をペアリング解除</string>
<string name="custom_device_list">IP アドレスでデバイスを追加</string>
<string name="delete_custom_device">%s を削除しますか?</string>
<string name="custom_device_fab_hint">デバイスを追加</string>
<string name="undo">元に戻す</string>
<string name="share_notification_preference">うるさい通知</string>
<string name="share_notification_preference_summary">ファイル受信時にバイブレートし、音声を再生します</string>
<string name="share_destination_customize">行き先ディレクトリをカスタマイズ</string>
<string name="share_destination_customize_summary_disabled">受信したファイルは Downloads に保存されます</string>
<string name="share_destination_customize_summary_enabled">ファイルは以下のディレクトリに保存されます</string>
<string name="share_destination_folder_preference">行き先ディレクトリ</string>
<string name="share">共有</string>
<string name="share_received_file">\"%s\" を共有</string>
<string name="title_activity_notification_filter">通知フィルタ</string>
<string name="filter_apps_info">選択されたアプリケーションの通知が同期されます。</string>
<string name="sftp_internal_storage">内部ストレージ</string>
<string name="sftp_sdcard_num">SD カード %d</string>
<string name="sftp_sdcard">SD カード</string>
<string name="sftp_readonly">(読み取り専用)</string>
<string name="add_device_dialog_title">デバイスを追加</string>
<string name="add_device_hint">ホスト名/IPアドレス</string>
<string name="sftp_preference_detected_sdcards">検出された SD カード</string>
<string name="sftp_preference_configured_storage_locations">設定されたストレージの場所</string>
<string name="sftp_preference_add_storage_location_title">ストレージの場所を追加</string>
<string name="sftp_preference_edit_storage_location">ストレージの場所を編集</string>
<string name="sftp_storage_preference_storage_location">ストレージの場所</string>
<string name="sftp_storage_preference_storage_location_already_configured">この場所は既に設定されています</string>
<string name="sftp_storage_preference_click_to_select">クリックして選択</string>
<string name="sftp_storage_preference_display_name">表示名</string>
<string name="sftp_storage_preference_display_name_already_used">この表示名は既に使用されています</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">表示名は空にできません</string>
<string name="sftp_action_mode_menu_delete">削除</string>
<string name="sftp_no_sdcard_detected">SD カードが検出されません</string>
<string name="sftp_no_storage_locations_configured">ストレージの場所が設定されていません</string>
<string name="sftp_saf_permission_explanation">リモートからファイルにアクセスするには、ストレージの場所を設定する必要があります</string>
<string name="add_host_hint">ホスト名かIPアドレス</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="device_paired">デバイス ペアリング済み</string>
<string name="device_rename_title">デバイス名を変更</string>
<string name="device_rename_confirm">名前変更</string>
<string name="refresh">更新</string>
<string name="unreachable_description">このペアリング済みデバイスは到達不可です。同一のネットワーク内に 接続されていることを確認してください</string>
<string name="on_data_message">モバイルデータ接続を使用しているようです。KDE Connect はローカルネットワークでしか機能しません</string>
<string name="no_file_browser">ファイルブラウザがインストールされていません</string>
<string name="pref_plugin_telepathy">SMS を送信</string>
<string name="pref_plugin_telepathy_desc">デスクトップからテキストメッセージを送信</string>
<string name="plugin_not_supported">このプラグインはデバイスにサポートされていません</string>
<string name="findmyphone_title">スマートフォンを捜索</string>
<string name="findmyphone_title_tablet">タブレットを捜索</string>
<string name="findmyphone_title_tv">TV を捜索</string>
<string name="findmyphone_description">このデバイスを鳴らすことで捜索できます</string>
<string name="findmyphone_found">発見</string>
<string name="open">開く</string>
<string name="close">閉じる</string>
<string name="no_permissions_storage">ストレージにアクセスするには権限を許可する必要があります</string>
<string name="plugins_need_permission">いくつかのプラグインが機能するには権限が必要です (タップして詳細情報を表示):</string>
<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_optional_permission_explanation">電話番号ではなく連絡先名を見るには、端末の連絡先へのアクセスを許可する必要があります</string>
<string name="contacts_permission_explanation">連絡先をデスクトップと共有するために、連絡先の権限が必要です</string>
<string name="select_ringtone">着信音を選択</string>
<string name="telephony_pref_blocked_title">ブロックされた番号</string>
<string name="telephony_pref_blocked_dialog_desc">これらの番号からの通話と SMS を表示しません。一行に1つの電話番号を指定してください</string>
<string name="mpris_coverart_description">現在のメディアのカバーアート</string>
<string name="device_icon_description">デバイスアイコン</string>
<string name="settings_icon_description">設定アイコン</string>
<string name="presenter_fullscreen">フルスクリーン</string>
<string name="presenter_exit">プレゼンテーションを終了</string>
<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="add_command_description">デスクトップから新しいコマンドを追加できます</string>
<string name="pref_plugin_mprisreceiver">メディアプレーヤーの操作</string>
<string name="pref_plugin_mprisreceiver_desc">他のデバイスからスマートフォンのメディアプレーヤーを操作</string>
<string name="dark_theme">ダーク テーマ</string>
<string name="notification_channel_default">他の通知</string>
<string name="notification_channel_persistent">永続的なインジケータ</string>
<string name="notification_channel_media_control">メディアの操作</string>
<string name="notification_channel_filetransfer">ファイル転送</string>
<string name="mpris_stop">現在のプレーヤーを停止</string>
<string name="copy_url_to_clipboard">URL をクリップボードにコピー</string>
<string name="clipboard_toast">クリップボードにコピーしました</string>
<string name="runcommand_notreachable">デバイスは到達不可です</string>
<string name="runcommand_notpaired">デバイスはペアリングされていません</string>
<string name="runcommand_noruncommandplugin">このデバイスは \'コマンドを実行\' プラグインを有効にしていません</string>
<string name="pref_plugin_findremotedevice">リモートデバイスを捜索</string>
<string name="pref_plugin_findremotedevice_desc">リモートデバイスを捜索</string>
<string name="pref_plugin_systemvolume">システム音量</string>
<string name="pref_plugin_systemvolume_desc">リモートデバイスのシステム音量を操作</string>
<string name="mute">ミュート</string>
<string name="all">すべて</string>
<string name="devices">デバイス</string>
<string name="settings_rename">デバイス名</string>
<string name="settings_dark_mode">ダーク テーマ</string>
<string name="settings_more_settings_title">その他の設定</string>
<string name="settings_more_settings_text">デバイスごとの設定はデバイス内の \'プラグイン設定\' にあります</string>
<string name="setting_persistent_notification">永続的な通知を表示</string>
<string name="setting_persistent_notification_oreo">永続的な通知</string>
<string name="setting_persistent_notification_description">タップして通知設定内で有効化/無効化します</string>
<string name="extra_options">追加オプション</string>
<string name="privacy_options">プライバシーオプション</string>
<string name="set_privacy_options">プライバシーオプションを設定</string>
<string name="new_notification">新しい通知</string>
<string name="notification_channel_receivenotification">他のデバイスからの通知</string>
<string name="take_picture">カメラを起動</string>
</resources>

View File

@@ -1,6 +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="foreground_notification_devices">연결됨: %s</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>
<string name="pref_plugin_sftp">파일 시스템 보기</string>
@@ -9,6 +13,7 @@
<string name="pref_plugin_clipboard_desc">클립보드 내용 동기화</string>
<string name="pref_plugin_mousepad">원격 입력</string>
<string name="pref_plugin_mousepad_desc">내 휴대폰이나 태블릿을 터치패드와 키보드로 사용하기</string>
<string name="pref_plugin_presenter">슬라이드 쇼 리모콘</string>
<string name="pref_plugin_presenter_desc">내 장치로 프레젠테이션 슬라이드 전환하기</string>
<string name="pref_plugin_remotekeyboard">원격 키 입력 받기</string>
<string name="pref_plugin_remotekeyboard_desc">원격 장치의 키 입력 이벤트 받기</string>
@@ -33,6 +38,7 @@
<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">핑 보내기</string>
<string name="open_mpris_controls">멀티미디어 제어</string>
<string name="remotekeyboard_editing_only_title">편집할 때에만 원격 키 처리하기</string>
@@ -90,7 +96,32 @@
<string name="pairing_request_from">%1s에서 연결 요청</string>
<string name="received_url_title">%1s에서 링크 받음</string>
<string name="received_url_text">\'%1s\'을(를) 열려면 누르십시오</string>
<plurals name="incoming_file_title">
<item quantity="other">%2$s에서 보낸 파일 %1$d개 받음</item>
</plurals>
<plurals name="incoming_files_text">
<item quantity="other">(파일 %3$d개 중 %2$d개): %1$s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="other">%2$s(으)로 파일 %1$d개 보내는 중</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="other">(파일 %3$d개 중 %2$d개): %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="other">%1$s에서 파일 %2$d개 받음</item>
</plurals>
<plurals name="received_files_fail_title">
<item quantity="other">%1$s에서 보낸 파일 %3$d개 중 %2$d개를 받을 수 없음</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="other">%1$s(으)로 파일 %2$d개 보냄</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="other">%1$s(으)로 파일 %3$d개 중 %2$d개를 보낼 수 없음</item>
</plurals>
<string name="received_file_text">\'%1s\'을(를) 열려면 누르십시오</string>
<string name="cannot_create_file">파일 %s을(를) 만들 수 없음</string>
<string name="tap_to_answer">눌러서 응답하기</string>
<string name="reconnect">다시 연결</string>
<string name="right_click">오른쪽 단추 누름 신호 보내기</string>
@@ -102,6 +133,7 @@
<string name="pairing_reject">거부</string>
<string name="device">장치</string>
<string name="pair_device">장치 연결</string>
<string name="settings">설정</string>
<string name="mpris_play">재생</string>
<string name="mpris_pause">일시 정지</string>
<string name="mpris_previous">이전</string>
@@ -111,6 +143,7 @@
<string name="mpris_volume">음량</string>
<string name="mpris_settings">멀티미디어 설정</string>
<string name="mpris_time_settings_title">되감기/빨리감기 단추</string>
<string name="mpris_time_settings_summary">눌렀을 때 되감거나 빨리 감을 시간 조정</string>
<string-array name="mpris_time_entries">
<item>10초</item>
<item>20초</item>
@@ -119,6 +152,8 @@
<item>2분</item>
</string-array>
<string name="mpris_notification_settings_title">미디어 제어 알림 보이기</string>
<string name="mpris_notification_settings_summary">KDE Connect를 열지 않고 미디어 재생기 제어</string>
<string name="share_to">다음으로 공유…</string>
<string name="protocol_version_older">이 장치의 프로토콜 버전이 오래되었습니다</string>
<string name="protocol_version_newer">이 장치의 프로토콜 버전이 더 새롭습니다</string>
<string name="general_settings">일반 설정</string>
@@ -127,11 +162,16 @@
<string name="device_name">장치 이름</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">잘못된 장치 이름</string>
<string name="shareplugin_text_saved">텍스트 수신, 클립보드 복사됨</string>
<string name="shareplugin_text_saved">텍스트 수신, 클립보드 복사됨</string>
<string name="custom_devices_settings">사용자 정의 장치 목록</string>
<string name="pair_device_action">새 장치 연결</string>
<string name="unpair_device_action">%s 연결 해제</string>
<string name="custom_device_list">IP로 장치 추가</string>
<string name="delete_custom_device">%s을(를) 삭제하시겠습니까?</string>
<string name="custom_device_deleted">사용자 정의 장치 삭제됨</string>
<string name="custom_device_list_help">장치가 자동으로 감지되지 않았다면 떠 다니는 동작 단추를 눌러서 IP 주소나 호스트 이름으로 장치를 수동으로 추가할 수 있습니다</string>
<string name="custom_device_fab_hint">장치 추가</string>
<string name="undo">실행 취소</string>
<string name="share_notification_preference">시끄러운 알림</string>
<string name="share_notification_preference_summary">파일을 받았을 때 진동과 소리로 알림</string>
<string name="share_destination_customize">대상 디렉터리 사용자 정의</string>
@@ -147,7 +187,28 @@
<string name="sftp_sdcard">SD 카드</string>
<string name="sftp_readonly">(읽기 전용)</string>
<string name="sftp_camera">카메라 사진</string>
<string name="add_device_dialog_title">장치 추가</string>
<string name="add_device_hint">호스트 이름이나 IP 주소</string>
<string name="sftp_preference_detected_sdcards">감지된 SD 카드</string>
<string name="sftp_preference_edit_sdcard_title">SD 카드 편집</string>
<string name="sftp_preference_configured_storage_locations">설정된 저장소 위치</string>
<string name="sftp_preference_add_storage_location_title">저장소 위치 추가</string>
<string name="sftp_preference_edit_storage_location">저장소 위치 편집</string>
<string name="sftp_preference_add_camera_shortcut">카메라 폴더 바로 가기 추가</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">카메라 폴더 바로 가기 추가</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">카메라 폴더 바로 가기 추가하지 않음</string>
<string name="sftp_storage_preference_storage_location">저장소 위치</string>
<string name="sftp_storage_preference_storage_location_already_configured">이 위치는 이미 설정됨</string>
<string name="sftp_storage_preference_click_to_select">눌러서 선택</string>
<string name="sftp_storage_preference_display_name">표시 이름</string>
<string name="sftp_storage_preference_display_name_already_used">이 표시 이름이 이미 사용 중임</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">표시 이름을 비워둘 수 없음</string>
<string name="sftp_action_mode_menu_delete">삭제</string>
<string name="sftp_no_sdcard_detected">SD 카드가 감지되지 않았음</string>
<string name="sftp_no_storage_locations_configured">저장소 위치가 설정되지 않았음</string>
<string name="sftp_saf_permission_explanation">원격으로 파일에 접근하려면 저장소 위치를 설정해야 함</string>
<string name="add_host">호스트/IP 주소 추가</string>
<string name="add_host_hint">호스트 이름이나 IP</string>
<string name="no_players_connected">재생기를 찾을 수 없음</string>
<string name="mpris_player_on_device">%2$s의 %1$s</string>
<string name="send_files">파일 보내기</string>
@@ -177,6 +238,7 @@
<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>
<string name="contacts_permission_explanation">데스크톱과 주소록을 공유하려면 주소록 접근 권한이 필요합니다</string>
<string name="select_ringtone">벨소리 선택</string>
@@ -187,6 +249,7 @@
<string name="settings_icon_description">설정 아이콘</string>
<string name="presenter_fullscreen">전체 화면</string>
<string name="presenter_exit">프레젠테이션 끝내기</string>
<string name="presenter_lock_tip">장치 잠금 상태에서는 음량 키를 이전/다음 슬라이드 단추로 사용할 수 있습니다</string>
<string name="add_command">명령 추가</string>
<string name="addcommand_explanation">등록된 명령이 없습니다</string>
<string name="addcommand_explanation2">시스템 설정의 KDE Connect에서 새로운 명령을 추가할 수 있습니다</string>
@@ -194,9 +257,13 @@
<string name="pref_plugin_mprisreceiver">미디어 재생기 제어</string>
<string name="pref_plugin_mprisreceiver_desc">다른 장치에서 휴대폰 미디어 재생기 제어</string>
<string name="dark_theme">어두운 테마</string>
<string name="mpris_stop">현재 플레이어 정지</string>
<string name="notification_channel_default">기타 알림</string>
<string name="notification_channel_persistent">항상 표시된 표시기</string>
<string name="notification_channel_media_control">미디어 제어</string>
<string name="notification_channel_filetransfer">파일 전송</string>
<string name="mpris_stop">현재 재생기 정지</string>
<string name="copy_url_to_clipboard">클립보드로 URL 복사</string>
<string name="clipboard_toast">클립보드 복사됨</string>
<string name="clipboard_toast">클립보드 복사됨</string>
<string name="runcommand_notreachable">장치에 접근할 수 없음</string>
<string name="runcommand_notpaired">장치가 연결되지 않음</string>
<string name="runcommand_nosuchdevice">장치가 없음</string>
@@ -207,4 +274,23 @@
<string name="pref_plugin_systemvolume">시스템 음량</string>
<string name="pref_plugin_systemvolume_desc">원격 장치의 시스템 음량 제어</string>
<string name="mute">음소거</string>
<string name="all">모두</string>
<string name="devices">장치</string>
<string name="settings_rename">장치 이름</string>
<string name="settings_dark_mode">어두운 테마</string>
<string name="settings_more_settings_title">더 많은 설정</string>
<string name="settings_more_settings_text">장치별 설정은 각각 장치의 \'플러그인 설정\'에서 확인할 수 있습니다.</string>
<string name="setting_persistent_notification">항상 표시되는 알림 보이기</string>
<string name="setting_persistent_notification_oreo">항상 표시되는 알림</string>
<string name="setting_persistent_notification_description">알림 설정에서 활성화/비활성화하려면 누르십시오</string>
<string name="extra_options">추가 설정</string>
<string name="privacy_options">프라이버시 설정</string>
<string name="set_privacy_options">프라이버시 설정 변경</string>
<string name="new_notification">새 알림</string>
<string name="block_contents">알림 내용 숨기기</string>
<string name="block_images">알림 이미지 숨기기</string>
<string name="notification_channel_receivenotification">다른 장치의 알림</string>
<string name="take_picture">카메라 실행</string>
<string name="plugin_photo_desc">카메라 앱을 실행하여 쉽게 사진을 찍고 전송</string>
<string name="no_app_for_opening">이 파일을 열 수 있는 앱을 찾을 수 없음</string>
</resources>

View File

@@ -1,75 +1,173 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Telefono žinutės ir skambučiai</string>
<string name="kde_connect">KDE Connect</string>
<string name="foreground_notification_no_devices">Neprisijungta prie jokio įrenginio</string>
<string name="foreground_notification_devices">Prisijungta prie: %s</string>
<string name="pref_plugin_telephony">Telefonijos pranešiklis</string>
<string name="pref_plugin_telephony_desc">Siųsti gaunamųjų skambučių pranešimus</string>
<string name="pref_plugin_battery">Akumuliatoriaus ataskaita</string>
<string name="pref_plugin_battery_desc">Periodiškai pranešti apie akumuliatoriaus būseną</string>
<string name="pref_plugin_sftp">Failų sistemos naršymas</string>
<string name="pref_plugin_sftp_desc">Leidžia nuotoliniu būdu naršyti šio įrenginio failų sistemą</string>
<string name="pref_plugin_clipboard">Iškarpinės sinchronizavimas</string>
<string name="pref_plugin_clipboard_desc">Bendrinti iškarpinės turinį</string>
<string name="pref_plugin_mousepad">Nuotolinis įvedimas</string>
<string name="pref_plugin_mousepad_desc">Naudoti savo telefoną ar planšetę kaip jutiklinį kilimėlį ir klaviatūrą</string>
<string name="pref_plugin_presenter">Nuotolinis skaidrių rodymas</string>
<string name="pref_plugin_presenter_desc">Naudoti savo įrenginį pateikties skaidrių keitimui</string>
<string name="pref_plugin_remotekeyboard">Gauti nuotolinius klavišų paspaudimus</string>
<string name="pref_plugin_remotekeyboard_desc">Gauti klavišų paspaudimo įvykius iš nuotolinių įrenginių</string>
<string name="pref_plugin_mpris">Įvairialypės terpės valdikliai</string>
<string name="pref_plugin_mpris_desc">Pateikia jūsų medijos leistuvės nuotolinį valdymą</string>
<string name="pref_plugin_runcommand">Vykdyti komandą</string>
<string name="pref_plugin_runcommand_desc">Paleisti nuotolines komandas iš savo telefono ar planšetės</string>
<string name="pref_plugin_contacts">Adresatų sinchronizavimas</string>
<string name="pref_plugin_contacts_desc">Leisti sinchronizuoti įrenginio adresatų knygą</string>
<string name="pref_plugin_ping">Ryšio patikrinimas</string>
<string name="pref_plugin_ping_desc">Siųsti ir gauti ryšio patikrinimus</string>
<string name="pref_plugin_notifications">Pranešimų sinchronizavimas</string>
<string name="pref_plugin_sharereceiver">Dalintis ir gauti</string>
<string name="device_list_empty">Įrenginių nerasta</string>
<string name="pref_plugin_notifications_desc">Gauti prieigą prie savo pranešimų iš kitų įrenginių</string>
<string name="pref_plugin_receive_notifications">Gauti pranešimus</string>
<string name="pref_plugin_receive_notifications_desc">Gauti pranešimus iš kito įrenginio ir rodyti juos Android įrenginyje</string>
<string name="pref_plugin_sharereceiver">Bendrinti ir gauti</string>
<string name="pref_plugin_sharereceiver_desc">Bendrinti failus ir URL adresus tarp įrenginių</string>
<string name="plugin_not_available">Ši ypatybė jūsų Android versijoje yra neprieinama</string>
<string name="device_list_empty">Jokių įrenginių</string>
<string name="ok">Gerai</string>
<string name="cancel">Atšaukti</string>
<string name="open_settings">Atverti nuostatas</string>
<string name="cancel">Atsisakyti</string>
<string name="open_settings">Atverti nustatymus</string>
<string name="no_permissions">Norėdami gauti prieigą prie pranešimų, turite suteikti leidimą</string>
<string name="no_permission_mprisreceiver">Norėdami valdyti savo medijos leistuves, turite suteikti prieigą prie pranešimų</string>
<string name="no_permissions_remotekeyboard">Norėdami gauti klavišų paspaudimus, turite aktyvuoti KDE Connect nuotolinę klaviatūrą</string>
<string name="send_ping">Tikrinti ryšį</string>
<string name="open_mpris_controls">Garso ir vaizdo kūrinių grojimo valdymas</string>
<string name="open_mpris_controls">Įvairialypės terpės valdymas</string>
<string name="remotekeyboard_editing_only_title">Apdoroti nuotolinius klavišus tik redaguojant</string>
<string name="remotekeyboard_not_connected">Nėra jokio aktyvaus nuotolinės klaviatūros ryšio, užmegzkite jį naudodami kdeconnect</string>
<string name="remotekeyboard_connected">Nuotolinės klaviatūros ryšys yra aktyvus</string>
<string name="remotekeyboard_multiple_connections">Yra daugiau nei vienas nuotolinės klaviatūros ryšys, pasirinkite kurį įrenginį konfigūruoti</string>
<string name="open_mousepad">Nuotolinis įvedimas</string>
<string name="mousepad_info">Norėdami judinti pelės žymeklį, judinkite pirštu po ekraną. Bakstelėkite vienkartiniam spustelėjimui ir naudokite du/tris pirštus dešiniajam ir viduriniajam mygtukui. Naudokite 2 pirštus, norėdami slinkti. Naudokite ilgą paspaudimą, norėdami tempti.</string>
<string name="mousepad_double_tap_settings_title">Nustatyti bakstelėjimo dviem pirštais veiksmą</string>
<string name="mousepad_triple_tap_settings_title">Nustatyti bakstelėjimo trimis pirštais veiksmą</string>
<string name="mousepad_sensitivity_settings_title">Nustatyti jutiklinio kilimėlio jautrumą</string>
<string name="mousepad_acceleration_profile_settings_title">Nustatyti rodyklės pagreitį</string>
<string name="mousepad_scroll_direction_title">Apversti slinkimo kryptį</string>
<string-array name="mousepad_tap_entries">
<item>Right click</item>
<item>Middle click</item>
<item>Dešinysis spustelėjimas</item>
<item>Vidurinysis spustelėjimas</item>
<item>Nieko</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
<item>Lėčiausias</item>
<item>Virš lėčiausio</item>
<item>Numatytasis</item>
<item>Virš numatytojo</item>
<item>Greičiausias</item>
</string-array>
<string-array name="mousepad_acceleration_profile_entries">
<item>No Acceleration</item>
<item>Weakest</item>
<item>Weaker</item>
<item>Medium</item>
<item>Stronger</item>
<item>Strongest</item>
<item>Jokio pagreičio</item>
<item>Silpniausias</item>
<item>Silpnesnis</item>
<item>Vidutinis</item>
<item>Stipresnis</item>
<item>Stipriausias</item>
</string-array>
<string name="category_connected_devices">Prijungti įrenginiai</string>
<string name="category_not_paired_devices">Pasiekiami įrenginiai</string>
<string name="category_remembered_devices">Įsimintieji įrenginiai</string>
<string name="device_menu_plugins">Papildinio nuostatos</string>
<string name="device_menu_unpair">Atrišti</string>
<string name="device_not_reachable">Nepavyksta pasiekti suporuoto įrenginio</string>
<string name="category_not_paired_devices">Prieinami įrenginiai</string>
<string name="category_remembered_devices">Įsiminti įrenginiai</string>
<string name="device_menu_plugins">Priedo nustatymai</string>
<string name="device_menu_unpair">Panaikinti suporavimą</string>
<string name="device_not_reachable">Suporuotas įrenginys nepasiekiamas</string>
<string name="pair_new_device">Suporuoti naują įrenginį</string>
<string name="unknown_device">Nežinomas įrenginys</string>
<string name="error_not_reachable">Įrenginys nepasiekiamas</string>
<string name="error_already_requested">Jau paprašyta suporuoti</string>
<string name="error_already_requested">Suporavimas jau užklaustas</string>
<string name="error_already_paired">Įrenginys jau suporuotas</string>
<string name="error_could_not_send_package">Nepavyksta išsiųsti paketo</string>
<string name="error_timed_out">Skirtasis laikas baigėsi</string>
<string name="error_canceled_by_user">Naudotojas atšaukė užduotį</string>
<string name="error_canceled_by_other_peer">Porininkas atšaukė užduotį</string>
<string name="error_invalid_key">Gautas netinkamas raktas</string>
<string name="pair_requested">Paprašyta suporuoti</string>
<string name="tap_to_answer">Norėdami atsakyti, palieskite</string>
<string name="error_could_not_send_package">Nepavyko išsiųsti paketo</string>
<string name="error_timed_out">Pasibaigė skirtas laikas</string>
<string name="error_canceled_by_user">Naudotojas atsisakė</string>
<string name="error_canceled_by_other_peer">Lygiarangis atsisakė</string>
<string name="error_invalid_key">Gautas negaliojantis raktas</string>
<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>
<string name="received_url_title">Gauta nuoroda iš %1s</string>
<string name="received_url_text">Bakstelėkite, norėdami atverti \"%1s\"</string>
<plurals name="incoming_file_title">
<item quantity="one">Gaunamas %1$d failas iš %2$s</item>
<item quantity="few">Gaunami %1$d failai iš %2$s</item>
<item quantity="many">Gaunama %1$d failų iš %2$s</item>
<item quantity="other">Gaunamas %1$d failas iš %2$s</item>
</plurals>
<plurals name="incoming_files_text">
<item quantity="one">Failas: %1s</item>
<item quantity="few">(Failas %2$d iš %3$d) : %1$s</item>
<item quantity="many">(Failas %2$d iš %3$d) : %1$s</item>
<item quantity="other">(Failas %2$d iš %3$d) : %1$s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="one">Siunčiamas %1$d failas į %2$s</item>
<item quantity="few">Siunčiami %1$d failai į %2$s</item>
<item quantity="many">Siunčiama %1$d failų į %2$s</item>
<item quantity="other">Siunčiamas %1$d failas į %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Failas: %1$s</item>
<item quantity="few">(Failas %2$d iš %3$d) : %1$s</item>
<item quantity="many">(Failas %2$d iš %3$d) : %1$s</item>
<item quantity="other">(Failas %2$d iš %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Gautas failas iš %1$s</item>
<item quantity="few">Gauti %2$d failai iš %1$s</item>
<item quantity="many">Gauta %2$d failų iš %1$s</item>
<item quantity="other">Gautas %2$d failas iš %1$s</item>
</plurals>
<plurals name="received_files_fail_title">
<item quantity="one">Nepavyko gauti failo iš %1$s</item>
<item quantity="few">Nepavyko gauti %2$d iš %3$d failų iš %1$s</item>
<item quantity="many">Nepavyko gauti %2$d iš %3$d failų iš %1$s</item>
<item quantity="other">Nepavyko gauti %2$d iš %3$d failo iš %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Failas išsiųstas į %1$s</item>
<item quantity="few">Išsiųsti %2$d failai į %1$s</item>
<item quantity="many">Išsiųsta %2$d failų į %1$s</item>
<item quantity="other">Išsiųstas %2$d failas į %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Nepavyko išsiųsti failo į %1$s</item>
<item quantity="few">Nepavyko išsiųsti %2$d iš %3$d failų į %1$s</item>
<item quantity="many">Nepavyko išsiųsti %2$d iš %3$d failų į %1$s</item>
<item quantity="other">Nepavyko išsiųsti %2$d iš %3$d failo į %1$s</item>
</plurals>
<string name="received_file_text">Bakstelėkite,norėdami atverti \"%1s\"</string>
<string name="cannot_create_file">Nepavyksta sukurti failo %s</string>
<string name="tap_to_answer">Bakstelėkite norėdami atsakyti</string>
<string name="reconnect">Prisijungti iš naujo</string>
<string name="right_click">Siųsti dešinįjį spustelėjimą</string>
<string name="middle_click">Siųsti vidurinįjį spustelėjimą</string>
<string name="show_keyboard">Rodyti klaviatūrą</string>
<string name="device_not_paired">Įrenginys nesuporuotas</string>
<string name="request_pairing">Bandyti suporuoti</string>
<string name="request_pairing">Užklausti suporuoti</string>
<string name="pairing_accept">Priimti</string>
<string name="pairing_reject">Atmesti</string>
<string name="device">Įrenginys</string>
<string name="pair_device">Suporuoti įrenginį</string>
<string name="mpris_play">Groti</string>
<string name="settings">Nustatyti</string>
<string name="mpris_play">Atkurti</string>
<string name="mpris_pause">Pristabdyti</string>
<string name="mpris_previous">Ankstesnis</string>
<string name="mpris_rew">Atsukti</string>
<string name="mpris_ff">Prasukti</string>
<string name="mpris_next">Tolesnis</string>
<string name="mpris_rew">Persukti atgal</string>
<string name="mpris_ff">Persukti pirmyn</string>
<string name="mpris_next">Kitas</string>
<string name="mpris_volume">Garsumas</string>
<string name="mpris_settings">Įvairialypės terpės nuostatos</string>
<string name="mpris_time_settings_title">Prasukimo ir atsukimo mygtukai</string>
<string name="mpris_settings">Įvairialypės terpės nustatymai</string>
<string name="mpris_time_settings_title">Persukimo pirmyn/atgal mygtukai</string>
<string name="mpris_time_settings_summary">Reguliuoti persukimo pirmyn/atgal laiką, spustelėjus</string>
<string-array name="mpris_time_entries">
<item>10 sekundžių</item>
<item>20 sekundžių</item>
@@ -77,33 +175,146 @@
<item>1 minutė</item>
<item>2 minutės</item>
</string-array>
<string name="general_settings">Bendrosios nuostatos</string>
<string name="plugin_settings">Nuostatos</string>
<string name="plugin_settings_with_name">%s nuostatos</string>
<string name="mpris_notification_settings_title">Rodyti įvairialypės terpės valdymo pranešimus</string>
<string name="mpris_notification_settings_summary">Leisti valdyti medijos leistuves neatveriant KDE Connect</string>
<string name="share_to">Bendrinti su…</string>
<string name="protocol_version_older">Šis įrenginys naudoja seną protokolo versiją</string>
<string name="protocol_version_newer">Šis įrenginys naudoja naujesnę protokolo versiją</string>
<string name="general_settings">Bendri nustatymai</string>
<string name="plugin_settings">Nustatymai</string>
<string name="plugin_settings_with_name">%s nustatymai</string>
<string name="device_name">Įrenginio pavadinimas</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Netinkamas įrenginio pavadinimas</string>
<string name="custom_devices_settings">Savitas įrenginių sąrašas</string>
<string name="shareplugin_text_saved">Gautas tekstas, įrašytas į iškarpinę</string>
<string name="custom_devices_settings">Tinkintų įrenginių sąrašas</string>
<string name="pair_device_action">Suporuoti naują įrenginį</string>
<string name="unpair_device_action">Atrišti %s</string>
<string name="custom_device_list">Pridėti įrenginį pagal IP</string>
<string name="unpair_device_action">Panaikinti suporavimą su %s</string>
<string name="custom_device_list">Pridėti įrenginius pagal IP</string>
<string name="delete_custom_device">Ištrinti %s?</string>
<string name="custom_device_deleted">Tinkintas įrenginys ištrintas</string>
<string name="custom_device_list_help">Jeigu jūsų įrenginys nėra automatiškai aptinkamas, galite pridėti jo IP adresą arba mazgo pavadinimą, spusteldami ant slankiojančio veiksmo mygtuko</string>
<string name="custom_device_fab_hint">Pridėti įrenginį</string>
<string name="undo">Atšaukti</string>
<string name="share_notification_preference">Triukšmingi pranešimai</string>
<string name="share_notification_preference_summary">Gaunant failą, vibruoti ir atkurti garsą</string>
<string name="share_destination_customize">Tinkinti paskirties katalogą</string>
<string name="share_destination_customize_summary_disabled">Gauti failai atsiras Atsiuntimų aplanke</string>
<string name="share_destination_customize_summary_enabled">Failai bus saugomi žemiau esančiame kataloge</string>
<string name="share_destination_folder_preference">Paskirties katalogas</string>
<string name="share">Bendrinti</string>
<string name="share_received_file">Bendrinti \"%s\"</string>
<string name="title_activity_notification_filter">Pranešimų filtras</string>
<string name="filter_apps_info">Pranešimai bus sinchronizuojami pasirinktoms programėlėms.</string>
<string name="sftp_internal_storage">Vidinė saugykla</string>
<string name="sftp_sdcard_num">SD kortelė %d</string>
<string name="sftp_sdcard">SD kortelė</string>
<string name="sftp_readonly">(tik skaitymui)</string>
<string name="sftp_camera">Nuotraukos</string>
<string name="add_host">Pridėti kompiuterį / IP</string>
<string name="sftp_camera">Kameros nuotraukos</string>
<string name="add_device_dialog_title">Pridėti įrenginį</string>
<string name="add_device_hint">Mazgo pavadinimas arba IP adresas</string>
<string name="sftp_preference_detected_sdcards">Aptiktos SD kortelės</string>
<string name="sftp_preference_edit_sdcard_title">Taisyti SD kortelė</string>
<string name="sftp_preference_configured_storage_locations">Sukonfigūruotos saugyklų vietos</string>
<string name="sftp_preference_add_storage_location_title">Pridėti saugyklos vietą</string>
<string name="sftp_preference_edit_storage_location">Taisyti saugyklos vietą</string>
<string name="sftp_preference_add_camera_shortcut">Pridėti kameros aplanko trumpinį</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Pridėti šaukinį į kameros aplanką</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Nepridėti šaukinio į kameros aplanką</string>
<string name="sftp_storage_preference_storage_location">Saugyklos vieta</string>
<string name="sftp_storage_preference_storage_location_already_configured">Ši vieta jau yra sukonfigūruota</string>
<string name="sftp_storage_preference_click_to_select">spustelėkite norėdami pasirinkti</string>
<string name="sftp_storage_preference_display_name">Rodomas pavadinimas</string>
<string name="sftp_storage_preference_display_name_already_used">Šis rodomas pavadinimas jau yra naudojamas</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Rodomas pavadinimas negali būti tuščias</string>
<string name="sftp_action_mode_menu_delete">Ištrinti</string>
<string name="sftp_no_sdcard_detected">Neaptikta jokios SD kortelės</string>
<string name="sftp_no_storage_locations_configured">Nėra sukonfigūruota jokių saugyklos vietų</string>
<string name="sftp_saf_permission_explanation">Norėdami gauti nuotolinę prieigą prie failų, turite sukonfigūruoti saugyklos vietas</string>
<string name="add_host">Pridėti mazgą/IP</string>
<string name="add_host_hint">Mazgo pavadinimas ar IP</string>
<string name="no_players_connected">Nerasta jokių leistuvių</string>
<string name="mpris_player_on_device">%1$s - %2$s</string>
<string name="send_files">Siųsti failus</string>
<string name="pairing_title">KDE Connect įrenginiai</string>
<string name="pairing_description">Čia turėtų pasirodyti to kiti paties tinklo įrenginiai, kuriuose veikia „KDE Connect</string>
<string name="pairing_title">KDE Connect įrenginiai</string>
<string name="pairing_description">Čia turėtų atsirasti kiti to paties tinklo įrenginiai, naudojantys KDE Connect.</string>
<string name="device_paired">Įrenginys suporuotas</string>
<string name="device_rename_title">Pervadinti įrenginį</string>
<string name="device_rename_confirm">Pervadinti</string>
<string name="refresh">Atnaujinti</string>
<string name="unreachable_description">Šis suporuotas įrenginys nepasiekiamas. Patikrinkite, ar jis prisijungęs prie to paties tinklo.</string>
<string name="pref_plugin_telepathy">Siųsti SMS</string>
<string name="plugin_not_supported">Telefonas nepalaiko šio papildinio</string>
<string name="findmyphone_found">Radau</string>
<string name="refresh">Įkelti iš naujo</string>
<string name="unreachable_description">Šis suporuotas įrenginys nepasiekiamas. Įsitikinkite, kad jis yra prijungtas prie to paties tinklo.</string>
<string name="on_data_message">Atrodo, kad naudojate mobiliųjų duomenų ryšį. KDE Connect veikia tik vietiniuose tinkluose.</string>
<string name="no_file_browser">Nėra įdiegta jokių failų tvarkytuvių.</string>
<string name="pref_plugin_telepathy">Siųsti SMS žinutę</string>
<string name="pref_plugin_telepathy_desc">Siųsti tekstines žinutes iš savo darbalaukio</string>
<string name="plugin_not_supported">Šis įrenginys nepalaiko šio priedo</string>
<string name="findmyphone_title">Rasti telefoną</string>
<string name="findmyphone_title_tablet">Rasti planšetę</string>
<string name="findmyphone_title_tv">Rasti televizorių</string>
<string name="findmyphone_description">Skambina į šį įrenginį, kad galėtumėte jį rasti</string>
<string name="findmyphone_found">Rastas</string>
<string name="open">Atverti</string>
<string name="close">Užverti</string>
<string name="no_permissions_storage">Norėdami gauti prieigą prie saugyklos, turite suteikti leidimus</string>
<string name="plugins_need_permission">Kai kurie priedai tam, kad veiktų, reikalauja leidimų (bakstelėkite išsamesnei informacijai):</string>
<string name="permission_explanation">Šis priedas 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ų priedų 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>
<string name="contacts_permission_explanation">Norėdami bendrinti savo adresatų knygą su darbalaukiu, turite suteikti adresatų leidimą</string>
<string name="select_ringtone">Pasirinkti skambučio melodiją</string>
<string name="telephony_pref_blocked_title">Užblokuoti numeriai</string>
<string name="telephony_pref_blocked_dialog_desc">Nerodyti skambučių ir SMS žinučių iš šių numerių. Nurodykite kiekvienoje eilutėje po vieną</string>
<string name="mpris_coverart_description">Dabartinės įvairialypės terpės iliustracija</string>
<string name="device_icon_description">Įrenginio ženkliukas</string>
<string name="settings_icon_description">Nustatymų ženkliukas</string>
<string name="presenter_fullscreen">Visas ekranas</string>
<string name="presenter_exit">Išeiti iš pristatymo</string>
<string name="presenter_lock_tip">Galite užrakinti savo įrenginį ir perėjimui prie kitos/ankstesnės skaidrės naudoti garsumo mygtukus</string>
<string name="add_command">Pridėti komandą</string>
<string name="addcommand_explanation">Nėra registruota jokių komandų</string>
<string name="addcommand_explanation2">Galite pridėti naujas komandas KDE Connect sistemos nustatymuose</string>
<string name="add_command_description">Galite pridėti komandas darbalaukyje</string>
<string name="pref_plugin_mprisreceiver">Medijos leistuvės valdymas</string>
<string name="pref_plugin_mprisreceiver_desc">Valdyti savo telefonų medijos leistuves iš kito įrenginio</string>
<string name="dark_theme">Tamsus apipavidalinimas</string>
<string name="notification_channel_default">Kiti pranešimai</string>
<string name="notification_channel_persistent">Pastovus indikatorius</string>
<string name="notification_channel_media_control">Įvairialypės terpės valdymas</string>
<string name="notification_channel_filetransfer">Failų persiuntimas</string>
<string name="mpris_stop">Stabdyti dabartinę leistuvę</string>
<string name="copy_url_to_clipboard">Kopijuoti URL į iškarpinę</string>
<string name="clipboard_toast">Nukopijuota į iškarpinę</string>
<string name="runcommand_notreachable">Device is not reachable</string>
<string name="runcommand_notpaired">Device is not paired</string>
<string name="runcommand_nosuchdevice">Nėra tokio įrenginio</string>
<string name="runcommand_noruncommandplugin">Šis įrenginys neturi įjungto priedo Vykdyti komandą</string>
<string name="pref_plugin_findremotedevice">Rasti nuotolinį įrenginį</string>
<string name="pref_plugin_findremotedevice_desc">Skambinti į nuotolinį įrenginį</string>
<string name="ring">Skambinti</string>
<string name="pref_plugin_systemvolume">Sistemos garsumas</string>
<string name="pref_plugin_systemvolume_desc">Valdyti nuotolinio įrenginio sistemos garsumą</string>
<string name="mute">Nutildyti</string>
<string name="all">Visi</string>
<string name="devices">Įrenginiai</string>
<string name="settings_rename">Įrenginio pavadinimas</string>
<string name="settings_dark_mode">Tamsus apipavidalinimas</string>
<string name="settings_more_settings_title">Daugiau nustatymų</string>
<string name="settings_more_settings_text">Nustatymus kiekvienam atskiram įrenginiui galima rasti įrenginyje \"Priedo nustatymuose\".</string>
<string name="setting_persistent_notification">Rodyti pastovų pranešimą</string>
<string name="setting_persistent_notification_oreo">Pastovus pranešimas</string>
<string name="setting_persistent_notification_description">Bakstelėkite, norėdami įjungti/išjungti Pranešimų nustatymuose</string>
<string name="extra_options">Papildomos parinktys</string>
<string name="privacy_options">Privatumo parinktys</string>
<string name="set_privacy_options">Nustatyti savo privatumo parinktis</string>
<string name="new_notification">Naujas pranešimas</string>
<string name="block_contents">Blokuoti pranešimų turinį</string>
<string name="block_images">Blokuoti pranešimuose paveiksliukus</string>
<string name="notification_channel_receivenotification">Pranešimai iš kitų įrenginių</string>
<string name="take_picture">Paleisti kamerą</string>
<string name="plugin_photo_desc">Paleisti kameros programėlę, kad būtų palengvintas nuotraukų darymas ir persiuntimas</string>
<string name="no_app_for_opening">Šio failo atvėrimui nerasta jokios tinkamos programėlės</string>
</resources>

View File

@@ -6,37 +6,39 @@
<string name="pref_plugin_telephony">Powiadomienia telefonu</string>
<string name="pref_plugin_telephony_desc">Wysyła powiadomienia o rozmowach przychodzących</string>
<string name="pref_plugin_battery">Stan baterii</string>
<string name="pref_plugin_battery_desc">Okresowo zgłasza stan baterii</string>
<string name="pref_plugin_sftp">Udostępnienie systemu plików</string>
<string name="pref_plugin_sftp_desc">Zezwala na zdalne przeglądanie systemu plików tego urządzenia</string>
<string name="pref_plugin_battery_desc">Okresowo powiadamia o stanie baterii</string>
<string name="pref_plugin_sftp">Udostępnianie systemu plików</string>
<string name="pref_plugin_sftp_desc">Daje dostęp do plików innemu urządzeniu</string>
<string name="pref_plugin_clipboard">Synchronizacja schowka</string>
<string name="pref_plugin_clipboard_desc">Udostępnia zawartość schowka</string>
<string name="pref_plugin_mousepad">Zdalne sterowanie</string>
<string name="pref_plugin_mousepad_desc">Umożliwia wykorzystanie telefonu lub tabletu jako myszy i klawiatury</string>
<string name="pref_plugin_presenter_desc">Umożliwia zmianę slajdów w prezentacji telefonem</string>
<string name="pref_plugin_remotekeyboard">Odbiera zdalne naciśnięcia klawiszy</string>
<string name="pref_plugin_remotekeyboard_desc">Odbiera naciśnięcia klawiszy od urządzeń zdalnych</string>
<string name="pref_plugin_mousepad_desc">Wykorzystuje ekran jako mysz i klawiaturę</string>
<string name="pref_plugin_presenter">Sterowanie pokazem przeźroczy</string>
<string name="pref_plugin_presenter_desc">Przełącza przeźrocza przy użyciu telefonu</string>
<string name="pref_plugin_remotekeyboard">Odbieranie zdalnych naciśnięć klawiszy</string>
<string name="pref_plugin_remotekeyboard_desc">Odbiera naciśnięcia klawiszy z innego urządzenia</string>
<string name="pref_plugin_mpris">Sterowanie multimediami</string>
<string name="pref_plugin_mpris_desc">Umożliwia zdalne sterowanie odtwarzaczem multimedialnym</string>
<string name="pref_plugin_mpris_desc">Steruje odtwarzaczem multimedialnym</string>
<string name="pref_plugin_runcommand">Wykonywanie poleceń</string>
<string name="pref_plugin_runcommand_desc">Umożliwia wykonywanie zdalnych poleceń z telefonu lub tabletu</string>
<string name="pref_plugin_runcommand_desc">Wykonuje zdalne polecenia z telefonu lub tabletu</string>
<string name="pref_plugin_contacts">Synchronizacja kontaktów</string>
<string name="pref_plugin_contacts_desc">Umożliwia synchronizację książki kontaktów urządzenia</string>
<string name="pref_plugin_contacts_desc">Synchronizuje książkę kontaktów urządzenia</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Wysyła i odbiera pingi</string>
<string name="pref_plugin_notifications">Powiadomienia synchronizacji</string>
<string name="pref_plugin_notifications_desc">Udostępnianie powiadomień telefonu innym urządzeniom</string>
<string name="pref_plugin_receive_notifications">Otrzymywanie powiadomień</string>
<string name="pref_plugin_receive_notifications_desc">Odbiera powiadomienia z innych urządzeń i wyświetla je na Androidzie</string>
<string name="pref_plugin_notifications">Synchronizacja powiadomień</string>
<string name="pref_plugin_notifications_desc">Wysyła powiadomienia na inne urządzenia</string>
<string name="pref_plugin_receive_notifications">Odbieranie powiadomień</string>
<string name="pref_plugin_receive_notifications_desc">Odbiera powiadomienia z innych urządzeń</string>
<string name="pref_plugin_sharereceiver">Udostępnianie i odbieranie</string>
<string name="pref_plugin_sharereceiver_desc">Współdzielenie plików i adresów URL pomiędzy urządzeniami</string>
<string name="pref_plugin_sharereceiver_desc">Współdzieli pliki i adresy URL pomiędzy urządzeniami</string>
<string name="plugin_not_available">Funkcja ta nie jest dostępna w twojej wersji Androida</string>
<string name="device_list_empty">Brak urządzeń</string>
<string name="ok">OK</string>
<string name="cancel">Zaniechaj</string>
<string name="open_settings">Otwórz ustawienia</string>
<string name="no_permissions">Musisz przydzielić uprawnienia, aby uzyskać dostęp do powiadomień</string>
<string name="no_permission_mprisreceiver">Aby móc sterować odtwarzaczami multimedialnymi musisz udzielić dostępu powiadomieniom</string>
<string name="no_permissions">Aby uzyskać dostęp do powiadomień, należy przydzielić uprawnienia</string>
<string name="no_permission_mprisreceiver">Aby móc sterować odtwarzaczami multimedialnymi, należy udzielić dostępu powiadomieniom</string>
<string name="no_permissions_remotekeyboard">Aby otrzymywać naciśnięcia klawiszy, należy włączyć zdalną klawiaturę KDE Connect</string>
<string name="send_ping">Wyślij ping</string>
<string name="open_mpris_controls">Sterowanie multimediami</string>
<string name="remotekeyboard_editing_only_title">Obsługuj zdalne naciśnięcia klawiszy tylko podczas edycji</string>
@@ -44,7 +46,7 @@
<string name="remotekeyboard_connected">Połączenie zdalnej klawiatury jest nawiązane</string>
<string name="remotekeyboard_multiple_connections">Nawiązano więcej niż jedno połączenie zdalnej klawiatury, wybierz urządzenie do ustawienia</string>
<string name="open_mousepad">Zdalne sterowanie</string>
<string name="mousepad_info">Przesuń palcem po ekranie, aby przesunąć wskaźnik myszy. Stuknij, aby wywołać naciśniecie lewym przyciskiem myszy i użyj dwóch/trzech palców, aby wywołać naciśniecie prawym i środkowym przyciskiem myszy. Przewijaj przy użyciu dwóch palców. Przyciśnij na dłużej, aby przeciągnąć i upuścić.</string>
<string name="mousepad_info">Przesuwanie palcem po ekranie przesuwa wskaźnik myszy. Stuknięcie jednym, dwoma i trzema palcami wywołuje odpowiednio naciśnięcie lewym, prawym i środkowym przyciskiem myszy. Dwa palce przewijają. Długie naciśnięcie rozpoczyna czynność przeciągania i upuszczania.</string>
<string name="mousepad_double_tap_settings_title">Ustaw działanie po dwukrotnym stuknięciu palcem</string>
<string name="mousepad_triple_tap_settings_title">Ustaw działanie po trzykrotnym stuknięciu palcem</string>
<string name="mousepad_sensitivity_settings_title">Ustaw czułość gładzika</string>
@@ -73,7 +75,7 @@
<string name="category_connected_devices">Podłączone urządzenia</string>
<string name="category_not_paired_devices">Dostępne urządzenia</string>
<string name="category_remembered_devices">Zapamiętane urządzenia</string>
<string name="device_menu_plugins">Ustawienia wtyczki</string>
<string name="device_menu_plugins">Ustawienia wtyczek</string>
<string name="device_menu_unpair">Odparuj</string>
<string name="device_not_reachable">Sparowane urządzenie nieosiągalne</string>
<string name="pair_new_device">Sparuj nowe urządzenie</string>
@@ -82,22 +84,22 @@
<string name="error_already_requested">Już zażądano parowania</string>
<string name="error_already_paired">Urządzenie już sparowano</string>
<string name="error_could_not_send_package">Nie można wysłać pakietu</string>
<string name="error_timed_out">Przekroczono czasu odpowiedzi</string>
<string name="error_canceled_by_user">Anulowane przez użytkownika</string>
<string name="error_canceled_by_other_peer">Anulowane przez innego partnera</string>
<string name="error_timed_out">Upłynął czas na odpowiedź</string>
<string name="error_canceled_by_user">Użytkownik zaniechał</string>
<string name="error_canceled_by_other_peer">Inny uczestnik zaniechał</string>
<string name="error_invalid_key">Otrzymano nieprawidłowy klucz</string>
<string name="encryption_info_title">Zaszyfrowane informacje</string>
<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>
<string name="received_url_title">Odebrano odsyłacz od %1s</string>
<string name="received_url_text">Znacznik do otwarcia \'%1s\'</string>
<string name="received_url_text">Stuknij, aby otworzyć \'%1s\'</string>
<plurals name="incoming_file_title">
<item quantity="one">Odebrano %1$d plik z %2$s</item>
<item quantity="few">Odebrano %1$d pliki z %2$s</item>
<item quantity="many">Odebrano %1$d plików z %2$s</item>
<item quantity="one">Odbieranie %1$d pliku od %2$s</item>
<item quantity="few">Odbieranie %1$d plików od %2$s</item>
<item quantity="many">Odbieranie %1$d plików od %2$s</item>
<item quantity="other"/>
</plurals>
<plurals name="incoming_files_text">
@@ -106,6 +108,18 @@
<item quantity="many">(Plik %2$d z %3$d) : %1$s</item>
<item quantity="other"/>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="one">Wysyłanie %1$d pliku do %2$s</item>
<item quantity="few">Wysyłanie %1$d plików do %2$s</item>
<item quantity="many">Wysyłanie %1$d plików do %2$s</item>
<item quantity="other"/>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Plik: %1$s</item>
<item quantity="few">(Plik %2$d z %3$d) : %1$s</item>
<item quantity="many">(Plik %2$d z %3$d) : %1$s</item>
<item quantity="other"/>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Odebrano plik od %1$s</item>
<item quantity="few">Odebrano %2$d pliki od %1$s</item>
@@ -118,6 +132,18 @@
<item quantity="many">Nie udało się odebrać %2$d z %3$d plików od %1$s</item>
<item quantity="other"/>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Wyślij plik do %1$s</item>
<item quantity="few">Wyślij %2$d pliki do %1$s</item>
<item quantity="many">Wyślij %2$d plików do %1$s</item>
<item quantity="other"/>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Nie udało się wysłać pliku do %1$s</item>
<item quantity="few">Nie udało się wysłać %2$d z %3$d plików do %1$s</item>
<item quantity="many">Nie udało się wysłać %2$d z %3$d plików do %1$s</item>
<item quantity="other"/>
</plurals>
<string name="received_file_text">Stuknij, aby otworzyć \'%1s\'</string>
<string name="cannot_create_file">Nie można utworzyć pliku %s</string>
<string name="tap_to_answer">Stuknij, aby odpowiedzieć</string>
@@ -150,7 +176,7 @@
<item>2 minuty</item>
</string-array>
<string name="mpris_notification_settings_title">Pokaż powiadomienia sterowania mediami</string>
<string name="mpris_notification_settings_summary">Umożliwia sterowanie odtwarzaczami bez otwierania KDE Connect</string>
<string name="mpris_notification_settings_summary">Steruje odtwarzaczami bez otwierania KDE Connect</string>
<string name="share_to">Współdziel z...</string>
<string name="protocol_version_older">Urządzenie to używa starej wersji protokołu</string>
<string name="protocol_version_newer">Urządzenie to używa nowszej wersji protokołu</string>
@@ -166,6 +192,10 @@
<string name="unpair_device_action">Rozparuj %s</string>
<string name="custom_device_list">Dodaj urządzenia po numerze IP</string>
<string name="delete_custom_device">Usunąć %s?</string>
<string name="custom_device_deleted">Usunięto własne urządzenie</string>
<string name="custom_device_list_help">Jeśli twoje urządzenie nie zostało samo wykryte, to możesz dodać jego adres IP lub nazwę gospodarza klikając na pływającym przycisku działania</string>
<string name="custom_device_fab_hint">Dodaj urządzenie</string>
<string name="undo">Cofnij</string>
<string name="share_notification_preference">Hałaśliwe powiadomienia</string>
<string name="share_notification_preference_summary">Zadrżyj i odegraj dźwięk przy odebraniu pliku</string>
<string name="share_destination_customize">Własny katalog docelowy</string>
@@ -176,17 +206,38 @@
<string name="share_received_file">Udostępnij \"%s\"</string>
<string name="title_activity_notification_filter">Filtr powiadomień</string>
<string name="filter_apps_info">Powiadomienia zostaną zsynchronizowane z wybranymi aplikacjami.</string>
<string name="sftp_internal_storage">"Pamięć wewnętrzna "</string>
<string name="sftp_internal_storage">Pamięć wewnętrzna</string>
<string name="sftp_sdcard_num">Karta SD %d</string>
<string name="sftp_sdcard">Karta SD</string>
<string name="sftp_readonly">(tylko do odczytu)</string>
<string name="sftp_camera">Zdjęcia z aparatu</string>
<string name="add_device_dialog_title">Dodaj urządzenie</string>
<string name="add_device_hint">Nazwa gospodarza lub adres IP</string>
<string name="sftp_preference_detected_sdcards">Wykryte karty SD</string>
<string name="sftp_preference_edit_sdcard_title">Edytuj kartę SD</string>
<string name="sftp_preference_configured_storage_locations">Wybrane miejsca w pamięci</string>
<string name="sftp_preference_add_storage_location_title">Dodawanie miejsca w pamięci</string>
<string name="sftp_preference_edit_storage_location">Edytowanie miejsca w pamięci</string>
<string name="sftp_preference_add_camera_shortcut">Dodaj skrót katalogu aparatu</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Dodaj skrót do katalogu aparatu</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Nie dodawaj skrótu do katalogu aparatu</string>
<string name="sftp_storage_preference_storage_location">Miejsce w pamięci</string>
<string name="sftp_storage_preference_storage_location_already_configured">To miejsce było już ustawione</string>
<string name="sftp_storage_preference_click_to_select">kliknij, aby wybrać</string>
<string name="sftp_storage_preference_display_name">Nazwa do wyświetlania</string>
<string name="sftp_storage_preference_display_name_already_used">Nazwa do wyświetlania jest już używana</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Nazwa do wyświetlania nie może być pusta</string>
<string name="sftp_action_mode_menu_delete">Usuń</string>
<string name="sftp_no_sdcard_detected">Nie wykryto żadnej karty SD</string>
<string name="sftp_no_storage_locations_configured">Nie ustawiono miejsc w pamięci</string>
<string name="sftp_saf_permission_explanation">Aby przeglądać pliki zdalnie, należy ustawić miejsca w pamięci</string>
<string name="add_host">Dodaj gospodarza/IP</string>
<string name="add_host_hint">Nazwa gospodarza lub IP</string>
<string name="no_players_connected">Nie znaleziono żadnego odtwarzacza</string>
<string name="mpris_player_on_device">%1$s na %2$s</string>
<string name="send_files">Wyślij pliki</string>
<string name="pairing_title">Urządzenia KDE Connect</string>
<string name="pairing_description">Tutaj powinny pojawić się inne urządzenia uruchmione z KDE Connect w twojej sieci.</string>
<string name="pairing_description">Tu powinny pojawić się urządzenia podłączone do tej samej sieci i które także mają uruchomione KDE Connect.</string>
<string name="device_paired">Urządzenie sparowane</string>
<string name="device_rename_title">Zmień nazwę urządzenia</string>
<string name="device_rename_confirm">Zmień nazwę</string>
@@ -205,13 +256,14 @@
<string name="open">Otwórz</string>
<string name="close">Zamknij</string>
<string name="no_permissions_storage">Musisz nadać uprawnienia, aby uzyskać dostęp do pamięci masowej</string>
<string name="plugins_need_permission">Niektóre z wtyczek wymagają uprawnień do działania (stuknij po więcej informacji)</string>
<string name="plugins_need_permission">Niektóre wtyczki wymagają uprawnień do swojego działania (stuknij, aby dowiedzieć się więcej)</string>
<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 z wtyczek mają ograniczone możliwości ze względu na ograniczone uprawnienia (stuknij po więcej informacji)</string>
<string name="share_optional_permission_explanation">Aby udostępniać pliki z twojego telefonu na twoim komputerze musisz pozowolić na dostęp do pamięci telefonu</string>
<string name="telepathy_permission_explanation">Aby odczytywać i pisać SMSy z twojego komputera musisz nadać uprawnienia do SMSów</string>
<string name="telephony_optional_permission_explanation">Aby widzieć nazwę kontaktu zamiast numeru telefonu musisz pozwolić na dostęp do kontaktów telefonu</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>
<string name="contacts_permission_explanation">Aby współdzielić swoją książkę adresową z komputerem musisz udzielić uprawnień do kontaktów</string>
<string name="select_ringtone">Wybierz dzwonek</string>
<string name="telephony_pref_blocked_title">Zablokowane numery</string>
@@ -221,9 +273,10 @@
<string name="settings_icon_description">Ikona ustawień</string>
<string name="presenter_fullscreen">Pełny ekran</string>
<string name="presenter_exit">Zakończ prezentację</string>
<string name="presenter_lock_tip">Możesz zablokować swoje urządzenie i używać przycisków głośności, aby przejść do kolejnego/poprzedniego przeźrocza</string>
<string name="add_command">Dodaj polecenie</string>
<string name="addcommand_explanation">Nie zarejestrowano żadnych poleceń</string>
<string name="addcommand_explanation2">Nowe polecenia można dodawać w ustawieniach systemowych KDE Connect</string>
<string name="addcommand_explanation2">Nowe polecenie można dodać w ustawieniach systemowych KDE Connect</string>
<string name="add_command_description">Możesz dodawać polecenia na pulpicie</string>
<string name="pref_plugin_mprisreceiver">Sterowanie odtwarzaczem mediów</string>
<string name="pref_plugin_mprisreceiver_desc">Sterowanie odtwarzaczami multimedialnymi z innego urządzenia</string>
@@ -250,15 +303,18 @@
<string name="settings_rename">Nazwa urządzenia</string>
<string name="settings_dark_mode">Ciemny wygląd</string>
<string name="settings_more_settings_title">Więcej ustawień</string>
<string name="settings_more_settings_text">Wstępne ustawienia urządzenia można znaleźć w \'Ustawieniach wtyczki\' z poziomu urządzenia.</string>
<string name="settings_more_settings_text">Ustawienia nadawane urządzeniu można znaleźć w \'Ustawienia wtyczek\' w danym urządzeniu.</string>
<string name="setting_persistent_notification">Pokaż nieznikające powiadomienie</string>
<string name="setting_persistent_notification_oreo">Nieznikające powiadomienie</string>
<string name="setting_persistent_notification_description">Stuknij, aby włączyć/wyłączyć ustawienia powiadomienia</string>
<string name="setting_persistent_notification_description">Stuknij, aby je ustawić w ustawieniach powiadamiania</string>
<string name="extra_options">Opcje dodatkowe</string>
<string name="privacy_options">Opcje prywatności</string>
<string name="set_privacy_options">Ustaw swoje opcje prywatności</string>
<string name="new_notification">Nowe powiadomienie</string>
<string name="block_contents">Blokuj treści w powiadomieniach</string>
<string name="block_images">Blokuj obrazy w powiadomieniach</string>
<string name="notification_channel_receivenotification">Powiadomienie z innych urządzeń</string>
<string name="notification_channel_receivenotification">Powiadomienia z innych urządzeń</string>
<string name="take_picture">Uruchom aparat</string>
<string name="plugin_photo_desc">Uruchom aplikację aparatu, aby ułatwić robienie i przesyłanie zdjęć</string>
<string name="no_app_for_opening">Nie znaleziono aplikacji do otwarcia tego pliku</string>
</resources>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_clipboard_desc">Использование общего буфера обмена</string>
<string name="pref_plugin_mousepad">Удалённый ввод</string>
<string name="pref_plugin_mousepad_desc">Использование телефона или планшета в качестве сенсорной панели и клавиатуры</string>
<string name="pref_plugin_presenter">Пульт управления слайд-шоу</string>
<string name="pref_plugin_presenter_desc">Использование телефона для смены слайдов презентации</string>
<string name="pref_plugin_remotekeyboard">Получение удалённых нажатий клавиш</string>
<string name="pref_plugin_remotekeyboard_desc">Получение нажатий клавиш с удалённых устройств</string>
@@ -35,6 +36,8 @@
<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">Отправить тестовый сигнал</string>
<string name="open_mpris_controls">Управление воспроизведением</string>
<string name="remotekeyboard_editing_only_title">Обрабатывать удалённые нажатия только при редактировании</string>
@@ -149,6 +152,8 @@
<string name="share_destination_customize_summary_disabled">Полученные файлы появятся в каталоге Загрузки</string>
<string name="share_destination_customize_summary_enabled">Файлы будут сохранены в указанном ниже каталоге</string>
<string name="share_destination_folder_preference">Целевой каталог</string>
<string name="share">Отправить</string>
<string name="share_received_file">Поделиться «%s» с помощью:</string>
<string name="title_activity_notification_filter">Фильтр уведомлений</string>
<string name="filter_apps_info">Уведомления будут синхронизированы для выбранных приложений.</string>
<string name="sftp_internal_storage">Встроенная память</string>
@@ -156,6 +161,18 @@
<string name="sftp_sdcard">SD-карта</string>
<string name="sftp_readonly">(только чтение)</string>
<string name="sftp_camera">Фотографии с камеры</string>
<string name="add_device_dialog_title">Добавление устройства</string>
<string name="add_device_hint">Имя хоста или IP-адрес</string>
<string name="sftp_preference_configured_storage_locations">Настроенные общие папки</string>
<string name="sftp_preference_add_storage_location_title">Добавить общую папку</string>
<string name="sftp_preference_edit_storage_location">Изменение общей папки</string>
<string name="sftp_storage_preference_storage_location">Расположение на устройстве</string>
<string name="sftp_storage_preference_storage_location_already_configured">Это расположение уже было добавлено</string>
<string name="sftp_storage_preference_display_name">Отображаемое имя</string>
<string name="sftp_storage_preference_display_name_already_used">Отображаемое имя уже используется</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Отображаемое имя не может быть пустым</string>
<string name="sftp_no_storage_locations_configured">Нет настроенных общих папок</string>
<string name="sftp_saf_permission_explanation">Чтобы разрешить удалённый доступ к файлам, необходимо настроить список общих папок.</string>
<string name="add_host">Добавить хост/IP-адрес</string>
<string name="no_players_connected">Медиапроигрывателей не найдено</string>
<string name="mpris_player_on_device">%1$s на %2$s</string>
@@ -174,6 +191,7 @@
<string name="plugin_not_supported">Этот модуль не поддерживается устройством</string>
<string name="findmyphone_title">Поиск телефона</string>
<string name="findmyphone_title_tablet">Поиск планшета</string>
<string name="findmyphone_title_tv">Поиск телевизора</string>
<string name="findmyphone_description">Подача звукового сигнала на устройстве, чтобы вы могли его найти</string>
<string name="findmyphone_found">Найден</string>
<string name="open">Открыть</string>
@@ -185,12 +203,28 @@
<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>
<string name="presenter_fullscreen">На весь экран</string>
<string name="presenter_exit">Закончить слайд-шоу</string>
<string name="presenter_lock_tip">Вы можете заблокировать устройство и использовать кнопки управления громкостью для перехода к предыдущему/следующему слайду.</string>
<string name="addcommand_explanation">Нет настроенных команд.</string>
<string name="addcommand_explanation2">Вы можете добавить новые команды в «Параметрах системы» в разделе «KDE Connect».</string>
<string name="pref_plugin_mprisreceiver">Управление мультимедиа</string>
<string name="dark_theme">Тёмное оформление</string>
<string name="notification_channel_default">Прочие уведомления</string>
<string name="notification_channel_persistent">Постоянный индикатор</string>
<string name="notification_channel_media_control">Управление воспроизведением</string>
<string name="notification_channel_filetransfer">Передача файлов</string>
<string name="copy_url_to_clipboard">Копировать URL в буфер обмена</string>
<string name="clipboard_toast">Скопировано в буфер обмена</string>
<string name="runcommand_notreachable">Устройство недоступно</string>
<string name="runcommand_notpaired">Устройство не сопряжено</string>
<string name="devices">Устройства</string>
<string name="settings_rename">Имя устройства</string>
<string name="settings_dark_mode">Тёмное оформление</string>
<string name="settings_more_settings_title">Дополнительные параметры</string>
<string name="setting_persistent_notification">Показывать постоянное уведомление</string>
<string name="setting_persistent_notification_oreo">Постоянное уведомление</string>
<string name="notification_channel_receivenotification">Уведомления с других устройств</string>
</resources>

View File

@@ -1,6 +1,10 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="kde_connect">KDE Connect</string>
<string name="foreground_notification_no_devices">Hiçbir aygıta bağlı değil</string>
<string name="foreground_notification_devices">Bağlı: %s</string>
<string name="pref_plugin_telephony">Telefon bildiricisi</string>
<string name="pref_plugin_telephony_desc">Gelen aramalar için bildirim gönder</string>
<string name="pref_plugin_battery">Batarya raporu</string>
<string name="pref_plugin_battery_desc">Batarya durumunu belirli aralıklarla raporla</string>
<string name="pref_plugin_sftp">Dosya sistemi gösterme</string>
@@ -9,12 +13,16 @@
<string name="pref_plugin_clipboard_desc">Pano içeriğini paylaş</string>
<string name="pref_plugin_mousepad">Uzak girdi</string>
<string name="pref_plugin_mousepad_desc">Telefonunuzu veya tabletinizi, dokunmatik veya klavye olarak kullanın</string>
<string name="pref_plugin_presenter">Uzaktan slayt gösterisi</string>
<string name="pref_plugin_presenter_desc">Sunumdaki slaytları değiştirmek için aygıtınızı kullanın</string>
<string name="pref_plugin_remotekeyboard">Uzak tuşa basmaları getir</string>
<string name="pref_plugin_remotekeyboard_desc">Tuş basma eylemlerini, uzak aygıtlardan getir</string>
<string name="pref_plugin_mpris">Çoklu ortam denetimleri</string>
<string name="pref_plugin_mpris_desc">Ortam oynatıcınız için uzak denetim sağlar</string>
<string name="pref_plugin_runcommand">Komut Çalıştır</string>
<string name="pref_plugin_runcommand_desc">Uzak komutları, telefon veya tabletinizden tetikler</string>
<string name="pref_plugin_contacts">Rehber Eşitleyici</string>
<string name="pref_plugin_contacts_desc">Aygıtın rehberini eşitle</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Ping gönder ve al</string>
<string name="pref_plugin_notifications">Bildirim eşitleme</string>
@@ -29,6 +37,8 @@
<string name="cancel">İptal</string>
<string name="open_settings">Ayarları</string>
<string name="no_permissions">Bildirimler erişebilmek için izine ihtiyacınız var</string>
<string name="no_permission_mprisreceiver">Medya oynatıcılarınızı kontrol edebilmek için bildirimlere erişim izni vermeniz gerekir</string>
<string name="no_permissions_remotekeyboard">Tuşlara basmak için KDE Connect Uzak Klavye\'yi etkinleştirmeniz gerekir</string>
<string name="send_ping">Ping gönder</string>
<string name="open_mpris_controls">Çoklu ortam denetimi</string>
<string name="remotekeyboard_editing_only_title">Uzak tuşları, sadece düzenleme yaparken işle</string>
@@ -36,9 +46,11 @@
<string name="remotekeyboard_connected">Uzak klavye bağlantısı etkin</string>
<string name="remotekeyboard_multiple_connections">Birden çok uzak klavye bağlantısı mevcut, yapılandırmak istediğiniz aygıtı seçin</string>
<string name="open_mousepad">Girdi sil</string>
<string name="mousepad_info">İmleç kontrolü için parmağınızı ekranda hareket ettirin. Bir tıklama için hafifçe vurun, sağ ve orta düğmeler için iki/üç parmağınızı kullanın. Kaydırmak için 2 parmağınızı kullanın. Bırakıp sürüklemek için uzun basın.</string>
<string name="mousepad_double_tap_settings_title">İki parmak dokunma eylemini ayarla</string>
<string name="mousepad_triple_tap_settings_title">Üç parmak dokunma eylemini ayarla</string>
<string name="mousepad_sensitivity_settings_title">Dokunmatik yüzey hassasiyetini ayarla</string>
<string name="mousepad_acceleration_profile_settings_title">İşaretçi ivmesini ayarla</string>
<string name="mousepad_scroll_direction_title">Ters Kaydırma Yönü</string>
<string-array name="mousepad_tap_entries">
<item>Sağ tık</item>
@@ -53,12 +65,12 @@
<item>En Hızlı</item>
</string-array>
<string-array name="mousepad_acceleration_profile_entries">
<item>No Acceleration</item>
<item>Weakest</item>
<item>Weaker</item>
<item>Medium</item>
<item>Stronger</item>
<item>Strongest</item>
<item>Hızlandırıcı Yok</item>
<item>Zayıf</item>
<item>Güçsüz</item>
<item>Orta</item>
<item>Güçlü</item>
<item>Kuvvetli</item>
</string-array>
<string name="category_connected_devices">Bağlı aygıtlar</string>
<string name="category_not_paired_devices">Kullanılabilir aygıtlar</string>
@@ -84,7 +96,40 @@
<string name="pairing_request_from">%1s için eşleşme talebi</string>
<string name="received_url_title">%1s üzerinden bağlantı alındı</string>
<string name="received_url_text">\'%1s\' açmak için dokunun</string>
<plurals name="incoming_file_title">
<item quantity="one">%2$s içinden %1$d dosya alınıyor</item>
<item quantity="other">%2$s içinden %1$d dosyalar alınıyor</item>
</plurals>
<plurals name="incoming_files_text">
<item quantity="one">Dosya: %1s</item>
<item quantity="other">(Dosya %2$d %3$d) : %1$s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="one">%1$d dosya %2$s gönderiliyor</item>
<item quantity="other">%1$d dosyalar %2$s gönderiliyor</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Dosya: %1$s</item>
<item quantity="other">(Dosya %2$d %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">%1$s ögesinden alınan dosya</item>
<item quantity="other">%1$s içinden %2$d dosya alındı</item>
</plurals>
<plurals name="received_files_fail_title">
<item quantity="one">%1$s dosya alınamadı</item>
<item quantity="other">%1$s içindeki %2$d / %3$d dosya alınamadı</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Dosyayı şuraya gönder %1$s</item>
<item quantity="other">Gönder %2$d şuraya %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Dosya gönderilemedi %1$s</item>
<item quantity="other">Dosya gönderilemedi %2$d %3$d şuraya %1$s</item>
</plurals>
<string name="received_file_text">\'%1s\' açmak için dokunun</string>
<string name="cannot_create_file">Dosya oluşturulamıyor %s</string>
<string name="tap_to_answer">Cevap için dokunun</string>
<string name="reconnect">Yeniden Bağlan</string>
<string name="right_click">Sağ Tık Gönder</string>
@@ -96,7 +141,9 @@
<string name="pairing_reject">Reddet</string>
<string name="device">Aygıt</string>
<string name="pair_device">Aygıt eşleştir</string>
<string name="settings">Ayarlar</string>
<string name="mpris_play">Oynat</string>
<string name="mpris_pause">Duraklat</string>
<string name="mpris_previous">Önceki</string>
<string name="mpris_rew">Geri Sar</string>
<string name="mpris_ff">Hızlı İleri Sar</string>
@@ -104,6 +151,7 @@
<string name="mpris_volume">Ses</string>
<string name="mpris_settings">Çoklu Ortam Ayarları</string>
<string name="mpris_time_settings_title">İleri/geri düğmeleri</string>
<string name="mpris_time_settings_summary">Basıldığında hızlı ileri/geri sarma süresini ayarlayın</string>
<string-array name="mpris_time_entries">
<item>10 saniye</item>
<item>20 saniye</item>
@@ -111,6 +159,9 @@
<item>1 dakika</item>
<item>2 dakika</item>
</string-array>
<string name="mpris_notification_settings_title">Medya kontrol bildirimini göster</string>
<string name="mpris_notification_settings_summary">KDE Connect\'i açmadan medya oynatıcılarınızı kontrol etmenize izin verin</string>
<string name="share_to">Paylaş…</string>
<string name="protocol_version_older">Bu aygıt, eski bir protokol sürümü kullanıyor</string>
<string name="protocol_version_newer">Bu aygıt, daha yeni bir protokol sürümü kullanıyor</string>
<string name="general_settings">Genel Ayarlar</string>
@@ -124,12 +175,19 @@
<string name="pair_device_action">Yeni bir aygıt eşleştir</string>
<string name="unpair_device_action">Ayır %s</string>
<string name="custom_device_list">IP\'ye göre aygıtları ekle</string>
<string name="delete_custom_device">Sil %s?</string>
<string name="custom_device_deleted">Özel aygıt silindi</string>
<string name="custom_device_list_help">Cihazınız otomatik olarak algılanmazsa, İşlem Düğmesine tıklayarak IP adresini veya ana bilgisayar adını ekleyebilirsiniz</string>
<string name="custom_device_fab_hint">Aygıt ekle</string>
<string name="undo">Geri al</string>
<string name="share_notification_preference">Sesli bildirimler</string>
<string name="share_notification_preference_summary">Bir dosya alırken, ses çıkar ve titret</string>
<string name="share_destination_customize">Hedef dizini özelleştir</string>
<string name="share_destination_customize_summary_disabled">Gelen dosyalar İndirilenler\'de gözükecektir</string>
<string name="share_destination_customize_summary_enabled">Dosyalar aşağıdaki dizinden depolanacaktır</string>
<string name="share_destination_folder_preference">Hedef dizin</string>
<string name="share">Paylaş</string>
<string name="share_received_file">Paylaş \"%s\"</string>
<string name="title_activity_notification_filter">Bildirim süzgeci</string>
<string name="filter_apps_info">Bildirimler, seçili uygulamalar için eşitlenecektir.</string>
<string name="sftp_internal_storage">Harici depolama</string>
@@ -137,7 +195,28 @@
<string name="sftp_sdcard">SD kart</string>
<string name="sftp_readonly">(salt okunur)</string>
<string name="sftp_camera">Kamera resimleri</string>
<string name="add_device_dialog_title">Aygıt ekle</string>
<string name="add_device_hint">Makine adı veya IP adresi</string>
<string name="sftp_preference_detected_sdcards">Algılanan SD kartlar</string>
<string name="sftp_preference_edit_sdcard_title">SD Kartı Düzenle</string>
<string name="sftp_preference_configured_storage_locations">Yapılandırılmış depolama yerleri</string>
<string name="sftp_preference_add_storage_location_title">Depolama yeri ekle</string>
<string name="sftp_preference_edit_storage_location">Depolama yerini düzenle</string>
<string name="sftp_preference_add_camera_shortcut">Kamera klasörü kısayolu ekle</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Kamera klasörüne kısayol ekle</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Kamera klasörüne kısayol eklemeyin</string>
<string name="sftp_storage_preference_storage_location">Depolama yeri</string>
<string name="sftp_storage_preference_storage_location_already_configured">Bu konum zaten yapılandırılmış</string>
<string name="sftp_storage_preference_click_to_select">seçmek için tıkla</string>
<string name="sftp_storage_preference_display_name">Ekran adı</string>
<string name="sftp_storage_preference_display_name_already_used">Bu görünen ad zaten kullanılıyor</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Görünen ad boş olamaz</string>
<string name="sftp_action_mode_menu_delete">Sil</string>
<string name="sftp_no_sdcard_detected">SD kart algılanmadı</string>
<string name="sftp_no_storage_locations_configured">Yapılandırılmış depolama yeri yok</string>
<string name="sftp_saf_permission_explanation">Dosyalara uzaktan erişmek için depolama konumlarını yapılandırmanız gerekir</string>
<string name="add_host">Makine/IP ekle</string>
<string name="add_host_hint">Makine adı yada IP</string>
<string name="no_players_connected">Onatıcı bulunamadı</string>
<string name="mpris_player_on_device">%2$s üzerindeki %1$s</string>
<string name="send_files">Dosyaları gönder</string>
@@ -155,6 +234,7 @@
<string name="plugin_not_supported">Eklenti, aygıt tarafından desteklenmiyor</string>
<string name="findmyphone_title">Telefonumu bul</string>
<string name="findmyphone_title_tablet">Tabletimi bul</string>
<string name="findmyphone_title_tv">TV\'mi bul</string>
<string name="findmyphone_description">Aygıtı bulmak için onu çaldır</string>
<string name="findmyphone_found">Bulundu</string>
<string name="open"></string>
@@ -166,5 +246,59 @@
<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>
<string name="contacts_permission_explanation">Rehberinizi masaüstüyle paylaşmak için rehbere izin vermeniz gerekir</string>
<string name="select_ringtone">Bir zil sesi seç</string>
<string name="telephony_pref_blocked_title">Engellenen numaralar</string>
<string name="telephony_pref_blocked_dialog_desc">Bu numaralardan gelen aramaları ve SMS\'leri gösterme. Lütfen her satıra bir numara belirtin</string>
<string name="mpris_coverart_description">Güncel medyanın kapak resmi</string>
<string name="device_icon_description">Aygıt simgesi</string>
<string name="settings_icon_description">Ayarlar simgesi</string>
<string name="presenter_fullscreen">Tam ekran</string>
<string name="presenter_exit">Sunumdan çık</string>
<string name="presenter_lock_tip">Bir önceki/bir sonraki slayta geçmek için aygıtınızı kilitleyebilir ve ses seviyesi tuşlarını kullanabilirsiniz</string>
<string name="add_command">Komut ekle</string>
<string name="addcommand_explanation">Kayıtlı komut yok</string>
<string name="addcommand_explanation2">KDE Connect Sistem Ayarlarında yeni komutlar ekleyebilirsiniz</string>
<string name="add_command_description">Masaüstüne komut ekleyebilirsiniz</string>
<string name="pref_plugin_mprisreceiver">Medya Oynatıcı Kontrolü</string>
<string name="pref_plugin_mprisreceiver_desc">Telefonunuzun medya oynatıcılarını başka bir cihazdan kontrol edin</string>
<string name="dark_theme">Karanlık tema</string>
<string name="notification_channel_default">Diğer bildirimler</string>
<string name="notification_channel_persistent">Kalıcı gösterge</string>
<string name="notification_channel_media_control">Medya kontrolü</string>
<string name="notification_channel_filetransfer">Dosya aktarımı</string>
<string name="mpris_stop">Geçerli oynatıcıyı durdur</string>
<string name="copy_url_to_clipboard">URL\'yi panoya kopyala</string>
<string name="clipboard_toast">Panoya kopyalandı</string>
<string name="runcommand_notreachable">Aygıt erişilebilir değil</string>
<string name="runcommand_notpaired">Aygıt eşleştirilmedi</string>
<string name="runcommand_nosuchdevice">Böyle bir aygıt yok</string>
<string name="runcommand_noruncommandplugin">Bu cihazda Komut Çalıştır Eklentisi etkin değil</string>
<string name="pref_plugin_findremotedevice">Uzak aygıtı bul</string>
<string name="pref_plugin_findremotedevice_desc">Uzak aygıtı çaldır</string>
<string name="ring">Zil sesi</string>
<string name="pref_plugin_systemvolume">Sistem sesi</string>
<string name="pref_plugin_systemvolume_desc">Uzak cihazın sistem sesini kontrol et</string>
<string name="mute">Sessiz</string>
<string name="all">Tümü</string>
<string name="devices">Aygıtlar</string>
<string name="settings_rename">Aygıt adı</string>
<string name="settings_dark_mode">Karanlık tema</string>
<string name="settings_more_settings_title">Daha fazla ayar</string>
<string name="settings_more_settings_text">Aygıt başına ayarlar, bir aygıt içinden \'Eklenti ayarları\' altında bulunabilir.</string>
<string name="setting_persistent_notification">Kalıcı bildirim göster</string>
<string name="setting_persistent_notification_oreo">Kalıcı bildirim</string>
<string name="setting_persistent_notification_description">Bildirim ayarlarında etkinleştirmek/devre dışı bırakmak için dokun</string>
<string name="extra_options">Ek seçenekler</string>
<string name="privacy_options">Gizlilik seçenekleri</string>
<string name="set_privacy_options">Gizlilik seçeneklerinizi ayarlayın</string>
<string name="new_notification">Yeni bildirim</string>
<string name="block_contents">Bildirimlerin içeriğini engelle</string>
<string name="block_images">Bildirimlerde görüntüleri engelle</string>
<string name="notification_channel_receivenotification">Diğer aygıtlardan gelen bildirimler</string>
<string name="take_picture">Kamerayı başlat</string>
<string name="plugin_photo_desc">Fotoğraf çekmeyi ve aktarmayı kolaylaştırmak için kamera uygulamasını başlatın</string>
<string name="no_app_for_opening">Bu dosyayı açmak için uygun bir uygulama bulunamadı</string>
</resources>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_clipboard_desc">共享剪贴板内容</string>
<string name="pref_plugin_mousepad">远程输入</string>
<string name="pref_plugin_mousepad_desc">将您的手机用或平板电脑用作触摸板和键盘</string>
<string name="pref_plugin_presenter">幻灯片遥控器</string>
<string name="pref_plugin_presenter_desc">使用移动设备切换幻灯片</string>
<string name="pref_plugin_remotekeyboard">接收远程按键</string>
<string name="pref_plugin_remotekeyboard_desc">从远程设备接收按键事件</string>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--Set application-wide security config using base-config tag.-->
<base-config cleartextTrafficPermitted="true"/>
</network-security-config>

View File

@@ -100,24 +100,9 @@ public class BackgroundService extends Service {
}
}
public static void addGuiInUseCounter(Context activity) {
addGuiInUseCounter(activity, false);
}
public static void addGuiInUseCounter(final Context activity, final boolean forceNetworkRefresh) {
BackgroundService.RunCommand(activity, service -> {
boolean refreshed = service.acquireDiscoveryMode(activity);
if (!refreshed && forceNetworkRefresh) {
service.onNetworkChange();
}
});
}
public static void removeGuiInUseCounter(final Context activity) {
BackgroundService.RunCommand(activity, service -> {
//If no user interface is open, close the connections open to other devices
service.releaseDiscoveryMode(activity);
});
private boolean isInDiscoveryMode() {
//return !discoveryModeAcquisitions.isEmpty();
return true; // Keep it always on for now
}
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
@@ -209,7 +194,7 @@ public class BackgroundService extends Service {
device = new Device(BackgroundService.this, identityPacket, link);
if (device.isPaired() || device.isPairRequested() || device.isPairRequestedByPeer()
|| link.linkShouldBeKeptAlive()
|| !discoveryModeAcquisitions.isEmpty()) {
|| isInDiscoveryMode()) {
devices.put(deviceId, device);
device.addPairingCallback(devicePairingCallback);
} else {

View File

@@ -401,14 +401,11 @@ public class Device implements BaseLink.PacketReceiver {
.build();
NotificationHelper.notifyCompat(notificationManager, notificationId, noti);
BackgroundService.addGuiInUseCounter(context);
}
public void hidePairingNotification() {
final NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
BackgroundService.removeGuiInUseCounter(context);
}
//
@@ -421,6 +418,8 @@ public class Device implements BaseLink.PacketReceiver {
public void addLink(NetworkPacket identityPacket, BaseLink link) {
//FilesHelper.LogOpenFileCount();
links.add(link);
link.addPacketReceiver(this);
this.protocolVersion = identityPacket.getInt("protocolVersion");
@@ -448,9 +447,6 @@ public class Device implements BaseLink.PacketReceiver {
}
}
links.add(link);
try {
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
@@ -499,7 +495,6 @@ public class Device implements BaseLink.PacketReceiver {
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
}
link.addPacketReceiver(this);
reloadPluginsFromSettings();

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2014 Albert Vaca Cintora <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.Helpers;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
class ImagesHelper {
public static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
}

View File

@@ -1,17 +1,14 @@
package org.kde.kdeconnect.Helpers;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkInfo;
import android.util.Log;
import java.io.FileReader;
import java.io.LineNumberReader;
public class NetworkHelper {
public static boolean isOnMobileNetwork(Context context) {
return false;
/*
if (context == null) {
return false;
}
@@ -52,6 +49,8 @@ public class NetworkHelper {
Log.e("isOnMobileNetwork", "Something went wrong, but this is non-critical.", e);
}
return false;
*/
}
}

View File

@@ -20,9 +20,12 @@
package org.kde.kdeconnect.Helpers;
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.Build;
import android.os.Looper;
@@ -32,17 +35,26 @@ import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@SuppressLint("InlinedApi")
public class SMSHelper {
/**
@@ -62,7 +74,6 @@ public class SMSHelper {
*/
@RequiresApi(Build.VERSION_CODES.KITKAT)
private static Uri getSMSURIGood() {
// TODO: Why not use Telephony.MmsSms.CONTENT_URI?
return Telephony.Sms.CONTENT_URI;
}
@@ -74,12 +85,34 @@ public class SMSHelper {
}
}
private static Uri getMMSUri() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return Telephony.Mms.CONTENT_URI;
} else {
// Same as with getSMSUriBad, this is unsafe if the manufacturer did their own thing
// before this was part of the API
return Uri.parse("content://mms/");
}
}
private static Uri getMMSPartUri() {
// Android says we should have Telephony.Mms.Part.CONTENT_URI. Alas, we do not.
return Uri.parse("content://mms/part/");
}
/**
* Get the base address for all message conversations
*/
private static Uri getConversationUri() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return Telephony.MmsSms.CONTENT_CONVERSATIONS_URI;
} else if ("Samsung".equals(Build.MANUFACTURER)){
// For some presumably asinine reason, Samsung devices do not support the regular SmsMms column.
// However, according to https://stackoverflow.com/a/13640868/3723163, we can work around it this way.
// By my understanding, "simple=true" means we can't support multi-target messages.
// Go complain to Samsung about their crappy OS changes!
Log.i("SMSHelper", "Samsung compatibility mode enabled. This may cause some features to not work properly.");
return Uri.parse("content://mms-sms/conversations?simple=true");
} else {
// As with getSMSUriBad, this is potentially unsafe depending on whether a specific
// manufacturer decided to do their own thing
@@ -87,6 +120,26 @@ public class SMSHelper {
}
}
@RequiresApi(api = Build.VERSION_CODES.FROYO)
private static Uri getCompleteConversationsUri() {
// This glorious - but completely undocumented - content URI gives us all messages, both MMS and SMS,
// in all conversations
// See https://stackoverflow.com/a/36439630/3723163
return Uri.parse("content://mms-sms/complete-conversations");
}
/**
* Column used to discriminate between SMS and MMS messages
* Unfortunately, this column is not defined for Telephony.MmsSms.CONTENT_CONVERSATIONS_URI
* (aka. content://mms-sms/conversations)
* which gives us the first message in every conversation, but it *is* defined for
* content://mms-sms/conversations/<threadID> which gives us the complete conversation matching
* that threadID, so at least it's partially useful to us.
*/
private static String getTransportTypeDiscriminatorColumn() {
return Telephony.MmsSms.TYPE_DISCRIMINATOR_COLUMN;
}
/**
* Get all the messages in a requested thread
*
@@ -94,53 +147,108 @@ public class SMSHelper {
* @param threadID Thread to look up
* @return List of all messages in the thread
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static List<Message> getMessagesInThread(Context context, ThreadID threadID) {
final String selection = ThreadID.lookupColumn + " == ?";
final String[] selectionArgs = new String[] { threadID.toString() };
public static @NonNull List<Message> getMessagesInThread(
@NonNull Context context,
@NonNull ThreadID threadID
) {
Uri uri = Uri.withAppendedPath(getConversationUri(), threadID.toString());
return getMessagesWithFilter(context, selection, selectionArgs);
return getMessages(uri, context, null, null, null, null);
}
/**
* Get all messages which have a timestamp after the requested timestamp
* 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
*
* @param timestamp epoch in millis matching the timestamp to return
* @return null if no matching message is found, otherwise return a Message
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static List<Message> getMessagesSinceTimestamp(Context context, long timestamp) {
final String selection = Message.DATE + " > ?";
final String[] selectionArgs = new String[] {Long.toString(timestamp)};
public static @Nullable Message getNewestMessage(
@NonNull Context context
) {
List<Message> messages = getMessagesWithFilter(context, null, null, 1L);
return getMessagesWithFilter(context, selection, selectionArgs);
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);
}
}
/**
* Gets Messages for caller functions, such as: getMessagesWithFilter() and getConversations()
* Gets messages which match the selection
*
* @param Uri Uri indicating the messages database to read
* @param uri Uri indicating the messages database to read
* @param context android.content.Context running the request.
* @param selection Parameterizable filter to use with the ContentResolver query. May be null.
* @param selectionArgs Parameters for selection. May be null.
* @return Returns HashMap<ThreadID, List<Message>>, which is transformed in caller functions into other classes.
* @param sortOrder Sort ordering passed to Android's content resolver. May be null for unspecified
* @param numberToGet Number of things to get from the result. Pass null to get all
* @return Returns List<Message> of all messages in the return set, either in the order of sortOrder or in an unspecified order
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private static HashMap<ThreadID, List<Message>> getMessages(Uri Uri,
Context context,
String selection,
String[] selectionArgs) {
HashMap<ThreadID, List<Message>> toReturn = new HashMap<>();
try (Cursor myCursor = context.getContentResolver().query(
Uri,
Message.smsColumns,
private static @NonNull List<Message> getMessages(
@NonNull Uri uri,
@NonNull Context context,
@Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable String sortOrder,
@Nullable Long numberToGet
) {
List<Message> toReturn = new ArrayList<>();
Set<String> allColumns = new HashSet<>();
allColumns.addAll(Arrays.asList(Message.smsColumns));
allColumns.addAll(Arrays.asList(Message.mmsColumns));
if (uri != getConversationUri()) {
// See https://issuetracker.google.com/issues/134592631
allColumns.add(getTransportTypeDiscriminatorColumn());
}
String[] fetchColumns = {};
fetchColumns = allColumns.toArray(fetchColumns);
try (Cursor myCursor = context.getContentResolver().query(
uri,
fetchColumns,
selection,
selectionArgs,
null)
sortOrder)
) {
if (myCursor != null && myCursor.moveToFirst()) {
int threadColumn = myCursor.getColumnIndexOrThrow(ThreadID.lookupColumn);
do {
int transportTypeColumn = myCursor.getColumnIndex(getTransportTypeDiscriminatorColumn());
TransportType transportType;
if (transportTypeColumn < 0) {
// The column didn't actually exist. See https://issuetracker.google.com/issues/134592631
// Try to determine using other information
int messageBoxColumn = myCursor.getColumnIndex(Telephony.Mms.MESSAGE_BOX);
// MessageBoxColumn is defined for MMS only
boolean messageBoxExists = !myCursor.isNull(messageBoxColumn);
if (messageBoxExists) {
transportType = TransportType.MMS;
} else {
// There is room here for me to have made an assumption and we'll guess wrong
// The penalty is the user will potentially get some garbled data, so that's not too bad.
transportType = TransportType.SMS;
}
} else {
String transportTypeString = myCursor.getString(transportTypeColumn);
if ("mms".equals(transportTypeString)) {
transportType = TransportType.MMS;
} else if ("sms".equals(transportTypeString)) {
transportType = TransportType.SMS;
} else {
Log.w("SMSHelper", "Skipping message with unknown TransportType: " + transportTypeString);
continue;
}
}
HashMap<String, String> messageInfo = new HashMap<>();
for (int columnIdx = 0; columnIdx < myCursor.getColumnCount(); columnIdx++) {
String colName = myCursor.getColumnName(columnIdx);
@@ -148,38 +256,41 @@ public class SMSHelper {
messageInfo.put(colName, body);
}
Message message = new Message(messageInfo);
ThreadID threadID = new ThreadID(message.threadID);
if (!toReturn.containsKey(threadID)) {
toReturn.put(threadID, new ArrayList<>());
if (transportType == TransportType.SMS) {
parseSMS(context, messageInfo);
} else if (transportType == TransportType.MMS) {
parseMMS(context, messageInfo);
}
toReturn.get(threadID).add(message);
} while (myCursor.moveToNext());
} else {
// No conversations or SMSes available?
Message message = new Message(messageInfo);
toReturn.add(message);
} while ((numberToGet == null || toReturn.size() != numberToGet) && myCursor.moveToNext());
}
} catch (SQLiteException e) {
throw new MessageAccessException(fetchColumns, uri, e);
}
return toReturn;
}
/**
* Get all messages matching the passed filter. See documentation for Android's ContentResolver
*
* @param context android.content.Context running the request
* @param selection Parameterizable filter to use with the ContentResolver query. May be null.
* @param selectionArgs Parameters for selection. May be null.
* @return List of messages matching the filter
* @param numberToGet Number of things to return. Pass null to get all
* @return List of messages matching the filter, from newest to oldest
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private static List<Message> getMessagesWithFilter(Context context, String selection, String[] selectionArgs) {
HashMap<ThreadID, List<Message>> result = getMessages(SMSHelper.getSMSUri(), context, selection, selectionArgs);
List<Message> toReturn = new ArrayList<>();
private static List<Message> getMessagesWithFilter(
@NonNull Context context,
@Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable Long numberToGet
) {
String sortOrder = Message.DATE + " DESC";
for(Map.Entry<ThreadID, List<Message>> entry : result.entrySet()) {
toReturn.addAll(entry.getValue());
}
return toReturn;
return getMessages(getCompleteConversationsUri(), context, selection, selectionArgs, sortOrder, numberToGet);
}
/**
@@ -189,28 +300,230 @@ public class SMSHelper {
* @param context android.content.Context running the request
* @return Mapping of thread_id to the first message in each thread
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static Map<ThreadID, Message> getConversations(Context context) {
HashMap<ThreadID, List<Message>> result = getMessages(SMSHelper.getConversationUri(), context, null, null);
HashMap<ThreadID, Message> toReturn = new HashMap<>();
public static Map<ThreadID, Message> getConversations(
@NonNull Context context
) {
Uri uri = SMSHelper.getConversationUri();
for(Map.Entry<ThreadID, List<Message>> entry : result.entrySet()) {
ThreadID returnThreadID = entry.getKey();
List<Message> messages = entry.getValue();
List<Message> unthreadedMessages = getMessages(uri, context, null, null, null, null);
toReturn.put(returnThreadID, messages.get(0));
Map<ThreadID, Message> toReturn = new HashMap<>();
for (Message message : unthreadedMessages) {
ThreadID tID = message.threadID;
if (toReturn.containsKey(tID)) {
Log.w("SMSHelper", "getConversations got two messages for the same ThreadID: " + tID);
}
toReturn.put(tID, message);
}
return toReturn;
}
private static void addEventFlag(
@NonNull Map<String, String> messageInfo,
@NonNull int eventFlag
) {
int oldEvent = 0; //Default value
String oldEventString = messageInfo.get(Message.EVENT);
if (oldEventString != null) {
oldEvent = Integer.parseInt(oldEventString);
}
messageInfo.put(Message.EVENT, Integer.toString(oldEvent | eventFlag));
}
/**
* Do any parsing of an SMS message which still needs to be done
*/
private static void parseSMS(
@NonNull Context context,
@NonNull Map<String, String> messageInfo
) {
addEventFlag(messageInfo, Message.EVENT_TEXT_MESSAGE);
}
/**
* Parse all parts of the MMS message into the messageInfo format
* Original implementation from https://stackoverflow.com/a/6446831/3723163
*/
private static void parseMMS(
@NonNull Context context,
@NonNull Map<String, String> messageInfo
) {
addEventFlag(messageInfo, Message.EVENT_UNKNOWN);
String[] columns = {
Telephony.Mms.Part._ID, // The content ID of this part
Telephony.Mms.Part._DATA, // The location in the filesystem of the data
Telephony.Mms.Part.CONTENT_TYPE, // The mime type of the data
Telephony.Mms.Part.TEXT, // The plain text body of this MMS
Telephony.Mms.Part.CHARSET, // Charset of the plain text body
};
String mmsID = messageInfo.get(Message.U_ID);
String selection = Telephony.Mms.Part.MSG_ID + " = ?";
String[] selectionArgs = {mmsID};
// Get text body and attachments of the message
try (Cursor cursor = context.getContentResolver().query(
getMMSPartUri(),
columns,
selection,
selectionArgs,
null
)) {
if (cursor != null && cursor.moveToFirst()) {
int partIDColumn = cursor.getColumnIndexOrThrow(Telephony.Mms.Part._ID);
int contentTypeColumn = cursor.getColumnIndexOrThrow(Telephony.Mms.Part.CONTENT_TYPE);
int dataColumn = cursor.getColumnIndexOrThrow(Telephony.Mms.Part._DATA);
int textColumn = cursor.getColumnIndexOrThrow(Telephony.Mms.Part.TEXT);
// TODO: Parse charset (As usual, it is skimpily documented) (Possibly refer to MMS spec)
do {
Long partID = cursor.getLong(partIDColumn);
String contentType = cursor.getString(contentTypeColumn);
String data = cursor.getString(dataColumn);
if ("text/plain".equals(contentType)) {
String body;
if (data != null) {
// data != null means the data is on disk. Go get it.
body = getMmsText(context, partID);
} else {
body = cursor.getString(textColumn);
}
messageInfo.put(Message.BODY, body);
addEventFlag(messageInfo, Message.EVENT_TEXT_MESSAGE);
} //TODO: Parse more content types (photos and other attachments) here
} while (cursor.moveToNext());
}
}
// Determine whether the message was in- our out- bound
long messageBox = Long.parseLong(messageInfo.get(Telephony.Mms.MESSAGE_BOX));
if (messageBox == Telephony.Mms.MESSAGE_BOX_INBOX) {
messageInfo.put(Message.TYPE, Integer.toString(Telephony.Sms.MESSAGE_TYPE_INBOX));
} else if (messageBox == Telephony.Mms.MESSAGE_BOX_SENT) {
messageInfo.put(Message.TYPE, Integer.toString(Telephony.Sms.MESSAGE_TYPE_SENT));
} else {
// As an undocumented feature, it looks like the values of Mms.MESSAGE_BOX_*
// are the same as Sms.MESSAGE_TYPE_* of the same type. So by default let's just use
// the value we've got.
// This includes things like drafts, which are a far-distant plan to support
messageInfo.put(Message.TYPE, messageInfo.get(Telephony.Mms.MESSAGE_BOX));
}
// Get address(es) of the message
List<String> addresses = getMmsAddresses(context, Long.parseLong(mmsID));
// It looks like addresses[0] is always the sender of the message and
// following addresses are recipient(s)
// This usually means the addresses list is at least 2 long, but there are cases (special
// telco service messages) where it is not (only 1 long in that case, just the "sender")
// The address field which will get written to the message.
// Remember that this is always the address of the other side of the conversation
String address = "";
if (addresses.size() > 2) {
// TODO: Collect addresses for multi-target MMS
// Probably we will need to figure out the user's address at this point and strip it out of the list
addEventFlag(messageInfo, Message.EVENT_MULTI_TARGET);
} else {
if (messageBox == Telephony.Mms.MESSAGE_BOX_INBOX) {
address = addresses.get(0);
} else if (messageBox == Telephony.Mms.MESSAGE_BOX_SENT) {
address = addresses.get(1);
} else {
Log.w("SMSHelper", "Unknown message type " + messageBox + " while parsing addresses.");
// Not much smart to do here. Just leave as default.
}
}
messageInfo.put(Message.ADDRESS, address);
// Canonicalize the date field
// SMS uses epoch milliseconds, MMS uses epoch seconds. Standardize on milliseconds.
long rawDate = Long.parseLong(messageInfo.get(Message.DATE));
messageInfo.put(Message.DATE, Long.toString(rawDate * 1000));
}
/**
* Get the address(es) of an MMS message
* Original implementation from https://stackoverflow.com/a/6446831/3723163
*/
private static @NonNull List<String> getMmsAddresses(
@NonNull Context context,
@NonNull Long messageID
) {
Uri uri = ContentUris.appendId(getMMSUri().buildUpon(), messageID).appendPath("addr").build();
String[] columns = {
Telephony.Mms.Addr.MSG_ID, // ID of the message for which we are fetching addresses
Telephony.Mms.Addr.ADDRESS, // Address of this part
Telephony.Mms.Addr.CHARSET, // Charset of the returned address (where relevant) //TODO: Handle
};
String selection = Telephony.Mms.Addr.MSG_ID + " = ?";
String[] selectionArgs = {messageID.toString()};
List<String> addresses = new ArrayList<>();
try (Cursor addrCursor = context.getContentResolver().query(
uri,
columns,
selection,
selectionArgs,
null
)) {
if (addrCursor != null && addrCursor.moveToFirst()) {
int addressIndex = addrCursor.getColumnIndex(Telephony.Mms.Addr.ADDRESS);
do {
String address = addrCursor.getString(addressIndex);
addresses.add(address);
} while (addrCursor.moveToNext());
}
}
return addresses;
}
/**
* Get a text part of an MMS message
* Original implementation from https://stackoverflow.com/a/6446831/3723163
*/
private static String getMmsText(
@NonNull Context context,
@NonNull Long id
) {
Uri partURI = ContentUris.withAppendedId(getMMSPartUri(), id);
StringBuilder body = new StringBuilder();
try (InputStream is = context.getContentResolver().openInputStream(partURI)) {
if (is != null) {
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader reader = new BufferedReader(isr);
String temp = reader.readLine();
while (temp != null) {
body.append(temp);
temp = reader.readLine();
}
}
} catch (IOException e) {
throw new SMSHelper.MessageAccessException(partURI, e);
}
return body.toString();
}
/**
* Register a ContentObserver for the Messages database
*
* @param observer ContentObserver to alert on Message changes
*/
public static void registerObserver(ContentObserver observer, Context context) {
public static void registerObserver(
@NonNull ContentObserver observer,
@NonNull Context context
) {
context.getContentResolver().registerContentObserver(
SMSHelper.getSMSUri(),
SMSHelper.getConversationUri(),
true,
observer
);
@@ -219,7 +532,6 @@ public class SMSHelper {
/**
* Represent an ID used to uniquely identify a message thread
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static class ThreadID {
final Long threadID;
static final String lookupColumn = Telephony.Sms.THREAD_ID;
@@ -244,10 +556,32 @@ public class SMSHelper {
}
}
/**
* Indicate that some error has occurred while reading a message.
* More useful for logging than catching and handling
*/
public static class MessageAccessException extends RuntimeException {
MessageAccessException(Uri uri, Throwable cause) {
super("Error getting messages from " + uri.toString(), cause);
}
MessageAccessException(String[] availableColumns, Uri uri, Throwable cause) {
super("Error getting messages from " + uri.toString() + " . Available columns were: " + Arrays.toString(availableColumns), cause);
}
}
/**
* Represent all known transport types
*/
public enum TransportType {
SMS,
MMS,
// Maybe in the future there will be more TransportType, but for now these are all I know about
}
/**
* Represent a message and all of its interesting data columns
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static class Message {
final String address;
@@ -255,8 +589,10 @@ public class SMSHelper {
public final long date;
final int type;
final int read;
final long threadID; // ThreadID is *int* for SMS messages but *long* for MMS
final int uID;
final ThreadID threadID; // ThreadID is *int* for SMS messages but *long* for MMS
final long uID;
final int event;
final int subscriptionID;
/**
* Named constants which are used to construct a Message
@@ -268,15 +604,19 @@ public class SMSHelper {
static final String TYPE = Telephony.Sms.TYPE; // Compare with Telephony.TextBasedSmsColumns.MESSAGE_TYPE_*
static final String READ = Telephony.Sms.READ; // Whether we have received a read report for this message (int)
static final String THREAD_ID = ThreadID.lookupColumn; // Magic number which binds (message) threads
static final String U_ID = Telephony.Sms._ID; // Something which uniquely identifies this message
static final String U_ID = Telephony.Sms._ID; // Something which uniquely identifies this message
static final String EVENT = "event";
static final String SUBSCRIPTION_ID = Telephony.Sms.SUBSCRIPTION_ID; // An ID which appears to identify a SIM card
/**
* Event flags
* A message should have a bitwise-or of event flags before delivering the packet
* Any events not supported by the receiving device should be ignored
*/
public static final int TEXT_MESSAGE = 0x1; // This message has a "body" field which contains
// pure, human-readable text
public static final int EVENT_UNKNOWN = 0x0; // The message was of some type we did not understand
public static final int EVENT_TEXT_MESSAGE = 0x1; // This message has a "body" field which contains
// pure, human-readable text
public static final int EVENT_MULTI_TARGET = 0x2; // Indicates that this message has multiple recipients
/**
* Define the columns which are to be extracted from the Android SMS database
@@ -289,6 +629,16 @@ public class SMSHelper {
Message.READ,
Message.THREAD_ID,
Message.U_ID,
Message.SUBSCRIPTION_ID,
};
static final String[] mmsColumns = new String[]{
Message.U_ID,
Message.THREAD_ID,
Message.DATE,
Message.READ,
Telephony.Mms.TEXT_ONLY,
Telephony.Mms.MESSAGE_BOX, // Compare with Telephony.BaseMmsColumns.MESSAGE_BOX_*
};
Message(final HashMap<String, String> messageInfo) {
@@ -298,15 +648,17 @@ public class SMSHelper {
if (messageInfo.get(Message.TYPE) == null)
{
// To be honest, I have no idea why this happens. The docs say the TYPE field is mandatory.
// Just stick some junk in here and hope we can figure it out later.
// Quick investigation suggests that these are multi-target MMSes
Log.w("SMSHelper", "Encountered undefined message type");
type = -1;
// Proceed anyway, maybe this is not an important problem.
} else {
type = Integer.parseInt(messageInfo.get(Message.TYPE));
}
read = Integer.parseInt(messageInfo.get(Message.READ));
threadID = Long.parseLong(messageInfo.get(Message.THREAD_ID));
threadID = new ThreadID(Long.parseLong(messageInfo.get(Message.THREAD_ID)));
uID = Integer.parseInt(messageInfo.get(Message.U_ID));
subscriptionID = Integer.parseInt(messageInfo.get(Message.SUBSCRIPTION_ID));
event = Integer.parseInt(messageInfo.get(Message.EVENT));
}
public JSONObject toJSONObject() throws JSONException {
@@ -319,6 +671,8 @@ public class SMSHelper {
json.put(Message.READ, read);
json.put(Message.THREAD_ID, threadID);
json.put(Message.U_ID, uID);
json.put(Message.SUBSCRIPTION_ID, subscriptionID);
json.put(Message.EVENT, event);
return json;
}

View File

@@ -29,8 +29,11 @@ import android.util.Log;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.RandomHelper;
import org.spongycastle.asn1.x500.RDN;
import org.spongycastle.asn1.x500.X500Name;
import org.spongycastle.asn1.x500.X500NameBuilder;
import org.spongycastle.asn1.x500.style.BCStyle;
import org.spongycastle.asn1.x500.style.IETFUtils;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.X509v3CertificateBuilder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
@@ -62,6 +65,7 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
public class SslHelper {
@@ -80,12 +84,39 @@ public class SslHelper {
Log.e("SslHelper", "Error getting keys, can't create certificate");
return;
}
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
if (!settings.contains("certificate")) {
try {
String deviceId = DeviceHelper.getDeviceId(context);
boolean needsToGenerateCertificate = false;
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
if (settings.contains("certificate")) {
try {
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] certificateBytes = Base64.decode(globalSettings.getString("certificate", ""), 0);
X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes);
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
String certDeviceId = getCommonNameFromCertificate(cert);
if (!certDeviceId.equals(deviceId)) {
Log.e("KDE/SslHelper", "The certificate stored is from a different device id! (found: " + certDeviceId + " expected:" + deviceId + ")");
needsToGenerateCertificate = true;
} else {
certificate = cert;
}
} catch (Exception e) {
Log.e("KDE/SslHelper", "Exception reading own certificate", e);
needsToGenerateCertificate = true;
}
} else {
needsToGenerateCertificate = true;
}
if (needsToGenerateCertificate) {
Log.i("KDE/SslHelper", "Generating a certificate");
try {
X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
nameBuilder.addRDN(BCStyle.CN, DeviceHelper.getDeviceId(context));
nameBuilder.addRDN(BCStyle.CN, deviceId);
nameBuilder.addRDN(BCStyle.OU, "KDE Connect");
nameBuilder.addRDN(BCStyle.O, "KDE");
Calendar calendar = Calendar.getInstance();
@@ -107,20 +138,9 @@ public class SslHelper {
SharedPreferences.Editor edit = settings.edit();
edit.putString("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
edit.apply();
} catch (Exception e) {
Log.e("KDE/initialiseCert", "Exception", e);
}
} else {
try {
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] certificateBytes = Base64.decode(globalSettings.getString("certificate", ""), 0);
X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes);
certificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
} catch (Exception e) {
Log.e("KDE/SslHelper", "Exception reading own certificate", e);
}
}
}
@@ -247,4 +267,11 @@ public class SslHelper {
return new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
}
private static String getCommonNameFromCertificate(X509Certificate cert) {
X500Principal principal = cert.getSubjectX500Principal();
X500Name x500name = new X500Name(principal.getName());
RDN rdn = x500name.getRDNs(BCStyle.CN)[0];
return IETFUtils.valueToString(rdn.getFirst().getValue());
}
}

View File

@@ -173,6 +173,13 @@ public class MousePadActivity extends AppCompatActivity implements GestureDetect
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_mousepad, menu);
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> {
if (!plugin.isKeyboardEnabled()) {
menu.removeItem(R.id.menu_show_keyboard);
}
});
return true;
}
@@ -366,17 +373,5 @@ public class MousePadActivity extends AppCompatActivity implements GestureDetect
imm.toggleSoftInputFromWindow(keyListenerView.getWindowToken(), 0, 0);
}
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
}
}

View File

@@ -15,8 +15,8 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.Plugins.MousePadPlugin;
@@ -36,6 +36,17 @@ public class MousePadPlugin extends Plugin {
//public final static String PACKET_TYPE_MOUSEPAD = "kdeconnect.mousepad";
public final static String PACKET_TYPE_MOUSEPAD_REQUEST = "kdeconnect.mousepad.request";
private final static String PACKET_TYPE_MOUSEPAD_KEYBOARDSTATE = "kdeconnect.mousepad.keyboardstate";
private boolean keyboardEnabled = true;
@Override
public boolean onPacketReceived(NetworkPacket np) {
keyboardEnabled = np.getBoolean("state", true);
return true;
}
@Override
public String getDisplayName() {
@@ -71,7 +82,7 @@ public class MousePadPlugin extends Plugin {
@Override
public String[] getSupportedPacketTypes() {
return new String[0];
return new String[]{PACKET_TYPE_MOUSEPAD_KEYBOARDSTATE};
}
@Override
@@ -135,4 +146,8 @@ public class MousePadPlugin extends Plugin {
device.sendPacket(np);
}
boolean isKeyboardEnabled() {
return keyboardEnabled;
}
}

View File

@@ -445,16 +445,4 @@ public class MprisActivity extends AppCompatActivity {
nowPlayingText.setSelected(true);
}
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
}
}

View File

@@ -191,35 +191,44 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
}
private Pair<Device, MprisPlugin.MprisPlayer> findPlayer(BackgroundService service) {
//First try the previously displayed player
//First try the previously displayed player (if still playing) or the previous displayed device (otherwise)
if (notificationDevice != null && mprisDevices.contains(notificationDevice)) {
Device device = service.getDevice(notificationDevice);
if (device != null && device.isPluginEnabled("MprisPlugin")) {
if (shouldShowPlayer(notificationPlayer)){
return new Pair<>(device, notificationPlayer);
}
// Try a different player for the same device
MprisPlugin.MprisPlayer player = getPlayerFromDevice(device);
if (player != null) {
return new Pair<>(device, player);
}
MprisPlugin.MprisPlayer player;
if (notificationPlayer != null && notificationPlayer.isPlaying()) {
player = getPlayerFromDevice(device, notificationPlayer);
} else {
player = getPlayerFromDevice(device, null);
}
if (player != null) {
return new Pair<>(device, player);
}
}
// Try a different player from another device
for (Device otherDevice : service.getDevices().values()) {
MprisPlugin.MprisPlayer player = getPlayerFromDevice(otherDevice);
MprisPlugin.MprisPlayer player = getPlayerFromDevice(otherDevice, null);
if (player != null) {
return new Pair<>(otherDevice, player);
}
}
//So no player is playing. Try the previously displayed player again
// This will succeed if it's paused:
// that allows pausing and subsequently resuming via the notification
if (notificationDevice != null && mprisDevices.contains(notificationDevice)) {
Device device = service.getDevice(notificationDevice);
MprisPlugin.MprisPlayer player = getPlayerFromDevice(device, notificationPlayer);
if (player != null) {
return new Pair<>(device, player);
}
}
return new Pair<>(null, null);
}
private MprisPlugin.MprisPlayer getPlayerFromDevice(Device device) {
private MprisPlugin.MprisPlayer getPlayerFromDevice(Device device, MprisPlugin.MprisPlayer preferredPlayer) {
if (!mprisDevices.contains(device.getDeviceId()))
return null;
@@ -229,6 +238,12 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
return null;
}
//First try the preferred player, if supplied
if (plugin.hasPlayer(preferredPlayer) && shouldShowPlayer(preferredPlayer)) {
return preferredPlayer;
}
//Otherwise, accept any playing player
MprisPlugin.MprisPlayer player = plugin.getPlayingPlayer();
if (shouldShowPlayer(player)) {
return player;
@@ -383,7 +398,7 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
iCloseNotification.setAction(MprisMediaNotificationReceiver.ACTION_CLOSE_NOTIFICATION);
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayer());
PendingIntent piCloseNotification = PendingIntent.getActivity(service, 0, iCloseNotification, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent piCloseNotification = PendingIntent.getBroadcast(service, 0, iCloseNotification, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setDeleteIntent(piCloseNotification);
}
@@ -448,6 +463,9 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
//Clear the current player and media session
notificationPlayer = null;
if (mediaSession != null) {
mediaSession.setPlaybackState(new PlaybackStateCompat.Builder().build());
mediaSession.setMetadata(new MediaMetadataCompat.Builder().build());
mediaSession.setActive(false);
mediaSession.release();
mediaSession = null;
}

View File

@@ -422,6 +422,10 @@ public class MprisPlugin extends Plugin {
return null;
}
boolean hasPlayer(MprisPlayer player) {
return players.containsValue(player);
}
private void requestPlayerList() {
NetworkPacket np = new NetworkPacket(PACKET_TYPE_MPRIS_REQUEST);
np.set("requestPlayerList", true);

View File

@@ -34,6 +34,7 @@ import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.AlertDialogFragment;
import org.kde.kdeconnect.UserInterface.StartActivityAlertDialogFragment;
import org.kde.kdeconnect_tp.R;
@@ -44,17 +45,16 @@ import java.util.List;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
//FIXME: Breaks on Android 4 because it extends OnActiveSessionsChangedListener
//@PluginFactory.LoadablePlugin
public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.OnActiveSessionsChangedListener {
@PluginFactory.LoadablePlugin
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
public class MprisReceiverPlugin extends Plugin {
private final static String PACKET_TYPE_MPRIS = "kdeconnect.mpris";
private final static String PACKET_TYPE_MPRIS_REQUEST = "kdeconnect.mpris.request";
private static final String TAG = "MprisReceiver";
private HashMap<String, MprisReceiverPlayer> players;
private MediaSessionChangeListener mediaSessionChangeListener;
@Override
public boolean onCreate() {
@@ -68,7 +68,9 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
if (null == manager)
return false;
manager.addOnActiveSessionsChangedListener(MprisReceiverPlugin.this, new ComponentName(context, NotificationReceiver.class), new Handler(Looper.getMainLooper()));
assert(mediaSessionChangeListener == null);
mediaSessionChangeListener = new MediaSessionChangeListener();
manager.addOnActiveSessionsChangedListener(mediaSessionChangeListener, new ComponentName(context, NotificationReceiver.class), new Handler(Looper.getMainLooper()));
createPlayers(manager.getActiveSessions(new ComponentName(context, NotificationReceiver.class)));
sendPlayerList();
@@ -83,8 +85,9 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
public void onDestroy() {
super.onDestroy();
MediaSessionManager manager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
if (manager != null) {
manager.removeOnActiveSessionsChangedListener(MprisReceiverPlugin.this);
if (manager != null && mediaSessionChangeListener != null) {
manager.removeOnActiveSessionsChangedListener(mediaSessionChangeListener);
mediaSessionChangeListener = null;
}
}
@@ -169,18 +172,20 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
return new String[]{PACKET_TYPE_MPRIS};
}
@Override
public void onActiveSessionsChanged(@Nullable List<MediaController> controllers) {
private final class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener {
@Override
public void onActiveSessionsChanged(@Nullable List<MediaController> controllers) {
if (null == controllers) {
return;
}
players.clear();
createPlayers(controllers);
sendPlayerList();
if (null == controllers) {
return;
}
players.clear();
createPlayers(controllers);
sendPlayerList();
}
private void createPlayer(MediaController controller) {

View File

@@ -218,18 +218,6 @@ public class NotificationFilterActivity extends AppCompatActivity {
}
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
}
private Drawable resizeIcon(Drawable icon, int maxSize) {
Resources res = getResources();

View File

@@ -35,10 +35,18 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.text.SpannableString;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import org.json.JSONArray;
import org.kde.kdeconnect.Helpers.AppsHelper;
@@ -59,11 +67,9 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
@PluginFactory.LoadablePlugin
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
@@ -73,7 +79,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
private final static String PACKET_TYPE_NOTIFICATION_REPLY = "kdeconnect.notification.reply";
private final static String PACKET_TYPE_NOTIFICATION_ACTION = "kdeconnect.notification.action";
private final static String TAG = "NotificationsPlugin";
private final static String TAG = "KDE/NotificationsPlugin";
private AppDatabase appDatabase;
@@ -157,7 +163,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
if (statusBarNotification == null) {
Log.w("onNotificationRemoved", "notification is null");
Log.w(TAG, "onNotificationRemoved: notification is null");
return;
}
String id = getNotificationKeyCompat(statusBarNotification);
@@ -191,7 +197,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
if (!appDatabase.isEnabled(statusBarNotification.getPackageName())) {
return;
// we dont want notification from this app
// we don't want notification from this app
}
String key = getNotificationKeyCompat(statusBarNotification);
@@ -221,85 +227,165 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
NetworkPacket np = new NetworkPacket(PACKET_TYPE_NOTIFICATION);
boolean isUpdate = currentNotifications.contains(key);
//If it's an update, the other end should have the icon already: no need to extract it and create the payload again
if (!isUpdate) {
//If it's an update, the other end should have the icon already: no need to extract it and create the payload again
try {
Bitmap appIcon;
Context foreignContext = context.createPackageContext(statusBarNotification.getPackageName(), 0);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
appIcon = iconToBitmap(foreignContext, notification.getLargeIcon());
} else {
appIcon = notification.largeIcon;
}
if (appIcon == null) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
appIcon = iconToBitmap(foreignContext, notification.getSmallIcon());
} else {
PackageManager pm = context.getPackageManager();
Resources foreignResources = pm.getResourcesForApplication(statusBarNotification.getPackageName());
Drawable foreignIcon = foreignResources.getDrawable(notification.icon);
appIcon = drawableToBitmap(foreignIcon);
}
}
if (appIcon != null && !appDatabase.getPrivacy(packageName, AppDatabase.PrivacyOptions.BLOCK_IMAGES)) {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
byte[] bitmapData = outStream.toByteArray();
Log.e("PAYLOAD", "PAYLOAD: " + getChecksum(bitmapData));
np.setPayload(new NetworkPacket.Payload(bitmapData));
np.set("payloadHash", getChecksum(bitmapData));
}
} catch (Exception e) {
Log.e("NotificationsPlugin", "Error retrieving icon", e);
}
} else {
currentNotifications.add(key);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (notification.actions != null && notification.actions.length > 0) {
actions.put(key, new LinkedList<>());
JSONArray jsonArray = new JSONArray();
for (Notification.Action action : notification.actions) {
Bitmap appIcon = extractIcon(statusBarNotification, notification);
if (null == action.title)
continue;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH)
if (action.getRemoteInputs() != null && action.getRemoteInputs().length > 0)
continue;
jsonArray.put(action.title.toString());
actions.get(key).add(action);
}
np.set("actions", jsonArray);
if (appIcon != null && !appDatabase.getPrivacy(packageName, AppDatabase.PrivacyOptions.BLOCK_IMAGES)) {
attachIcon(np, appIcon);
}
}
np.set("actions", extractActions(notification, key));
np.set("id", key);
np.set("isClearable", statusBarNotification.isClearable());
np.set("appName", appName == null ? packageName : appName);
np.set("time", Long.toString(statusBarNotification.getPostTime()));
if (!appDatabase.getPrivacy(packageName, AppDatabase.PrivacyOptions.BLOCK_CONTENTS)) {
RepliableNotification rn = extractRepliableNotification(statusBarNotification);
if (rn.pendingIntent != null) {
if (rn != null) {
np.set("requestReplyId", rn.id);
pendingIntents.put(rn.id, rn);
}
np.set("ticker", getTickerText(notification));
np.set("title", getNotificationTitle(notification));
np.set("text", getNotificationText(notification));
Pair<String, String> conversation = extractConversation(notification);
if (conversation.first != null) {
np.set("title", conversation.first);
} else {
np.set("title", extractStringFromExtra(getExtras(notification), NotificationCompat.EXTRA_TITLE));
}
np.set("text", extractText(notification, conversation));
}
device.sendPacket(np);
}
private String extractText(Notification notification, Pair<String, String> conversation) {
if (conversation.second != null) {
return conversation.second;
}
Bundle extras = getExtras(notification);
if (extras.containsKey(NotificationCompat.EXTRA_BIG_TEXT)) {
return extractStringFromExtra(extras, NotificationCompat.EXTRA_BIG_TEXT);
}
return extractStringFromExtra(extras, NotificationCompat.EXTRA_TEXT);
}
@NonNull
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private static Bundle getExtras(Notification notification) {
// NotificationCompat.getExtras() is expected to return non-null values for JELLY_BEAN+
return Objects.requireNonNull(NotificationCompat.getExtras(notification));
}
private void attachIcon(NetworkPacket np, Bitmap appIcon) {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
byte[] bitmapData = outStream.toByteArray();
np.setPayload(new NetworkPacket.Payload(bitmapData));
np.set("payloadHash", getChecksum(bitmapData));
}
@Nullable
private Bitmap extractIcon(StatusBarNotification statusBarNotification, Notification notification) {
try {
Context foreignContext = context.createPackageContext(statusBarNotification.getPackageName(), 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && notification.getLargeIcon() != null) {
return iconToBitmap(foreignContext, notification.getLargeIcon());
} else if (notification.largeIcon != null) {
return notification.largeIcon;
}
PackageManager pm = context.getPackageManager();
Resources foreignResources = pm.getResourcesForApplication(statusBarNotification.getPackageName());
Drawable foreignIcon = foreignResources.getDrawable(notification.icon); //Might throw Resources.NotFoundException
return drawableToBitmap(foreignIcon);
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
Log.e(TAG, "Package not found", e);
}
return null;
}
@Nullable
private JSONArray extractActions(Notification notification, String key) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return null;
}
if (notification.actions == null || notification.actions.length == 0) {
return null;
}
actions.put(key, new LinkedList<>());
JSONArray jsonArray = new JSONArray();
for (Notification.Action action : notification.actions) {
if (null == action.title)
continue;
// Check whether it is a reply action. We have special treatment for them
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH)
if (action.getRemoteInputs() != null && action.getRemoteInputs().length > 0)
continue;
jsonArray.put(action.title.toString());
actions.get(key).add(action);
}
return jsonArray;
}
private Pair<String, String> extractConversation(Notification notification) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
return new Pair<>(null, null);
if (!notification.extras.containsKey(Notification.EXTRA_MESSAGES))
return new Pair<>(null, null);
Parcelable[] ms = notification.extras.getParcelableArray(Notification.EXTRA_MESSAGES);
if (ms == null)
return new Pair<>(null, null);
String title = notification.extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
boolean isGroupConversation = notification.extras.getBoolean(NotificationCompat.EXTRA_IS_GROUP_CONVERSATION);
StringBuilder messagesBuilder = new StringBuilder();
for (Parcelable p : ms) {
Bundle m = (Bundle) p;
if (isGroupConversation) {
messagesBuilder.append(m.get("sender"));
messagesBuilder.append(": ");
}
messagesBuilder.append(extractStringFromExtra(m, "text"));
messagesBuilder.append("\n");
}
return new Pair<>(title, messagesBuilder.toString());
}
private Bitmap drawableToBitmap(Drawable drawable) {
if (drawable == null) return null;
@@ -328,13 +414,13 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH)
private void replyToNotification(String id, String message) {
if (pendingIntents.isEmpty() || !pendingIntents.containsKey(id)) {
Log.e("NotificationsPlugin", "No such notification");
Log.e(TAG, "No such notification");
return;
}
RepliableNotification repliableNotification = pendingIntents.get(id);
if (repliableNotification == null) {
Log.e("NotificationsPlugin", "No such notification");
Log.e(TAG, "No such notification");
return;
}
RemoteInput[] remoteInputs = new RemoteInput[repliableNotification.remoteInputs.size()];
@@ -344,7 +430,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
Bundle localBundle = new Bundle();
int i = 0;
for (RemoteInput remoteIn : repliableNotification.remoteInputs) {
getDetailsOfNotification(remoteIn);
remoteInputs[i] = remoteIn;
localBundle.putCharSequence(remoteInputs[i].getResultKey(), message);
i++;
@@ -354,91 +439,38 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
try {
repliableNotification.pendingIntent.send(context, 0, localIntent);
} catch (PendingIntent.CanceledException e) {
Log.e("NotificationPlugin", "replyToNotification error: " + e.getMessage());
Log.e(TAG, "replyToNotification error: " + e.getMessage());
}
pendingIntents.remove(id);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH)
private void getDetailsOfNotification(RemoteInput remoteInput) {
//Some more details of RemoteInput... no idea what for but maybe it will be useful at some point
String resultKey = remoteInput.getResultKey();
String label = remoteInput.getLabel().toString();
Boolean canFreeForm = remoteInput.getAllowFreeFormInput();
if (remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
String[] possibleChoices = new String[remoteInput.getChoices().length];
for (int i = 0; i < remoteInput.getChoices().length; i++) {
possibleChoices[i] = remoteInput.getChoices()[i].toString();
}
}
}
private String getNotificationTitle(Notification notification) {
final String TITLE_KEY = "android.title";
final String TEXT_KEY = "android.text";
String title = "";
if (notification != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
Bundle extras = notification.extras;
title = extractStringFromExtra(extras, TITLE_KEY);
} catch (Exception e) {
Log.e("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText, e);
}
}
}
return title;
}
@Nullable
private RepliableNotification extractRepliableNotification(StatusBarNotification statusBarNotification) {
RepliableNotification repliableNotification = new RepliableNotification();
if (statusBarNotification != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
try {
if (statusBarNotification.getNotification().actions != null) {
for (Notification.Action act : statusBarNotification.getNotification().actions) {
if (act != null && act.getRemoteInputs() != null) {
// Is a reply
repliableNotification.remoteInputs.addAll(Arrays.asList(act.getRemoteInputs()));
repliableNotification.pendingIntent = act.actionIntent;
break;
}
}
repliableNotification.packageName = statusBarNotification.getPackageName();
repliableNotification.tag = statusBarNotification.getTag();//TODO find how to pass Tag with sending PendingIntent, might fix Hangout problem
}
} catch (Exception e) {
Log.e("NotificationPlugin", "problem extracting notification wear for " + statusBarNotification.getNotification().tickerText, e);
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return null;
}
if (statusBarNotification.getNotification().actions == null) {
return null;
}
for (Notification.Action act : statusBarNotification.getNotification().actions) {
if (act != null && act.getRemoteInputs() != null) {
// Is a reply
RepliableNotification repliableNotification = new RepliableNotification();
repliableNotification.remoteInputs.addAll(Arrays.asList(act.getRemoteInputs()));
repliableNotification.pendingIntent = act.actionIntent;
repliableNotification.packageName = statusBarNotification.getPackageName();
repliableNotification.tag = statusBarNotification.getTag(); //TODO find how to pass Tag with sending PendingIntent, might fix Hangout problem
return repliableNotification;
}
}
return repliableNotification;
return null;
}
private String getNotificationText(Notification notification) {
final String TEXT_KEY = "android.text";
String text = "";
if (notification != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
Bundle extras = notification.extras;
Object extraTextExtra = extras.get(TEXT_KEY);
if (extraTextExtra != null) text = extraTextExtra.toString();
} catch (Exception e) {
Log.e("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText, e);
}
}
}
return text;
}
private static String extractStringFromExtra(Bundle extras, String key) {
Object extra = extras.get(key);
if (extra == null) {
@@ -448,7 +480,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
} else if (extra instanceof SpannableString) {
return extra.toString();
} else {
Log.e("NotificationsPlugin", "Don't know how to extract text from extra of type: " + extra.getClass().getCanonicalName());
Log.e(TAG, "Don't know how to extract text from extra of type: " + extra.getClass().getCanonicalName());
return null;
}
}
@@ -459,32 +491,26 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
* instead the ticker text.
*/
private String getTickerText(Notification notification) {
final String TITLE_KEY = "android.title";
final String TEXT_KEY = "android.text";
String ticker = "";
if (notification != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
Bundle extras = notification.extras;
String extraTitle = extractStringFromExtra(extras, TITLE_KEY);
String extraText = extractStringFromExtra(extras, TEXT_KEY);
try {
Bundle extras = getExtras(notification);
String extraTitle = extractStringFromExtra(extras, NotificationCompat.EXTRA_TITLE);
String extraText = extractStringFromExtra(extras, NotificationCompat.EXTRA_TEXT);
if (extraTitle != null && extraText != null && !extraText.isEmpty()) {
ticker = extraTitle + ": " + extraText;
} else if (extraTitle != null) {
ticker = extraTitle;
} else if (extraText != null) {
ticker = extraText;
}
} catch (Exception e) {
Log.e("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText, e);
}
if (extraTitle != null && !TextUtils.isEmpty(extraText)) {
ticker = extraTitle + ": " + extraText;
} else if (extraTitle != null) {
ticker = extraTitle;
} else if (extraText != null) {
ticker = extraText;
}
} catch (Exception e) {
Log.e(TAG, "problem parsing notification extras for " + notification.tickerText, e);
}
if (ticker.isEmpty()) {
ticker = (notification.tickerText != null) ? notification.tickerText.toString() : "";
}
if (ticker.isEmpty()) {
ticker = (notification.tickerText != null) ? notification.tickerText.toString() : "";
}
return ticker;
@@ -568,12 +594,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
//For compat with API<21, because lollipop changed the way to cancel notifications
private static void cancelNotificationCompat(NotificationReceiver service, String compatKey) {
if (Build.VERSION.SDK_INT >= 21) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
service.cancelNotification(compatKey);
} else {
int first = compatKey.indexOf(':');
if (first == -1) {
Log.e("cancelNotificationCompa", "Not formatted like a notification key: " + compatKey);
Log.e(TAG, "Not formatted like a notification key: " + compatKey);
return;
}
int last = compatKey.lastIndexOf(':');
@@ -597,7 +623,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
String tag = statusBarNotification.getTag();
if (tag != null && tag.startsWith("kdeconnectId:"))
result = Integer.toString(statusBarNotification.getId());
else if (Build.VERSION.SDK_INT >= 21) {
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
result = statusBarNotification.getKey();
} else {
String packageName = statusBarNotification.getPackageName();
@@ -616,7 +642,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
md.update(data);
return bytesToHex(md.digest());
} catch (NoSuchAlgorithmException e) {
Log.e("KDEConnect", "Error while generating checksum", e);
Log.e(TAG, "Error while generating checksum", e);
}
return null;
}

View File

@@ -82,7 +82,6 @@ public class PresenterActivity extends AppCompatActivity {
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
if (mMediaSession != null) {
mMediaSession.setActive(true);
return;
@@ -93,7 +92,6 @@ public class PresenterActivity extends AppCompatActivity {
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
if (mMediaSession != null) {
PowerManager pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);

View File

@@ -34,7 +34,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import org.kde.kdeconnect.UserInterface.DeviceSettingsActivity;
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
import org.kde.kdeconnect.UserInterface.MainActivity;
import org.kde.kdeconnect_tp.R;
@@ -167,10 +167,10 @@ public class RemoteKeyboardService
if (instances.size() == 1) { // single instance of RemoteKeyboardPlugin -> access its settings
RemoteKeyboardPlugin plugin = instances.get(0);
if (plugin != null) {
Intent intent = new Intent(this, DeviceSettingsActivity.class);
Intent intent = new Intent(this, PluginSettingsActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(DeviceSettingsActivity.EXTRA_DEVICE_ID, plugin.getDeviceId());
intent.putExtra(DeviceSettingsActivity.EXTRA_PLUGIN_KEY, plugin.getPluginKey());
intent.putExtra(PluginSettingsActivity.EXTRA_DEVICE_ID, plugin.getDeviceId());
intent.putExtra(PluginSettingsActivity.EXTRA_PLUGIN_KEY, plugin.getPluginKey());
startActivity(intent);
}
} else { // != 1 instance of plugin -> show main activity view

View File

@@ -22,6 +22,7 @@
package org.kde.kdeconnect.Plugins.SMSPlugin;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -54,17 +55,18 @@ 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;
import java.util.concurrent.locks.ReentrantLock;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import static org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin.PACKET_TYPE_TELEPHONY;
@PluginFactory.LoadablePlugin
@SuppressLint("InlinedApi")
public class SMSPlugin extends Plugin {
/**
@@ -161,18 +163,15 @@ public class SMSPlugin extends Plugin {
private final Lock mostRecentTimestampLock = new ReentrantLock();
private class MessageContentObserver extends ContentObserver {
final SMSPlugin mPlugin;
/**
* Create a ContentObserver to watch the Messages database. onChange is called for
* every subscribed change
*
* @param parent Plugin which owns this observer
* @param handler Handler object used to make the callback
*/
MessageContentObserver(SMSPlugin parent, Handler handler) {
MessageContentObserver(Handler handler) {
super(handler);
mPlugin = parent;
}
/**
@@ -181,40 +180,34 @@ public class SMSPlugin extends Plugin {
* In this case, this onChange expects to be called whenever *anything* in the Messages
* database changes and simply reports those updated messages to anyone who might be listening
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onChange(boolean selfChange) {
if (mPlugin.mostRecentTimestamp == 0) {
// Lock so no one uses the mostRecentTimestamp between the moment we read it and the
// moment we update it. This is because reading the Messages DB can take long.
mostRecentTimestampLock.lock();
if (mostRecentTimestamp == 0) {
// Since the timestamp has not been initialized, we know that nobody else
// has requested a message. That being the case, there is most likely
// nobody listening for message updates, so just drop them
mostRecentTimestampLock.unlock();
return;
}
mostRecentTimestampLock.lock();
// Grab the mostRecentTimestamp into the local stack because reading the Messages
// database could potentially be a long operation
long mostRecentTimestamp = mPlugin.mostRecentTimestamp;
mostRecentTimestampLock.unlock();
List<SMSHelper.Message> messages = SMSHelper.getMessagesSinceTimestamp(mPlugin.context, mostRecentTimestamp);
SMSHelper.Message message = SMSHelper.getNewestMessage(context);
if (messages.size() == 0) {
// Our onChange often gets called many times for a single message. Don't make unnecessary
// noise
if (message == null || message.date <= mostRecentTimestamp) {
// onChange can trigger many times for a single message. Don't make unnecessary noise
mostRecentTimestampLock.unlock();
return;
}
// Update the most recent counter
mostRecentTimestampLock.lock();
for (SMSHelper.Message message : messages) {
if (message.date > mostRecentTimestamp) {
mPlugin.mostRecentTimestamp = message.date;
}
}
mostRecentTimestamp = message.date;
mostRecentTimestampLock.unlock();
// Send the alert about the update
device.sendPacket(constructBulkMessagePacket(messages));
device.sendPacket(constructBulkMessagePacket(Collections.singleton(message)));
}
}
@@ -275,7 +268,6 @@ public class SMSPlugin extends Plugin {
device.sendPacket(np);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public boolean onCreate() {
permissionExplanation = R.string.telepathy_permission_explanation;
@@ -285,7 +277,7 @@ public class SMSPlugin extends Plugin {
context.registerReceiver(receiver, filter);
Looper helperLooper = SMSHelper.MessageLooper.getLooper();
ContentObserver messageObserver = new MessageContentObserver(this, new Handler(helperLooper));
ContentObserver messageObserver = new MessageContentObserver(new Handler(helperLooper));
SMSHelper.registerObserver(messageObserver, context);
return true;
@@ -301,7 +293,6 @@ public class SMSPlugin extends Plugin {
return context.getResources().getString(R.string.pref_plugin_telepathy_desc);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public boolean onPacketReceived(NetworkPacket np) {
@@ -344,7 +335,6 @@ public class SMSPlugin extends Plugin {
* @param messages Messages to include in the packet
* @return NetworkPacket of type PACKET_TYPE_SMS_MESSAGE
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private static NetworkPacket constructBulkMessagePacket(Collection<SMSHelper.Message> messages) {
NetworkPacket reply = new NetworkPacket(PACKET_TYPE_SMS_MESSAGE);
@@ -354,8 +344,6 @@ public class SMSPlugin extends Plugin {
try {
JSONObject json = message.toJSONObject();
json.put("event", SMSHelper.Message.TEXT_MESSAGE);
body.put(json);
} catch (JSONException e) {
Log.e("Conversations", "Error serializing message");
@@ -373,7 +361,6 @@ public class SMSPlugin extends Plugin {
* <p>
* Send one packet of type PACKET_TYPE_SMS_MESSAGE with the first message in all conversations
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private boolean handleRequestConversations(NetworkPacket packet) {
Map<SMSHelper.ThreadID, SMSHelper.Message> conversations = SMSHelper.getConversations(this.context);
@@ -394,7 +381,6 @@ public class SMSPlugin extends Plugin {
return true;
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private boolean handleRequestConversation(NetworkPacket packet) {
SMSHelper.ThreadID threadID = new SMSHelper.ThreadID(packet.getLong("threadID"));
@@ -443,6 +429,9 @@ public class SMSPlugin extends Plugin {
};
}
/**
* I suspect we can actually go lower than this, but it might get unstable
*/
@Override
public int getMinSdk() {
return Build.VERSION_CODES.KITKAT;

View File

@@ -42,7 +42,7 @@ import org.json.JSONObject;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.StorageHelper;
import org.kde.kdeconnect.UserInterface.DeviceSettingsActivity;
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
import org.kde.kdeconnect_tp.R;
@@ -185,7 +185,7 @@ public class SftpSettingsFragment
private void restoreActionMode() {
try {
if (savedActionModeState.getBoolean(KEY_ACTION_MODE_ENABLED)) {
actionMode = ((DeviceSettingsActivity)requireActivity()).startSupportActionMode(this);
actionMode = ((PluginSettingsActivity)requireActivity()).startSupportActionMode(this);
if (actionMode != null) {
JSONArray jsonArray = savedActionModeState.getJSONArray(KEY_ACTION_MODE_SELECTED_ITEMS);
@@ -438,7 +438,7 @@ public class SftpSettingsFragment
@Override
public void onLongClick(StoragePreference storagePreference) {
if (actionMode == null) {
actionMode = ((DeviceSettingsActivity)requireActivity()).startSupportActionMode(this);
actionMode = ((PluginSettingsActivity)requireActivity()).startSupportActionMode(this);
if (actionMode != null) {
for (int i = 0, count = preferenceCategory.getPreferenceCount(); i < count; i++) {

View File

@@ -66,8 +66,7 @@ public class CompositeReceiveFileJob extends BackgroundJob<Device, Void> {
lock = new Object();
networkPacketList = new ArrayList<>();
receiveNotification = new ReceiveNotification(device);
receiveNotification.addCancelAction(getId());
receiveNotification = new ReceiveNotification(device, getId());
currentFileNum = 0;
totalNumFiles = 0;
totalPayloadSize = 0;
@@ -190,7 +189,7 @@ public class CompositeReceiveFileJob extends BackgroundJob<Device, Void> {
numFiles = totalNumFiles;
}
if (numFiles == 1 && currentNetworkPacket.has("open")) {
if (numFiles == 1 && currentNetworkPacket.getBoolean("open", false)) {
receiveNotification.cancel();
openFile(fileDocument);
} else {

View File

@@ -65,8 +65,7 @@ public class CompositeUploadFileJob extends BackgroundJob<Device, Void> {
totalPayloadSize = 0;
totalSend = 0;
prevProgressPercentage = 0;
uploadNotification = new UploadNotification(getDevice());
uploadNotification.addCancelAction(getId());
uploadNotification = new UploadNotification(getDevice(), getId());
sendPacketStatusCallback = new SendPacketStatusCallback();
}

View File

@@ -48,15 +48,16 @@ class ReceiveNotification {
private final int notificationId;
private NotificationCompat.Builder builder;
private final Device device;
private long currentJobId;
private long jobId;
//https://documentation.onesignal.com/docs/android-customizations#section-big-picture
private static final int bigImageWidth = 1440;
private static final int bigImageHeight = 720;
public ReceiveNotification(Device device) {
public ReceiveNotification(Device device, long jobId) {
this.device = device;
this.jobId = jobId;
notificationId = (int) System.currentTimeMillis();
notificationManager = (NotificationManager) device.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(device.getContext(), NotificationHelper.Channels.FILETRANSFER)
@@ -64,6 +65,7 @@ class ReceiveNotification {
.setAutoCancel(true)
.setOngoing(true)
.setProgress(100, 0, true);
addCancelAction();
}
public void show() {
@@ -74,10 +76,8 @@ class ReceiveNotification {
notificationManager.cancel(notificationId);
}
public void addCancelAction(long jobId) {
builder.mActions.clear();
public void addCancelAction() {
currentJobId = jobId;
Intent cancelIntent = new Intent(device.getContext(), ShareBroadcastReceiver.class);
cancelIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
cancelIntent.setAction(SharePlugin.ACTION_CANCEL_SHARE);
@@ -88,12 +88,6 @@ class ReceiveNotification {
builder.addAction(R.drawable.ic_reject_pairing, device.getContext().getString(R.string.cancel), cancelPendingIntent);
}
public long getCurrentJobId() { return currentJobId; }
public int getNotificationId() {
return notificationId;
}
public void setTitle(String title) {
builder.setContentTitle(title);
builder.setTicker(title);

View File

@@ -153,7 +153,6 @@ public class ShareActivity extends AppCompatActivity {
});
} else {
BackgroundService.addGuiInUseCounter(this);
BackgroundService.RunCommand(this, service -> {
service.onNetworkChange();
service.addDeviceListChangedCallback("ShareActivity", this::updateComputerList);
@@ -166,7 +165,6 @@ public class ShareActivity extends AppCompatActivity {
@Override
protected void onStop() {
BackgroundService.RunCommand(this, service -> service.removeDeviceListChangedCallback("ShareActivity"));
BackgroundService.removeGuiInUseCounter(this);
super.onStop();
}

View File

@@ -39,9 +39,11 @@ class UploadNotification {
private NotificationCompat.Builder builder;
private final int notificationId;
private final Device device;
private long jobId;
UploadNotification(Device device) {
UploadNotification(Device device, long jobId) {
this.device = device;
this.jobId = jobId;
notificationId = (int) System.currentTimeMillis();
notificationManager = (NotificationManager) device.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
@@ -50,11 +52,10 @@ class UploadNotification {
.setAutoCancel(true)
.setOngoing(true)
.setProgress(100, 0, true);
addCancelAction();
}
void addCancelAction(long jobId) {
builder.mActions.clear();
void addCancelAction() {
Intent cancelIntent = new Intent(device.getContext(), ShareBroadcastReceiver.class);
cancelIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
cancelIntent.setAction(SharePlugin.ACTION_CANCEL_SHARE);

View File

@@ -123,10 +123,9 @@ public class TelephonyPlugin extends Plugin {
return;
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
final int finalIntState = intState;
if (finalIntState != lastState) {
callBroadcastReceived(finalIntState, number);
if (intState != lastState) {
lastState = intState;
callBroadcastReceived(intState, number);
}
}
}
@@ -213,7 +212,7 @@ public class TelephonyPlugin extends Plugin {
}
//Emit a missed call notification if needed
if (lastState == TelephonyManager.CALL_STATE_RINGING) {
if ("ringing".equals(lastPacket.getString("event", null))) {
np.set("event", "missedCall");
np.set("phoneNumber", lastPacket.getString("phoneNumber", null));
np.set("contactName", lastPacket.getString("contactName", null));
@@ -224,7 +223,6 @@ public class TelephonyPlugin extends Plugin {
}
lastPacket = np;
lastState = state;
}
private void unmuteRinger() {

View File

@@ -127,18 +127,5 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
return mDelegate;
}
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
getDelegate().onStop();
}
}

View File

@@ -167,18 +167,6 @@ public class CustomDevicesActivity extends AppCompatActivity implements CustomDe
return deserializeIpList(deviceListPrefs);
}
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
}
@Override
public void onCustomDeviceClicked(String customDevice) {
editingDeviceAtPosition = customDeviceList.indexOf(customDevice);

View File

@@ -44,7 +44,7 @@ import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.NetworkHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.List.CustomItem;
import org.kde.kdeconnect.UserInterface.List.PluginListHeaderItem;
import org.kde.kdeconnect.UserInterface.List.FailedPluginListItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.PluginItem;
@@ -71,6 +71,8 @@ public class DeviceFragment extends Fragment {
private static final String ARG_DEVICE_ID = "deviceId";
private static final String ARG_FROM_DEVICE_LIST = "fromDeviceList";
private static final String TAG = "KDE/DeviceFragment";
private View rootView;
private String mDeviceId;
private Device device;
@@ -133,12 +135,10 @@ public class DeviceFragment extends Fragment {
setHasOptionsMenu(true);
//Log.e("DeviceFragment", "device: " + deviceId);
BackgroundService.RunCommand(mActivity, service -> {
device = service.getDevice(mDeviceId);
if (device == null) {
Log.e("DeviceFragment", "Trying to display a device fragment but the device is not present");
Log.e(TAG, "Trying to display a device fragment but the device is not present");
mActivity.onDeviceSelected(null);
return;
}
@@ -208,8 +208,6 @@ public class DeviceFragment extends Fragment {
@Override
public void onPrepareOptionsMenu(Menu menu) {
//Log.e("DeviceFragment", "onPrepareOptionsMenu");
super.onPrepareOptionsMenu(menu);
menu.clear();
@@ -231,7 +229,7 @@ public class DeviceFragment extends Fragment {
}
menu.add(R.string.device_menu_plugins).setOnMenuItemClickListener(menuItem -> {
Intent intent = new Intent(mActivity, DeviceSettingsActivity.class);
Intent intent = new Intent(mActivity, PluginSettingsActivity.class);
intent.putExtra("deviceId", mDeviceId);
startActivity(intent);
return true;
@@ -290,7 +288,6 @@ public class DeviceFragment extends Fragment {
}
private void refreshUI() {
//Log.e("DeviceFragment", "refreshUI");
if (device == null || rootView == null) {
return;
@@ -355,7 +352,7 @@ public class DeviceFragment extends Fragment {
} catch (IllegalStateException e) {
//Ignore: The activity was closed while we were trying to update it
} catch (ConcurrentModificationException e) {
Log.e("DeviceActivity", "ConcurrentModificationException");
Log.e(TAG, "ConcurrentModificationException");
this.run(); //Try again
}
@@ -404,31 +401,15 @@ public class DeviceFragment extends Fragment {
};
private void createPluginsList(ConcurrentHashMap<String, Plugin> plugins, int headerText, FailedPluginListItem.Action action) {
if (!plugins.isEmpty()) {
if (plugins.isEmpty())
return;
TextView header = new TextView(mActivity);
header.setPadding(
((int) (16 * getResources().getDisplayMetrics().density)),
((int) (28 * getResources().getDisplayMetrics().density)),
((int) (16 * getResources().getDisplayMetrics().density)),
((int) (8 * getResources().getDisplayMetrics().density))
);
header.setOnClickListener(null);
header.setOnLongClickListener(null);
header.setText(headerText);
pluginListItems.add(new CustomItem(header));
for (Map.Entry<String, Plugin> entry : plugins.entrySet()) {
String pluginKey = entry.getKey();
final Plugin plugin = entry.getValue();
if (device.isPluginEnabled(pluginKey)) {
if (plugin == null) {
pluginListItems.add(new SmallEntryItem(pluginKey));
} else {
pluginListItems.add(new FailedPluginListItem(plugin, action));
}
}
pluginListItems.add(new PluginListHeaderItem(headerText));
for (Plugin plugin : plugins.values()) {
if (!device.isPluginEnabled(plugin.getPluginKey())) {
continue;
}
pluginListItems.add(new FailedPluginListItem(plugin, action));
}
}
}

View File

@@ -53,10 +53,10 @@ public class DeviceSettingsAlertDialogFragment extends AlertDialogFragment {
setCallback(new Callback() {
@Override
public void onPositiveButtonClicked() {
Intent intent = new Intent(requireActivity(), DeviceSettingsActivity.class);
Intent intent = new Intent(requireActivity(), PluginSettingsActivity.class);
intent.putExtra(DeviceSettingsActivity.EXTRA_DEVICE_ID, deviceId);
intent.putExtra(DeviceSettingsActivity.EXTRA_PLUGIN_KEY, pluginKey);
intent.putExtra(PluginSettingsActivity.EXTRA_DEVICE_ID, deviceId);
intent.putExtra(PluginSettingsActivity.EXTRA_PLUGIN_KEY, pluginKey);
requireActivity().startActivity(intent);
}
});

View File

@@ -15,32 +15,35 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.UserInterface.List;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
public class CustomItem implements ListAdapter.Item {
import org.kde.kdeconnect_tp.R;
private final View view;
public class PluginListHeaderItem implements ListAdapter.Item {
public CustomItem(View v) {
private final int text;
if (v == null)
throw new IllegalArgumentException("View must not be null");
this.view = v;
public PluginListHeaderItem(int text) {
this.text = text;
}
@NonNull
@Override
public View inflateView(LayoutInflater layoutInflater) {
return view;
TextView v = (TextView) layoutInflater.inflate(R.layout.list_item_plugin_header, null);
v.setText(text);
v.setOnClickListener(null);
v.setOnLongClickListener(null);
return v;
}
}

View File

@@ -284,14 +284,12 @@ public class MainActivity extends AppCompatActivity implements SharedPreferences
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this, true);
BackgroundService.RunCommand(this, service -> service.addDeviceListChangedCallback("MainActivity", this::updateDeviceList));
updateDeviceList();
}
@Override
protected void onStop() {
BackgroundService.removeGuiInUseCounter(this);
BackgroundService.RunCommand(this, service -> service.removeDeviceListChangedCallback("MainActivity"));
super.onStop();
}

View File

@@ -32,7 +32,7 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public class DeviceSettingsActivity
public class PluginSettingsActivity
extends AppCompatActivity
implements PluginPreference.PluginPreferenceCallback {
@@ -47,7 +47,7 @@ public class DeviceSettingsActivity
ThemeUtil.setUserPreferredTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_device_settings);
setContentView(R.layout.activity_plugin_settings);
if (getSupportActionBar() != null) {
getSupportActionBar().setDefaultDisplayHomeAsUpEnabled(true);
@@ -68,7 +68,7 @@ public class DeviceSettingsActivity
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragmentPlaceHolder);
if (fragment == null) {
if (pluginKey == null) {
fragment = DeviceSettingsFragment.newInstance(deviceId);
fragment = PluginSettingsListFragment.newInstance(deviceId);
} else {
Device device = BackgroundService.getInstance().getDevice(deviceId);
Plugin plugin = device.getPlugin(pluginKey);
@@ -96,17 +96,6 @@ public class DeviceSettingsActivity
return super.onOptionsItemSelected(item);
}
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
}
@Override
public void onStartPluginSettingsFragment(Plugin plugin) {

View File

@@ -80,6 +80,6 @@ public class PluginSettingsFragment extends PreferenceFragmentCompat {
}
public String getDeviceId() {
return ((DeviceSettingsActivity)requireActivity()).getDeviceId();
return ((PluginSettingsActivity)requireActivity()).getDeviceId();
}
}

View File

@@ -35,7 +35,7 @@ import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
public class DeviceSettingsFragment extends PreferenceFragmentCompat {
public class PluginSettingsListFragment extends PreferenceFragmentCompat {
private static final String ARG_DEVICE_ID = "deviceId";
private static final String KEY_RECYCLERVIEW_LAYOUTMANAGER_STATE = "RecyclerViewLayoutmanagerState";
@@ -50,8 +50,8 @@ public class DeviceSettingsFragment extends PreferenceFragmentCompat {
*/
private boolean stateSaved;
public static DeviceSettingsFragment newInstance(@NonNull String deviceId) {
DeviceSettingsFragment fragment = new DeviceSettingsFragment();
public static PluginSettingsListFragment newInstance(@NonNull String deviceId) {
PluginSettingsListFragment fragment = new PluginSettingsListFragment();
Bundle args = new Bundle();
args.putString(ARG_DEVICE_ID, deviceId);

View File

@@ -6,9 +6,9 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: http://bugs.kde.org\n"
"POT-Creation-Date: 2014-11-23 22:02-0800\n"
"PO-Revision-Date: 2014-11-23 22:02-0800\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2019-06-30 11:38+0200\n"
"PO-Revision-Date: 2019-06-30 11:38+0200\n"
"Last-Translator: Albert Vaca Cintora <albertvaka@gmail.com>\n"
"Language: en\n"
"MIME-Version: 1.0\n"