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

Compare commits

..

155 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
Albert Vaca Cintora
2bfd9c645f Release 1.12.7 2019-05-01 14:48:33 +02:00
Albert Vaca Cintora
87ddf47999 Upgrade gradle plugin for AS 3.4 2019-05-01 14:27:37 +02:00
Erik Duisters
c7a4f76f2b If Android creates a different filename than requested delete the newly created file and return false 2019-05-01 11:17:29 +00:00
Nicolas Fella
d950266431 Don't close notification for last player if not playing 2019-05-01 00:53:11 +02:00
Nicolas Fella
9f005b508a Hide our own media notification for Spotify when Spotify is running on the phone 2019-04-30 17:14:47 +00:00
Nicolas Fella
470eab525a Inhibit screen lock when remote keyboard is open 2019-04-30 15:59:35 +00:00
Simon Redman
9ff223e2b8 Read MediaStore.Images.ImageColumns.DATE_TAKEN for yet another way of accessing the last modified information 2019-04-30 15:58:30 +00:00
Erik Duisters
b9a0f310c7 Do not strip leading '/' from parentSuffix for non root files when determining parentDocumentId making copying into subdirectories work. 2019-04-30 06:55:06 +02:00
Nicolas Fella
2564586127 Revert icon size regression 2019-04-28 15:46:16 +02:00
Erik Duisters
d1c0a9763d Remove unnecessary logging 2019-04-27 18:53:04 +02:00
l10n daemon script
d163a57af6 GIT_SILENT made messages (after extraction) 2019-04-22 02:59:57 +02:00
Nicolas Fella
e882bebe4e Simplify switch 2019-04-20 22:38:44 +02:00
Nicolas Fella
d1565702cc Simplify switch 2019-04-20 22:37:06 +02:00
Nicolas Fella
1b8763db61 Format 2019-04-20 22:33:47 +02:00
Nicolas Fella
f4a4046b3b Rename updateComputerList -> updateDeviceList 2019-04-20 03:03:15 +02:00
Nicolas Fella
882afbbc43 Make ListAdapter.Item.inflateView nonNull 2019-04-20 00:09:51 +02:00
Nicolas Fella
8e60359496 Fix warning when overriding methods with nonnull annotations 2019-04-20 00:01:20 +02:00
Nicolas Fella
ba5a925075 Require KitKat for SMS plugin 2019-04-19 23:55:46 +02:00
Nicolas Fella
d7c8f61c80 Unify coding style 2019-04-19 23:47:18 +02:00
Nicolas Fella
7da6310926 Add nonnull annotation 2019-04-19 23:40:23 +02:00
Nicolas Fella
ccd92aca03 Remove redundant assignment 2019-04-19 23:37:30 +02:00
Nicolas Fella
c5a8406928 Organize imports 2019-04-19 23:34:42 +02:00
Nicolas Fella
a4afc84911 Use non-deprecated onAttach override 2019-04-19 23:34:14 +02:00
Nicolas Fella
6d70d0dc50 Remove unneeded drawable 2019-04-19 21:40:57 +02:00
Nicolas Fella
2a36194273 Add tools:keep to plugin prefs to avoid false-positive lint 2019-04-19 21:37:13 +02:00
Nicolas Fella
edc04f41a6 Remove unneeded file 2019-04-19 21:35:28 +02:00
Nicolas Fella
7f215b8cd5 Remove redundant namespace 2019-04-19 21:30:33 +02:00
Nicolas Fella
cf526953f3 Remove unnecessary supression 2019-04-19 21:29:39 +02:00
Simon Redman
dc5f10c073 Revert "Try-with-resources on inputstreams"
This reverts commit aa776739b7
2019-04-19 05:10:11 +00:00
Nicolas Fella
21b97d335c Add NonNull annotations 2019-04-19 01:31:19 +02:00
Nicolas Fella
f735520aba Organize imports 2019-04-19 01:12:06 +02:00
Nicolas Fella
0072911c5b Use java style instead of C style for arrays 2019-04-19 01:11:35 +02:00
Nicolas Fella
96a83ddcd3 Inline fields 2019-04-19 01:09:52 +02:00
Nicolas Fella
794f1d4706 Remove unneeded file 2019-04-19 00:57:26 +02:00
Nicolas Fella
e5c8160ee2 Revert "Bump gradle plugin version"
It seems to break things

This reverts commit 6bcb77f65f.
2019-04-19 00:52:01 +02:00
Nicolas Fella
25a0972606 Fix javadoc regression 2019-04-19 00:34:15 +02:00
Nicolas Fella
6bcb77f65f Bump gradle plugin version 2019-04-19 00:20:45 +02:00
Albert Vaca Cintora
235e93be5e More unclosed stuff found by lgtm.com 2019-04-18 21:06:56 +02:00
Albert Vaca Cintora
aa776739b7 Try-with-resources on inputstreams
As suggested by lgtm.com
2019-04-18 20:59:54 +02:00
Nicolas Fella
e8e01c9a51 Remove redundant padding declaration 2019-04-18 17:58:30 +00:00
Albert Vaca Cintora
168b16527c Remove compat with protocol 6 with RSA encryption
Since we already removed compat with protocol 7 ^.^u
2019-04-18 19:52:30 +02:00
Nicolas Fella
9a8ae36ef6 remove obsolete layout params 2019-04-18 17:23:50 +02:00
l10n daemon script
b80d7dcf50 GIT_SILENT made messages (after extraction) 2019-04-18 03:13:07 +02:00
Nicolas Fella
b1e8a66a8c Remove unused file 2019-04-18 02:50:51 +02:00
Nicolas Fella
b375d79653 Add version check 2019-04-18 01:34:00 +02:00
l10n daemon script
6f0ed846b8 GIT_SILENT made messages (after extraction) 2019-04-17 03:06:57 +02:00
l10n daemon script
2a7f953bd4 GIT_SILENT made messages (after extraction) 2019-04-16 03:12:21 +02:00
Nicolas Fella
728bfeefea Rename 2019-04-14 17:47:45 +00:00
Nicolas Fella
2ab76c6e90 Deduplicate code for listeners in MprisActivity 2019-04-14 17:47:45 +00:00
l10n daemon script
3f2b8495b3 GIT_SILENT made messages (after extraction) 2019-04-14 03:13:24 +02:00
Simon Redman
b9f3c8c7b1 Add merge request templates (copy from kdeconnect-kde) 2019-04-13 16:45:31 -06:00
l10n daemon script
a73c95094c GIT_SILENT made messages (after extraction) 2019-04-13 03:11:46 +02:00
l10n daemon script
64c20f5e63 GIT_SILENT made messages (after extraction) 2019-04-10 03:13:02 +02:00
l10n daemon script
f30462bafa GIT_SILENT made messages (after extraction) 2019-04-09 03:15:58 +02:00
Ajay Choudhary
7ce2130d12 Resize buttons on presentation remote 2019-04-08 10:00:38 -06:00
l10n daemon script
77b28fe9c4 GIT_SILENT made messages (after extraction) 2019-04-08 03:13:34 +02:00
Simon Redman
27a2c030d6 Replaced getString(..) != null with isNull(..) 2019-04-07 20:03:01 +00:00
Simon Redman
95f866ac98 Properly check for size existence before reading 2019-04-07 20:03:01 +00:00
Simon Redman
177c18e0f4 Clean up uriToNetworkPacket
- Remove redundant code
- Make top-level block function properly
2019-04-07 20:03:01 +00:00
Erik Duisters
a3fb423dd3 Upload files using a CompositeUploadFileJob making the upload cancelable 2019-04-07 17:54:12 +00:00
Nicolas Fella
3c97e1c067 Catch Exception when releasing storage URIs 2019-04-07 17:34:37 +00:00
Piyush Aggarwal
50dc239d64 fix telephony explanation 2019-04-07 16:45:06 +00:00
Piyush Aggarwal
f278bd17e1 fix docs and little maintenance 2019-04-03 16:57:47 +05:30
l10n daemon script
9c51f1e898 GIT_SILENT made messages (after extraction) 2019-04-02 03:16:19 +02:00
Nicolas Fella
5063f3d0f4 Use fancier lambda 2019-03-31 20:14:02 +02:00
Nicolas Fella
f1194e88c2 Use Log.e instead of printStackTrace 2019-03-31 20:09:44 +02:00
Nicolas Fella
386a9f3f40 remove unneeded throws 2019-03-31 19:26:21 +02:00
l10n daemon script
0510167c4e GIT_SILENT made messages (after extraction) 2019-03-31 04:10:56 +02:00
Nicolas Fella
1867150ebb Replace layout size values 2019-03-31 00:46:11 +01:00
Nicolas Fella
3022e3d835 Mark ignored Exceptions as such 2019-03-30 17:42:27 +01:00
Nicolas Fella
6e1fe3edbd Pass exception to log 2019-03-30 17:42:27 +01:00
Nicolas Fella
71609f144d Use fancier lambdas 2019-03-30 17:42:27 +01:00
Nicolas Fella
102ce97a19 Organize imports 2019-03-30 17:42:27 +01:00
Nicolas Fella
3a36a4a675 Use ButterKnife in MprisActivity 2019-03-30 16:42:34 +00:00
Nicolas Fella
655a9805e4 Fix logic in volumechange 2019-03-30 16:41:47 +00:00
l10n daemon script
f88f2da7b6 GIT_SILENT made messages (after extraction) 2019-03-30 03:09:24 +01:00
l10n daemon script
d46a3e49c5 GIT_SILENT made messages (after extraction) 2019-03-29 03:12:00 +01:00
l10n daemon script
9f6a1c70aa GIT_SILENT made messages (after extraction) 2019-03-27 03:10:41 +01:00
Nicolas Fella
2a61f431d6 Use more runwithplugin 2019-03-27 00:22:41 +01:00
Nicolas Fella
8b5734db43 Use runwithplugin 2019-03-27 00:11:04 +01:00
l10n daemon script
d3beb45b79 GIT_SILENT made messages (after extraction) 2019-03-26 03:08:25 +01:00
l10n daemon script
601cafa84b GIT_SILENT made messages (after extraction) 2019-03-25 03:11:50 +01:00
Nicolas Fella
210b7b7ed0 Show notification when file cannot be opened because no app is available 2019-03-24 16:43:04 +01:00
Nicolas Fella
5bf0118bf7 Fix file opening 2019-03-24 15:13:00 +01:00
l10n daemon script
c263e996fa GIT_SILENT made messages (after extraction) 2019-03-24 03:08:54 +01:00
Albert Vaca
8ff45db1f3 Bump version to release 2019-03-23 21:54:27 +01:00
Erik Duisters
a146767580 Properly handle non-root tree-uri's 2019-03-23 17:57:11 +01:00
l10n daemon script
315d9657dd GIT_SILENT made messages (after extraction) 2019-03-23 03:16:08 +01:00
Nicolas Fella
5f70db7568 Don't send reply actions
They are already handles by another codepath
2019-03-22 18:19:46 +01:00
Nicolas Fella
c2cd60773b Send notification actions
Summary:
Store PendingIntents from notification actions. Send list of notifications to desktop and trigger Intent when matching packet arrives.
CCBUG: 366475

Test Plan: Create test notification, trigger package is received correctly. Whether intent.send() is actually successful is NOT yet tested.

Reviewers: #kde_connect, sredman

Reviewed By: #kde_connect, sredman

Subscribers: sredman, apol, MatMaul, kdeconnect, mtijink, #kde_connect

Tags: #kde_connect

Differential Revision: https://phabricator.kde.org/D12294
2019-03-22 17:07:00 +01:00
142 changed files with 3471 additions and 1967 deletions

View File

@@ -0,0 +1,32 @@
## Summary
Add a description of your merge request here. What does your new feature do?
Describe in detail what your patch does, why it does that, etc. Merge requests
without an adequate description are difficult to review, and probably we will
ask for more information!
Please also keep this description up-to-date with any discussion that takes
place so that reviewers can understand your intent. This is especially
important if they didn't participate in the discussion.
Make sure to remove this comment when you are done.
Fill in the following lines as appropriate to automatically close GitLab issue or Bugzilla bugs
Fixes <!-- Gitlab Issue Number -->
BUG: <!-- bugzilla bug -->
## Test Plan
### Before:
Add a quick discription of the (buggy) behavior of the app before this fix
This section does not need to be too detailed because it should mostly be
covered by the bug report and the summary. Just share the steps for how to
reproduce the bug.
### After:
Add a more detailed description of how to exercise the new behavior, showing
that the bug has been fixed. If any other behavior has been changed, share
the steps to verify that the new behavior doesn't have any regressions.
/label ~bugfix

View File

@@ -0,0 +1,27 @@
## Summary
Add a description of your merge request here. What does your new feature do?
Describe in detail what your patch does, why it does that, etc. Merge requests
without an adequate description are difficult to review, and probably we will
ask for more information!
Please also keep this description up-to-date with any discussion that takes
place so that reviewers can understand your intent. This is especially
important if they didn't participate in the discussion.
Make sure to remove this comment when you are done.
Implements <!-- GitLab Issue Number -->
## Test Plan
Add a description of how to test your patch here. Tell us how to use the new
feature and what we should be seeing. If applicable, it is great to include
screenshots, either here or in the Summary section.
It can be difficult to understand a new feature from the text description in
the summary, so put enough detail here that so that we can understand how to run
the new feature and we can play with it ourselves to understand it.
/label ~feature

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="11250"
android:versionName="1.12.5">
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.3.2'
classpath 'com.android.tools.build:gradle:3.4.1'
}
}

View File

@@ -1,6 +1,6 @@
#Tue Jan 15 13:04:46 CET 2019
#Wed May 01 14:24:13 CEST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 537 B

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/drawer_header"
android:gravity="center|clip_vertical|clip_horizontal"
/>

View File

@@ -12,12 +12,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawableEnd="@drawable/ic_delete"
android:drawableLeft="@drawable/ic_delete"
android:drawableRight="@drawable/ic_delete"
android:drawableStart="@drawable/ic_delete"
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"/>
<FrameLayout
@@ -34,8 +30,6 @@
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingEnd="?android:attr/listPreferredItemPaddingRight"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:visibility="visible"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mpris_control_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -10,13 +10,15 @@
<Space
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.25" />
android:id="@+id/top_space"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dip"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:orientation="horizontal">
android:orientation="horizontal"
android:layout_below="@id/top_space"
android:layout_above="@id/textView">
<ImageButton
android:id="@+id/previous_button"
@@ -44,8 +46,7 @@
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.25"
android:gravity="center"
android:layout_alignParentBottom="true"
android:text="@string/presenter_lock_tip" />
</LinearLayout>
</RelativeLayout>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/refresh_list_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/refresh_list_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/devices_list"

View File

@@ -4,7 +4,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
<TextView
android:id="@+id/list_item_category_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"

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

@@ -32,7 +32,7 @@
<SeekBar
android:id="@+id/systemvolume_seek"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"

View File

@@ -122,7 +122,7 @@
<SeekBar
android:id="@+id/positionSeek"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1" />

View File

@@ -1,18 +0,0 @@
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/dark_theme"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="16dp"
android:paddingEnd="48dp"
android:paddingLeft="16dp"
android:paddingRight="48dp"
android:paddingStart="16dp"
android:paddingTop="4dp"
android:text="@string/dark_theme"
android:textColor="@android:color/white"
app:switchPadding="12dp"
tools:background="@drawable/drawer_header"
/>

View File

@@ -78,11 +78,7 @@
<string name="pairing_request_from">طلب اقتران من %1s</string>
<string name="received_url_title">استُلمت وصلة من %1s</string>
<string name="received_url_text">المس لفتح \'%1s\'</string>
<string name="outgoing_file_title">يرسل ملفًّا إلى %1s</string>
<string name="received_file_text">المس لفتح \'%1s\'</string>
<string name="sent_file_title">أرسل ملفًّا إلى %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">المس للإجابة</string>
<string name="reconnect">أعد الاتّصال</string>
<string name="right_click">أرسل نقرة باليمين</string>

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

@@ -59,11 +59,7 @@
<string name="pairing_request_from">Uparivanje zatraženo od %1s</string>
<string name="received_url_title">Primljena veza od %1s</string>
<string name="received_url_text">Kucni za otvaranje \'%1s\'</string>
<string name="outgoing_file_title">Slanje datoteke na %1s</string>
<string name="received_file_text">Kucni za otvaranje \'%1s\'</string>
<string name="sent_file_title">Poslana datoteka na %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Kucni za odgovor</string>
<string name="reconnect">Ponovo uspostavi vezu</string>
<string name="right_click">Pošalji Desni Klik</string>

View File

@@ -104,11 +104,13 @@
<item quantity="one">Fitxer: %1s</item>
<item quantity="other">(Fitxer %2$d de %3$d): %1$s</item>
</plurals>
<string name="outgoing_file_title">S\'està enviant el fitxer a %1s</string>
<string name="outgoing_files_title">S\'estan enviant fitxers a %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">S\'està enviant el fitxer %1$d a %2$s</item>
<item quantity="other">S\'estan enviant els fitxers %1$d a %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">S\'ha enviat %1$d fitxer</item>
<item quantity="other">S\'ha enviat %1$d de %2$d fitxers</item>
<item quantity="one">Fitxer: %1$s</item>
<item quantity="other">(Fitxer %2$d de %3$d): %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">S\'ha rebut un fitxer des de %1$s</item>
@@ -118,12 +120,16 @@
<item quantity="one">Ha fallat en rebre un fitxer des de %1$s</item>
<item quantity="other">Ha fallat en rebre %2$d de %3$d fitxers des de %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Fitxer enviat a %1$s</item>
<item quantity="other">%2$d fitxers enviats a %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Ha fallat en enviar el fitxer a %1$s</item>
<item quantity="other">Ha fallat en enviar %2$d de %3$d fitxers a %1$s</item>
</plurals>
<string name="received_file_text">Puntegeu per obrir «%1s»</string>
<string name="cannot_create_file">No s\'ha pogut crear el fitxer %s</string>
<string name="sent_file_title">Fitxer enviat a %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Ha fallat en enviar el fitxer a %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Puntegeu per a respondre</string>
<string name="reconnect">Reconnecta</string>
<string name="right_click">Envia un clic del botó dret</string>
@@ -240,7 +246,7 @@
<string name="plugins_need_optional_permission">Alguns connectors tenen característiques desactivades per la falta de permís (puntegeu per a més informació):</string>
<string name="share_optional_permission_explanation">Per a compartir fitxers entre el telèfon i l\'escriptori, haureu de donar accés a l\'emmagatzematge del telèfon</string>
<string name="telepathy_permission_explanation">Per a llegir i escriure SMS des de l\'escriptori, haureu de donar permís als SMS</string>
<string name="telephony_permission_explanation">Per a veure les trucades telefòniques i SMS des de l\'escriptori, haureu de donar permís a les trucades telefòniques i SMS</string>
<string name="telephony_permission_explanation">Per a veure les trucades telefòniques des de l\'escriptori, haureu de donar permís d\'accés al registre de trucades telefòniques i a l\'estat del telèfon</string>
<string name="telephony_optional_permission_explanation">Per a veure un nom de contacte en comptes d\'un número de telèfon, haureu de donar permís als contactes del telèfon</string>
<string name="contacts_permission_explanation">Per a compartir els vostres contactes amb l\'escriptori, caldrà que els hi doneu permís</string>
<string name="select_ringtone">Seleccioneu un to de la trucada</string>
@@ -294,4 +300,5 @@
<string name="notification_channel_receivenotification">Notificacions des d\'altres dispositius</string>
<string name="take_picture">Llança la càmera</string>
<string name="plugin_photo_desc">Llança l\'aplicació de la càmera per facilitar la presa i la transferència de fotografies</string>
<string name="no_app_for_opening">No s\'ha trobat cap aplicació adequada per obrir aquest fitxer</string>
</resources>

View File

@@ -106,14 +106,6 @@
<item quantity="many">(Soubor %2$d of %3$d) : %1$s</item>
<item quantity="other"/>
</plurals>
<string name="outgoing_file_title">Odesílám soubor do %1s</string>
<string name="outgoing_files_title">Odesílám soubory do %1s</string>
<plurals name="outgoing_files_text">
<item quantity="one">Odeslán %1$d soubor</item>
<item quantity="few">Odeslány %1$d ze %2$d souborů</item>
<item quantity="many">Odesláno %1$d ze %2$d souborů</item>
<item quantity="other"/>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Přijat soubor z %1$s</item>
<item quantity="few">Přijaty %2$d soubory z %1$s</item>
@@ -128,10 +120,6 @@
</plurals>
<string name="received_file_text">Ťukněte pro otevření \'%1s\'</string>
<string name="cannot_create_file">Nelze vytvořit soubor %s</string>
<string name="sent_file_title">Soubor byl odeslán do %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Odesílání souborů na %1s selhalo</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Ťukněte pro odpovězení</string>
<string name="reconnect">Znovu připojit</string>
<string name="right_click">Poslat kliknutí pravým tlačítkem</string>
@@ -224,7 +212,6 @@
<string name="plugins_need_optional_permission">Některé moduly mají vypnuté vlastnosti, kvůli nedostatečným oprávněním (ťukněte pro více informací):</string>
<string name="share_optional_permission_explanation">Pro sdílení souborů mezi telefonem a počítačem potřebujete udělit oprávnění k úložišti telefonu</string>
<string name="telepathy_permission_explanation">Pro čtení a psaní SMS z počítače musíte udělit oprávnění k SMS</string>
<string name="telephony_permission_explanation">Pro zobrazení telefonátů a SMS v počítači musíte udělit oprávnění k telefonování a SMS</string>
<string name="telephony_optional_permission_explanation">Pro zobrazení jména kontaktu u telefonního čísla je potřeba udělit oprávnění ke kontaktům v telefonu</string>
<string name="contacts_permission_explanation">Pro sdílení knihy kontaktů s pracovním prostředím, musíte udělit přístup ke kontaktům</string>
<string name="select_ringtone">Vybrat vyzváněcí tón</string>

View File

@@ -84,13 +84,7 @@
<string name="pairing_request_from">Parringsanmodning fra %1s</string>
<string name="received_url_title">Modtog link fra %1s</string>
<string name="received_url_text">Tap for at åbne \"%1s\"</string>
<string name="outgoing_file_title">Sender fil til %1s</string>
<string name="outgoing_files_title">Sender filer til %1s</string>
<string name="received_file_text">Tap for at åbne \"%1s\"</string>
<string name="sent_file_title">Fil sendt til %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Kunne ikke sende filen til %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Tap for at svare</string>
<string name="reconnect">Forbind igen</string>
<string name="right_click">Send højreklik</string>
@@ -172,6 +166,5 @@
<string name="plugins_need_optional_permission">Nogle plugins har deaktiverede funktioner pga. manglende tilladelser (tap for mere info):</string>
<string name="share_optional_permission_explanation">For at dele filer mellem din telefon og din desktop skal du give adgang til telefonens datalager.</string>
<string name="telepathy_permission_explanation">For at læse og skrive sms\'er fra din desktop, skal du give tilladelse til sms</string>
<string name="telephony_permission_explanation">For at se telefonopkald og sms\'er fra desktoppen, skal du give tilladelse til telefonopkald og sms.</string>
<string name="telephony_optional_permission_explanation">For at se et kontaktnavn i stedet for et telefonnummer, skal du give adgang til telefonens kontakter</string>
</resources>

View File

@@ -61,7 +61,7 @@
<item>Strongest</item>
</string-array>
<string name="category_connected_devices">Verbundene Geräte</string>
<string name="category_not_paired_devices">Verfügbare Gerät</string>
<string name="category_not_paired_devices">Verfügbare Geräte</string>
<string name="category_remembered_devices">Gemerkte Geräte</string>
<string name="device_menu_plugins">Modul-Einstellungen</string>
<string name="device_menu_unpair">Verbindung trennen</string>
@@ -84,17 +84,7 @@
<string name="pairing_request_from">Verbindungsanfrage von %1s</string>
<string name="received_url_title">Verknüpfung von %1s erhalten</string>
<string name="received_url_text">Tippen um „%1s“ zu öffnen</string>
<string name="outgoing_file_title">Datei wird an %1s gesendet</string>
<string name="outgoing_files_title">Datei wird an %1s gesendet</string>
<plurals name="outgoing_files_text">
<item quantity="one">%1$d Datei gesendet</item>
<item quantity="other">%1$d von %2$d Dateien gesendet</item>
</plurals>
<string name="received_file_text">Tippen um „%1s“ zu öffnen</string>
<string name="sent_file_title">Datei an %1s gesendet</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Das Senden der Datei an %1s ist fehlgeschlagen</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Tippen zum Antworten</string>
<string name="reconnect">Erneut verbinden</string>
<string name="right_click">Rechtsklick senden</string>
@@ -184,7 +174,6 @@
<string name="plugins_need_optional_permission">Einige Module haben eingeschränkte Funktionen wegen fehlender Berechtigungen, tippen Sie für weitere Informationen:</string>
<string name="share_optional_permission_explanation">m Dateien zwischen Rechner und Telefon auszutauschen, muss der Zugriff auf den Telefonspeicher gewährt werden</string>
<string name="telepathy_permission_explanation">Um SMS vom Rechner aus zu lesen und zu versenden, muss der Zugriff auf die SMS-Funktion gewährt werden</string>
<string name="telephony_permission_explanation">Um Telefonate und SMS auf dem Rechner zu sehen, müssen Berechtigungen für Anrufe und SMS erteilt werden</string>
<string name="telephony_optional_permission_explanation">Um einen Namen anstelle einer Telefonnummer zu sehen, muss der Zugriff auf das Adressbuch gewährt werden</string>
<string name="select_ringtone">Einen Klingelton auswählen</string>
<string name="telephony_pref_blocked_title">Unterdrückte Nummern</string>

View File

@@ -84,13 +84,7 @@
<string name="pairing_request_from">Αίτημα σύζευξης από %1s</string>
<string name="received_url_title">Ελήφθη σύνδεσμος από %1s</string>
<string name="received_url_text">Χτυπήστε για άνοιγμα \'%1s\'</string>
<string name="outgoing_file_title">Αποστολή αρχείου σε %1s</string>
<string name="outgoing_files_title">Αποστολή αρχείων σε %1s</string>
<string name="received_file_text">Χτυπήστε για άνοιγμα \'%1s\'</string>
<string name="sent_file_title">Εστάλη αρχείο σε %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Αποτυχία αποστολής αρχείου σε %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Χτυπήστε για να απαντήσετε</string>
<string name="reconnect">Επανασύνδεση</string>
<string name="right_click">Αποστολή δεξιού κλικ</string>
@@ -172,6 +166,5 @@
<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">Για να δείτε τηλεφωνικές κλήσεις και SMS από την επιφάνεια εργασίας, χρειάζεται να παραχωρήσετε δικαιώματα σε τηλεφωνικές κλήσεις και SMS</string>
<string name="telephony_optional_permission_explanation">Για να δείτε το όνομα επαφής αντί για τον αριθμό κλήσης χρειάζεται να παραχωρήσετε πρόσβαση στις επαφές στο τηλέφωνο</string>
</resources>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_clipboard_desc">Share the clipboard content</string>
<string name="pref_plugin_mousepad">Remote input</string>
<string name="pref_plugin_mousepad_desc">Use your phone or tablet as a touchpad and keyboard</string>
<string name="pref_plugin_presenter">Slideshow remote</string>
<string name="pref_plugin_presenter_desc">Use your device to change slides in a presentation</string>
<string name="pref_plugin_remotekeyboard">Receive remote keypresses</string>
<string name="pref_plugin_remotekeyboard_desc">Receive keypress events from remote devices</string>
@@ -37,6 +38,7 @@
<string name="open_settings">Open settings</string>
<string name="no_permissions">You need to grant permission to access notifications</string>
<string name="no_permission_mprisreceiver">To be able to control your media players you need to grant access to the notifications</string>
<string name="no_permissions_remotekeyboard">To receive keypresses you need to activate the KDE Connect Remote Keyboard</string>
<string name="send_ping">Send ping</string>
<string name="open_mpris_controls">Multimedia control</string>
<string name="remotekeyboard_editing_only_title">Handle remote keys only when editing</string>
@@ -94,17 +96,40 @@
<string name="pairing_request_from">Pairing request from %1s</string>
<string name="received_url_title">Received link from %1s</string>
<string name="received_url_text">Tap to open \'%1s\'</string>
<string name="outgoing_file_title">Sending file to %1s</string>
<string name="outgoing_files_title">Sending files to %1s</string>
<plurals name="incoming_file_title">
<item quantity="one">Receiving %1$d file from %2$s</item>
<item quantity="other">Receiving %1$d files from %2$s</item>
</plurals>
<plurals name="incoming_files_text">
<item quantity="one">File: %1s</item>
<item quantity="other">(File %2$d of %3$d) : %1$s</item>
</plurals>
<plurals name="outgoing_file_title">
<item quantity="one">Sending %1$d file to %2$s</item>
<item quantity="other">Sending %1$d files to %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Send files</item>
<item quantity="other">Sent %1$d out of %2$d files</item>
<item quantity="one">File: %1$s</item>
<item quantity="other">(File %2$d of %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Received file from %1$s</item>
<item quantity="other">Received %2$d files from %1$s</item>
</plurals>
<plurals name="received_files_fail_title">
<item quantity="one">Failed receiving file from %1$s</item>
<item quantity="other">Failed receiving %2$d of %3$d files from %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Sent file to %1$s</item>
<item quantity="other">Sent %2$d files to %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Failed sending file to %1$s</item>
<item quantity="other">Failed sending %2$d of %3$d files to %1$s</item>
</plurals>
<string name="received_file_text">Tap to open \'%1s\'</string>
<string name="sent_file_title">Sent file to %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Failed to send file to %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="cannot_create_file">Cannot create file %s</string>
<string name="tap_to_answer">Tap to answer</string>
<string name="reconnect">Reconnect</string>
<string name="right_click">Send Right Click</string>
@@ -150,6 +175,11 @@
<string name="pair_device_action">Pair a new device</string>
<string name="unpair_device_action">Unpair %s</string>
<string name="custom_device_list">Add devices by IP</string>
<string name="delete_custom_device">Delete %s?</string>
<string name="custom_device_deleted">Custom device deleted</string>
<string name="custom_device_list_help">If your device is not automatically detected you can add its IP address or hostname by clicking on the Floating Action Button</string>
<string name="custom_device_fab_hint">Add a device</string>
<string name="undo">Undo</string>
<string name="share_notification_preference">Noisy notifications</string>
<string name="share_notification_preference_summary">Vibrate and play a sound when receiving a file</string>
<string name="share_destination_customize">Customise destination directory</string>
@@ -165,7 +195,28 @@
<string name="sftp_sdcard">SD card</string>
<string name="sftp_readonly">(read only)</string>
<string name="sftp_camera">Camera pictures</string>
<string name="add_device_dialog_title">Add device</string>
<string name="add_device_hint">Hostname or IP address</string>
<string name="sftp_preference_detected_sdcards">Detected SD cards</string>
<string name="sftp_preference_edit_sdcard_title">Edit SD card</string>
<string name="sftp_preference_configured_storage_locations">Configured storage locations</string>
<string name="sftp_preference_add_storage_location_title">Add storage location</string>
<string name="sftp_preference_edit_storage_location">Edit storage location</string>
<string name="sftp_preference_add_camera_shortcut">Add camera folder shortcut</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Add a shortcut to the camera folder</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Do not add a shortcut to the camera folder</string>
<string name="sftp_storage_preference_storage_location">Storage location</string>
<string name="sftp_storage_preference_storage_location_already_configured">This location has already been configured</string>
<string name="sftp_storage_preference_click_to_select">click to select</string>
<string name="sftp_storage_preference_display_name">Display name</string>
<string name="sftp_storage_preference_display_name_already_used">This display name is already used</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Display name cannot be empty</string>
<string name="sftp_action_mode_menu_delete">Delete</string>
<string name="sftp_no_sdcard_detected">No SD card detected</string>
<string name="sftp_no_storage_locations_configured">No storage locations configured</string>
<string name="sftp_saf_permission_explanation">To access files remotely you have to configure storage locations</string>
<string name="add_host">Add host/IP</string>
<string name="add_host_hint">Hostname or IP</string>
<string name="no_players_connected">No players found</string>
<string name="mpris_player_on_device">%1$s on %2$s</string>
<string name="send_files">Send files</string>
@@ -195,7 +246,7 @@
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="share_optional_permission_explanation">To share files between your phone and your desktop you need to give access to the phone\'s storage</string>
<string name="telepathy_permission_explanation">To read and write SMS from your desktop you need to give permission to SMS</string>
<string name="telephony_permission_explanation">To see phone calls and SMS from the desktop you need to give permission to phone calls and SMS</string>
<string name="telephony_permission_explanation">To see phone calls on the desktop you need to give permission to phone call logs and phone state</string>
<string name="telephony_optional_permission_explanation">To see a contact name instead of a phone number you need to give access to the phone\'s contacts</string>
<string name="contacts_permission_explanation">To share your contacts book with the desktop, you need to give contacts permission</string>
<string name="select_ringtone">Select a ringtone</string>
@@ -206,6 +257,7 @@
<string name="settings_icon_description">Settings icon</string>
<string name="presenter_fullscreen">Full-screen</string>
<string name="presenter_exit">Exit presentation</string>
<string name="presenter_lock_tip">You can lock your device and use the volume keys to go to the previous/next slide</string>
<string name="add_command">Add a command</string>
<string name="addcommand_explanation">There are no commands registered</string>
<string name="addcommand_explanation2">You can add new commands in the KDE Connect System Settings</string>
@@ -237,4 +289,16 @@
<string name="settings_more_settings_title">More settings</string>
<string name="settings_more_settings_text">Per-device settings can be found under \'Plugin settings\' from within a device.</string>
<string name="setting_persistent_notification">Show persistent notification</string>
<string name="setting_persistent_notification_oreo">Persistent notification</string>
<string name="setting_persistent_notification_description">Tap to enable/disable in Notification settings</string>
<string name="extra_options">Extra options</string>
<string name="privacy_options">Privacy options</string>
<string name="set_privacy_options">Set your privacy options</string>
<string name="new_notification">New notification</string>
<string name="block_contents">Block contents of notifications</string>
<string name="block_images">Block images in notifications</string>
<string name="notification_channel_receivenotification">Notifications from other devices</string>
<string name="take_picture">Launch camera</string>
<string name="plugin_photo_desc">Launch the camera app to ease taking and transferring pictures</string>
<string name="no_app_for_opening">No suitable app found to open this file</string>
</resources>

View File

@@ -38,6 +38,7 @@
<string name="open_settings">Abrir preferencias</string>
<string name="no_permissions">Debe otorgar permisos para acceder a las notificaciones</string>
<string name="no_permission_mprisreceiver">Para poder controlar sus reproductores de medios, necesita dar acceso a las notificaciones</string>
<string name="no_permissions_remotekeyboard">Para recibir pulsaciones de teclado debe activar el teclado remoto de KDE Connect</string>
<string name="send_ping">Enviar ping</string>
<string name="open_mpris_controls">Control multimedia</string>
<string name="remotekeyboard_editing_only_title">Manejar teclas remotas solo al editar</string>
@@ -103,11 +104,13 @@
<item quantity="one">Archivo: %1s</item>
<item quantity="other">(Archivo %2$d de %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Enviando archivo a %1s</string>
<string name="outgoing_files_title">Enviando archivos a %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Enviando %1$d archivo a %2$s</item>
<item quantity="other">Enviando %1$d archivos a %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Enviado %1$d archivo</item>
<item quantity="other">Enviados %1$d de %2$d archivos</item>
<item quantity="one">Archivo: %1$s</item>
<item quantity="other">(Archivo %2$d de %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Recibido archivo desde %1$s</item>
@@ -117,12 +120,16 @@
<item quantity="one">Fallo recibiendo archivo desde %1$s</item>
<item quantity="other">Fallo recibiendo %2$d de %3$d archivos desde %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Archivo enviado a %1$s</item>
<item quantity="other">Enviados %2$d archivos a %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Fallo al enviar el archivo a %1$s</item>
<item quantity="other">Fallo al enviar %2$d de %3$d archivos a %1$s</item>
</plurals>
<string name="received_file_text">Pulse para abrir «%1s»</string>
<string name="cannot_create_file">No se pudo crear el archivo %s</string>
<string name="sent_file_title">Archivo enviado a %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Fallo al enviar el archivo a %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Pulse para responder</string>
<string name="reconnect">Reconectar</string>
<string name="right_click">Enviar clic derecho</string>
@@ -239,7 +246,7 @@
<string name="plugins_need_optional_permission">Algunos complementos tienen funcionalidades desactivadas por falta de permisos (pulse para más información):</string>
<string name="share_optional_permission_explanation">Para compartir archivos entre su teléfono y su escritorio, necesita dar acceso al almacenamiento de su teléfono</string>
<string name="telepathy_permission_explanation">Para leer y escribir SMS desde su escritorio, necesita dar permisos para SMS</string>
<string name="telephony_permission_explanation">Para ver las llamadas telefónicas y SMS desde su escritorio, necesita dar permisos para llamadas telefónicas y SMS</string>
<string name="telephony_permission_explanation">Para ver las llamadas telefónicas en el escritorio, necesita dar permisos al registro de llamadas telefónicas y al estado del teléfono</string>
<string name="telephony_optional_permission_explanation">Para ver el nombre de un contacto en lugar de un número telefónico, necesita dar acceso a los contactos de su teléfono</string>
<string name="contacts_permission_explanation">Para compartir sus contactos con el escritorio, necesita dar permisos de acceso a los mismos</string>
<string name="select_ringtone">Seleccionar tono</string>
@@ -293,4 +300,5 @@
<string name="notification_channel_receivenotification">Notificaciones desde otros dispositivos</string>
<string name="take_picture">Lanzar cámara</string>
<string name="plugin_photo_desc">Lanzar la aplicación de la cámara para facilitar tomar y transferir imágenes</string>
<string name="no_app_for_opening">No se encontró ninguna aplicación adecuada para abrir este archivo</string>
</resources>

View File

@@ -78,13 +78,7 @@
<string name="pairing_request_from">Paardumise soov seadmest %1s</string>
<string name="received_url_title">Lingi saamine seadmest %1s</string>
<string name="received_url_text">Koputa \"%1s\" avamiseks</string>
<string name="outgoing_file_title">Faili saatmine seadmesse %1s</string>
<string name="outgoing_files_title">Failide saatmine seadmesse %1s</string>
<string name="received_file_text">Koputa \"%1s\" avamiseks</string>
<string name="sent_file_title">Fail saadeti seadmesse %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Faili saatmine seadmesse %1s nurjus</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Koputa vastamiseks</string>
<string name="reconnect">Ühenda uuesti</string>
<string name="right_click">Saada paremklõps</string>

View File

@@ -102,12 +102,6 @@
<item quantity="one">Fitxategia: %1s</item>
<item quantity="other">(%3$d(e)tik %2$d fitxategia) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Fitxategia bidaltzen %1s-ra</string>
<string name="outgoing_files_title">Fitxategiak bidaltzen %1s-ra</string>
<plurals name="outgoing_files_text">
<item quantity="one">Bidali %1$d fitxategia</item>
<item quantity="other">Bidali %2$d fitxategitik %1$d</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Fitxategi bat jaso da %1$s-tik</item>
<item quantity="other">%2$d fitxategi jaso dira %1$s-tik</item>
@@ -118,10 +112,6 @@
</plurals>
<string name="received_file_text">Tak egin \'%1s\' irekitzeko</string>
<string name="cannot_create_file">Ezin da sortu %s fitxategia</string>
<string name="sent_file_title">Fitxategia bidalita %1s-ra</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Huts egin du fitxategia %1s(e)ra bidaltzea</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Tak egin erantzuteko</string>
<string name="reconnect">Birkonektatu</string>
<string name="right_click">Bidali eskumako klik</string>
@@ -213,7 +203,6 @@
<string name="plugins_need_optional_permission">Plugin batzuek desgaitutako eginbideak dituzte baimenak faltan dituztelako (tak egin informazio gehiagorako):</string>
<string name="share_optional_permission_explanation">Zure telefonoa eta mahaigainaren artean fitxategiak partekatzeko telefonoaren biltegiratzea atzitzeko baimena eman behar duzu</string>
<string name="telepathy_permission_explanation">SMSak zure mahaigainetik bidali ahal izateko, SMSak erabiltzeko baimena eman behar duzu</string>
<string name="telephony_permission_explanation">Telefono deiak eta SMSak zure mahaigainetik ikusteko, telefono deiak eta SMSak erabiltzeko baimena eman behar duzu</string>
<string name="telephony_optional_permission_explanation">Telefono zenbakiaren ordez kontaktuaren izena ikusteko telefonoko kontaktuak atzitzeko baimena eman behar duzu</string>
<string name="contacts_permission_explanation">Zure kontaktuen liburuak mahaigainarekin partekatzeko, kontaktuetara baimena eman behar duzu</string>
<string name="select_ringtone">Hautatu dei-tonu bat</string>

View File

@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="kde_connect">KDE Connect</string>
<string name="foreground_notification_no_devices">Ei laiteyhteyksiä</string>
<string name="foreground_notification_no_devices">Ei laiteyhteyttä</string>
<string name="foreground_notification_devices">Yhdistetty laitteeseen: %s</string>
<string name="pref_plugin_telephony">Puhelinilmoitukset</string>
<string name="pref_plugin_telephony_desc">Lähetä ilmoitukset saapuvista puheluista</string>
@@ -13,6 +13,7 @@
<string name="pref_plugin_clipboard_desc">Jaa leikepöydän sisältö</string>
<string name="pref_plugin_mousepad">Kauko-ohjaus</string>
<string name="pref_plugin_mousepad_desc">Käytä puhelinta tai tablettia hiirenä ja näppäimistönä</string>
<string name="pref_plugin_presenter">Etädiaesitys</string>
<string name="pref_plugin_presenter_desc">Käytä laitettasi esitysdiojen vaihtamiseen</string>
<string name="pref_plugin_remotekeyboard">Vastaanota etänäppäinpainallukset</string>
<string name="pref_plugin_remotekeyboard_desc">Vastaanottaa etälaitteiden näppäinpainallustapahtumat</string>
@@ -37,6 +38,7 @@
<string name="open_settings">Avaa asetukset</string>
<string name="no_permissions">Ilmoitusten näkemiseksi sinun on annettava käyttöoikeus ilmoituksiin</string>
<string name="no_permission_mprisreceiver">Hallitaksesi mediasoittimiasi sinun on annettava käyttöoikeudet ilmoituksiin</string>
<string name="no_permissions_remotekeyboard">Näppäinpainallusten vastaanottamiseksi KDE Connectin etänäppäimistö on aktivoitava</string>
<string name="send_ping">Lähetä tiedustelupaketti</string>
<string name="open_mpris_controls">Multimedian ohjaus</string>
<string name="remotekeyboard_editing_only_title">Käsittele etänäppäimet vain muokattaessa</string>
@@ -102,11 +104,13 @@
<item quantity="one">Tiedosto: %1s</item>
<item quantity="other">(Tiedosto %2$d / %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Lähetetään tiedostoa laitteeseen %1s</string>
<string name="outgoing_files_title">Lähetetään tiedostoa laitteeseen %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Lähetetään %1$d tiedosto laitteeseen %2$s</item>
<item quantity="other">Lähetetään %1$d tiedostoa laitteeseen %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Lähetetty %1$d tiedosto</item>
<item quantity="other">Lähetetty %1$d/%2$d tiedostoa</item>
<item quantity="one">Tiedosto: %1$s</item>
<item quantity="other">(Tiedosto %2$d/%3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Vastaanotettiin tiedosto lähettäjältä %1$s</item>
@@ -116,12 +120,16 @@
<item quantity="one">Ei voitu vastaanottaa tiedostoa lähettäjältä %1$s</item>
<item quantity="other">Ei voitu vastaanottaa %2$d/%3$d tiedostoa lähettäjältä %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Tiedosto lähetetty laitteeseen %1$s</item>
<item quantity="other">%2$d tiedostoa lähetetty laitteeseen %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Tiedoston lähetys laitteelle %1$s epäonnistui</item>
<item quantity="other">%2$d/%3$d tiedoston lähetys laitteelle %1$s epäonnistui</item>
</plurals>
<string name="received_file_text">Avaa ”%1s” napauttamalla</string>
<string name="cannot_create_file">Ei voida luoda tiedostoa %s</string>
<string name="sent_file_title">Tiedosto lähetetty laitteeseen %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Tiedoston lähetys laitteelle %1s epäonnistui</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Vastaa napauttamalla</string>
<string name="reconnect">Yhdistä uudelleen</string>
<string name="right_click">Lähetä oikean painikkeen napsautus</string>
@@ -168,6 +176,10 @@
<string name="unpair_device_action">Poista laitepari %s</string>
<string name="custom_device_list">Lisää laitteita IP:llä</string>
<string name="delete_custom_device">Poistetaanko %s?</string>
<string name="custom_device_deleted">Poistettiin mukautettu laite</string>
<string name="custom_device_list_help">Ellei laitetta tunnisteta automaattisesti, sen IP-osoitteen tai konenimen voi lisätä napsauttamalla kelluvaa toimintopainiketta</string>
<string name="custom_device_fab_hint">Lisää laite</string>
<string name="undo">Kumoa</string>
<string name="share_notification_preference">Äänekkäät ilmoitukset</string>
<string name="share_notification_preference_summary">Värise ja soita ääni tiedoston saapuessa</string>
<string name="share_destination_customize">Vaihda kohdekansio</string>
@@ -183,7 +195,28 @@
<string name="sftp_sdcard">SD-kortti</string>
<string name="sftp_readonly">(vain luku)</string>
<string name="sftp_camera">Kamerakuvat</string>
<string name="add_device_dialog_title">Lisää laite</string>
<string name="add_device_hint">Konenimi tai IP-osoite</string>
<string name="sftp_preference_detected_sdcards">Havaitut SD-kortit</string>
<string name="sftp_preference_edit_sdcard_title">Muokkaa SD-korttia</string>
<string name="sftp_preference_configured_storage_locations">Asetetut tallennustilat</string>
<string name="sftp_preference_add_storage_location_title">Lisää tallennustila</string>
<string name="sftp_preference_edit_storage_location">Muokkaa tallennustilaa</string>
<string name="sftp_preference_add_camera_shortcut">Lisää oikopolku kamerakansioon</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Lisää oikopolku kamerakansioon</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Älä lisää oikopolkua kamerakansioon</string>
<string name="sftp_storage_preference_storage_location">Tallennustila</string>
<string name="sftp_storage_preference_storage_location_already_configured">Tämä sijainti on jo asetettu</string>
<string name="sftp_storage_preference_click_to_select">valitse napsauttamalla</string>
<string name="sftp_storage_preference_display_name">Näyttönimi</string>
<string name="sftp_storage_preference_display_name_already_used">Tämä näyttönimi on jo käytössä</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Näyttönimi ei voi olla tyhjä</string>
<string name="sftp_action_mode_menu_delete">Poista</string>
<string name="sftp_no_sdcard_detected">SD-kortteja ei havaittu</string>
<string name="sftp_no_storage_locations_configured">Tallennustiloja ei ole asetettu</string>
<string name="sftp_saf_permission_explanation">Tallennustilat on asetettava etätiedostojen käyttämiseksi</string>
<string name="add_host">Lisää kone/IP</string>
<string name="add_host_hint">Konenimi tai IP</string>
<string name="no_players_connected">Soittimia ei löytynyt</string>
<string name="mpris_player_on_device">%1$s laitteella %2$s</string>
<string name="send_files">Lähetä tiedostoja</string>
@@ -213,7 +246,7 @@
<string name="plugins_need_optional_permission">Jotkin liitännäisten ominaisuudet eivät ole käytössä puuttuvien käyttöoikeuksien takia (lisätietoa napsauttamalla):</string>
<string name="share_optional_permission_explanation">Jakaaksesi tiedostoja puhelimen ja työpöydän välillä sinun on annettava käyttöoikeudet puhelimen tallennustilaan</string>
<string name="telepathy_permission_explanation">Lukeaksesi ja lähettääksesi tekstiviestejä työpöydältä sinun on annettava käyttöoikeudet tekstiviesteihin</string>
<string name="telephony_permission_explanation">Nähdäksesi soitot ja tekstiviestit työpöydältä sinun on annettava käyttöoikeudet puheluihin ja tekstiviesteihin</string>
<string name="telephony_permission_explanation">Puhelujen näyttäminen työpöydällä vaatii käyttöoikeuden puhelulokiin ja puhelimen tilaan</string>
<string name="telephony_optional_permission_explanation">Puhelimen yhteystietoihin on annettava käyttöoikeudet, jotta voit nähdä yhteystiedoissa nimet puhelinnumerojen sijaan</string>
<string name="contacts_permission_explanation">Käyttääksesi yhteystietoja työpöydältä sinun on annettava yhteystietojen käyttöoikeudet</string>
<string name="select_ringtone">Valitse soittoääni</string>
@@ -224,6 +257,7 @@
<string name="settings_icon_description">Asetuskuvake</string>
<string name="presenter_fullscreen">Koko näyttö</string>
<string name="presenter_exit">Poistu esitystilasta</string>
<string name="presenter_lock_tip">Laitteen voi lukita ja käyttää äänenvoimakkuuspainikkeita edellinen/seuraava dia -painikkeina</string>
<string name="add_command">Lisää komento</string>
<string name="addcommand_explanation">Komentoja ei ole rekisteröity</string>
<string name="addcommand_explanation2">Voit lisätä uusia komentoja KDE Connectin järjestelmäasetuksissa</string>
@@ -255,6 +289,7 @@
<string name="settings_more_settings_title">Lisää asetuksia</string>
<string name="settings_more_settings_text">Laitekohtaiset asetukset löytyvät laitteen ”Liitännäisasetuksista”.</string>
<string name="setting_persistent_notification">Näytä pysyvä ilmoitus</string>
<string name="setting_persistent_notification_oreo">Pysyvä ilmoitus</string>
<string name="extra_options">Lisäasetukset</string>
<string name="privacy_options">Yksityisyysasetukset</string>
<string name="set_privacy_options">Aseta yksityisyysasetukset</string>
@@ -262,4 +297,7 @@
<string name="block_contents">Estä ilmoitusten sisältö</string>
<string name="block_images">Estä ilmoitusten kuvat</string>
<string name="notification_channel_receivenotification">Muiden laitteiden ilmoitukset</string>
<string name="take_picture">Käynnistä kamera</string>
<string name="plugin_photo_desc">Helpota kuvien ottamista ja siirtämistä käynnistämällä kamerasovellus</string>
<string name="no_app_for_opening">Tämän tiedoston avaamiseen sopivaa sovellusta ei löytynyt</string>
</resources>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_clipboard_desc">Partage le contenu du presse-papiers</string>
<string name="pref_plugin_mousepad">Contrôle distant</string>
<string name="pref_plugin_mousepad_desc">Utilisez votre téléphone ou tablette comme un pavé tactile et un clavier</string>
<string name="pref_plugin_presenter">Télécommande de présentation</string>
<string name="pref_plugin_presenter_desc">Utilisez votre appareil pour changer les diapositives d\'une présentation</string>
<string name="pref_plugin_remotekeyboard">Recevoir les appuis de touches distants</string>
<string name="pref_plugin_remotekeyboard_desc">Recevoir les appuis de touches des périphériques distants</string>
@@ -37,10 +38,11 @@
<string name="open_settings">Accéder aux paramètres</string>
<string name="no_permissions">Vous devez accorder la permission d\'accéder aux notifications</string>
<string name="no_permission_mprisreceiver">Pour pouvoir contrôler vos lecteurs multimédia, veuillez permettre l\'accès aux notifications</string>
<string name="no_permissions_remotekeyboard">Vous devez activer le Clavier à distance KDE Connect pour recevoir les appuis sur les touches</string>
<string name="send_ping">Envoyer un « Ping »</string>
<string name="open_mpris_controls">Contrôles multimédia</string>
<string name="remotekeyboard_editing_only_title">Gérer les appuis de touches à distance uniquement lors de l\'édition</string>
<string name="remotekeyboard_not_connected">Aucune connexion active d\'un clavier sans fil disponible, établissez-en une dans KDE Connect</string>
<string name="remotekeyboard_not_connected">Aucune connexion active d\'un clavier à distance disponible, établissez-en une dans KDE Connect</string>
<string name="remotekeyboard_connected">La connexion au clavier sans fil est active</string>
<string name="remotekeyboard_multiple_connections">Plusieurs connexions à des claviers sans fil sont disponibles, sélectionnez le périphérique à configurer</string>
<string name="open_mousepad">Contrôle distant</string>
@@ -102,12 +104,6 @@
<item quantity="one">Fichier : %1s</item>
<item quantity="other">(Fichier %2$d sur %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Envoi d\'un fichier à %1s</string>
<string name="outgoing_files_title">Envoi de fichiers à %1s</string>
<plurals name="outgoing_files_text">
<item quantity="one">%1$d fichier envoyé</item>
<item quantity="other">%1$d fichiers envoyés sur %2$d</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Fichier reçu de %1$s</item>
<item quantity="other">%2$d fichiers reçus de %1$s</item>
@@ -118,10 +114,6 @@
</plurals>
<string name="received_file_text">Appuyez pour ouvrir %1s</string>
<string name="cannot_create_file">Impossible de créer le fichier %s</string>
<string name="sent_file_title">Fichier envoyé à %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Impossible d\'envoyer le fichier à %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Cliquer pour répondre</string>
<string name="reconnect">Reconnecter</string>
<string name="right_click">Envoyer un clic droit</string>
@@ -168,6 +160,10 @@
<string name="unpair_device_action">Dissocier %s</string>
<string name="custom_device_list">Ajouter des périphériques par IP</string>
<string name="delete_custom_device">Supprimer %s ?</string>
<string name="custom_device_deleted">Périphérique personnalisé supprimé</string>
<string name="custom_device_list_help">Si votre périphérique n\'est pas détecté automatiquement, vous pouvez ajouter son adresse IP ou son nom d\'hôte en cliquant sur le bouton d\'action flottant</string>
<string name="custom_device_fab_hint">Ajouter un périphérique</string>
<string name="undo">Annuler</string>
<string name="share_notification_preference">Notifications sonores</string>
<string name="share_notification_preference_summary">Vibrer et jouer un son quand un fichier est reçu</string>
<string name="share_destination_customize">Personnaliser le dossier de destination</string>
@@ -183,6 +179,26 @@
<string name="sftp_sdcard">Carte SD</string>
<string name="sftp_readonly">(lecture seule)</string>
<string name="sftp_camera">Images de l\'appareil photo</string>
<string name="add_device_dialog_title">Ajouter un périphérique</string>
<string name="add_device_hint">Nom d\'hôte ou adresse IP</string>
<string name="sftp_preference_detected_sdcards">Cartes SD détectées</string>
<string name="sftp_preference_edit_sdcard_title">Modifier la carte SD</string>
<string name="sftp_preference_configured_storage_locations">Emplacements de stockage configurés</string>
<string name="sftp_preference_add_storage_location_title">Ajouter un emplacement de stockage</string>
<string name="sftp_preference_edit_storage_location">Modifier un emplacement de stockage</string>
<string name="sftp_preference_add_camera_shortcut">Ajouter un raccourci pour le dossier de l\'appareil photo</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Ajouter un raccourci vers le dossier de l\'appareil photo</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Ne pas ajouter de raccourci vers le dossier de l\'appareil photo</string>
<string name="sftp_storage_preference_storage_location">Emplacement de stockage</string>
<string name="sftp_storage_preference_storage_location_already_configured">Cet emplacement est déjà configuré</string>
<string name="sftp_storage_preference_click_to_select">cliquez pour sélectionner</string>
<string name="sftp_storage_preference_display_name">Nom d\'affichage</string>
<string name="sftp_storage_preference_display_name_already_used">Ce nom d\'affichage est déjà utilisé</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Impossible de configurer un nom d\'affichage vide</string>
<string name="sftp_action_mode_menu_delete">Supprimer</string>
<string name="sftp_no_sdcard_detected">Aucune carte SD détectée</string>
<string name="sftp_no_storage_locations_configured">Aucun emplacement stockage n\'est configuré</string>
<string name="sftp_saf_permission_explanation">Vous devez configurer des emplacements de stockage pour accéder aux fichiers à distance</string>
<string name="add_host">Ajouter hôte/IP</string>
<string name="add_host_hint">"Nom d\'hôte ou adresse IP "</string>
<string name="no_players_connected">Aucun lecteur trouvé</string>
@@ -214,7 +230,6 @@
<string name="plugins_need_optional_permission">Certaines fonctionnalités de modules externes sont désactivées faute de permissions suffisantes (tapez pour plus d\'informations) :</string>
<string name="share_optional_permission_explanation">Pour partager des fichiers entre votre téléphone et votre ordinateur, veuillez permettre l\'accès à la mémoire de stockage du téléphone</string>
<string name="telepathy_permission_explanation">Pour lire et écrire des SMS depuis votre ordinateur, veuillez permettre l\'accès aux SMS</string>
<string name="telephony_permission_explanation">Pour voir les appels et les SMS depuis votre ordinateur, veuillez permettre l\'accès aux appels et aux SMS</string>
<string name="telephony_optional_permission_explanation">Pour voir le nom du contact au lieu du numéro de téléphone, veuillez permettre l\'accès aux contacts du téléphone</string>
<string name="contacts_permission_explanation">Pour partager votre carnet de contacts avec votre ordinateur, veuillez permettre l\'accès aux contacts du téléphone</string>
<string name="select_ringtone">Sélectionnez une sonnerie</string>
@@ -225,6 +240,7 @@
<string name="settings_icon_description">Icône des paramètres</string>
<string name="presenter_fullscreen">Plein écran</string>
<string name="presenter_exit">Quitter la présentation</string>
<string name="presenter_lock_tip">Vous pouvez verrouiller votre appareil et utiliser les touches de volume pour passer d\'une diapositive à l\'autre</string>
<string name="add_command">Ajouter une commande</string>
<string name="addcommand_explanation">Aucune commande enregistrée</string>
<string name="addcommand_explanation2">Vous pouvez ajouter de nouvelles commandes dans la configuration système de KDE Connect</string>
@@ -265,4 +281,7 @@
<string name="block_contents">Bloquer les contenus des notifications</string>
<string name="block_images">Bloquer les images des notifications</string>
<string name="notification_channel_receivenotification">Notifications provenant d\'autres périphériques</string>
<string name="take_picture">Lancer l\'appareil photo</string>
<string name="plugin_photo_desc">Lancer l\'application appareil photo pour prendre et transférer des photos</string>
<string name="no_app_for_opening">Aucune application adaptée trouvée pour ouvrir ce fichier.</string>
</resources>

View File

@@ -104,11 +104,13 @@
<item quantity="one">Ficheiro: %1s</item>
<item quantity="other">(Ficheiro %2$d de %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Enviando un ficheiro a %1s</string>
<string name="outgoing_files_title">Enviando os ficheiros a %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Enviando %1$d ficheiro a %2$s</item>
<item quantity="other">Enviando %1$d ficheiros a %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Enviouse %1$d ficheiro.</item>
<item quantity="other">Enviáronse %1$d de %2$d ficheiros.</item>
<item quantity="one">Ficheiro: %1$s</item>
<item quantity="other">(Ficheiro %2$d de %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Recibiuse un ficheiro de %1$s</item>
@@ -118,12 +120,16 @@
<item quantity="one">A recepción do ficheiro de %1$s fallou</item>
<item quantity="other">A recepción de %2$d de %3$d ficheiros de %1$s fallou</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Enviouse o ficheiro a %1$s</item>
<item quantity="other">Enviáronse os %2$d ficheiros a %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Non se puido enviar o ficheiro a %1$s</item>
<item quantity="other">Non se puideron enviar %2$d dos %3$d ficheiros a %1$s</item>
</plurals>
<string name="received_file_text">Toque para abrir «%1s».</string>
<string name="cannot_create_file">Non se pode crear o ficheiro %s</string>
<string name="sent_file_title">Enviouse o ficheiro a %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Non se puido enviar o ficheiro a %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Toque para contestar</string>
<string name="reconnect">Conectar de novo</string>
<string name="right_click">Enviar un clic secundario</string>
@@ -240,7 +246,7 @@
<string name="plugins_need_optional_permission">Algúns complementos teñen funcionalidades desactivadas por mor dunha falta de permisos (toque para máis información):</string>
<string name="share_optional_permission_explanation">Para compartir ficheiros entre o teléfono e o escritorio ten que dar acceso ao almacenamento do teléfono.</string>
<string name="telepathy_permission_explanation">Para ler e escribir SMS desde o escritorio ten que dar permiso de SMS.</string>
<string name="telephony_permission_explanation">Para ver as chamadas de teléfono e os SMS desde o escritorio ten que dar permiso a chamadas de teléfono e a SMS.</string>
<string name="telephony_permission_explanation">Para ver as chamadas de teléfono no escritorio ten que dar permiso aos rexistros de chamadas telefónicas e ao estado do teléfono</string>
<string name="telephony_optional_permission_explanation">Para ver o nome dun contacto en vez dun número de teléfono ten que dar acceso aos contactos do teléfono.</string>
<string name="contacts_permission_explanation">Para compartir o caderno de contactos co escritorio ten que dar permiso de contactos</string>
<string name="select_ringtone">Seleccione un son de chamada</string>
@@ -294,4 +300,5 @@
<string name="notification_channel_receivenotification">Notificacións desde outros dispositivos</string>
<string name="take_picture">Iniciar a cámara</string>
<string name="plugin_photo_desc">Iniciar o aplicativo da cámara para facilitar sacar e transferir imaxes</string>
<string name="no_app_for_opening">Non se atopou ningún aplicativo axeitado para abrir este ficheiro</string>
</resources>

View File

@@ -84,13 +84,7 @@
<string name="pairing_request_from">בוקשה התאמה מ־%1s</string>
<string name="received_url_title">התקבל קישור מ־%1s</string>
<string name="received_url_text">לחץ כדי לפתוח את \"%1s\"</string>
<string name="outgoing_file_title">שולח קובץ אל %1s</string>
<string name="outgoing_files_title">שולח קובצים אל %1s</string>
<string name="received_file_text">לחץ כדי לפתוח את \"%1s\"</string>
<string name="sent_file_title">הקובץ נשלח אל %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">נכשל בשליחת הקובץ אל %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">לחץ כדי לענות</string>
<string name="reconnect">התחבר מחדש</string>
<string name="right_click">שלח לחיצה ימנית</string>

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 isi 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>
@@ -99,11 +99,6 @@
<plurals name="incoming_files_text">
<item quantity="other">File: %1s</item>
</plurals>
<string name="outgoing_file_title">Mengirim file ke %1s</string>
<string name="outgoing_files_title">Mengirim file ke %1s</string>
<plurals name="outgoing_files_text">
<item quantity="other">Kirimkan file %1$d</item>
</plurals>
<plurals name="received_files_title">
<item quantity="other">File yang diterima dari %1$s</item>
</plurals>
@@ -112,10 +107,6 @@
</plurals>
<string name="received_file_text">Ketuk untuk membuka \'%1s\'</string>
<string name="cannot_create_file">Gak bisa menciptakan file %s</string>
<string name="sent_file_title">Kirim file ke %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Gagal mengirim file ke %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Ketuk untuk menjawab</string>
<string name="reconnect">Sambung-ulang</string>
<string name="right_click">Kirim Klik Kanan</string>
@@ -156,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>
@@ -206,7 +197,6 @@
<string name="plugins_need_optional_permission">Beberapa plugin yang memiliki fitur dinonfungsikan karena kurangnya perizinan (ketuk untuk info selebihnya):</string>
<string name="share_optional_permission_explanation">Untuk membagikan file antara teleponmu dan desktopmu kamu harus memberikan akses ke penyimpanan teleponmu</string>
<string name="telepathy_permission_explanation">Untuk membaca dan menulis SMS dari desktopmu kamu harus memberikan perizinan untuk SMS</string>
<string name="telephony_permission_explanation">Untuk melihat paggian telepon dan SMS dari desktopmu kamu harus memberikan perizinan untuk panggilan telepon dan SMS</string>
<string name="telephony_optional_permission_explanation">Untuk melihat nama kontak alih-alih nomor telepon, kamu harus memberikan akses ke kontak telepon</string>
<string name="contacts_permission_explanation">Untuk membagikan buku kontak dengan desktopmu, kamu harus memberikan perizinan kontak</string>
<string name="select_ringtone">Pilih sebuah ringtone</string>

View File

@@ -38,6 +38,7 @@
<string name="open_settings">Apri impostazioni</string>
<string name="no_permissions">Devi concedere i permessi per l\'accesso alle notifiche</string>
<string name="no_permission_mprisreceiver">Per poter controllare i tuoi lettori multimediali devi accordare l\'accesso alle notifiche</string>
<string name="no_permissions_remotekeyboard">Per ricevere pressioni di tasti, devi attivare la tastiera remota di KDE Connect</string>
<string name="send_ping">Invia ping</string>
<string name="open_mpris_controls">Controllo multimediale</string>
<string name="remotekeyboard_editing_only_title">Gestisci i tasti remoti solo durante la modifica</string>
@@ -103,11 +104,13 @@
<item quantity="one">File: %1s</item>
<item quantity="other">(File %2$d di %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Invio file a %1s</string>
<string name="outgoing_files_title">Invio file a %1s</string>
<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">Inviato %1$d file</item>
<item quantity="other">Inviati %1$d di %2$d file</item>
<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>
@@ -117,12 +120,16 @@
<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="sent_file_title">File inviato a %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Invio del file a %1s non riuscito</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Tocca per rispondere</string>
<string name="reconnect">Riconnetti</string>
<string name="right_click">Invia clic tasto destro</string>
@@ -190,7 +197,26 @@
<string name="sftp_camera">Immagini fotocamera</string>
<string name="add_device_dialog_title">Aggiungi dispositivo</string>
<string name="add_device_hint">Nome host o indirizzo IP</string>
<string name="sftp_preference_detected_sdcards">Schede SD rilevate</string>
<string name="sftp_preference_edit_sdcard_title">Modifica scheda SD</string>
<string name="sftp_preference_configured_storage_locations">Posizioni di archiviazione configurate</string>
<string name="sftp_preference_add_storage_location_title">Aggiungi posizione di archiviazione</string>
<string name="sftp_preference_edit_storage_location">Modifica posizione di archiviazione</string>
<string name="sftp_preference_add_camera_shortcut">Aggiungi scorciatoia alla cartella della fotocamera</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Aggiungi una scorciatoia alla cartella della fotocamera</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Non aggiungere una scorciatoia alla cartella della fotocamera</string>
<string name="sftp_storage_preference_storage_location">Posizione di archiviazione</string>
<string name="sftp_storage_preference_storage_location_already_configured">Questa posizione è già stata configurata</string>
<string name="sftp_storage_preference_click_to_select">clic per selezionare</string>
<string name="sftp_storage_preference_display_name">Nome visualizzato</string>
<string name="sftp_storage_preference_display_name_already_used">Il nome visualizzato è già in uso</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Il nome visualizzato non può essere vuoto</string>
<string name="sftp_action_mode_menu_delete">Elimina</string>
<string name="sftp_no_sdcard_detected">Nessuna scheda SD rilevata</string>
<string name="sftp_no_storage_locations_configured">Nessuna posizione di archiviazione configurata</string>
<string name="sftp_saf_permission_explanation">Per accedere da remoto ai file, devi configurare posizioni di archiviazione</string>
<string name="add_host">Aggiungi host/IP</string>
<string name="add_host_hint">Nome host o IP</string>
<string name="no_players_connected">Nessun lettore trovato</string>
<string name="mpris_player_on_device">%1$s su %2$s</string>
<string name="send_files">Invia file</string>
@@ -220,7 +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 e gli SMS dal desktop devi dare l\'autorizzazione per telefonate e 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>
@@ -272,4 +298,7 @@
<string name="block_contents">Blocca i contenuti delle notifiche</string>
<string name="block_images">Blocca le immagini nelle notifiche</string>
<string name="notification_channel_receivenotification">Notifiche da altri dispositivi</string>
<string name="take_picture">Avvia fotocamera</string>
<string name="plugin_photo_desc">Avvia l\'applicazione della fotocamera per scattare e trasferire foto con facilità</string>
<string name="no_app_for_opening">Nessuna applicazione appropriata trovata per aprire questo file</string>
</resources>

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,16 +96,32 @@
<string name="pairing_request_from">%1s에서 연결 요청</string>
<string name="received_url_title">%1s에서 링크 받음</string>
<string name="received_url_text">\'%1s\'을(를) 열려면 누르십시오</string>
<string name="outgoing_file_title">%1s(으)로 파일 보내는 중</string>
<string name="outgoing_files_title">%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">파일 %2$d개 중 %1$d개 보냄</item>
<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="sent_file_title">%1s(으)로 파일 보냄</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">파일을 %1s(으)로 보낼 수 없음</string>
<string name="sent_file_failed_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>
@@ -111,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>
@@ -120,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>
@@ -128,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>
@@ -136,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>
@@ -156,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>
@@ -186,7 +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_permission_explanation">데스크톱에서 통화와 문자 메시지를 보려면 통화 기록 및 휴대폰 상태 접근 권한이 필요합니다</string>
<string name="telephony_optional_permission_explanation">전화번호 대신 연락처에 등록된 이름을 보려면 주소록 접근 권한이 필요합니다</string>
<string name="contacts_permission_explanation">데스크톱과 주소록을 공유하려면 주소록 접근 권한이 필요합니다</string>
<string name="select_ringtone">벨소리 선택</string>
@@ -197,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>
@@ -204,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>
@@ -217,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

@@ -104,11 +104,13 @@
<item quantity="one">Bestand: %1s</item>
<item quantity="other">(Bestand %2$d van %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Bestand wordt verzonden naar %1s</string>
<string name="outgoing_files_title">Bezig bestanden te verzenden naar %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">%1$d bestand wordt naar %2$s verzonden</item>
<item quantity="other">%1$d bestanden worden naar %2$s verzonden</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Bestand %1$d verzenden</item>
<item quantity="other">%1$d bestanden uit %2$d verzenden</item>
<item quantity="one">Bestand: %1$s</item>
<item quantity="other">(Bestand %2$d van %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Ontvangen van bestand vanaf %1$s</item>
@@ -118,12 +120,16 @@
<item quantity="one">Ontvangen van bestand vanaf %1$s is mislukt</item>
<item quantity="other">Ontvangen van %2$d van %3$d bestanden vanaf %1$s is mislukt</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Bestand verzonden naar %1$s</item>
<item quantity="other">%2$d bestanden verzonden naar %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Verzenden van bestand naar %1$s is mislukt</item>
<item quantity="other">Verzenden van %2$d van %3$d bestanden naar %1$s is mislukt</item>
</plurals>
<string name="received_file_text">Tap om \'%1s\' te openen</string>
<string name="cannot_create_file">Kan bestand %s niet aanmaken</string>
<string name="sent_file_title">Bestand verzonden naar %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Verzenden van bestand naar %1s is mislukt</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Tap om te antwoorden</string>
<string name="reconnect">Opnieuw verbinden</string>
<string name="right_click">Verstuur een rechter muisklik</string>
@@ -240,7 +246,7 @@
<string name="plugins_need_optional_permission">Sommige plug-ins hebben functies uitgeschakeld vanwege ontbrekende toestemming (tik voor meer informatie):</string>
<string name="share_optional_permission_explanation">Om bestanden tussen uw telefoon en uw bureaublad te delen moet u toegang geven tot de opslag van uw telefoon</string>
<string name="telepathy_permission_explanation">Om een SMS te lezen of te schrijven vanaf uw bureaublad moet u toestemming geven tot SMS</string>
<string name="telephony_permission_explanation">Om telefoonoproepen en SMS te zien vanaf het bureaublad moet u toestemming geven tot telefoonoproepen en SMS</string>
<string name="telephony_permission_explanation">Om telefoonoproepen te zien op het bureaublad moet u toestemming geven tot telefoonoproeplogs en telefoonstatus</string>
<string name="telephony_optional_permission_explanation">Om een naam van een contactpersoon te zien in plaats van een telefoonnummer moet u toegang geven tot de contacten in uw telefoon</string>
<string name="contacts_permission_explanation">Om uw contactpersoennboek te delen met het bureaublad moet u contacten rechten geven</string>
<string name="select_ringtone">Selecteer een ringtone</string>
@@ -294,4 +300,5 @@
<string name="notification_channel_receivenotification">Meldingen van andere apparaten</string>
<string name="take_picture">Start camera</string>
<string name="plugin_photo_desc">Start de camera-app om nemen en overdragen van afbeeldingen te vergemakkelijken</string>
<string name="no_app_for_opening">Geen geschikte toepassing gevonden om dit bestand te openen</string>
</resources>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_clipboard_desc">Del innhaldet på utklippstavla</string>
<string name="pref_plugin_mousepad">Fjernstyring</string>
<string name="pref_plugin_mousepad_desc">Bruk telefonen eller nettbrettet som styreplate og tastatur</string>
<string name="pref_plugin_presenter">Fjernstyring av lysbiletvising</string>
<string name="pref_plugin_presenter_desc">Bruk eininga til å byta lysbilete i presentasjonar</string>
<string name="pref_plugin_remotekeyboard">Ta imot eksterne tastetrykk</string>
<string name="pref_plugin_remotekeyboard_desc">Ta imot tastetrykk frå eksterne einingar</string>
@@ -24,10 +25,10 @@
<string name="pref_plugin_contacts_desc">Tillat synkronisering av adresseboka på eininga</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send og ta imot pingsignal</string>
<string name="pref_plugin_notifications">Varslingssynkronisering</string>
<string name="pref_plugin_notifications">Synkroniser varslingar</string>
<string name="pref_plugin_notifications_desc">Få tilgang til varslingar frå andre einingar</string>
<string name="pref_plugin_receive_notifications"> varslingar</string>
<string name="pref_plugin_receive_notifications_desc"> varslingar frå den andre eininga og vis dei i Android</string>
<string name="pref_plugin_receive_notifications">Ta imot varslingar</string>
<string name="pref_plugin_receive_notifications_desc">Ta imot varslingar frå den andre eininga og vis dei i Android</string>
<string name="pref_plugin_sharereceiver">Del og ta imot</string>
<string name="pref_plugin_sharereceiver_desc">Del filer og nettadresser mellom einingar</string>
<string name="plugin_not_available">Denne funksjonen er ikkje tilgjengeleg i din Android-versjon</string>
@@ -37,6 +38,7 @@
<string name="open_settings">Opna innstillingar</string>
<string name="no_permissions">Du må gje tilgang til lesing av varslingar</string>
<string name="no_permission_mprisreceiver">Du må gje tilgang til varslingar for å kunne kontrollera mediespelarar</string>
<string name="no_permissions_remotekeyboard">For å ta imot tastetrykk må du slå på fjerntastaturet i KDE Connect</string>
<string name="send_ping">Send pingsignal</string>
<string name="open_mpris_controls">Mediekontroll</string>
<string name="remotekeyboard_editing_only_title">Handter eksterne tastetrykk berre ved redigering</string>
@@ -72,7 +74,7 @@
</string-array>
<string name="category_connected_devices">Tilkopla einingar</string>
<string name="category_not_paired_devices">Tilgjengelege einingar</string>
<string name="category_remembered_devices">Hugs einingar</string>
<string name="category_remembered_devices">Hugsa einingar</string>
<string name="device_menu_plugins">Programtillegg-oppsett</string>
<string name="device_menu_unpair">Løys paring</string>
<string name="device_not_reachable">Får ikkje kontakt med para eining</string>
@@ -102,11 +104,13 @@
<item quantity="one">Fil: %1s</item>
<item quantity="other">(Fil %2$d av %3$d): %1$s</item>
</plurals>
<string name="outgoing_file_title">Sender fil til %1s</string>
<string name="outgoing_files_title">Sender filer til %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Sender %1$d fil til %2$s</item>
<item quantity="other">Sender %1$d filer til %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Sende %1$d fil</item>
<item quantity="other">Sende %1$d av %2$d filer</item>
<item quantity="one">Fil: %1$s</item>
<item quantity="other">(Fil %2$d av %3$d): %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Fekk fil frå %1$s</item>
@@ -116,12 +120,16 @@
<item quantity="one">Klarte ikkje ta imot fil frå %1$s</item>
<item quantity="other">Klarte ikkje ta imot %2$d av %3$d filer frå %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Sende fil til %1$s</item>
<item quantity="other">Sende %2$d filer til %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Klarte ikkje senda fil til %1$s</item>
<item quantity="other">Klarte ikkje senda %2$d av %3$d filer til %1$s</item>
</plurals>
<string name="received_file_text">Trykk for å opna «%1s»</string>
<string name="cannot_create_file">Klarte ikkje oppretta fila %s</string>
<string name="sent_file_title">Send fil til %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Klarte ikkje senda fil til %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Trykk for å svara</string>
<string name="reconnect">Kopla til på nytt</string>
<string name="right_click">Send høgreklikk</string>
@@ -143,7 +151,7 @@
<string name="mpris_volume">Lydstyrke</string>
<string name="mpris_settings">Medieinnstillingar</string>
<string name="mpris_time_settings_title">Spoleknappar</string>
<string name="mpris_time_settings_summary">Juster tida for spoling ved trykking</string>
<string name="mpris_time_settings_summary">Juster kor langt spoleknappane skal spola</string>
<string-array name="mpris_time_entries">
<item>10 sekund</item>
<item>20 sekund</item>
@@ -168,8 +176,12 @@
<string name="unpair_device_action">Løys paring for %s</string>
<string name="custom_device_list">Legg til eining basert på IP</string>
<string name="delete_custom_device">Vil du sletta %s?</string>
<string name="custom_device_deleted">Tilpassa eining er sletta</string>
<string name="custom_device_list_help">Viss eininga ikkje vert funnen automatisk, kan du leggja til IP-adressa eller vertsnamnet til eininga ved å trykka på handlingsknappen</string>
<string name="custom_device_fab_hint">Legg til eining</string>
<string name="undo">Angra</string>
<string name="share_notification_preference">Lydvarsling</string>
<string name="share_notification_preference_summary">Vibrer og spel ein lyd ved mottak av fil</string>
<string name="share_notification_preference_summary">Vibrer og spel ein lyd ved mottak av filer</string>
<string name="share_destination_customize">Sjølvvald målmappe</string>
<string name="share_destination_customize_summary_disabled">Mottekne filer vert lagra i nedlastingsmappa</string>
<string name="share_destination_customize_summary_enabled">Mottekne filer vert lagra i mappa nedanfor</string>
@@ -183,7 +195,28 @@
<string name="sftp_sdcard">SD-kort</string>
<string name="sftp_readonly">(skriveverna)</string>
<string name="sftp_camera">Kamerabilete</string>
<string name="add_device_dialog_title">Legg til eining</string>
<string name="add_device_hint">Vertsnamn/IP-adresse</string>
<string name="sftp_preference_detected_sdcards">Oppdaga SD-kort</string>
<string name="sftp_preference_edit_sdcard_title">Rediger SD-kort</string>
<string name="sftp_preference_configured_storage_locations">Oppsette lagringsområde</string>
<string name="sftp_preference_add_storage_location_title">Legg til lagringsområde</string>
<string name="sftp_preference_edit_storage_location">Rediger lagringsområde</string>
<string name="sftp_preference_add_camera_shortcut">Legg til snarveg til kameramappe</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Legg til ein snarveg til kameramappa</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Ikkje legg til ein snarveg til kameramappa</string>
<string name="sftp_storage_preference_storage_location">Lagringsområde</string>
<string name="sftp_storage_preference_storage_location_already_configured">Dette lagringsområdet er alt sett opp</string>
<string name="sftp_storage_preference_click_to_select">trykk for å velja</string>
<string name="sftp_storage_preference_display_name">Visingsnamn</string>
<string name="sftp_storage_preference_display_name_already_used">Dette visingsnamnet er alt i bruk</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Visiningsnamnet kan ikkje vera tomt</string>
<string name="sftp_action_mode_menu_delete">Slett</string>
<string name="sftp_no_sdcard_detected">Fann ikkje noko SD-kort</string>
<string name="sftp_no_storage_locations_configured">Ingen lagringsområde er sette opp</string>
<string name="sftp_saf_permission_explanation">For å få tilgang til over nettet må du setja opp lagringsområde</string>
<string name="add_host">Legg til vert/IP</string>
<string name="add_host_hint">Vertsnamn/IP-adresse</string>
<string name="no_players_connected">Fann ingen spelarar</string>
<string name="mpris_player_on_device">%1$s på %2$s</string>
<string name="send_files">Send filer</string>
@@ -210,20 +243,21 @@
<string name="plugins_need_permission">Nokre av tillegga treng utvida løyva for å fungera (trykk på dei for meir informasjon):</string>
<string name="permission_explanation">Dette tillegget treng utvida løyve for å fungera</string>
<string name="optional_permission_explanation">Du må gje utvida løyve for at alle funksjonane skal fungera</string>
<string name="plugins_need_optional_permission">På grunn av manglande løyve har nokre av tillegga funksjonar slåtte av (trykk på dei for meir informasjon):</string>
<string name="plugins_need_optional_permission">På grunn av manglande løyve vil desse funksjonane ikkje verka (trykk på dei for meir informasjon):</string>
<string name="share_optional_permission_explanation">For å kunna dela filer mellom telefonen og datamaskina må du gje appen lese- og skriveløyve til lagringsområdet på telefonen</string>
<string name="telepathy_permission_explanation">For å kunna lesa og skriva tekstmeldingar frå datamaskina må du gje appen tilgang til SMS</string>
<string name="telephony_permission_explanation">For å kunna sjå telefonsamtalar og tekstmeldingar frå datamaskina må du gje appen tilgang til telefon- og SMS-funksjonar</string>
<string name="telephony_permission_explanation">For å kunna sjå telefonsamtalar på datamaskina må du gje appen tilgang til samtaleloggar og telefonstatus</string>
<string name="telephony_optional_permission_explanation">For å kunna sjå namn på kontaktar i staden for berre telefonnummeret må du gje appen tilgang til kontaktlista di</string>
<string name="contacts_permission_explanation">For å kunna dela adresseboka di med datamaskina må du gje appen lese- og skriveløyve til adresseboka</string>
<string name="select_ringtone">Vel ringjetone</string>
<string name="telephony_pref_blocked_title">Blokkerte nummer</string>
<string name="telephony_pref_blocked_dialog_desc">Ikkje vis oppringingar og SMS-ar frå desse telefonnummera. Skriv inn eitt telefonnummer per linje.</string>
<string name="telephony_pref_blocked_dialog_desc">Ikkje vis oppringingar eller SMS-ar frå desse telefonnummera. Skriv inn eitt telefonnummer per linje.</string>
<string name="mpris_coverart_description">Omslagsbilete til gjeldande mediefil</string>
<string name="device_icon_description">Einingsikon</string>
<string name="settings_icon_description">Innstillingsikon</string>
<string name="presenter_fullscreen">Fullskjerm</string>
<string name="presenter_exit">Avslutt presentasjon</string>
<string name="presenter_lock_tip">Du kan låsa eininga og bruka lydstyrke-knappane som førre/neste-knappar</string>
<string name="add_command">Legg til kommando</string>
<string name="addcommand_explanation">Ingen kommandoar er registrerte</string>
<string name="addcommand_explanation2">Du kan leggja til nye kommandoar i systeminnstillingane til KDE Connect</string>
@@ -264,4 +298,7 @@
<string name="block_contents">Blokker varslingsinnhald</string>
<string name="block_images">Blokker bilete i varslingar</string>
<string name="notification_channel_receivenotification">Varslingar frå andre einingar</string>
<string name="take_picture">Opna kamera</string>
<string name="plugin_photo_desc">Opna kamera-appen for å gjera det lettare å ta og overføra bilete</string>
<string name="no_app_for_opening">Fann ikkje nokon app som kan opna denne fila</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,12 +108,16 @@
<item quantity="many">(Plik %2$d z %3$d) : %1$s</item>
<item quantity="other"/>
</plurals>
<string name="outgoing_file_title">Wysyłanie pliku do %1s</string>
<string name="outgoing_files_title">Wysyłanie pliku do %1s</string>
<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">Wysłano %1$d plik</item>
<item quantity="few">Wysłano %1$d z %2$d plików</item>
<item quantity="many">Wysłano %1$d z %2$d plików</item>
<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">
@@ -126,12 +132,20 @@
<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="sent_file_title">Plik wysłano do %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Nie udało się wysyłanie pliku do %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Stuknij, aby odpowiedzieć</string>
<string name="reconnect">Połącz ponownie</string>
<string name="right_click">Wyślij naciskając prawym</string>
@@ -162,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>
@@ -178,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>
@@ -188,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>
@@ -217,14 +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_permission_explanation">Aby widzieć rozmowy telefoniczne i SMSy z twojego komputera musisz nadać uprawnienia na rozmowy telefoniczne i SMSy</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>
@@ -234,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>
@@ -263,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

@@ -38,6 +38,7 @@
<string name="open_settings">Abrir configurações</string>
<string name="no_permissions">Você precisa conceder permissão para acessar as notificações</string>
<string name="no_permission_mprisreceiver">Para ser possível controlar os seus leitores multimídia é necessário conceder acesso às notificações</string>
<string name="no_permissions_remotekeyboard">Para receber eventos de teclado é preciso ativar o Teclado Remoto do KDE Connect</string>
<string name="send_ping">Enviar ping</string>
<string name="open_mpris_controls">Controle multimídia</string>
<string name="remotekeyboard_editing_only_title">Lidar com as teclas remotas apenas na edição</string>
@@ -103,11 +104,13 @@
<item quantity="one">Arquivo: %1s</item>
<item quantity="other">(Arquivo %2$d de %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Enviando arquivo para %1s</string>
<string name="outgoing_files_title">Enviando arquivos para %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Enviando %1$d arquivo para %2$s</item>
<item quantity="other">Enviando %1$d arquivos para %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Enviado %1$d arquivo</item>
<item quantity="other">Enviados %1$d de %2$d arquivos</item>
<item quantity="one">Arquivo: %1$s</item>
<item quantity="other">(Arquivo %2$d de %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Arquivo recebido de %1$s</item>
@@ -117,12 +120,16 @@
<item quantity="one">Falha na recepção do arquivo de %1$s</item>
<item quantity="other">Falha na recepção de %2$d de %3$d arquivos de %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Enviar arquivo para %1$s</item>
<item quantity="other">Enviar %2$d arquivos para %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Não foi possível enviar o arquivo para %1$s</item>
<item quantity="other">Não foi possível enviar %2$d de %3$d arquivos para o %1$s</item>
</plurals>
<string name="received_file_text">Toque para abrir o \'%1s\'</string>
<string name="cannot_create_file">Não foi possível criar o arquivo %s</string>
<string name="sent_file_title">Enviar arquivo para %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Não foi possível enviar o arquivo para o %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Toque para responder</string>
<string name="reconnect">Reconectar</string>
<string name="right_click">Enviar um Botão Direito</string>
@@ -239,7 +246,7 @@
<string name="plugins_need_optional_permission">Alguns plugins possuem recursos desativados devido à falta de permissões (toque para obter mais informações):</string>
<string name="share_optional_permission_explanation">Para compartilhar arquivos entre o seu celular e o seu ambiente de trabalho é necessário permissão para acessar o armazenamento do seu celular</string>
<string name="telepathy_permission_explanation">Para ler e gravar SMS a partir do seu ambiente de trabalho é necessário conceder permissão para SMS</string>
<string name="telephony_permission_explanation">Para ver as chamadas e SMS do celular a partir do seu ambiente de trabalho é necessário conceder permissão para as chamadas telefônicas e SMS</string>
<string name="telephony_permission_explanation">Para ver as chamadas telefônicas no seu ambiente de trabalho é preciso dar permissões para registro de chamadas telefônicas e do estado do telefone</string>
<string name="telephony_optional_permission_explanation">Para ver o nome de um contato em vez do seu número de telefone é necessário conceder acesso aos contatos do celular</string>
<string name="contacts_permission_explanation">Para compartilhar o seu livro de endereços com o ambiente de trabalho é necessário conceder permissão para os contatos</string>
<string name="select_ringtone">Selecionar um toque de chamada</string>
@@ -293,4 +300,5 @@
<string name="notification_channel_receivenotification">Notificações dos outros dispositivos</string>
<string name="take_picture">Iniciar câmera</string>
<string name="plugin_photo_desc">Iniciar o aplicativo da câmera para facilitar a captura e transferência de fotos</string>
<string name="no_app_for_opening">Não foi encontrado nenhum aplicativo adequado para abrir este arquivo</string>
</resources>

View File

@@ -38,6 +38,7 @@
<string name="open_settings">Abrir a configuração</string>
<string name="no_permissions">Precisa de dar permissões de acesso às notificações</string>
<string name="no_permission_mprisreceiver">Para poder controlar os seus leitores multimédia, terá de dar acesso às notificações</string>
<string name="no_permissions_remotekeyboard">Para receber eventos de teclado, tem de activar o Teclado Remoto do KDE Connect</string>
<string name="send_ping">Enviar um pedido de contacto</string>
<string name="open_mpris_controls">Comando multimédia</string>
<string name="remotekeyboard_editing_only_title">Lidar com as teclas remotas apenas na edição</string>
@@ -103,11 +104,13 @@
<item quantity="one">Ficheiro: %1$s</item>
<item quantity="other">(Ficheiro %2$d de %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">A enviar o ficheiro para o %1s</string>
<string name="outgoing_files_title">A enviar os ficheiros para o %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">A enviar %1$d ficheiro para o %2$s</item>
<item quantity="other">A enviar %1$d ficheiros para o %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Foi enviado %1$d ficheiro</item>
<item quantity="other">Foram enviados %1$d de %2$d ficheiros</item>
<item quantity="one">Ficheiro: %1$s</item>
<item quantity="other">(Ficheiro %2$d e %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Ficheiro recebido de %1$s</item>
@@ -117,12 +120,16 @@
<item quantity="one">Falhou a recepção do ficheiro de %1$s</item>
<item quantity="other">Falhou a recepção de %2$d de %3$d ficheiros de %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Foi enviado um ficheiro para %1$s</item>
<item quantity="other">Foram enviados %2$d ficheiros para %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Não foi possível enviar o ficheiro para o %1$s</item>
<item quantity="other">Não foi possível enviar %2$d de %3$d ficheiros para o %1$s</item>
</plurals>
<string name="received_file_text">Toque para abrir o \'%1s\'</string>
<string name="cannot_create_file">Não é possível criar o ficheiro %s</string>
<string name="sent_file_title">O ficheiro foi enviado para %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Não foi possível enviar o ficheiro para o %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Toque para responder</string>
<string name="reconnect">Ligar de Novo</string>
<string name="right_click">Enviar um Botão Direito</string>
@@ -190,6 +197,26 @@
<string name="sftp_camera">Fotografias</string>
<string name="add_device_dialog_title">Adicionar um dispositivo</string>
<string name="add_device_hint">Nome ou IP da máquina</string>
<string name="sftp_preference_detected_sdcards">Cartões SD detectados</string>
<string name="sftp_preference_edit_sdcard_title">Editar o cartão SD</string>
<string name="sftp_preference_configured_storage_locations">Locais de armazenamento configurados</string>
<string name="sftp_preference_add_storage_location_title">Adicionar um local de armazenamento</string>
<string name="sftp_preference_edit_storage_location">Editar o local de armazenamento</string>
<string name="sftp_preference_add_camera_shortcut">Adicionar um atalho para a pasta da câmara</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Adicionar um atalho para a pasta da câmara</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Não adicionar um atalho para a pasta da câmara</string>
<string name="sftp_storage_preference_storage_location">Local de armazenamento</string>
<string name="sftp_storage_preference_storage_location_already_configured">Este local já foi configurado</string>
<string name="sftp_storage_preference_click_to_select">carregar para seleccionar</string>
<string name="sftp_storage_preference_display_name">Nome visível</string>
<string name="sftp_storage_preference_display_name_already_used">Este nome visível já foi usado</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">O nome visível não pode estar vazio</string>
<string name="sftp_action_mode_menu_delete">Apagar</string>
<string name="sftp_no_sdcard_detected">Nenhum cartão SD detectado</string>
<string name="sftp_no_storage_locations_configured">Não estão configurados locais de armazenamento</string>
<string name="sftp_saf_permission_explanation">Para aceder a ficheiros remotos, tem de configurar os locais de armazenamento</string>
<string name="add_host">Adicionar uma máquina/IP</string>
<string name="add_host_hint">Nome ou IP da máquina</string>
<string name="no_players_connected">Não foram encontrados leitores</string>
<string name="mpris_player_on_device">%1$s em %2$s</string>
<string name="send_files">Enviar os ficheiros</string>
@@ -219,7 +246,7 @@
<string name="plugins_need_optional_permission">Alguns \'plugins\' têm funcionalidades desactivadas devido à falta de permissões (toque para obter mais informações):</string>
<string name="share_optional_permission_explanation">Para partilhar ficheiros entre o seu telemóvel e o seu ambiente de trabalho, precisa de permissão para aceder ao armazenamento do seu telemóvel</string>
<string name="telepathy_permission_explanation">Para ler e escrever SMS\'s a partir do seu ambiente de trabalho, precisa de dar permissões para os SMS\'s</string>
<string name="telephony_permission_explanation">Para ver as chamadas e os SMS\'s a partir do seu ambiente de trabalho, precisa de dar permissões para as chamadas telefónicas e SMS\'s</string>
<string name="telephony_permission_explanation">Para ver as chamadas telefónicas a partir do seu ambiente de trabalho, precisa de dar permissões para o registo de chamadas telefónicas e do estado do telemóvel</string>
<string name="telephony_optional_permission_explanation">Para ver o nome de um contacto em vez do seu número de telefone, precisa de dar acesso aos contactos do telemóvel</string>
<string name="contacts_permission_explanation">Para partilhar o seu livro de contactos com o ambiente de trabalho, precisa de dar permissões para os contactos</string>
<string name="select_ringtone">Seleccione um toque de chamada</string>
@@ -271,4 +298,7 @@
<string name="block_contents">Bloquear o conteúdo das notificações</string>
<string name="block_images">Bloquear as imagens das notificações</string>
<string name="notification_channel_receivenotification">Notificações dos outros dispositivos</string>
<string name="take_picture">Lançar a câmara</string>
<string name="plugin_photo_desc">Lança a aplicação da câmara para facilitar a tirada de fotografias e sua transferência</string>
<string name="no_app_for_opening">Não existe nenhuma aplicação adequada para abrir este ficheiro</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>
@@ -90,8 +93,6 @@
<string name="pairing_request_from">Запрос на сопряжение от %1s</string>
<string name="received_url_title">Получена ссылка от %1s</string>
<string name="received_url_text">Нажмите, чтобы открыть «%1s»</string>
<string name="outgoing_file_title">Отправка файла на %1s</string>
<string name="outgoing_files_title">Отправка файлов на %1s</string>
<plurals name="received_files_title">
<item quantity="one">Получен %2$d файл с %1$s</item>
<item quantity="few">Получены %2$d файла с %1$s</item>
@@ -105,10 +106,6 @@
<item quantity="other">Не удалось получить файл с %1$s</item>
</plurals>
<string name="received_file_text">Нажмите, чтобы открыть «%1s»</string>
<string name="sent_file_title">Файл отправлен на %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Не удалось отправить файл на %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Нажмите, чтобы ответить</string>
<string name="reconnect">Подключить заново</string>
<string name="right_click">Нажатие правой кнопки</string>
@@ -155,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>
@@ -162,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>
@@ -180,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>
@@ -191,13 +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">Чтобы видеть телефонные звонки и 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

@@ -106,14 +106,6 @@
<item quantity="many">(Súbor %2$d z %3$d) : %1$s</item>
<item quantity="other"/>
</plurals>
<string name="outgoing_file_title">Posielam súbor pre %1s</string>
<string name="outgoing_files_title">Posielam súbor do %1s</string>
<plurals name="outgoing_files_text">
<item quantity="one">Odoslaný %1$d súbor</item>
<item quantity="few">Odoslané súbory %1$d z %2$d</item>
<item quantity="many">Odoslaných súborov %1$d z %2$d</item>
<item quantity="other"/>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Prijatý súbor od %1$s</item>
<item quantity="few">Prijaté %2$d súbory od %1$s</item>
@@ -128,10 +120,6 @@
</plurals>
<string name="received_file_text">Ťuknite na otvorenie \'%1s\'</string>
<string name="cannot_create_file">Nomožno vytvoriť súbor %s</string>
<string name="sent_file_title">Poslať súbor pre %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Zlyhalo poslanie súboru do %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Tapnite na odpoveď</string>
<string name="reconnect">Znovu pripojiť</string>
<string name="right_click">Poslať kliknutie pravým</string>
@@ -193,7 +181,10 @@
<string name="sftp_sdcard">SD karta</string>
<string name="sftp_readonly">(iba na čítanie)</string>
<string name="sftp_camera">Obrázky fotoaparátu</string>
<string name="add_device_dialog_title">Pridať zariadenie</string>
<string name="sftp_storage_preference_display_name">Zobraziť meno</string>
<string name="add_host">Pridať hostiteľa/IP</string>
<string name="add_host_hint">Názov hostiteľa alebo IP</string>
<string name="no_players_connected">Nenašli sa žiadne prehrávače</string>
<string name="mpris_player_on_device">%1$s na %2$s</string>
<string name="send_files">Odoslať súbory</string>
@@ -223,7 +214,6 @@
<string name="plugins_need_optional_permission">Niektoré pluginy majú zakázané funkcie pre nedostatok opránení (ťuknite pre viac info):</string>
<string name="share_optional_permission_explanation">Na zdieľanie súborov medzi vašim telefónom a počítačom potrebujete dať prístup k úložisku telefónu</string>
<string name="telepathy_permission_explanation">Na čítanie a písanie SMS z vašeho počítača, potrebujete dať oprávnienie na SMS</string>
<string name="telephony_permission_explanation">Aby ste videli telefónne hovory a SMS z počítača, potrebujete dať oprávnenie na hovory a SMS</string>
<string name="telephony_optional_permission_explanation">Aby ste videli meno kontaktu namiesto čísla, potrebujete dať oprávnenie na telefónne kontakty</string>
<string name="contacts_permission_explanation">Na zdieľanie kontaktov s počítačom, potrebujete dať oprávnenie na kontakty</string>
<string name="select_ringtone">Nastaviť tón zvonenia</string>

View File

@@ -63,7 +63,6 @@
<string name="category_connected_devices">Повезани уређаји</string>
<string name="category_not_paired_devices">Доступни уређаји</string>
<string name="category_remembered_devices">Упамћени уређаји</string>
<string name="plugins_failed_to_load">Неуспело учитавање прикључка (тапните за више информација):</string>
<string name="device_menu_plugins">Поставке прикључака</string>
<string name="device_menu_unpair">Распари</string>
<string name="device_not_reachable">Упарени уређај није доступан</string>
@@ -85,13 +84,7 @@
<string name="pairing_request_from">Захтев за упаривање са %1s</string>
<string name="received_url_title">Примљена веза са %1s</string>
<string name="received_url_text">Тапните да отворите „%1s“</string>
<string name="outgoing_file_title">Шаљем фајл на %1s</string>
<string name="outgoing_files_title">Шаљем фајлове на %1s</string>
<string name="received_file_text">Тапните да отворите „%1s“</string>
<string name="sent_file_title">Послат фајл на %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Неуспело слање фајла на %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Тапните да одговорите</string>
<string name="reconnect">Поново повежи</string>
<string name="right_click">Пошаљи десни клик</string>
@@ -140,7 +133,6 @@
<string name="title_activity_notification_filter">Филтер обавештења</string>
<string name="filter_apps_info">Обавештења ће се синхронизовати за изабране апликације.</string>
<string name="sftp_internal_storage">Унутрашња меморија</string>
<string name="sftp_all_files">сви фајлови</string>
<string name="sftp_sdcard_num">СД картица %d</string>
<string name="sftp_sdcard">СД картица</string>
<string name="sftp_readonly">(само за читање)</string>
@@ -148,7 +140,6 @@
<string name="add_host">Додај домаћина/ИП</string>
<string name="add_host_hint">Име домаћина или ИП</string>
<string name="no_players_connected">Нисам нашао плејере</string>
<string name="custom_dev_list_help">Користите ову опцију само ако се ваши уређаји не приказују аутоматски. Унесите ИП адресу или име домаћина и додирните дугме да га додате на листу. Додирните постојећу ставку да бисте је уклонили.</string>
<string name="mpris_player_on_device">%1$s на %2$s</string>
<string name="send_files">Пошаљи фајлове</string>
<string name="pairing_title">Уређаји КДЕ‑конекције</string>
@@ -174,9 +165,7 @@
<string name="permission_explanation">Овај прикључак тражи дозволе да би радио.</string>
<string name="optional_permission_explanation">Морате дати допунске дозволе за активирање свих функција.</string>
<string name="plugins_need_optional_permission">Неки прикључци имају деактивиране могућности због недостатка дозвола (тапните за више информација):</string>
<string name="sftp_permission_explanation">Програм захтева дозволе да би са рачунара приступио фајловима на телефону.</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>
</resources>

View File

@@ -97,18 +97,20 @@
<string name="received_url_title">Tog emot länk från %1s</string>
<string name="received_url_text">Rör för att öppna \'%1s\'</string>
<plurals name="incoming_file_title">
<item quantity="one">Tog emot %1$d fil från %2$s</item>
<item quantity="other">Tog emot %1$d filer från %2$s</item>
<item quantity="one">Tar emot %1$d fil från %2$s</item>
<item quantity="other">Tar emot %1$d filer från %2$s</item>
</plurals>
<plurals name="incoming_files_text">
<item quantity="one">Fil: %1s</item>
<item quantity="other">(Fil %2$d av %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Skickar fil till %1s</string>
<string name="outgoing_files_title">Skickar filer till %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Skickar %1$d fil till %2$s</item>
<item quantity="other">Skickar %1$d filer till %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Skickade %1$d fil</item>
<item quantity="other">Skickade %1$d av %2$d filer</item>
<item quantity="one">Fil: %1$s</item>
<item quantity="other">(Fil %2$d av %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Tog emot fil från %1$s</item>
@@ -118,12 +120,16 @@
<item quantity="one">Misslyckades ta emot fil från %1$s</item>
<item quantity="other">Misslyckades ta emot %2$d av %3$d filer från %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Skickade fil till %1$s</item>
<item quantity="other">Skickade %2$d filer till %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Misslyckades skicka fil till %1$s</item>
<item quantity="other">Misslyckades skicka %2$d av %3$d filer till %1$s</item>
</plurals>
<string name="received_file_text">Rör för att öppna \'%1s\'</string>
<string name="cannot_create_file">Kan inte skapa filen %s</string>
<string name="sent_file_title">Skickade fil till %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Misslyckades skicka fil till %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Rör för att svara</string>
<string name="reconnect">Anslut igen</string>
<string name="right_click">Skicka högerklick</string>
@@ -240,7 +246,7 @@
<string name="plugins_need_optional_permission">Vissa insticksprogram har inaktiverade funktioner på grund av att rättigheter saknas (rör för mer information):</string>
<string name="share_optional_permission_explanation">För att dela filer mellan telefonen och skrivbordet behöver du ge tillgång till telefonens lagringsutrymme</string>
<string name="telepathy_permission_explanation">För att läsa och skriva SMS från skrivbordet måste du ge rättigheter för SMS</string>
<string name="telephony_permission_explanation">För att se telefonsamtal och SMS från skrivbordet måste du ge rättigheter för telefonsamtal och SMS</string>
<string name="telephony_permission_explanation">För att se telefonsamtal skrivbordet måste du ge rättigheter för telefonsamtalsloggar och telefontillstånd</string>
<string name="telephony_optional_permission_explanation">För att se ett kontaktnamn istället för ett telefonnummer måste du ge tillgång till telefonens kontakter</string>
<string name="contacts_permission_explanation">För att dela kontaktlistan med skrivbordet måste du ge rättigheter för kontakter</string>
<string name="select_ringtone">Välj en ringsignal</string>
@@ -294,4 +300,5 @@
<string name="notification_channel_receivenotification">Underrättelser från andra apparater</string>
<string name="take_picture">Starta kamera</string>
<string name="plugin_photo_desc">Starta kameraprogrammet för att förenkla att ta och överföra bilder</string>
<string name="no_app_for_opening">Inget lämpligt program hittades för att öppna filen</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,13 +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>
<string name="outgoing_file_title">Dosya şuraya gönderiliyor, %1s</string>
<string name="outgoing_files_title">Dosyalar şuraya gönderiliyor, %1s</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="sent_file_title">Dosyayı şuraya gönder, %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Dosyayı şuraya gönderme başarısız, %1s</string>
<string name="sent_file_failed_text">%1s</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>
@@ -102,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>
@@ -110,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>
@@ -117,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>
@@ -130,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>
@@ -143,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>
@@ -161,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>
@@ -172,6 +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ünden telefon çağrılarını ve SMS görebilmek için izin 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

@@ -108,13 +108,17 @@
<item quantity="many">(Файл %2$d з %3$d): %1$s</item>
<item quantity="other">Файл: %1$s</item>
</plurals>
<string name="outgoing_file_title">Надсилаємо файл до %1s</string>
<string name="outgoing_files_title">Надсилаємо файли на %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Надсилаємо %1$d файл до %2$s</item>
<item quantity="few">Надсилаємо %1$d файли до %2$s</item>
<item quantity="many">Надсилаємо %1$d файлів до %2$s</item>
<item quantity="other">Надсилаємо %1$d файл до %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Надіслано %1$d з %2$d файла</item>
<item quantity="few">Надіслано %1$d з %2$d файлів</item>
<item quantity="many">Надіслано %1$d з %2$d файлів</item>
<item quantity="other">Надіслано %1$d файл</item>
<item quantity="one">(Файл %2$d з %3$d) : %1$s</item>
<item quantity="few">(Файл %2$d з %3$d) : %1$s</item>
<item quantity="many">(Файл %2$d з %3$d) : %1$s</item>
<item quantity="other">Файл: %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Отримано %2$d файл з %1$s</item>
@@ -128,12 +132,20 @@
<item quantity="many">Не вдалося отримати %2$d з %3$d файлів з %1$s</item>
<item quantity="other">Не вдалося отримати файл з %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">До %1$s надіслано %2$d файл</item>
<item quantity="few">До %1$s надіслано %2$d файли</item>
<item quantity="many">До %1$s надіслано %2$d файлів</item>
<item quantity="other">Файл надіслано до %1s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Не вдалося надіслати %2$d з %3$d файлів до %1$s</item>
<item quantity="few">Не вдалося надіслати %2$d з %3$d файлів до %1$s</item>
<item quantity="many">Не вдалося надіслати %2$d з %3$d файлів до %1$s</item>
<item quantity="other">Не вдалося надіслати файл до %1$s</item>
</plurals>
<string name="received_file_text">Натисніть, щоб відкрити «%1s»</string>
<string name="cannot_create_file">Не вдалося створити файл %s</string>
<string name="sent_file_title">Файл надіслано до %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Не вдалося надіслати файл на %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Натисніть, щоб відповісти</string>
<string name="reconnect">З\'єднати знову</string>
<string name="right_click">Надіслати клацання правою кнопкою</string>
@@ -250,7 +262,7 @@
<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">"Щоб переглядати дзвінки і SMS з робочого комп’ютера, вам слід надати програмі доступ до дзвінків і SMS"</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>
@@ -304,4 +316,5 @@
<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

@@ -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>
@@ -100,11 +101,6 @@
<plurals name="incoming_files_text">
<item quantity="other">(%3$d 个文件中的第 %2$d 个)%1$s</item>
</plurals>
<string name="outgoing_file_title">正在向%1s发送文件</string>
<string name="outgoing_files_title">正在向 %1s 发送文件</string>
<plurals name="outgoing_files_text">
<item quantity="other">已发送 %2$d 个文件中的 %1$d 个</item>
</plurals>
<plurals name="received_files_title">
<item quantity="other">已从 %1$s 接收了 %2$d 个文件</item>
</plurals>
@@ -113,10 +109,6 @@
</plurals>
<string name="received_file_text">点击以打开“%1s”</string>
<string name="cannot_create_file">无法创建文件 %s</string>
<string name="sent_file_title">发送文件到%1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">向 %1s 发送文件失败</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">点击以应答</string>
<string name="reconnect">重新连接</string>
<string name="right_click">发送右键点击</string>
@@ -207,7 +199,6 @@
<string name="plugins_need_optional_permission">因缺少权限,某些插件的一些功能已禁用(点击以查看更多信息):</string>
<string name="share_optional_permission_explanation">您需要给予访问手机存储的权限才能在手机和桌面计算机之间分享文件</string>
<string name="telepathy_permission_explanation">从计算机桌面读取、写入短消息需要向应用程序授予 SMS 权限</string>
<string name="telephony_permission_explanation">您必须给予访问手机通话和短信的权限才能从桌面计算机查看通话记录和短信</string>
<string name="telephony_optional_permission_explanation">要查看联系人姓名而非电话号码,您需要授予访问手机通讯录的权限</string>
<string name="contacts_permission_explanation">要与桌面共享通讯薄,您需要给予联系人权限</string>
<string name="select_ringtone">选择铃声</string>

View File

@@ -102,10 +102,11 @@
<plurals name="incoming_files_text">
<item quantity="other">(檔案 %2$d/%3$d%1$s</item>
</plurals>
<string name="outgoing_file_title">正在將檔案發送到 %1s</string>
<string name="outgoing_files_title">正在將檔案發送到 %1s</string>
<plurals name="outgoing_file_title">
<item quantity="other">正在將 %1$d 個檔案傳送至 %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="other">傳送 %1$d 個檔案,共 %2$d 個檔案</item>
<item quantity="other">(第 %2$d共 %3$d個檔案%1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="other">已從 %1$s 接收 %2$d 個檔案</item>
@@ -113,12 +114,14 @@
<plurals name="received_files_fail_title">
<item quantity="other">無法從 %1$s 接收到 %2$d/%3$d 個檔案</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="other">已將 %2$d 傳送至 %1$s</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="other">無法將第 %2$d (共 %3$d) 個檔案傳送至 %1$s</item>
</plurals>
<string name="received_file_text">點擊開啟 \'%1s\'</string>
<string name="cannot_create_file">無法建立 %s 檔案</string>
<string name="sent_file_title">將檔案傳送到 %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">傳送到 %1s 的檔案失敗</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">點擊即可應答</string>
<string name="reconnect">重新連線</string>
<string name="right_click">傳送右鍵點擊</string>
@@ -235,7 +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_permission_explanation">為了要在桌面上檢視手機通話,您需要提供手機通話記錄及手機狀態的權限。</string>
<string name="telephony_optional_permission_explanation">為了要讓聯絡人名稱取代手機號碼,您需要提供手機通訊錄的權限。</string>
<string name="contacts_permission_explanation">為了要與電腦分享您的通訊錄,您必須提供「聯絡人」的權限</string>
<string name="select_ringtone">選擇一個鈴聲</string>
@@ -289,4 +292,5 @@
<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

@@ -135,11 +135,13 @@
<item quantity="one">File: %1s</item>
<item quantity="other">(File %2$d of %3$d) : %1$s</item>
</plurals>
<string name="outgoing_file_title">Sending file to %1s</string>
<string name="outgoing_files_title">Sending files to %1s</string>
<plurals name="outgoing_file_title">
<item quantity="one">Sending %1$d file to %2$s</item>
<item quantity="other">Sending %1$d files to %2$s</item>
</plurals>
<plurals name="outgoing_files_text">
<item quantity="one">Sent %1$d file</item>
<item quantity="other">Sent %1$d out of %2$d files</item>
<item quantity="one">File: %1$s</item>
<item quantity="other">(File %2$d of %3$d) : %1$s</item>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Received file from %1$s</item>
@@ -149,12 +151,16 @@
<item quantity="one">Failed receiving file from %1$s</item>
<item quantity="other">Failed receiving %2$d of %3$d files from %1$s</item>
</plurals>
<plurals name="sent_files_title">
<item quantity="one">Sent file to %1$s</item>
<item quantity="other">Sent %2$d files to %1$s"</item>
</plurals>
<plurals name="send_files_fail_title">
<item quantity="one">Failed sending file to %1$s</item>
<item quantity="other">Failed sending %2$d of %3$d files to %1$s</item>
</plurals>
<string name="received_file_text">Tap to open \'%1s\'</string>
<string name="cannot_create_file">Cannot create file %s</string>
<string name="sent_file_title">Sent file to %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Failed to send file to %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Tap to answer</string>
<string name="reconnect">Reconnect</string>
<string name="right_click">Send Right Click</string>
@@ -289,7 +295,7 @@
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="share_optional_permission_explanation">To share files between your phone and your desktop you need to give access to the phone\'s storage</string>
<string name="telepathy_permission_explanation">To read and write SMS from your desktop you need to give permission to SMS</string>
<string name="telephony_permission_explanation">To see phone calls from the desktop you need to give permission to access phone calls</string>
<string name="telephony_permission_explanation">To see phone calls on the desktop you need to give permission to phone call logs and phone state</string>
<string name="telephony_optional_permission_explanation">To see a contact name instead of a phone number you need to give access to the phone\'s contacts</string>
<string name="contacts_permission_explanation">To share your contacts book with the desktop, you need to give contacts permission</string>
<string name="select_ringtone">Select a ringtone</string>
@@ -354,4 +360,5 @@
<string name="plugin_photo_desc">Launch the camera app to ease taking and transferring pictures</string>
<string name="findmyphone_preference_key_ringtone" translatable="false">findmyphone_ringtone</string>
<string name="no_app_for_opening">No suitable app found to open this file</string>
</resources>

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:keep="@xml/findmyphoneplugin_preferences">
<Preference
android:key="@string/findmyphone_preference_key_ringtone"

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:keep="@xml/mousepadplugin_preferences">
<ListPreference
android:id="@+id/mousepad_double_tap_preference"

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:keep="@xml/mprisplugin_preferences">
<ListPreference
android:id="@+id/mpris_time_preference"

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

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:keep="@xml/remotekeyboardplugin_preferences">
<CheckBoxPreference
android:id="@+id/remotekeyboard_editing_only"

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:keep="@xml/sftpplugin_preferences">
<PreferenceCategory
android:key="@string/sftp_preference_key_preference_category"
android:title="@string/sftp_preference_detected_sdcards"

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:keep="@xml/shareplugin_preferences">
<CheckBoxPreference
android:id="@+id/share_destination_customize"

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:keep="@xml/telephonyplugin_preferences">
<EditTextPreference
android:dialogMessage="@string/telephony_pref_blocked_dialog_desc"

View File

@@ -26,7 +26,6 @@ import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPacket;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
@@ -90,6 +89,4 @@ public abstract class BaseLink {
//TO OVERRIDE, should be sync
public abstract boolean sendPacket(NetworkPacket np, Device.SendPacketStatusCallback callback);
@Deprecated
public abstract boolean sendPacketEncrypted(NetworkPacket np, Device.SendPacketStatusCallback callback, PublicKey key);
}

View File

@@ -32,7 +32,6 @@ import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.NetworkPacket;
import java.io.IOException;
@@ -41,7 +40,6 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.security.PublicKey;
import java.util.UUID;
public class BluetoothLink extends BaseLink {
@@ -87,14 +85,6 @@ public class BluetoothLink extends BaseLink {
return;
}
if (np.getType().equals(NetworkPacket.PACKET_TYPE_ENCRYPTED)) {
try {
np = RsaHelper.decrypt(np, privateKey);
} catch (Exception e) {
Log.e("BluetoothLink/receiving", "Exception decrypting the package", e);
}
}
if (np.hasPayloadTransferInfo()) {
BluetoothSocket transferSocket = null;
try {
@@ -144,7 +134,7 @@ public class BluetoothLink extends BaseLink {
continueAccepting = false;
try {
socket.close();
} catch (IOException e) {
} catch (IOException ignored) {
}
linkProvider.disconnectedLink(this, getDeviceId(), socket);
}
@@ -158,16 +148,7 @@ public class BluetoothLink extends BaseLink {
}
@Override
public boolean sendPacket(NetworkPacket np, Device.SendPacketStatusCallback callback) {
return sendPacketInternal(np, callback, null);
}
@Override
public boolean sendPacketEncrypted(NetworkPacket np, Device.SendPacketStatusCallback callback, PublicKey key) {
return sendPacketInternal(np, callback, key);
}
private boolean sendPacketInternal(NetworkPacket np, final Device.SendPacketStatusCallback callback, PublicKey key) {
public boolean sendPacket(NetworkPacket np, final Device.SendPacketStatusCallback callback) {
/*if (!isConnected()) {
Log.e("BluetoothLink", "sendPacketEncrypted failed: not connected");
@@ -186,15 +167,6 @@ public class BluetoothLink extends BaseLink {
np.setPayloadTransferInfo(payloadTransferInfo);
}
if (key != null) {
try {
np = RsaHelper.encrypt(np, key);
} catch (Exception e) {
callback.onFailure(e);
return false;
}
}
sendMessage(np);
if (serverSocket != null) {

View File

@@ -149,7 +149,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
Log.e("KDEConnect", "Exception", e);
}
}
}
@@ -160,7 +160,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
serverSocket = bluetoothAdapter
.listenUsingRfcommWithServiceRecord("KDE Connect", SERVICE_UUID);
} catch (IOException e) {
e.printStackTrace();
Log.e("KDEConnect", "Exception", e);
return;
}

View File

@@ -52,7 +52,7 @@ public class BluetoothPairingHandler extends BasePairingHandler {
}
@Override
public void packageReceived(NetworkPacket np) throws Exception {
public void packageReceived(NetworkPacket np) {
boolean wantsPair = np.getBoolean("pair");

View File

@@ -27,7 +27,6 @@ import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.StringsHelper;
import org.kde.kdeconnect.NetworkPacket;
@@ -42,7 +41,6 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.channels.NotYetConnectedException;
import java.security.PublicKey;
import javax.net.ssl.SSLSocket;
@@ -61,7 +59,7 @@ public class LanLink extends BaseLink {
// because it's probably trying to find me and
// potentially ask for pairing.
private volatile Socket socket = null;
private volatile SSLSocket socket = null;
private final LinkDisconnectedCallback callback;
@@ -71,14 +69,14 @@ public class LanLink extends BaseLink {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
Log.e("LanLink", "Error", e);
}
}
//Returns the old socket
public Socket reset(final Socket newSocket, ConnectionStarted connectionSource) throws IOException {
public SSLSocket reset(final SSLSocket newSocket, ConnectionStarted connectionSource) throws IOException {
Socket oldSocket = socket;
SSLSocket oldSocket = socket;
socket = newSocket;
this.connectionSource = connectionSource;
@@ -121,7 +119,7 @@ public class LanLink extends BaseLink {
return oldSocket;
}
public LanLink(Context context, String deviceId, LanLinkProvider linkProvider, Socket socket, ConnectionStarted connectionSource) throws IOException {
public LanLink(Context context, String deviceId, LanLinkProvider linkProvider, SSLSocket socket, ConnectionStarted connectionSource) throws IOException {
super(context, deviceId, linkProvider);
callback = linkProvider;
reset(socket, connectionSource);
@@ -139,7 +137,8 @@ public class LanLink extends BaseLink {
}
//Blocking, do not call from main thread
private boolean sendPacketInternal(NetworkPacket np, final Device.SendPacketStatusCallback callback, PublicKey key) {
@Override
public boolean sendPacket(NetworkPacket np, final Device.SendPacketStatusCallback callback) {
if (socket == null) {
Log.e("KDE/sendPacket", "Not yet connected");
callback.onFailure(new NotYetConnectedException());
@@ -159,11 +158,6 @@ public class LanLink extends BaseLink {
server = null;
}
//Encrypt if key provided
if (key != null) {
np = RsaHelper.encrypt(np, key);
}
//Log.e("LanLink/sendPacket", np.getType());
//Send body of the network package
@@ -188,9 +182,7 @@ public class LanLink extends BaseLink {
payloadSocket = server.accept();
//Convert to SSL if needed
if (socket instanceof SSLSocket) {
payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, false);
}
payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, false);
outputStream = payloadSocket.getOutputStream();
inputStream = np.getPayload().getInputStream();
@@ -201,7 +193,7 @@ public class LanLink extends BaseLink {
long size = np.getPayloadSize();
long progress = 0;
long timeSinceLastUpdate = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
while (!np.isCanceled() && (bytesRead = inputStream.read(buffer)) != -1) {
//Log.e("ok",""+bytesRead);
progress += bytesRead;
outputStream.write(buffer, 0, bytesRead);
@@ -216,14 +208,16 @@ public class LanLink extends BaseLink {
outputStream.flush();
Log.i("KDE/LanLink", "Finished sending payload ("+progress+" bytes written)");
} finally {
try { server.close(); } catch (Exception e) { }
try { payloadSocket.close(); } catch (Exception e) { }
try { server.close(); } catch (Exception ignored) { }
try { payloadSocket.close(); } catch (Exception ignored) { }
np.getPayload().close();
try { outputStream.close(); } catch (Exception e) { }
try { outputStream.close(); } catch (Exception ignored) { }
}
}
callback.onSuccess();
if (!np.isCanceled()) {
callback.onSuccess();
}
return true;
} catch (Exception e) {
if (callback != null) {
@@ -238,45 +232,19 @@ public class LanLink extends BaseLink {
}
}
//Blocking, do not call from main thread
@Override
public boolean sendPacket(NetworkPacket np, Device.SendPacketStatusCallback callback) {
return sendPacketInternal(np, callback, null);
}
//Blocking, do not call from main thread
@Override
public boolean sendPacketEncrypted(NetworkPacket np, Device.SendPacketStatusCallback callback, PublicKey key) {
return sendPacketInternal(np, callback, key);
}
private void receivedNetworkPacket(NetworkPacket np) {
if (np.getType().equals(NetworkPacket.PACKET_TYPE_ENCRYPTED)) {
try {
np = RsaHelper.decrypt(np, privateKey);
} catch(Exception e) {
e.printStackTrace();
Log.e("KDE/onPacketReceived","Exception decrypting the package");
}
}
if (np.hasPayloadTransferInfo()) {
Socket payloadSocket = new Socket();
try {
int tcpPort = np.getPayloadTransferInfo().getInt("port");
InetSocketAddress deviceAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
payloadSocket.connect(new InetSocketAddress(deviceAddress.getAddress(), tcpPort));
// Use ssl if existing link is on ssl
if (socket instanceof SSLSocket) {
payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, true);
}
payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, true);
np.setPayload(new NetworkPacket.Payload(payloadSocket, np.getPayloadSize()));
} catch (Exception e) {
try { payloadSocket.close(); } catch(Exception ignored) { }
e.printStackTrace();
Log.e("KDE/LanLink", "Exception connecting to payload remote socket");
Log.e("KDE/LanLink", "Exception connecting to payload remote socket", e);
}
}

View File

@@ -66,9 +66,6 @@ import javax.net.ssl.SSLSocket;
*/
public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDisconnectedCallback {
public static final int MIN_VERSION_WITH_SSL_SUPPORT = 6;
private static final int MIN_VERSION_WITH_NEW_PORT_SUPPORT = 7;
private final static int MIN_PORT = 1716;
private final static int MAX_PORT = 1764;
final static int PAYLOAD_TRANSFER_MIN_PORT = 1739;
@@ -93,7 +90,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
}
//They received my UDP broadcast and are connecting to me. The first thing they sned should be their identity.
private void tcpPacketReceived(Socket socket) throws Exception {
private void tcpPacketReceived(Socket socket) {
NetworkPacket networkPacket;
try {
@@ -102,7 +99,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
networkPacket = NetworkPacket.unserialize(message);
//Log.e("TcpListener","Received TCP package: "+networkPacket.serialize());
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Exception while receiving TCP packet", e);
return;
}
@@ -116,7 +113,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
}
//I've received their broadcast and should connect to their TCP socket and send my identity.
private void udpPacketReceived(DatagramPacket packet) throws Exception {
private void udpPacketReceived(DatagramPacket packet) {
final InetAddress address = packet.getAddress();
@@ -136,11 +133,6 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
}
}
if (identityPacket.getInt("protocolVersion") >= MIN_VERSION_WITH_NEW_PORT_SUPPORT && identityPacket.getInt("tcpPort") < MIN_PORT) {
Log.w("KDE/LanLinkProvider", "Ignoring a udp broadcast from legacy port because it comes from a device which knows about the new port.");
return;
}
Log.i("KDE/LanLinkProvider", "Broadcast identity package received from " + identityPacket.getString("deviceName"));
int tcpPort = identityPacket.getInt("tcpPort", MIN_PORT);
@@ -157,8 +149,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
identityPacketReceived(identityPacket, socket, LanLink.ConnectionStarted.Remotely);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Cannot connect to " + address);
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Cannot connect to " + address, e);
if (!reverseConnectionBlackList.contains(address)) {
Log.w("KDE/LanLinkProvider", "Blacklisting " + address);
reverseConnectionBlackList.add(address);
@@ -179,7 +170,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
try {
socket.setKeepAlive(true);
} catch (SocketException e) {
e.printStackTrace();
Log.e("LanLink", "Exception", e);
}
}
@@ -207,65 +198,58 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
// If I'm the TCP server I will be the SSL client and viceversa.
final boolean clientMode = (connectionStarted == LanLink.ConnectionStarted.Locally);
// Add ssl handler if device uses new protocol
// Do the SSL handshake
try {
if (identityPacket.getInt("protocolVersion") >= MIN_VERSION_WITH_SSL_SUPPORT) {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
boolean isDeviceTrusted = preferences.getBoolean(deviceId, false);
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
boolean isDeviceTrusted = preferences.getBoolean(deviceId, false);
if (isDeviceTrusted && !SslHelper.isCertificateStored(context, deviceId)) {
//Device paired with and old version, we can't use it as we lack the certificate
BackgroundService.RunCommand(context, service -> {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.unpair();
//Retry as unpaired
identityPacketReceived(identityPacket, socket, connectionStarted);
});
}
if (isDeviceTrusted && !SslHelper.isCertificateStored(context, deviceId)) {
//Device paired with and old version, we can't use it as we lack the certificate
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + identityPacket.getString("deviceName") + " trusted:" + isDeviceTrusted);
final SSLSocket sslsocket = SslHelper.convertToSslSocket(context, socket, deviceId, isDeviceTrusted, clientMode);
sslsocket.addHandshakeCompletedListener(event -> {
String mode = clientMode ? "client" : "server";
try {
Certificate certificate = event.getPeerCertificates()[0];
identityPacket.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
addLink(identityPacket, sslsocket, connectionStarted);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"), e);
BackgroundService.RunCommand(context, service -> {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.unpair();
//Retry as unpaired
identityPacketReceived(identityPacket, socket, connectionStarted);
});
}
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + identityPacket.getString("deviceName") + " trusted:" + isDeviceTrusted);
final SSLSocket sslsocket = SslHelper.convertToSslSocket(context, socket, deviceId, isDeviceTrusted, clientMode);
sslsocket.addHandshakeCompletedListener(event -> {
String mode = clientMode ? "client" : "server";
try {
Certificate certificate = event.getPeerCertificates()[0];
identityPacket.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
Log.i("KDE/LanLinkProvider", "Handshake as " + mode + " successful with " + identityPacket.getString("deviceName") + " secured with " + event.getCipherSuite());
addLink(identityPacket, sslsocket, connectionStarted);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"));
e.printStackTrace();
BackgroundService.RunCommand(context, service -> {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.unpair();
});
});
//Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection
new Thread(() -> {
try {
synchronized (this) {
sslsocket.startHandshake();
}
});
//Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection
new Thread(() -> {
try {
synchronized (this) {
sslsocket.startHandshake();
}
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPacket.getString("deviceName"));
e.printStackTrace();
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPacket.getString("deviceName"), e);
//String[] ciphers = sslsocket.getSupportedCipherSuites();
//for (String cipher : ciphers) {
// Log.i("SupportedCiphers","cipher: " + cipher);
//}
}
}).start();
} else {
addLink(identityPacket, socket, connectionStarted);
}
//String[] ciphers = sslsocket.getSupportedCipherSuites();
//for (String cipher : ciphers) {
// Log.i("SupportedCiphers","cipher: " + cipher);
//}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
Log.e("LanLink", "Exception", e);
}
}
@@ -281,9 +265,9 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
* @param identityPacket representation of remote device
* @param socket a new Socket, which should be used to receive packets from the remote device
* @param connectionOrigin which side started this connection
* @throws IOException if an exception is thrown by {@link LanLink#reset(Socket, LanLink.ConnectionStarted)}
* @throws IOException if an exception is thrown by {@link LanLink#reset(SSLSocket, LanLink.ConnectionStarted)}
*/
private void addLink(final NetworkPacket identityPacket, Socket socket, LanLink.ConnectionStarted connectionOrigin) throws IOException {
private void addLink(final NetworkPacket identityPacket, SSLSocket socket, LanLink.ConnectionStarted connectionOrigin) throws IOException {
String deviceId = identityPacket.getString("deviceId");
LanLink currentLink = visibleComputers.get(deviceId);
@@ -311,8 +295,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
udpServer.setReuseAddress(true);
udpServer.setBroadcast(true);
} catch (SocketException e) {
Log.e("LanLinkProvider", "Error creating udp server");
e.printStackTrace();
Log.e("LanLinkProvider", "Error creating udp server", e);
return;
}
new Thread(() -> {
@@ -324,8 +307,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
udpServer.receive(packet);
udpPacketReceived(packet);
} catch (Exception e) {
e.printStackTrace();
Log.e("LanLinkProvider", "UdpReceive exception");
Log.e("LanLinkProvider", "UdpReceive exception", e);
}
}
Log.w("UdpListener", "Stopping UDP listener");
@@ -336,8 +318,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
try {
tcpServer = openServerSocketOnFreePort(MIN_PORT);
} catch (Exception e) {
Log.e("LanLinkProvider", "Error creating tcp server");
e.printStackTrace();
Log.e("LanLinkProvider", "Error creating tcp server", e);
return;
}
new Thread(() -> {
@@ -347,8 +328,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
configureSocket(socket);
tcpPacketReceived(socket);
} catch (Exception e) {
e.printStackTrace();
Log.e("LanLinkProvider", "TcpReceive exception");
Log.e("LanLinkProvider", "TcpReceive exception", e);
}
}
Log.w("TcpListener", "Stopping TCP listener");
@@ -398,8 +378,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
socket.setBroadcast(true);
bytes = identity.serialize().getBytes(StringsHelper.UTF8);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Failed to create DatagramSocket");
Log.e("KDE/LanLinkProvider", "Failed to create DatagramSocket", e);
}
if (bytes != null) {
@@ -410,8 +389,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
socket.send(new DatagramPacket(bytes, bytes.length, client, MIN_PORT));
//Log.i("KDE/LanLinkProvider","Udp identity package sent to address "+client);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Sending udp identity package failed. Invalid address? (" + ipstr + ")");
Log.e("KDE/LanLinkProvider", "Sending udp identity package failed. Invalid address? (" + ipstr + ")", e);
}
}
}
@@ -449,12 +427,12 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
try {
tcpServer.close();
} catch (Exception e) {
e.printStackTrace();
Log.e("LanLink", "Exception", e);
}
try {
udpServer.close();
} catch (Exception e) {
e.printStackTrace();
Log.e("LanLink", "Exception", e);
}
}

View File

@@ -22,7 +22,6 @@ package org.kde.kdeconnect.Backends.LanBackend;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
@@ -31,9 +30,7 @@ import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect_tp.R;
import java.security.KeyFactory;
import java.security.cert.CertificateEncodingException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Timer;
import java.util.TimerTask;
@@ -54,14 +51,11 @@ public class LanPairingHandler extends BasePairingHandler {
private NetworkPacket createPairPacket() {
NetworkPacket np = new NetworkPacket(NetworkPacket.PACKET_TYPE_PAIR);
np.set("pair", true);
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(mDevice.getContext());
String publicKey = "-----BEGIN PUBLIC KEY-----\n" + globalSettings.getString("publicKey", "").trim()+ "\n-----END PUBLIC KEY-----\n";
np.set("publicKey", publicKey);
return np;
}
@Override
public void packageReceived(NetworkPacket np) throws Exception{
public void packageReceived(NetworkPacket np) {
boolean wantsPair = np.getBoolean("pair");
@@ -77,15 +71,6 @@ public class LanPairingHandler extends BasePairingHandler {
if (wantsPair) {
//Retrieve their public key
try {
String publicKeyContent = np.getString("publicKey").replace("-----BEGIN PUBLIC KEY-----\n","").replace("-----END PUBLIC KEY-----\n", "");
byte[] publicKeyBytes = Base64.decode(publicKeyContent, 0);
mDevice.publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch (Exception e) {
//IGNORE
}
if (mPairStatus == PairStatus.Requested) { //We started pairing
hidePairingNotification();
@@ -158,11 +143,7 @@ public class LanPairingHandler extends BasePairingHandler {
@Override
public void onFailure(Throwable e) {
if (e != null) {
e.printStackTrace();
} else {
Log.e("LanPairing/onFailure", "Unknown (null) exception");
}
Log.e("LanPairing/onFailure", "Exception", e);
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_could_not_send_package));
}
};
@@ -187,11 +168,7 @@ public class LanPairingHandler extends BasePairingHandler {
@Override
public void onFailure(Throwable e) {
if (e != null) {
e.printStackTrace();
} else {
Log.e("LanPairing/onFailure", "Unknown (null) exception");
}
Log.e("LanPairing/onFailure", "Exception", e);
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_not_reachable));
}
};
@@ -212,25 +189,15 @@ public class LanPairingHandler extends BasePairingHandler {
//Log.e("KDE/PairingDone", "Pairing Done");
SharedPreferences.Editor editor = mDevice.getContext().getSharedPreferences(mDevice.getDeviceId(), Context.MODE_PRIVATE).edit();
if (mDevice.publicKey != null) {
try {
String encodedPublicKey = Base64.encodeToString(mDevice.publicKey.getEncoded(), 0);
editor.putString("publicKey", encodedPublicKey);
} catch (Exception e) {
Log.e("KDE/PairingDone", "Error encoding public key");
}
}
try {
String encodedCertificate = Base64.encodeToString(mDevice.certificate.getEncoded(), 0);
editor.putString("certificate", encodedCertificate);
} catch (NullPointerException n) {
Log.w("KDE/PairingDone", "Certificate is null, remote device does not support ssl");
Log.w("KDE/PairingDone", "Certificate is null, remote device does not support ssl", n);
} catch (CertificateEncodingException c) {
Log.e("KDE/PairingDOne", "Error encoding certificate");
Log.e("KDE/PairingDOne", "Error encoding certificate", c);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Pairng", "Exception");
Log.e("KDE/Pairng", "Exception", e);
}
editor.apply();

View File

@@ -28,8 +28,6 @@ import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPacket;
import java.security.PublicKey;
public class LoopbackLink extends BaseLink {
public LoopbackLink(Context context, BaseLinkProvider linkProvider) {
@@ -58,8 +56,4 @@ public class LoopbackLink extends BaseLink {
return true;
}
@Override
public boolean sendPacketEncrypted(NetworkPacket np, Device.SendPacketStatusCallback callback, PublicKey key) {
return sendPacket(np, callback);
}
}

View File

@@ -33,7 +33,7 @@ public class LoopbackPairingHandler extends BasePairingHandler {
}
@Override
public void packageReceived(NetworkPacket np) throws Exception {
public void packageReceived(NetworkPacket np) {
}

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

@@ -35,7 +35,6 @@ import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Plugins.Plugin;
@@ -45,10 +44,8 @@ import org.kde.kdeconnect_tp.R;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -68,7 +65,6 @@ public class Device implements BaseLink.PacketReceiver {
private final String deviceId;
private String name;
public PublicKey publicKey;
public Certificate certificate;
private int notificationId;
private int protocolVersion;
@@ -81,7 +77,7 @@ public class Device implements BaseLink.PacketReceiver {
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
private List<String> m_supportedPlugins = new ArrayList<>();
private List<String> supportedPlugins = new ArrayList<>();
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Plugin> pluginsWithoutPermissions = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Plugin> pluginsWithoutOptionalPermissions = new ConcurrentHashMap<>();
@@ -150,19 +146,8 @@ public class Device implements BaseLink.PacketReceiver {
this.protocolVersion = NetworkPacket.ProtocolVersion; //We don't know it yet
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
try {
String publicKeyStr = settings.getString("publicKey", null);
if (publicKeyStr != null) {
byte[] publicKeyBytes = Base64.decode(publicKeyStr, 0);
publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
}
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception deserializing stored public key for device");
}
//Assume every plugin is supported until addLink is called and we can get the actual list
m_supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
//Do not load plugins yet, the device is not present
//reloadPluginsFromSettings();
@@ -179,7 +164,6 @@ public class Device implements BaseLink.PacketReceiver {
this.pairStatus = PairStatus.NotPaired;
this.protocolVersion = 0;
this.deviceType = DeviceType.Computer;
this.publicKey = null;
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
@@ -417,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);
}
//
@@ -437,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");
@@ -459,23 +442,18 @@ public class Device implements BaseLink.PacketReceiver {
certificate = SslHelper.parseCertificate(certificateBytes);
Log.i("KDE/Device", "Got certificate ");
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Error getting certificate");
Log.e("KDE/Device", "Error getting certificate", e);
}
}
links.add(link);
try {
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
link.setPrivateKey(privateKey);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception reading our own private key"); //Should not happen
Log.e("KDE/Device", "Exception reading our own private key", e); //Should not happen
}
Log.i("KDE/Device", "addLink " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size());
@@ -512,12 +490,11 @@ public class Device implements BaseLink.PacketReceiver {
Set<String> outgoingCapabilities = identityPacket.getStringSet("outgoingCapabilities", null);
Set<String> incomingCapabilities = identityPacket.getStringSet("incomingCapabilities", null);
if (incomingCapabilities != null && outgoingCapabilities != null) {
m_supportedPlugins = new Vector<>(PluginFactory.pluginsForCapabilities(incomingCapabilities, outgoingCapabilities));
supportedPlugins = new Vector<>(PluginFactory.pluginsForCapabilities(incomingCapabilities, outgoingCapabilities));
} else {
m_supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
}
link.addPacketReceiver(this);
reloadPluginsFromSettings();
@@ -549,8 +526,6 @@ public class Device implements BaseLink.PacketReceiver {
@Override
public void onPacketReceived(NetworkPacket np) {
hackToMakeRetrocompatiblePacketTypes(np);
if (NetworkPacket.PACKET_TYPE_PAIR.equals(np.getType())) {
Log.i("KDE/Device", "Pair package");
@@ -559,8 +534,7 @@ public class Device implements BaseLink.PacketReceiver {
try {
ph.packageReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("PairingPacketReceived", "Exception");
Log.e("PairingPacketReceived", "Exception", e);
}
}
} else if (isPaired()) {
@@ -578,8 +552,7 @@ public class Device implements BaseLink.PacketReceiver {
try {
plugin.onPacketReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception in " + plugin.getPluginKey() + "'s onPacketReceived()");
Log.e("KDE/Device", "Exception in " + plugin.getPluginKey() + "'s onPacketReceived()", e);
//try { Log.e("KDE/Device", "NetworkPacket:" + np.serialize()); } catch (Exception _) { }
}
}
@@ -604,8 +577,7 @@ public class Device implements BaseLink.PacketReceiver {
try {
plugin.onUnpairedDevicePacketReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception in " + plugin.getDisplayName() + "'s onPacketReceived() in unPairedPacketListeners");
Log.e("KDE/Device", "Exception in " + plugin.getDisplayName() + "'s onPacketReceived() in unPairedPacketListeners", e);
}
}
} else {
@@ -631,11 +603,7 @@ public class Device implements BaseLink.PacketReceiver {
@Override
public void onFailure(Throwable e) {
if (e != null) {
e.printStackTrace();
} else {
Log.e("KDE/sendPacket", "Unknown (null) exception");
}
Log.e("KDE/sendPacket", "Exception", e);
}
};
@@ -661,20 +629,12 @@ public class Device implements BaseLink.PacketReceiver {
}
*/
hackToMakeRetrocompatiblePacketTypes(np);
boolean useEncryption = (protocolVersion < LanLinkProvider.MIN_VERSION_WITH_SSL_SUPPORT && (!np.getType().equals(NetworkPacket.PACKET_TYPE_PAIR) && isPaired()));
boolean success = false;
//Make a copy to avoid concurrent modification exception if the original list changes
for (final BaseLink link : links) {
if (link == null)
continue; //Since we made a copy, maybe somebody destroyed the link in the meanwhile
if (useEncryption) {
success = link.sendPacketEncrypted(np, callback, publicKey);
} else {
success = link.sendPacket(np, callback);
}
success = link.sendPacket(np, callback);
if (success) break; //If the link didn't call sendSuccess(), try the next one
}
@@ -734,10 +694,7 @@ public class Device implements BaseLink.PacketReceiver {
success = plugin.onCreate();
} catch (Exception e) {
success = false;
e.printStackTrace();
}
if (!success) {
Log.e("KDE/addPlugin", "plugin failed to load " + pluginKey);
Log.e("KDE/addPlugin", "plugin failed to load " + pluginKey, e);
}
plugins.put(pluginKey, plugin);
@@ -775,8 +732,7 @@ public class Device implements BaseLink.PacketReceiver {
plugin.onDestroy();
//Log.e("removePlugin","removed " + pluginKey);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/removePlugin", "Exception calling onDestroy for plugin " + pluginKey);
Log.e("KDE/removePlugin", "Exception calling onDestroy for plugin " + pluginKey, e);
}
return true;
@@ -796,7 +752,7 @@ public class Device implements BaseLink.PacketReceiver {
HashMap<String, ArrayList<String>> newPluginsByIncomingInterface = new HashMap<>();
for (String pluginKey : m_supportedPlugins) {
for (String pluginKey : supportedPlugins) {
PluginFactory.PluginInfo pluginInfo = PluginFactory.getPluginInfo(pluginKey);
@@ -810,7 +766,6 @@ public class Device implements BaseLink.PacketReceiver {
boolean success = addPlugin(pluginKey);
if (success) {
for (String packageType : pluginInfo.getSupportedPacketTypes()) {
packageType = hackToMakeRetrocompatiblePacketTypes(packageType);
ArrayList<String> plugins = newPluginsByIncomingInterface.get(packageType);
if (plugins == null) plugins = new ArrayList<>();
plugins.add(pluginKey);
@@ -877,17 +832,7 @@ public class Device implements BaseLink.PacketReceiver {
}
public List<String> getSupportedPlugins() {
return m_supportedPlugins;
}
private void hackToMakeRetrocompatiblePacketTypes(NetworkPacket np) {
if (protocolVersion >= 6) return;
np.mType = np.getType().replace(".request", "");
}
private String hackToMakeRetrocompatiblePacketTypes(String type) {
if (protocolVersion >= 6) return type;
return type.replace(".request", "");
return supportedPlugins;
}
}

View File

@@ -39,8 +39,7 @@ public class AppsHelper {
} catch (final PackageManager.NameNotFoundException e) {
e.printStackTrace();
Log.e("AppsHelper", "Could not resolve name " + packageName);
Log.e("AppsHelper", "Could not resolve name " + packageName, e);
return null;
@@ -57,15 +56,8 @@ public class AppsHelper {
return pm.getApplicationIcon(ai);
} catch (final PackageManager.NameNotFoundException e) {
e.printStackTrace();
Log.e("AppsHelper", "Could not find icon for " + packageName);
Log.e("AppsHelper", "Could not find icon for " + packageName, e);
return null;
}
}
}

View File

@@ -46,6 +46,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.collection.LongSparseArray;
@@ -191,7 +192,7 @@ public class ContactsHelper {
}
} catch (IOException e) {
// If you are experiencing this, please open a bug report indicating how you got here
e.printStackTrace();
Log.e("Contacts", "Exception while fetching vcards", e);
}
// At this point we are screwed:
@@ -213,7 +214,6 @@ public class ContactsHelper {
* @param IDs collection of uIDs to look up
* @return Mapping of uIDs to the corresponding VCard
*/
@SuppressWarnings("UnnecessaryContinue")
private static Map<uID, VCardBuilder> getVCardsSlow(Context context, Collection<uID> IDs) {
Map<uID, VCardBuilder> toReturn = new HashMap<>();
@@ -239,10 +239,10 @@ public class ContactsHelper {
toReturn.put(ID, new VCardBuilder(vcard.toString()));
} catch (IOException e) {
// If you are experiencing this, please open a bug report indicating how you got here
e.printStackTrace();
Log.e("Contacts", "Exception while fetching vcards", e);
} catch (NullPointerException e) {
// If you are experiencing this, please open a bug report indicating how you got here
e.printStackTrace();
Log.e("Contacts", "Exception while fetching vcards", e);
}
}
@@ -394,6 +394,7 @@ public class ContactsHelper {
.append("\n");
}
@NonNull
public String toString() {
return vcardBody.toString() + VCARD_END;
}
@@ -415,9 +416,14 @@ public class ContactsHelper {
static final String COLUMN = ContactsContract.Contacts.LOOKUP_KEY;
public uID(String lookupKey) {
if (lookupKey == null)
throw new IllegalArgumentException("lookUpKey should not be null");
contactLookupKey = lookupKey;
}
@NonNull
public String toString() {
return this.contactLookupKey;
}

View File

@@ -496,8 +496,7 @@ public class DeviceHelper {
}
} catch (Exception e) {
//Some phones might not define BRAND or MODEL, ignore exceptions
Log.e("Exception", e.getMessage());
e.printStackTrace();
Log.e("Exception", e.getMessage(), e);
}
if (deviceName == null || deviceName.isEmpty()) {
return "Android"; //Could not find a name

View File

@@ -20,11 +20,14 @@
package org.kde.kdeconnect.Helpers;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import android.webkit.MimeTypeMap;
@@ -32,9 +35,12 @@ import org.kde.kdeconnect.NetworkPacket;
import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
public class FilesHelper {
public static final String LOG_TAG = "SendFileActivity";
private static String getFileExt(String filename) {
//return MimeTypeMap.getFileExtensionFromUrl(filename);
return filename.substring((filename.lastIndexOf(".") + 1));
@@ -119,71 +125,152 @@ public class FilesHelper {
InputStream inputStream = cr.openInputStream(uri);
NetworkPacket np = new NetworkPacket(type);
String filename = null;
long size = -1;
Long lastModified = null;
if (uri.getScheme().equals("file")) {
// file:// is a non media uri, so we cannot query the ContentProvider
np.set("filename", uri.getLastPathSegment());
try {
size = new File(uri.getPath()).length();
} catch (Exception e) {
Log.e("SendFileActivity", "Could not obtain file size");
e.printStackTrace();
File mFile = new File(uri.getPath());
filename = mFile.getName();
size = mFile.length();
lastModified = mFile.lastModified();
} catch (NullPointerException e) {
Log.e(LOG_TAG, "Received bad file URI", e);
}
} else {
// Probably a content:// uri, so we query the Media content provider
// Since we used Intent.CATEGORY_OPENABLE, these two columns are the only ones we are
// guaranteed to have: https://developer.android.com/reference/android/provider/OpenableColumns
String[] proj = {
OpenableColumns.SIZE,
OpenableColumns.DISPLAY_NAME,
};
Cursor cursor = null;
try {
String[] proj = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DISPLAY_NAME};
cursor = cr.query(uri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
try (Cursor cursor = cr.query(uri, proj, null, null, null)) {
int nameColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
int sizeColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
cursor.moveToFirst();
String path = cursor.getString(column_index);
np.set("filename", Uri.parse(path).getLastPathSegment());
size = new File(path).length();
} catch (Exception unused) {
Log.w("SendFileActivity", "Could not resolve media to a file, trying to get info as media");
filename = cursor.getString(nameColumnIndex);
try {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
cursor.moveToFirst();
String name = cursor.getString(column_index);
np.set("filename", name);
} catch (Exception e) {
e.printStackTrace();
Log.e("SendFileActivity", "Could not obtain file name");
// It is recommended to check for the value to be null because there are
// situations were we don't know the size (for instance, if the file is
// not local to the device)
if (!cursor.isNull(sizeColumnIndex)) {
size = cursor.getInt(sizeColumnIndex);
}
try {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
cursor.moveToFirst();
//For some reason this size can differ from the actual file size!
size = cursor.getInt(column_index);
} catch (Exception e) {
Log.e("SendFileActivity", "Could not obtain file size");
e.printStackTrace();
}
} finally {
try {
cursor.close();
} catch (Exception ignored) {
}
lastModified = getLastModifiedTime(context, uri);
} catch (Exception e) {
Log.e(LOG_TAG, "Problem getting file information", e);
}
}
if (filename != null) {
np.set("filename", filename);
} else {
// It would be very surprising if this happens
Log.e(LOG_TAG, "Unable to read filename");
}
if (lastModified != null) {
np.set("lastModified", lastModified);
} else {
// This would not be too surprising, and probably means we need to improve
// FilesHelper.getLastModifiedTime
Log.w(LOG_TAG, "Unable to read file last modified time");
}
np.setPayload(new NetworkPacket.Payload(inputStream, size));
return np;
} catch (Exception e) {
Log.e("SendFileActivity", "Exception creating network packet", e);
e.printStackTrace();
Log.e(LOG_TAG, "Exception creating network packet", e);
return null;
}
}
/**
* By hook or by crook, get the last modified time of the passed content:// URI
*
* This is a challenge because different content sources have different columns defined, and
* I don't know how to tell what the source of the content is.
*
* Therefore, my brilliant solution is to just try everything until something works.
*
* Will return null if nothing worked.
*/
public static Long getLastModifiedTime(final Context context, final Uri uri) {
ContentResolver cr = context.getContentResolver();
Long lastModifiedTime = null;
// Open a cursor without a column because we do not yet know what columns are defined
try (Cursor cursor = cr.query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
String[] allColumns = cursor.getColumnNames();
// MediaStore.MediaColumns.DATE_MODIFIED resolves to "date_modified"
// I see this column defined in case we used the Gallery app to select the file to transfer
// This can occur both for devices running Storage Access Framework (SAF) if we select
// the Gallery to provide the file to transfer, as well as for older devices by doing the same
int mediaDataModifiedColumnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED);
// DocumentsContract.Document.COLUMN_LAST_MODIFIED resolves to "last_modified"
// I see this column defined when, on a device using SAF we select a file using the
// file browser
// According to https://developer.android.com/reference/kotlin/android/provider/DocumentsContract
// all "document providers" must provide certain columns. Do we actually have a DocumentProvider here?
// I do not think this code path will ever happen for a non-media file is selected on
// an API < KitKat device, since those will be delivered as a file:// URI and handled
// accordingly. Therefore, it is safe to ignore the warning that this field requires
// API 19
@SuppressLint("InlinedApi")
int documentLastModifiedColumnIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_LAST_MODIFIED);
// If we have an image, it may be the case that MediaStore.MediaColumns.DATE_MODIFIED
// catches the modification date, but if not, here is another column we can look for.
// This should be checked *after* DATE_MODIFIED since I think that column might give
// better information
int imageDateTakenColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
// Report whether the captured timestamp is in milliseconds or seconds
// The truthy-ness of this value for each different type of column is known from either
// experimentation or the docs (when docs exist...)
boolean milliseconds;
int properColumnIndex;
if (mediaDataModifiedColumnIndex >= 0) {
properColumnIndex = mediaDataModifiedColumnIndex;
milliseconds = false;
} else if (documentLastModifiedColumnIndex >= 0) {
properColumnIndex = documentLastModifiedColumnIndex;
milliseconds = true;
} else if (imageDateTakenColumnIndex >= 0) {
properColumnIndex = imageDateTakenColumnIndex;
milliseconds = true;
} else {
// Nothing worked :(
String formattedColumns = Arrays.toString(allColumns);
Log.w("SendFileActivity", "Unable to get file modification time. Available columns were: " + formattedColumns);
return null;
}
if (!cursor.isNull(properColumnIndex)) {
lastModifiedTime = cursor.getLong(properColumnIndex);
}
if (!milliseconds) {
lastModifiedTime *= 1000;
milliseconds = true;
}
}
}
return lastModifiedTime;
}
}

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;
}
@@ -36,25 +33,24 @@ public class NetworkHelper {
return false; //We are connected to at least one non-mobile network
}
if (mobile) { //We suspect we are on a mobile net
try {
try (LineNumberReader is = new LineNumberReader(new FileReader("/proc/net/arp"))) {
//Check the number of network neighbours, on data it should be 0
LineNumberReader is = new LineNumberReader(new FileReader("/proc/net/arp"));
is.skip(Long.MAX_VALUE);
//Log.e("NetworkHelper", "procnetarp has " + is.getLineNumber() + " lines");
if (is.getLineNumber() > 1) { //The first line are the headers
return false; //I have neighbours, so this doesn't look like a mobile network
}
} catch (Exception e) {
Log.e("NetworkHelper", "Exception reading procnetarp");
e.printStackTrace();
Log.e("NetworkHelper", "Exception reading procnetarp", e);
}
}
return mobile;
} catch (Exception e) {
e.printStackTrace();
Log.d("isOnMobileNetwork", "Something went wrong, but this is non-critical.");
Log.e("isOnMobileNetwork", "Something went wrong, but this is non-critical.", e);
}
return false;
*/
}
}

View File

@@ -1,7 +0,0 @@
package org.kde.kdeconnect.Helpers;
class ObjectsHelper {
public static boolean equals(Object a, Object b) {
return (a == null) ? (b == null) : a.equals(b);
}
}

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,16 +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 {
/**
@@ -61,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;
}
@@ -73,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
@@ -86,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
*
@@ -93,50 +147,108 @@ public class SMSHelper {
* @param threadID Thread to look up
* @return List of all messages in the thread
*/
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
*/
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
*/
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);
@@ -144,37 +256,41 @@ public class SMSHelper {
messageInfo.put(colName, body);
}
Message message = new Message(messageInfo);
ThreadID threadID = new ThreadID(message.m_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
*/
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);
}
/**
@@ -184,27 +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
*/
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
);
@@ -221,13 +540,14 @@ public class SMSHelper {
this.threadID = threadID;
}
@NonNull
public String toString() {
return this.threadID.toString();
return threadID.toString();
}
@Override
public int hashCode() {
return this.threadID.hashCode();
return threadID.hashCode();
}
@Override
@@ -236,18 +556,43 @@ 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
*/
public static class Message {
final String m_address;
final String m_body;
public final long m_date;
final int m_type;
final int m_read;
final long m_threadID; // ThreadID is *int* for SMS messages but *long* for MMS
final int m_uID;
final String address;
final String body;
public final long date;
final int type;
final int read;
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
@@ -259,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
@@ -280,43 +629,57 @@ 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) {
m_address = messageInfo.get(Message.ADDRESS);
m_body = messageInfo.get(Message.BODY);
m_date = Long.parseLong(messageInfo.get(Message.DATE));
address = messageInfo.get(Message.ADDRESS);
body = messageInfo.get(Message.BODY);
date = Long.parseLong(messageInfo.get(Message.DATE));
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
m_type = -1;
Log.w("SMSHelper", "Encountered undefined message type");
type = -1;
// Proceed anyway, maybe this is not an important problem.
} else {
m_type = Integer.parseInt(messageInfo.get(Message.TYPE));
type = Integer.parseInt(messageInfo.get(Message.TYPE));
}
m_read = Integer.parseInt(messageInfo.get(Message.READ));
m_threadID = Long.parseLong(messageInfo.get(Message.THREAD_ID));
m_uID = Integer.parseInt(messageInfo.get(Message.U_ID));
read = Integer.parseInt(messageInfo.get(Message.READ));
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 {
JSONObject json = new JSONObject();
json.put(Message.ADDRESS, m_address);
json.put(Message.BODY, m_body);
json.put(Message.DATE, m_date);
json.put(Message.TYPE, m_type);
json.put(Message.READ, m_read);
json.put(Message.THREAD_ID, m_threadID);
json.put(Message.U_ID, m_uID);
json.put(Message.ADDRESS, address);
json.put(Message.BODY, body);
json.put(Message.DATE, date);
json.put(Message.TYPE, type);
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;
}
@Override
public String toString() {
return this.m_body;
return body;
}
}

View File

@@ -26,11 +26,6 @@ import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.kde.kdeconnect.NetworkPacket;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
@@ -40,8 +35,6 @@ import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
public class RsaHelper {
public static void initialiseRsaKeys(Context context) {
@@ -55,8 +48,7 @@ public class RsaHelper {
keyGen.initialize(2048);
keyPair = keyGen.genKeyPair();
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/initializeRsaKeys", "Exception");
Log.e("KDE/initializeRsaKeys", "Exception", e);
return;
}
@@ -78,67 +70,12 @@ public class RsaHelper {
return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
}
public static PublicKey getPublicKey(Context context, String deviceId) throws GeneralSecurityException {
SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
}
public static PrivateKey getPrivateKey(Context context) throws GeneralSecurityException {
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
}
public static NetworkPacket encrypt(NetworkPacket np, PublicKey publicKey) throws GeneralSecurityException, JSONException {
String serialized = np.serialize();
int chunkSize = 128;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
JSONArray chunks = new JSONArray();
while (serialized.length() > 0) {
if (serialized.length() < chunkSize) {
chunkSize = serialized.length();
}
String chunk = serialized.substring(0, chunkSize);
serialized = serialized.substring(chunkSize);
byte[] chunkBytes = chunk.getBytes(Charset.defaultCharset());
byte[] encryptedChunk;
encryptedChunk = cipher.doFinal(chunkBytes);
chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP));
}
//Log.i("NetworkPacket", "Encrypted " + chunks.length()+" chunks");
NetworkPacket encrypted = new NetworkPacket(NetworkPacket.PACKET_TYPE_ENCRYPTED);
encrypted.set("data", chunks);
encrypted.setPayload(np.getPayload());
return encrypted;
}
public static NetworkPacket decrypt(NetworkPacket np, PrivateKey privateKey) throws GeneralSecurityException, JSONException {
JSONArray chunks = np.getJSONArray("data");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
StringBuilder decryptedJson = new StringBuilder();
for (int i = 0; i < chunks.length(); i++) {
byte[] encryptedChunk = Base64.decode(chunks.getString(i), Base64.NO_WRAP);
String decryptedChunk = new String(cipher.doFinal(encryptedChunk));
decryptedJson.append(decryptedChunk);
}
NetworkPacket decrypted = NetworkPacket.unserialize(decryptedJson.toString());
decrypted.setPayload(np.getPayload());
return decrypted;
}
}

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,14 +65,10 @@ 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 {
public enum SslMode {
Client,
Server
}
public static X509Certificate certificate; //my device's certificate
public static final BouncyCastleProvider BC = new BouncyCastleProvider();
@@ -85,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();
@@ -112,21 +138,8 @@ public class SslHelper {
SharedPreferences.Editor edit = settings.edit();
edit.putString("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
edit.apply();
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/initialiseCert", "Exception");
}
} 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.printStackTrace();
Log.e("KDE/initialiseCert", "Exception", e);
}
}
}
@@ -195,8 +208,7 @@ public class SslHelper {
}
return tlsContext;
} catch (Exception e) {
Log.e("KDE/SslHelper", "Error creating tls context");
e.printStackTrace();
Log.e("KDE/SslHelper", "Error creating tls context", e);
}
return null;
@@ -255,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

@@ -26,6 +26,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
@@ -89,10 +90,10 @@ public class StorageHelper {
try (Scanner scanner = new Scanner(new File("/proc/mounts"))) {
mounts = scanner.useDelimiter("\\A").next();
} catch (Exception e) {
e.printStackTrace();
Log.e("StorageHelper", "Exception while getting storageList", e);
}
File dirs[] = storage.listFiles();
File[] dirs = storage.listFiles();
for (File dir : dirs) {
//Log.e("getStorageList", "path: "+dir.getAbsolutePath());
if (dir.isDirectory() && dir.canRead() && dir.canExecute()) {
@@ -129,7 +130,7 @@ public class StorageHelper {
}
}
} catch (Exception e) {
e.printStackTrace();
Log.e("StorageHelper", "Exception", e);
}
for (String line : entries) {

View File

@@ -44,12 +44,10 @@ public class NetworkPacket {
public final static String PACKET_TYPE_IDENTITY = "kdeconnect.identity";
public final static String PACKET_TYPE_PAIR = "kdeconnect.pair";
public final static String PACKET_TYPE_ENCRYPTED = "kdeconnect.encrypted";
public static Set<String> protocolPacketTypes = new HashSet<String>() {{
add(PACKET_TYPE_IDENTITY);
add(PACKET_TYPE_PAIR);
add(PACKET_TYPE_ENCRYPTED);
}};
private long mId;
@@ -57,6 +55,7 @@ public class NetworkPacket {
private JSONObject mBody;
private Payload mPayload;
private JSONObject mPayloadTransferInfo;
private volatile boolean canceled;
private NetworkPacket() {
@@ -70,6 +69,9 @@ public class NetworkPacket {
mPayloadTransferInfo = new JSONObject();
}
public boolean isCanceled() { return canceled; }
public void cancel() { canceled = true; }
public String getType() {
return mType;
}
@@ -91,7 +93,7 @@ public class NetworkPacket {
if (value == null) return;
try {
mBody.put(key, value);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
@@ -114,7 +116,7 @@ public class NetworkPacket {
public void set(String key, int value) {
try {
mBody.put(key, value);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
@@ -129,7 +131,7 @@ public class NetworkPacket {
public void set(String key, boolean value) {
try {
mBody.put(key, value);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
@@ -144,7 +146,7 @@ public class NetworkPacket {
public void set(String key, double value) {
try {
mBody.put(key, value);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
@@ -155,7 +157,7 @@ public class NetworkPacket {
public void set(String key, JSONArray value) {
try {
mBody.put(key, value);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
@@ -166,7 +168,7 @@ public class NetworkPacket {
public void set(String key, JSONObject value) {
try {
mBody.put(key, value);
} catch (JSONException e) {
} catch (JSONException ignored) {
}
}
@@ -179,7 +181,7 @@ public class NetworkPacket {
try {
String str = jsonArray.getString(i);
list.add(str);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
return list;
@@ -197,7 +199,7 @@ public class NetworkPacket {
jsonArray.put(str);
}
mBody.put(key, jsonArray);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
@@ -210,7 +212,7 @@ public class NetworkPacket {
try {
String str = jsonArray.getString(i);
list.add(str);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
return list;
@@ -228,7 +230,7 @@ public class NetworkPacket {
jsonArray.put(str);
}
mBody.put(key, jsonArray);
} catch (Exception e) {
} catch (Exception ignored) {
}
}
@@ -279,8 +281,7 @@ public class NetworkPacket {
np.mBody.put("incomingCapabilities", new JSONArray(PluginFactory.getIncomingCapabilities()));
np.mBody.put("outgoingCapabilities", new JSONArray(PluginFactory.getOutgoingCapabilities()));
} catch (Exception e) {
e.printStackTrace();
Log.e("NetworkPacakge", "Exception on createIdentityPacket");
Log.e("NetworkPackage", "Exception on createIdentityPacket", e);
}
return np;
@@ -318,7 +319,7 @@ public class NetworkPacket {
private Socket inputSocket;
private long payloadSize;
Payload(long payloadSize) {
public Payload(long payloadSize) {
this((InputStream)null, payloadSize);
}

View File

@@ -29,7 +29,6 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPacket;
public class KeyListenerView extends View {
@@ -104,12 +103,7 @@ public class KeyListenerView extends View {
}
private void sendKeyPressPacket(final NetworkPacket np) {
BackgroundService.RunCommand(getContext(), service -> {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = device.getPlugin(MousePadPlugin.class);
if (mousePadPlugin == null) return;
mousePadPlugin.sendKeyboardPacket(np);
});
BackgroundService.RunWithPlugin(getContext(), deviceId, MousePadPlugin.class, plugin -> plugin.sendKeyboardPacket(np));
}
@Override

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

@@ -27,13 +27,13 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
@@ -41,7 +41,6 @@ import android.widget.TextView;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.SystemvolumePlugin.SystemvolumeFragment;
import org.kde.kdeconnect.UserInterface.ThemeUtil;
@@ -53,6 +52,8 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.fragment.app.FragmentManager;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MprisActivity extends AppCompatActivity {
@@ -61,6 +62,55 @@ public class MprisActivity extends AppCompatActivity {
private Runnable positionSeekUpdateRunnable = null;
private MprisPlugin.MprisPlayer targetPlayer = null;
@BindView(R.id.play_button)
ImageButton playButton;
@BindView(R.id.prev_button)
ImageButton prevButton;
@BindView(R.id.next_button)
ImageButton nextButton;
@BindView(R.id.rew_button)
ImageButton rewButton;
@BindView(R.id.ff_button)
ImageButton ffButton;
@BindView(R.id.time_textview)
TextView timeText;
@BindView(R.id.album_art)
ImageView albumArtView;
@BindView(R.id.player_spinner)
Spinner playerSpinner;
@BindView(R.id.no_players)
TextView noPlayers;
@BindView(R.id.now_playing_textview)
TextView nowPlayingText;
@BindView(R.id.positionSeek)
SeekBar positionBar;
@BindView(R.id.progress_slider)
LinearLayout progressSlider;
@BindView(R.id.volume_seek)
SeekBar volumeSeek;
@BindView(R.id.volume_layout)
LinearLayout volumeLayout;
@BindView(R.id.stop_button)
ImageButton stopButton;
@BindView(R.id.progress_textview)
TextView progressText;
private static String milisToProgress(long milis) {
int length = (int) (milis / 1000); //From milis to seconds
StringBuilder text = new StringBuilder();
@@ -81,14 +131,7 @@ public class MprisActivity extends AppCompatActivity {
private void connectToPlugin(final String targetPlayerName) {
BackgroundService.RunCommand(this, service -> {
final Device device = service.getDevice(deviceId);
final MprisPlugin mpris = device.getPlugin(MprisPlugin.class);
if (mpris == null) {
Log.e("MprisActivity", "device has no mpris plugin!");
return;
}
BackgroundService.RunWithPlugin(this, deviceId, MprisPlugin.class, mpris -> {
targetPlayer = mpris.getPlayerStatus(targetPlayerName);
addSytemvolumeFragment();
@@ -111,20 +154,19 @@ public class MprisActivity extends AppCompatActivity {
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
runOnUiThread(() -> {
Spinner spinner = findViewById(R.id.player_spinner);
//String prevPlayer = (String)spinner.getSelectedItem();
spinner.setAdapter(adapter);
playerSpinner.setAdapter(adapter);
if (playerList.isEmpty()) {
findViewById(R.id.no_players).setVisibility(View.VISIBLE);
spinner.setVisibility(View.GONE);
((TextView) findViewById(R.id.now_playing_textview)).setText("");
noPlayers.setVisibility(View.VISIBLE);
playerSpinner.setVisibility(View.GONE);
nowPlayingText.setText("");
} else {
findViewById(R.id.no_players).setVisibility(View.GONE);
spinner.setVisibility(View.VISIBLE);
noPlayers.setVisibility(View.GONE);
playerSpinner.setVisibility(View.VISIBLE);
}
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
playerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int pos, long id) {
@@ -156,7 +198,7 @@ public class MprisActivity extends AppCompatActivity {
if (targetPlayer != null) {
int targetIndex = adapter.getPosition(targetPlayer.getPlayer());
if (targetIndex >= 0) {
spinner.setSelection(targetIndex);
playerSpinner.setSelection(targetIndex);
} else {
targetPlayer = null;
}
@@ -164,7 +206,7 @@ public class MprisActivity extends AppCompatActivity {
//If no player selected, select the first one (if any)
if (targetPlayer == null && !playerList.isEmpty()) {
targetPlayer = mpris.getPlayerStatus(playerList.get(0));
spinner.setSelection(0);
playerSpinner.setSelection(0);
}
updatePlayerStatus(mpris);
});
@@ -172,7 +214,6 @@ public class MprisActivity extends AppCompatActivity {
});
});
}
private void addSytemvolumeFragment() {
@@ -210,55 +251,53 @@ public class MprisActivity extends AppCompatActivity {
}
String song = playerStatus.getCurrentSong();
TextView nowPlaying = findViewById(R.id.now_playing_textview);
if (!nowPlaying.getText().toString().equals(song)) {
nowPlaying.setText(song);
if (!nowPlayingText.getText().toString().equals(song)) {
nowPlayingText.setText(song);
}
Bitmap albumArt = playerStatus.getAlbumArt();
if (albumArt == null) {
Drawable placeholder_art = DrawableCompat.wrap(getResources().getDrawable(R.drawable.ic_album_art_placeholder));
DrawableCompat.setTint(placeholder_art, getResources().getColor(R.color.primary));
((ImageView) findViewById(R.id.album_art)).setImageDrawable(placeholder_art);
albumArtView.setImageDrawable(placeholder_art);
} else {
((ImageView) findViewById(R.id.album_art)).setImageBitmap(albumArt);
albumArtView.setImageBitmap(albumArt);
}
if (playerStatus.isSeekAllowed()) {
((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(playerStatus.getLength()));
SeekBar positionSeek = findViewById(R.id.positionSeek);
positionSeek.setMax((int) (playerStatus.getLength()));
positionSeek.setProgress((int) (playerStatus.getPosition()));
findViewById(R.id.progress_slider).setVisibility(View.VISIBLE);
timeText.setText(milisToProgress(playerStatus.getLength()));
positionBar.setMax((int) (playerStatus.getLength()));
positionBar.setProgress((int) (playerStatus.getPosition()));
progressSlider.setVisibility(View.VISIBLE);
} else {
findViewById(R.id.progress_slider).setVisibility(View.GONE);
progressSlider.setVisibility(View.GONE);
}
int volume = playerStatus.getVolume();
((SeekBar) findViewById(R.id.volume_seek)).setProgress(volume);
volumeSeek.setProgress(volume);
boolean isPlaying = playerStatus.isPlaying();
if (isPlaying) {
((ImageButton) findViewById(R.id.play_button)).setImageResource(R.drawable.ic_pause_black);
findViewById(R.id.play_button).setEnabled(playerStatus.isPauseAllowed());
playButton.setImageResource(R.drawable.ic_pause_black);
playButton.setEnabled(playerStatus.isPauseAllowed());
} else {
((ImageButton) findViewById(R.id.play_button)).setImageResource(R.drawable.ic_play_black);
findViewById(R.id.play_button).setEnabled(playerStatus.isPlayAllowed());
playButton.setImageResource(R.drawable.ic_play_black);
playButton.setEnabled(playerStatus.isPlayAllowed());
}
findViewById(R.id.volume_layout).setVisibility(playerStatus.isSetVolumeAllowed() ? View.VISIBLE : View.GONE);
findViewById(R.id.rew_button).setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
findViewById(R.id.ff_button).setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
volumeLayout.setVisibility(playerStatus.isSetVolumeAllowed() ? View.VISIBLE : View.GONE);
rewButton.setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
ffButton.setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE);
//Show and hide previous/next buttons simultaneously
if (playerStatus.isGoPreviousAllowed() || playerStatus.isGoNextAllowed()) {
findViewById(R.id.prev_button).setVisibility(View.VISIBLE);
findViewById(R.id.prev_button).setEnabled(playerStatus.isGoPreviousAllowed());
findViewById(R.id.next_button).setVisibility(View.VISIBLE);
findViewById(R.id.next_button).setEnabled(playerStatus.isGoNextAllowed());
prevButton.setVisibility(View.VISIBLE);
prevButton.setEnabled(playerStatus.isGoPreviousAllowed());
nextButton.setVisibility(View.VISIBLE);
nextButton.setEnabled(playerStatus.isGoNextAllowed());
} else {
findViewById(R.id.prev_button).setVisibility(View.GONE);
findViewById(R.id.next_button).setVisibility(View.GONE);
prevButton.setVisibility(View.GONE);
nextButton.setVisibility(View.GONE);
}
}
@@ -273,7 +312,7 @@ public class MprisActivity extends AppCompatActivity {
}
final int currentVolume = targetPlayer.getVolume();
if (currentVolume < 100 || currentVolume > 0) {
if (currentVolume <= 100 && currentVolume >= 0) {
int newVolume = currentVolume + step;
if (newVolume > 100) {
newVolume = 100;
@@ -310,11 +349,23 @@ public class MprisActivity extends AppCompatActivity {
}
}
private interface MprisPlayerCallback {
void performAction(MprisPlugin.MprisPlayer player);
}
private void performActionOnClick(View v, MprisPlayerCallback l) {
v.setOnClickListener(view -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer == null) return;
l.performAction(targetPlayer);
}));
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeUtil.setUserPreferredTheme(this);
setContentView(R.layout.activity_mpris);
ButterKnife.bind(this);
final String targetPlayerName = getIntent().getStringExtra("player");
getIntent().removeExtra("player");
@@ -328,37 +379,19 @@ public class MprisActivity extends AppCompatActivity {
BackgroundService.RunCommand(MprisActivity.this, service -> service.addConnectionListener(connectionReceiver));
connectToPlugin(targetPlayerName);
findViewById(R.id.play_button).setOnClickListener(view -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer == null) return;
targetPlayer.playPause();
}));
performActionOnClick(playButton, MprisPlugin.MprisPlayer::playPause);
findViewById(R.id.prev_button).setOnClickListener(view -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer == null) return;
targetPlayer.previous();
}));
performActionOnClick(prevButton, MprisPlugin.MprisPlayer::previous);
findViewById(R.id.rew_button).setOnClickListener(view -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer == null) return;
targetPlayer.seek(interval_time * -1);
}));
performActionOnClick(rewButton, p -> targetPlayer.seek(interval_time * -1));
findViewById(R.id.ff_button).setOnClickListener(view -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer == null) return;
targetPlayer.seek(interval_time);
}));
performActionOnClick(ffButton, p -> p.seek(interval_time));
findViewById(R.id.next_button).setOnClickListener(view -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer == null) return;
targetPlayer.next();
}));
performActionOnClick(nextButton, MprisPlugin.MprisPlayer::next);
findViewById(R.id.stop_button).setOnClickListener(view -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer == null) return;
targetPlayer.stop();
}));
performActionOnClick(stopButton, MprisPlugin.MprisPlayer::stop);
((SeekBar) findViewById(R.id.volume_seek)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
volumeSeek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
@@ -377,22 +410,19 @@ public class MprisActivity extends AppCompatActivity {
});
positionSeekUpdateRunnable = () -> {
final SeekBar positionSeek = findViewById(R.id.positionSeek);
BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer != null) {
positionSeek.setProgress((int) (targetPlayer.getPosition()));
}
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable);
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 1000);
});
};
positionSeekUpdateRunnable = () -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer != null) {
positionBar.setProgress((int) (targetPlayer.getPosition()));
}
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable);
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 1000);
});
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200);
((SeekBar) findViewById(R.id.positionSeek)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
positionBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean byUser) {
((TextView) findViewById(R.id.progress_textview)).setText(milisToProgress(progress));
progressText.setText(milisToProgress(progress));
}
@Override
@@ -412,19 +442,7 @@ public class MprisActivity extends AppCompatActivity {
});
findViewById(R.id.now_playing_textview).setSelected(true);
}
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
nowPlayingText.setSelected(true);
}
}

View File

@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
*/
package org.kde.kdeconnect.Plugins.MprisPlugin;
@@ -31,17 +31,21 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.service.notification.StatusBarNotification;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Pair;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver;
import org.kde.kdeconnect_tp.R;
import java.util.HashSet;
import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import androidx.core.app.TaskStackBuilder;
import androidx.media.app.NotificationCompat.MediaStyle;
@@ -54,12 +58,14 @@ import androidx.media.app.NotificationCompat.MediaStyle;
* - The media session (via MediaSessionCompat; for lock screen control on
* older Android version. And in the future for lock screen album covers)
*/
public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceChangeListener {
public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceChangeListener, NotificationReceiver.NotificationListener {
private final static int MPRIS_MEDIA_NOTIFICATION_ID = 0x91b70463; // echo MprisNotification | md5sum | head -c 8
private final static String MPRIS_MEDIA_SESSION_TAG = "org.kde.kdeconnect_tp.media_session";
private static final MprisMediaSession instance = new MprisMediaSession();
private boolean spotifyRunning;
public static MprisMediaSession getInstance() {
return instance;
}
@@ -132,6 +138,19 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
mpris.setPlayerListUpdatedHandler("media_notification", mediaNotificationHandler);
mpris.setPlayerStatusUpdatedHandler("media_notification", mediaNotificationHandler);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
NotificationReceiver.RunCommand(context, service -> {
service.addListener(MprisMediaSession.this);
boolean serviceReady = service.isConnected();
if (serviceReady) {
onListenerConnected(service);
}
});
}
updateMediaNotification();
}
@@ -164,52 +183,77 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
* @param service The background service
*/
private void updateCurrentPlayer(BackgroundService service) {
Device device = null;
MprisPlugin.MprisPlayer playing = null;
//First try the previously displayed player
if (notificationDevice != null && mprisDevices.contains(notificationDevice) && notificationPlayer != null) {
device = service.getDevice(notificationDevice);
}
MprisPlugin mpris = null;
if (device != null) {
mpris = device.getPlugin(MprisPlugin.class);
}
if (mpris != null) {
playing = mpris.getPlayerStatus(notificationPlayer.getPlayer());
}
//If nonexistant or not playing, try a different player for the same device
if ((playing == null || !playing.isPlaying()) && mpris != null) {
MprisPlugin.MprisPlayer playingPlayer = mpris.getPlayingPlayer();
//Only replace the previously found player if we really found one
if (playingPlayer != null) {
playing = playingPlayer;
}
}
//If nonexistant or not playing, try a different player for another device
if (playing == null || !playing.isPlaying()) {
for (Device otherDevice : service.getDevices().values()) {
//First, check if we actually display notification for this device
if (!mprisDevices.contains(otherDevice.getDeviceId())) continue;
mpris = otherDevice.getPlugin(MprisPlugin.class);
if (mpris == null) continue;
MprisPlugin.MprisPlayer playingPlayer = mpris.getPlayingPlayer();
//Only replace the previously found player if we really found one
if (playingPlayer != null) {
playing = playingPlayer;
device = otherDevice;
break;
}
}
}
Pair<Device, MprisPlugin.MprisPlayer> player = findPlayer(service);
//Update the last-displayed device and player
notificationDevice = device == null ? null : device.getDeviceId();
notificationPlayer = playing;
notificationDevice = player.first == null ? null : player.first.getDeviceId();
notificationPlayer = player.second;
}
private Pair<Device, MprisPlugin.MprisPlayer> findPlayer(BackgroundService service) {
//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);
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, 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, MprisPlugin.MprisPlayer preferredPlayer) {
if (!mprisDevices.contains(device.getDeviceId()))
return null;
MprisPlugin plugin = device.getPlugin(MprisPlugin.class);
if (plugin == null) {
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;
}
return null;
}
private boolean shouldShowPlayer(MprisPlugin.MprisPlayer player) {
return player != null && !(player.isSpotify() && spotifyRunning);
}
/**
@@ -316,7 +360,7 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
*/
PendingIntent piOpenActivity = TaskStackBuilder.create(context)
.addNextIntentWithParentStack(iOpenActivity)
.getPendingIntent(Build.VERSION.SDK_INT > 15 ? 0 : (int)System.currentTimeMillis(), PendingIntent.FLAG_UPDATE_CURRENT);
.getPendingIntent(Build.VERSION.SDK_INT > 15 ? 0 : (int) System.currentTimeMillis(), PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notification = new NotificationCompat.Builder(context, NotificationHelper.Channels.MEDIA_CONTROL);
@@ -354,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);
}
@@ -419,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;
}
@@ -433,4 +480,33 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
notificationPlayer = player;
updateMediaNotification();
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onNotificationPosted(StatusBarNotification n) {
if (n.getPackageName().equals("com.spotify.music")) {
spotifyRunning = true;
updateMediaNotification();
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onNotificationRemoved(StatusBarNotification n) {
if (n.getPackageName().equals("com.spotify.music")) {
spotifyRunning = false;
updateMediaNotification();
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void onListenerConnected(NotificationReceiver service) {
for (StatusBarNotification n : service.getActiveNotifications()) {
if (n.getPackageName().equals("com.spotify.music")) {
spotifyRunning = true;
updateMediaNotification();
}
}
}
}

View File

@@ -87,7 +87,7 @@ public class MprisPlugin extends Plugin {
return player;
}
private boolean isSpotify() {
boolean isSpotify() {
return getPlayer().toLowerCase().equals("spotify");
}
@@ -307,8 +307,7 @@ public class MprisPlugin extends Plugin {
try {
playerStatusUpdated.get(key).dispatchMessage(new Message());
} catch (Exception e) {
e.printStackTrace();
Log.e("MprisControl", "Exception");
Log.e("MprisControl", "Exception", e);
playerStatusUpdated.remove(key);
}
}
@@ -355,8 +354,7 @@ public class MprisPlugin extends Plugin {
try {
playerListUpdated.get(key).dispatchMessage(new Message());
} catch (Exception e) {
e.printStackTrace();
Log.e("MprisControl", "Exception");
Log.e("MprisControl", "Exception", e);
playerListUpdated.remove(key);
}
}
@@ -424,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);
@@ -468,8 +470,7 @@ public class MprisPlugin extends Plugin {
try {
playerStatusUpdated.get(key).dispatchMessage(new Message());
} catch (Exception e) {
e.printStackTrace();
Log.e("MprisControl", "Exception");
Log.e("MprisControl", "Exception", e);
playerStatusUpdated.remove(key);
}
}

View File

@@ -45,7 +45,7 @@ class MprisReceiverCallback extends MediaController.Callback {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onPlaybackStateChanged(@NonNull PlaybackState state) {
public void onPlaybackStateChanged(PlaybackState state) {
plugin.sendMetadata(player);
}

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

@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
*/
package org.kde.kdeconnect.Plugins.NotificationsPlugin;
@@ -35,11 +35,20 @@ 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;
import org.kde.kdeconnect.NetworkPacket;
import org.kde.kdeconnect.Plugins.Plugin;
@@ -55,12 +64,12 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
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 {
@@ -68,11 +77,15 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
private final static String PACKET_TYPE_NOTIFICATION = "kdeconnect.notification";
private final static String PACKET_TYPE_NOTIFICATION_REQUEST = "kdeconnect.notification.request";
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 = "KDE/NotificationsPlugin";
private AppDatabase appDatabase;
private Set<String> currentNotifications;
private Map<String, RepliableNotification> pendingIntents;
private Map<String, List<Notification.Action>> actions;
private boolean serviceReady;
@Override
@@ -117,6 +130,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
pendingIntents = new HashMap<>();
currentNotifications = new HashSet<>();
actions = new HashMap<>();
appDatabase = new AppDatabase(context, true);
@@ -149,10 +163,13 @@ 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);
actions.remove(id);
NetworkPacket np = new NetworkPacket(PACKET_TYPE_NOTIFICATION);
np.set("id", id);
np.set("isCancel", true);
@@ -180,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);
@@ -210,66 +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) {
e.printStackTrace();
Log.e("NotificationsPlugin", "Error retrieving icon");
}
} else {
currentNotifications.add(key);
Bitmap appIcon = extractIcon(statusBarNotification, notification);
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;
@@ -298,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()];
@@ -314,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++;
@@ -324,94 +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.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText);
e.printStackTrace();
}
}
}
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.w("NotificationPlugin", "problem extracting notification wear for " + statusBarNotification.getNotification().tickerText);
e.printStackTrace();
}
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.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText);
e.printStackTrace();
}
}
}
return text;
}
private static String extractStringFromExtra(Bundle extras, String key) {
Object extra = extras.get(key);
if (extra == null) {
@@ -421,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;
}
}
@@ -432,33 +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.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText);
e.printStackTrace();
}
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;
@@ -476,7 +528,27 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public boolean onPacketReceived(final NetworkPacket np) {
if (np.getBoolean("request")) {
if (np.getType().equals(PACKET_TYPE_NOTIFICATION_ACTION) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
String key = np.getString("key");
String title = np.getString("action");
PendingIntent intent = null;
for (Notification.Action a : actions.get(key)) {
if (a.title.equals(title)) {
intent = a.actionIntent;
}
}
if (intent != null) {
try {
intent.send();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Triggering action failed", e);
}
}
} else if (np.getBoolean("request")) {
if (serviceReady) {
NotificationReceiver.RunCommand(context, this::sendCurrentNotifications);
@@ -512,7 +584,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public String[] getSupportedPacketTypes() {
return new String[]{PACKET_TYPE_NOTIFICATION_REQUEST, PACKET_TYPE_NOTIFICATION_REPLY};
return new String[]{PACKET_TYPE_NOTIFICATION_REQUEST, PACKET_TYPE_NOTIFICATION_REPLY, PACKET_TYPE_NOTIFICATION_ACTION};
}
@Override
@@ -522,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(':');
@@ -551,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();
@@ -570,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

@@ -25,9 +25,7 @@ public class PhotoActivity extends AppCompatActivity {
protected void onStart() {
super.onStart();
BackgroundService.RunWithPlugin(this, getIntent().getStringExtra("deviceId"), PhotoPlugin.class, plugin -> {
this.plugin = plugin;
});
BackgroundService.RunWithPlugin(this, getIntent().getStringExtra("deviceId"), PhotoPlugin.class, plugin -> this.plugin = plugin);
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {

View File

@@ -25,7 +25,6 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.widget.Button;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPacket;
@@ -185,21 +184,6 @@ public abstract class Plugin {
*/
public abstract String[] getOutgoingPacketTypes();
/**
* Creates a button that will be displayed in the user interface
* It can open an activity or perform any other action that the
* plugin would wants to expose to the user. Return null if no
* button should be displayed.
*/
@Deprecated
public Button getInterfaceButton(final Activity activity) {
if (!hasMainActivity()) return null;
Button b = new Button(activity);
b.setText(getActionName());
b.setOnClickListener(view -> startMainActivity(activity));
return b;
}
protected String[] getRequiredPermissions() {
return new String[0];
}

View File

@@ -141,8 +141,7 @@ public class PluginFactory {
plugin.setContext(context, device);
return plugin;
} catch (Exception e) {
Log.e("PluginFactory", "Could not instantiate plugin: " + pluginKey);
e.printStackTrace();
Log.e("PluginFactory", "Could not instantiate plugin: " + pluginKey, 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

@@ -29,11 +29,12 @@ import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
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;
@@ -119,6 +120,8 @@ public class RemoteKeyboardService
} finally {
RemoteKeyboardPlugin.releaseInstances();
}
getWindow().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
@@ -133,6 +136,8 @@ public class RemoteKeyboardService
} finally {
RemoteKeyboardPlugin.releaseInstances();
}
getWindow().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
@@ -162,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

Some files were not shown because too many files have changed in this diff Show More