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

Compare commits

..

180 Commits

Author SHA1 Message Date
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
Albert Vaca Cintora
ca90b64094 Bump version to release 2019-03-22 08:38:36 +01:00
Albert Vaca Cintora
7b6d8a4c35 Remove mention to SMS from the description 2019-03-22 08:38:36 +01:00
Simon Redman
b725e81c37 Re-add READ_SMS permission to SMS plugin
Previously was bugged: If the SMS plugin was enabled but the Telephony was not, the SMS plugin would not have worked. Good thing nobody noticed! :)
2019-03-21 21:10:22 -06:00
Nicolas Fella
17e9892c80 Fix showing callers number on Pie 2019-03-21 23:13:27 +00:00
Albert Vaca Cintora
a8bc4f24c7 Fix crash on old API 2019-03-21 23:02:04 +01:00
Albert Vaca Cintora
3b82cd44e4 After unbinding, this will prevent other code from using any view 2019-03-21 23:01:55 +01:00
Nicolas Fella
79272a6042 Reenable calllog permission 2019-03-21 22:46:04 +01:00
Simon Redman
12fb67a7aa Change hard-coded string to use library-provided constant
Does this cause problems with older SDKs? I don't think so. (If there are problems, I assume they were already there)
2019-03-21 11:12:43 -06:00
Nicolas Fella
d03520ce70 Fix lost packets after connecting 2019-03-21 16:36:00 +00:00
Andy Holmes
0453728407 SftpPlugin: fix SAF implementation for gvfs/ssh
Returning NULL from readSymbolicLink() results in an exception that
closes the SFTP connection for some SSH implementations, notably the one
used by Gvfs (GSConnect).

primitive-ftpd had a similar issue, which was fixed by returning and
empty string instead of NULL.
2019-03-21 08:16:47 -07:00
Albert Vaca Cintora
7dc023385e Remove directives that aren't needed
And changed pickFirst to merge, that concatenates the contents
2019-03-21 09:49:49 +01:00
Albert Vaca
a918f0cfe6 Bump version to release 2019-03-20 23:12:11 +01:00
Albert Vaca
aada931d6a Re-enable SMS plugin now that we know the secrets to approval 2019-03-20 23:11:58 +01:00
Albert Vaca
eed77c530b Revert "Stop using ClassIndex, it seems to be causing problems"
This reverts commit 7799f7e817.
2019-03-20 22:35:27 +01:00
Albert Vaca
3b7edf2d2f Merge branch 'master' into play-store 2019-03-20 22:28:53 +01:00
Albert Vaca
37657388c6 Uppercasing some stuff 2019-03-20 22:14:27 +01:00
Albert Vaca
360e4bc1cb Fix ConcurrentModificationException
When onPacketReceived was called at the same time as getSinks, because
getSinks uses .values() and onPacketReceived does .put()
2019-03-20 22:12:53 +01:00
Albert Vaca
b689548aa9 Remove private API usage 2019-03-20 22:07:02 +01:00
Albert Vaca
adac026dfa Change RunCommand to runWithPlugin 2019-03-20 22:06:45 +01:00
Nicolas Fella
d513a5305a Show device name in MPRIS notification 2019-03-20 20:50:06 +00:00
Albert Vaca
4d38f9753c Disable plugin that breaks on Android 4 2019-03-20 09:42:32 +01:00
Albert Vaca
18548fb6df Added missing "unregister" 2019-03-20 09:36:36 +01:00
l10n daemon script
fd5738aa99 GIT_SILENT made messages (after extraction) 2019-03-20 03:06:17 +01:00
Albert Vaca Cintora
7799f7e817 Stop using ClassIndex, it seems to be causing problems 2019-03-19 22:50:22 +01:00
Albert Vaca
8db8937927 Update version to release (to Play Store only) 2019-03-19 09:03:30 +01:00
Albert Vaca
13e6f2d250 Emergency update to fix Play Store's policy violation
- Make TelephonyPlugin only handle calls and not texts
- Disable SMSPlugin.
- Stop using READ_CALL_LOG permission (for contact names)
2019-03-19 09:00:45 +01:00
l10n daemon script
34db7f682f GIT_SILENT made messages (after extraction) 2019-03-19 03:07:26 +01:00
Nicolas Fella
f7763bf5a9 Request READ_CALL_LOG permission 2019-03-18 01:34:51 +00:00
Nicolas Fella
fb2859c24d Prompt user to enable remote keyboard 2019-03-17 14:42:16 +00:00
Nicolas Fella
145fa4f009 Remove dead code 2019-03-17 14:41:49 +00:00
l10n daemon script
e7f203ee3a GIT_SILENT made messages (after extraction) 2019-03-17 03:05:08 +01:00
l10n daemon script
c9feafb982 GIT_SILENT made messages (after extraction) 2019-03-14 03:01:50 +01:00
l10n daemon script
18c344bbc2 GIT_SILENT made messages (after extraction) 2019-03-13 03:10:45 +01:00
l10n daemon script
d73c8e914f GIT_SILENT made messages (after extraction) 2019-03-12 03:04:33 +01:00
l10n daemon script
86f4803083 GIT_SILENT made messages (after extraction) 2019-03-11 03:06:09 +01:00
l10n daemon script
eb875dceb0 GIT_SILENT made messages (after extraction) 2019-03-10 03:07:44 +01:00
Matthijs Tijink
3e85dd6160 Add all missing MPRIS properties in the media session control
Also simplified some code.
2019-03-09 20:06:32 +00:00
Matthijs Tijink
f9bc3f8e0b Block KDE Connect as media session
Also re-enable the media session mpris remote again
2019-03-09 13:57:33 +00:00
l10n daemon script
35ac407aee GIT_SILENT made messages (after extraction) 2019-03-09 03:06:44 +01:00
Albert Vaca Cintora
e033aad425 Bump version to release 2019-03-08 19:31:27 +01:00
Erik Duisters
f2e505b8af Allow shares from desktop to be canceled
Summary:
Allow in progress file transfers to be canceled

BUG: 349956

{F6373048}

{F6373050}

{F6373051}

Test Plan:
Send a large file from desktop to android
Press cancel in the progress notification

Result: the file transfer is cancelled and the cancelled file is deleted from storage

Reviewers: #kde_connect, nicolasfella, albertvaka

Reviewed By: #kde_connect, albertvaka

Subscribers: albertvaka, nicolasfella, kdeconnect

Tags: #kde_connect

Differential Revision: https://phabricator.kde.org/D16491
2019-03-08 19:08:29 +01:00
Erik Duisters
a6fdddf843 Use Storage Access Framework on SDK >= 21 (Lollipop and above)
Summary:
Use Storage Access Framework on Android running SDK >= 21 so writing to
sdcard will work again

|{F6546802}|{F6546803}|{F6546804}|
|API 21+|API 19-|Edit|

Test Plan:
Install patch on Android phone with Build.Version < 19 (Kitkat)

- Without a sdcard: Verify that dolphin displays an "All Files" entry that is empty
- With a sdcard and with "Add camera folder shortcut" turned off: Verify that dolphin displays the configured display name of the sdcard
- With a sdcard and with "Add camera folder shortcut" turned on: Verify that dolphin displays the configured display name of the sdcard and also lists a "Camera pictures" shortcut
- With a sdcard: Verify that when changing the display name or the "Add camera folder shortcut" preference dolphin displays the updated items (after pressing F5)
- With a sdcard: Verify that files can be read and written to/from the sdcard

Install patch on Android phone with Build.Version < 19 (Kitkat)
- Repeat the above tests except for the read/write test: Verify that files can be read from the sdcard

Install patch on Android phone with Build.Version > 21 (Lollipop)

- Without any configured storage locations: Verify dolphin displays an "All Files" entry that is empty
- With configured storage locations: Verify dolphin displays the display names of the configured storage locations and that entering a location displays the correct directory entries
- Make one or several changes to the configured storage locations: Verify dolphin displays the display names of the configured storage locations (after pressing F5) and that entering a location displays the correct directory entries

Reviewers: #kde_connect, albertvaka, sredman

Reviewed By: #kde_connect, albertvaka, sredman

Subscribers: albertvaka, sredman, kdeconnect

Tags: #kde_connect

Differential Revision: https://phabricator.kde.org/D18212
2019-03-08 15:02:10 +01:00
153 changed files with 6404 additions and 2199 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="11102"
android:versionName="1.11">
android:versionCode="11291"
android:versionName="1.12.9">
<supports-screens
android:anyDensity="true"
@@ -22,20 +22,23 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<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.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<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"
@@ -64,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
@@ -224,6 +227,12 @@
android:value="org.kde.kdeconnect.Plugins.SharePlugin.ShareChooserTargetService" />
</activity>
<receiver android:name="org.kde.kdeconnect.Plugins.SharePlugin.ShareBroadcastReceiver">
<intent-filter>
<action android:name="org.kde.kdeconnect.Plugins.SharePlugin.CancelShare" />
</intent-filter>
</receiver>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="org.kde.kdeconnect_tp.fileprovider"
@@ -252,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'
}
}
@@ -36,12 +36,9 @@ android {
}
}
packagingOptions {
pickFirst "META-INF/DEPENDENCIES"
pickFirst "META-INF/LICENSE"
pickFirst "META-INF/NOTICE"
pickFirst "META-INF/BCKEY.SF"
pickFirst "META-INF/BCKEY.DSA"
pickFirst "META-INF/INDEX.LIST"
merge "META-INF/DEPENDENCIES"
merge "META-INF/LICENSE"
merge "META-INF/NOTICE"
}
lintOptions {
abortOnError false
@@ -65,6 +62,11 @@ dependencies {
repositories {
jcenter()
google()
/* Needed for org.apache.sshd debugging
maven {
url "https://jitpack.io"
}
*/
}
implementation 'androidx.media:media:1.0.1'
@@ -77,6 +79,7 @@ dependencies {
implementation 'org.apache.sshd:sshd-core:0.14.0'
implementation 'org.apache.mina:mina-core:2.0.19' //For some reason, makes sshd-core:0.14.0 work without NIO, which isn't available until Android 8+
//implementation('com.github.bright:slf4android:0.1.6') { transitive = true } // For org.apache.sshd debugging
implementation 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0' //For SSL certificate generation
implementation 'com.jakewharton:butterknife:10.0.0'

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

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>

View File

@@ -1,9 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
<path android:fillColor="#FFF" android:pathData="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z" />
</vector>

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

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="?attr/dialogPreferredPadding"
android:paddingRight="?attr/dialogPreferredPadding"
android:paddingTop="10dp">
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
app:errorEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/storageLocation"
style="@style/Widget.MaterialComponents.TextInputEditText.FilledBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cursorVisible="false"
android:hint="@string/sftp_storage_preference_storage_location"
android:lines="1"
android:longClickable="false"
android:maxLines="1"
android:scrollHorizontally="true"
android:ellipsize="end"
android:inputType="text"
android:text="@string/sftp_storage_preference_click_to_select"
android:textColor="@android:color/darker_gray"
android:editable="false"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/storageDisplayNameInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="5dp"
app:errorEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/storageDisplayName"
style="@style/Widget.MaterialComponents.TextInputEditText.FilledBox.Dense"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/sftp_storage_preference_display_name"
android:lines="1"
android:maxLines="1"
android:scrollHorizontally="true"
android:ellipsize="end"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

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

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

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/delete"
android:title="@string/sftp_action_mode_menu_delete"
app:showAsAction="ifRoom"
android:icon="@drawable/ic_delete"/>
</menu>

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>
@@ -127,11 +123,11 @@
<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">بطاقة SD %d</string>
<string name="sftp_sdcard">بطاقة SD</string>
<string name="sftp_readonly">(للقراءة فقط)</string>
<string name="sftp_camera">صور الكاميرا</string>
<string name="add_host">أضف مضيفًا/م​إ</string>
<string name="no_players_connected">لم يُعثر على مشغّلات</string>
<string name="mpris_player_on_device">%1$s على %2$s</string>
<string name="send_files">أرسل ملفّات</string>

View File

@@ -1,64 +1,127 @@
<?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="ok">Aceutar</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="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-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_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="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\'agopiu 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="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_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_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>
@@ -106,11 +102,11 @@
<string name="title_activity_notification_filter">Filter napomena</string>
<string name="filter_apps_info">Notifikacije će biti sinhronizovane s izabranim aplikacijama.</string>
<string name="sftp_internal_storage">Unutrašnji smještaj</string>
<string name="sftp_all_files">Sve fajlove</string>
<string name="sftp_sdcard_num">SD kartica %d</string>
<string name="sftp_sdcard">SD kartica</string>
<string name="sftp_readonly">(samo za čitanje)</string>
<string name="sftp_camera">Slike sa kamere</string>
<string name="add_host">Dodaj host/IP</string>
<string name="no_players_connected">Nema nađenih igrača</string>
<string name="mpris_player_on_device">%1$s na %2$s</string>
</resources>

View File

@@ -5,7 +5,7 @@
<string name="foreground_notification_devices">Connectat a: %s</string>
<string name="pref_plugin_telephony">Notificador de la telefonia</string>
<string name="pref_plugin_telephony_desc">Envia notificacions per a les trucades entrants</string>
<string name="pref_plugin_battery">Informe de la bateria</string>
<string name="pref_plugin_battery">Informa de la bateria</string>
<string name="pref_plugin_battery_desc">Informa periòdicament sobre l\'estat de la bateria</string>
<string name="pref_plugin_sftp">Exposa el sistema de fitxers</string>
<string name="pref_plugin_sftp_desc">Permet navegar de forma remota pel sistema de fitxers del dispositiu</string>
@@ -14,9 +14,9 @@
<string name="pref_plugin_mousepad">Entrada remota</string>
<string name="pref_plugin_mousepad_desc">Usa el vostre telèfon o tauleta com un ratolí i un teclat</string>
<string name="pref_plugin_presenter">Presentació de diapositives remota</string>
<string name="pref_plugin_presenter_desc">Useu el dispositiu per canviar les dispositives d\'una presentació</string>
<string name="pref_plugin_remotekeyboard">S\'estan rebent pulsacions de tecla remotes</string>
<string name="pref_plugin_remotekeyboard_desc">S\'estan rebent esdeveniments de pulsacions de tecla des de dispositius remots</string>
<string name="pref_plugin_presenter_desc">Usa el dispositiu per a canviar les dispositives d\'una presentació</string>
<string name="pref_plugin_remotekeyboard">Rep les pulsacions remotes de tecla</string>
<string name="pref_plugin_remotekeyboard_desc">Rep els esdeveniments de pulsacions de tecla des de dispositius remots</string>
<string name="pref_plugin_mpris">Controls multimèdia</string>
<string name="pref_plugin_mpris_desc">Proporciona un comandament a distància pel reproductor multimèdia</string>
<string name="pref_plugin_runcommand">Executa una ordre</string>
@@ -24,10 +24,10 @@
<string name="pref_plugin_contacts">Sincronitzador dels contactes</string>
<string name="pref_plugin_contacts_desc">Permet sincronitzar la llibreta de contactes del dispositiu</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envia i rep els pings</string>
<string name="pref_plugin_ping_desc">Envia i rep els «ping»</string>
<string name="pref_plugin_notifications">Sincronitza les notificacions</string>
<string name="pref_plugin_notifications_desc">Accedeix a les vostres notificacions des d\'altres dispositius</string>
<string name="pref_plugin_receive_notifications">Recepció de les notificacions</string>
<string name="pref_plugin_receive_notifications">Rep les notificacions</string>
<string name="pref_plugin_receive_notifications_desc">Rep notificacions des d\'altres dispositius i mostrar-los a l\'Android</string>
<string name="pref_plugin_sharereceiver">Comparteix i rep</string>
<string name="pref_plugin_sharereceiver_desc">Comparteix els fitxers i els URL entre els dispositius</string>
@@ -38,6 +38,7 @@
<string name="open_settings">Obre l\'arranjament</string>
<string name="no_permissions">Us caldrà concedir permís per accedir a les notificacions</string>
<string name="no_permission_mprisreceiver">Per a poder controlar els reproductors multimèdia cal atorgar accés a les notificacions</string>
<string name="no_permissions_remotekeyboard">Per a rebre les pulsacions de tecles, haureu d\'activar el teclat remot del KDE Connect</string>
<string name="send_ping">Envia un ping</string>
<string name="open_mpris_controls">Control multimèdia</string>
<string name="remotekeyboard_editing_only_title">Fes servir les tecles remotes només en editar</string>
@@ -103,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>
@@ -117,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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Filtre per a les notificacions</string>
<string name="filter_apps_info">Les notificacions se sincronitzaran per a les aplicacions seleccionades.</string>
<string name="sftp_internal_storage">Emmagatzematge intern</string>
<string name="sftp_all_files">Tots els fitxers</string>
<string name="sftp_sdcard_num">Targeta SD %d</string>
<string name="sftp_sdcard">Targeta SD</string>
<string name="sftp_readonly">(només de lectura)</string>
<string name="sftp_camera">Imatges de la càmera</string>
<string name="add_device_dialog_title">Afegeix un dispositiu</string>
<string name="add_device_hint">Nom de la màquina o adreça IP</string>
<string name="sftp_preference_detected_sdcards">S\'han detectat targetes SD</string>
<string name="sftp_preference_edit_sdcard_title">Edita una targeta SD</string>
<string name="sftp_preference_configured_storage_locations">Ubicacions d\'emmagatzematge configurades</string>
<string name="sftp_preference_add_storage_location_title">Afegeix una ubicació d\'emmagatzematge</string>
<string name="sftp_preference_edit_storage_location">Edita una ubicació d\'emmagatzematge</string>
<string name="sftp_preference_add_camera_shortcut">Afegeix una drecera a la carpeta de càmera</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Afegeix una drecera a la carpeta de la càmera</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">No afegeixis cap drecera a la carpeta de la càmera</string>
<string name="sftp_storage_preference_storage_location">Ubicació d\'emmagatzematge</string>
<string name="sftp_storage_preference_storage_location_already_configured">Aquesta ubicació ja s\'ha configurat</string>
<string name="sftp_storage_preference_click_to_select">clic per seleccionar</string>
<string name="sftp_storage_preference_display_name">Nom a mostrar</string>
<string name="sftp_storage_preference_display_name_already_used">Aquest nom a mostrar ja està en ús</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">El nom a mostrar no pot estar buit</string>
<string name="sftp_action_mode_menu_delete">Suprimeix</string>
<string name="sftp_no_sdcard_detected">No s\'ha detectat cap targeta SD</string>
<string name="sftp_no_storage_locations_configured">No s\'ha configurat cap ubicació d\'emmagatzematge</string>
<string name="sftp_saf_permission_explanation">Per accedir remotament als fitxer cal configurar les ubicacions d\'emmagatzematge</string>
<string name="add_host">Afegeix una màquina/IP</string>
<string name="add_host_hint">Nom de la màquina o IP</string>
<string name="no_players_connected">No s\'ha trobat cap reproductor</string>
<string name="mpris_player_on_device">%1$s sobre el %2$s</string>
<string name="send_files">Envia els fitxers</string>
@@ -209,7 +235,7 @@
<string name="findmyphone_title">Troba el meu telèfon</string>
<string name="findmyphone_title_tablet">Troba la meva tauleta</string>
<string name="findmyphone_title_tv">Troba la meva TV</string>
<string name="findmyphone_description">Fa sonar aquest dispositiu perquè el pugueu trobar.</string>
<string name="findmyphone_description">Fa sonar aquest dispositiu perquè el pugueu trobar</string>
<string name="findmyphone_found">L\'he trobat</string>
<string name="open">Obre</string>
<string name="close">Tanca</string>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Aquest connector necessita permisos per a funcionar</string>
<string name="optional_permission_explanation">Us caldrà concedir permisos extres per accedir a totes les característiques</string>
<string name="plugins_need_optional_permission">Alguns connectors tenen característiques desactivades per la falta de permís (puntegeu per a més informació):</string>
<string name="sftp_permission_explanation">Per accedir als fitxers des del PC, l\'aplicació necessita permís per accedir a l\'emmagatzematge del telèfon</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>
@@ -273,6 +298,7 @@
<string name="block_contents">Bloca el contingut de les notificacions</string>
<string name="block_images">Bloca les imatges a les notificacions</string>
<string name="notification_channel_receivenotification">Notificacions des d\'altres dispositius</string>
<string name="take_picture">Pren una fotografia</string>
<string name="plugin_photo_desc">Pren una fotografia i l\'envia a un altre dispositiu</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>
@@ -189,11 +177,12 @@
<string name="title_activity_notification_filter">Filtr upozornění</string>
<string name="filter_apps_info">Upozorňování mezi vybranými aplikacemi bude synchronizováno.</string>
<string name="sftp_internal_storage">Interní úložiště</string>
<string name="sftp_all_files">Všechny soubory</string>
<string name="sftp_sdcard_num">SD karta %d</string>
<string name="sftp_sdcard">SD karta</string>
<string name="sftp_readonly">(pouze ke čtení)</string>
<string name="sftp_camera">Obrázky z fotoaparátu</string>
<string name="add_host">Přidat stroj/IP</string>
<string name="add_host_hint">Název hostitele nebo IP</string>
<string name="no_players_connected">Přehrávač nenalezen</string>
<string name="mpris_player_on_device">%1$s na %2$s</string>
<string name="send_files">Odeslat soubory</string>
@@ -221,10 +210,8 @@
<string name="permission_explanation">Tento modul potřebuje pro práci povolení</string>
<string name="optional_permission_explanation">Pro zpřístupnění všech funkcí potřebujete další oprávnění</string>
<string name="plugins_need_optional_permission">Některé moduly mají vypnuté vlastnosti, kvůli nedostatečným oprávněním (ťukněte pro více informací):</string>
<string name="sftp_permission_explanation">Pro přístup k souborům z vašeho počítače aplikace potřebuje oprávnění k úložišti telefonu</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>
@@ -139,11 +133,11 @@
<string name="title_activity_notification_filter">Bekendtgørelsesfilter</string>
<string name="filter_apps_info">Bekendtgørelser vil blive synkroniseret for de valgte apps.</string>
<string name="sftp_internal_storage">Intern lagring</string>
<string name="sftp_all_files">Alle filer</string>
<string name="sftp_sdcard_num">SD-kort %d</string>
<string name="sftp_sdcard">SD-kort</string>
<string name="sftp_readonly">(skrivebeskyttet)</string>
<string name="sftp_camera">Kamerabilleder</string>
<string name="add_host">Tilføj vært/IP</string>
<string name="no_players_connected">Ingen afspillere fundet</string>
<string name="mpris_player_on_device">%1$s på %2$s</string>
<string name="send_files">Send filer</string>
@@ -170,9 +164,7 @@
<string name="permission_explanation">Dette plugin kræver tilladelser for at virke</string>
<string name="optional_permission_explanation">Du skal give ekstra tilladelser for at aktivere alle funktioner</string>
<string name="plugins_need_optional_permission">Nogle plugins har deaktiverede funktioner pga. manglende tilladelser (tap for mere info):</string>
<string name="sftp_permission_explanation">For at tilgå filerne fra din pc, skal app\'en have tilladelse til at til gå telefonens datalager</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>
@@ -150,11 +140,11 @@
<string name="title_activity_notification_filter">Benachrichtigungs-Filter</string>
<string name="filter_apps_info">Benachrichtigungen werden zwischen den ausgewählten Anwendungen abgeglichen.</string>
<string name="sftp_internal_storage">Interner Speicher</string>
<string name="sftp_all_files">Alle Dateien</string>
<string name="sftp_sdcard_num">SD-Karte %d</string>
<string name="sftp_sdcard">SD-Karte</string>
<string name="sftp_readonly">(Nur lesen)</string>
<string name="sftp_camera">Kamerabilder</string>
<string name="add_host">Rechner/IP-Adresse hinzufügen</string>
<string name="no_players_connected">Keine Medienspieler gefunden</string>
<string name="mpris_player_on_device">%1$s auf %2$s</string>
<string name="send_files">Dateien senden</string>
@@ -182,10 +172,8 @@
<string name="permission_explanation">Dieses Modul benötigt zusätzliche Berechtigungen</string>
<string name="optional_permission_explanation">Es müssen weitere Berechtigungen erteilt werden, um alle Funktionen nutzen zu können</string>
<string name="plugins_need_optional_permission">Einige Module haben eingeschränkte Funktionen wegen fehlender Berechtigungen, tippen Sie für weitere Informationen:</string>
<string name="sftp_permission_explanation">Um vom Rechner auf den Telefonspeicher zuzugreifen, werden weitere Berechtigungen benötigt</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>
@@ -139,11 +133,11 @@
<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">SD card %d</string>
<string name="sftp_sdcard">SD card</string>
<string name="sftp_readonly">(ανάγνωση μόνο)</string>
<string name="sftp_camera">Φωτογραφίες</string>
<string name="add_host">Προσθήκη υπολογιστή/IP</string>
<string name="no_players_connected">Δεν βρέθηκαν συσκευές αναπαραγωγής</string>
<string name="mpris_player_on_device">%1$s σε %2$s</string>
<string name="send_files">Αποστολή αρχείων</string>
@@ -170,9 +164,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">Για να διαβάσετε και να γράψετε 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>
@@ -161,11 +191,32 @@
<string name="title_activity_notification_filter">Notification filter</string>
<string name="filter_apps_info">Notifications will be synchronised for the selected apps.</string>
<string name="sftp_internal_storage">Internal storage</string>
<string name="sftp_all_files">All files</string>
<string name="sftp_sdcard_num">SD card %d</string>
<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>
@@ -193,10 +244,9 @@
<string name="permission_explanation">This plugin needs permissions to work</string>
<string name="optional_permission_explanation">You need to grant extra permissions to enable all functions</string>
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="sftp_permission_explanation">To access your files from your PC the app needs permission to access your phone\'s storage</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>
@@ -207,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>
@@ -238,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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Filtro de notificaciones</string>
<string name="filter_apps_info">Las notificaciones se sincronizarán en las aplicaciones seleccionadas.</string>
<string name="sftp_internal_storage">Almacenamiento interno</string>
<string name="sftp_all_files">Todos los archivos</string>
<string name="sftp_sdcard_num">Tarjeta SD %d</string>
<string name="sftp_sdcard">Tarjeta SD</string>
<string name="sftp_readonly">(solo lectura)</string>
<string name="sftp_camera">Imágenes de la cámara</string>
<string name="add_device_dialog_title">Añadir dispositivo</string>
<string name="add_device_hint">Nombre o dirección IP</string>
<string name="sftp_preference_detected_sdcards">Tarjetas SD detectadas</string>
<string name="sftp_preference_edit_sdcard_title">Editar tarjeta SD</string>
<string name="sftp_preference_configured_storage_locations">Localizaciones de almacenamiento configuradas</string>
<string name="sftp_preference_add_storage_location_title">Añadir localización de almacenamiento</string>
<string name="sftp_preference_edit_storage_location">Editar localización de almacenamiento</string>
<string name="sftp_preference_add_camera_shortcut">Añadir acceso rápido a la carpeta de la cámara</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Añadir un acceso rápido a la carpeta de la cámara</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">No añadir un acceso rápido a la carpeta de la cámara</string>
<string name="sftp_storage_preference_storage_location">Localización de almacenamiento</string>
<string name="sftp_storage_preference_storage_location_already_configured">Esta localización ya ha sido configurada</string>
<string name="sftp_storage_preference_click_to_select">pulsar para seleccionar</string>
<string name="sftp_storage_preference_display_name">Mostrar nombre</string>
<string name="sftp_storage_preference_display_name_already_used">Este nombre de dispositivo ya está en uso</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">El nombre de dispositivo no puede estar vacío</string>
<string name="sftp_action_mode_menu_delete">Borrar</string>
<string name="sftp_no_sdcard_detected">No se ha detectado ninguna tarjeta SD</string>
<string name="sftp_no_storage_locations_configured">No hay configurada ninguna localización de almacenamiento</string>
<string name="sftp_saf_permission_explanation">Para acceder a los archivos remotamente debe configurar las localizaciones de almacenamiento</string>
<string name="add_host">Añadir servidor/IP</string>
<string name="add_host_hint">Nombre o IP</string>
<string name="no_players_connected">Ningún reproductor encontrado</string>
<string name="mpris_player_on_device">%1$s en %2$s</string>
<string name="send_files">Enviar archivos</string>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Este complemento necesita permisos para funcionar</string>
<string name="optional_permission_explanation">Debe otorgar permisos extra para activar todas las funciones</string>
<string name="plugins_need_optional_permission">Algunos complementos tienen funcionalidades desactivadas por falta de permisos (pulse para más información):</string>
<string name="sftp_permission_explanation">Para acceder a sus archivos desde su equipo, la aplicación necesita permisos para acceder al almacenamiento de su teléfono</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>
@@ -273,4 +298,7 @@
<string name="block_contents">Bloquear el contenido de las notificaciones</string>
<string name="block_images">Bloquear las imágenes en las notificaciones</string>
<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>
@@ -133,11 +127,11 @@
<string name="title_activity_notification_filter">Märguannete filter</string>
<string name="filter_apps_info">Valitud rakenduste märguanded sünkroonitakse</string>
<string name="sftp_internal_storage">Sisemine salvesti</string>
<string name="sftp_all_files">Kõik failid</string>
<string name="sftp_sdcard_num">SD-kaart %d</string>
<string name="sftp_sdcard">SD-kaart</string>
<string name="sftp_readonly">(kirjutuskaitstud)</string>
<string name="sftp_camera">Kaamera pildid</string>
<string name="add_host">Lisa masin/IP</string>
<string name="no_players_connected">Ühtegi mängijat ei leitud</string>
<string name="mpris_player_on_device">%1$s seadmes %2$s</string>
<string name="send_files">Saada faile</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>
@@ -179,11 +169,11 @@
<string name="title_activity_notification_filter">Jakinarazpenen iragazkia</string>
<string name="filter_apps_info">Aukeratutako aplikazioen jakinarazpenak sinkronizatuko dira</string>
<string name="sftp_internal_storage">Barne biltegiratzea</string>
<string name="sftp_all_files">Fitxategi guztiak</string>
<string name="sftp_sdcard_num">%d SD txartela</string>
<string name="sftp_sdcard">SD txartela</string>
<string name="sftp_readonly">(irakurri soilik)</string>
<string name="sftp_camera">Kamerako irudiak</string>
<string name="add_host">Gehitu ostalaria/IP</string>
<string name="no_players_connected">Ez da jokalaririk aurkitu</string>
<string name="mpris_player_on_device">%1$s - %2$s</string>
<string name="send_files">Bidali fitxategiak</string>
@@ -211,10 +201,8 @@
<string name="permission_explanation">Plugin honek baimena behar du funtzionatzeko</string>
<string name="optional_permission_explanation">Baimen gehiago eman behar dituzu funtzio guztiak gaitzeko</string>
<string name="plugins_need_optional_permission">Plugin batzuek desgaitutako eginbideak dituzte baimenak faltan dituztelako (tak egin informazio gehiagorako):</string>
<string name="sftp_permission_explanation">Zure fitxategiak PCtik atzitzeko aplikazioak zure telefonoaren biltegiratzea atzitzeko baimena behar du</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>
@@ -179,11 +191,32 @@
<string name="title_activity_notification_filter">Ilmoitussuodatin</string>
<string name="filter_apps_info">Valittujen sovellusten ilmoitukset synkronoidaan.</string>
<string name="sftp_internal_storage">Sisäinen muisti</string>
<string name="sftp_all_files">Kaikki tiedostot</string>
<string name="sftp_sdcard_num">SD-kortti %d</string>
<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>
@@ -211,10 +244,9 @@
<string name="permission_explanation">Liitännäinen tarvitsee toimiakseen lisäkäyttöoikeuksia</string>
<string name="optional_permission_explanation">Kaikkien toimintojen käyttämiseksi sinun on annettava lisäkäyttöoikeuksia</string>
<string name="plugins_need_optional_permission">Jotkin liitännäisten ominaisuudet eivät ole käytössä puuttuvien käyttöoikeuksien takia (lisätietoa napsauttamalla):</string>
<string name="sftp_permission_explanation">Sovellus tarvitsee puhelimen tallennustilan käyttöoikeudet voidakseen käyttää tietokoneesi tiedostoja</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>
@@ -225,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>
@@ -256,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>
@@ -263,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>
@@ -179,11 +175,32 @@
<string name="title_activity_notification_filter">Filtre des notifications</string>
<string name="filter_apps_info">Les notifications seront synchronisées pour les applications sélectionnées.</string>
<string name="sftp_internal_storage">Stockage interne</string>
<string name="sftp_all_files">Tous les fichiers</string>
<string name="sftp_sdcard_num">Carte SD %d</string>
<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>
<string name="mpris_player_on_device">%1$s sur %2$s</string>
<string name="send_files">Envoyer des fichiers</string>
@@ -211,10 +228,8 @@
<string name="permission_explanation">Ce module externe nécessite des permissions pour fonctionner</string>
<string name="optional_permission_explanation">Vous devez accorder des permissions supplémentaires pour activer toutes les fonctionnalités</string>
<string name="plugins_need_optional_permission">Certaines fonctionnalités de modules externes sont désactivées faute de permissions suffisantes (tapez pour plus d\'informations) :</string>
<string name="sftp_permission_explanation">Pour accéder aux fichiers de votre ordinateur, l\'application requiert la permission d\'accéder à la mémoire de stockage de votre téléphone</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

@@ -24,20 +24,21 @@
<string name="pref_plugin_contacts">Sincronizador de contactos</string>
<string name="pref_plugin_contacts_desc">Permitir sincronizar o caderno de contactos do dispositivo</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envíe e reciba pings.</string>
<string name="pref_plugin_ping_desc">Envíe e reciba pings</string>
<string name="pref_plugin_notifications">Sincronización de notificacións</string>
<string name="pref_plugin_notifications_desc">Acceda ás súas notificacións desde outros dispositivos.</string>
<string name="pref_plugin_notifications_desc">Acceda ás súas notificacións desde outros dispositivos</string>
<string name="pref_plugin_receive_notifications">Recibir notificacións</string>
<string name="pref_plugin_receive_notifications_desc">Recibir notificacións do outro dispositivo e mostralas en Android.</string>
<string name="pref_plugin_receive_notifications_desc">Recibir notificacións do outro dispositivo e mostralas en Android</string>
<string name="pref_plugin_sharereceiver">Compartir e recibir</string>
<string name="pref_plugin_sharereceiver_desc">Comparta ficheiros e enderezos URL entre dispositivos.</string>
<string name="plugin_not_available">Esta funcionalidade non está dispoñíbel para a súa versión de Android.</string>
<string name="pref_plugin_sharereceiver_desc">Comparta ficheiros e enderezos URL entre dispositivos</string>
<string name="plugin_not_available">Esta funcionalidade non está dispoñíbel para a súa versión de Android</string>
<string name="device_list_empty">Non hai dispositivos.</string>
<string name="ok">Aceptar</string>
<string name="cancel">Cancelar</string>
<string name="open_settings">Abrir a configuración</string>
<string name="no_permissions">Debe conceder permisos para acceder ás notificacións.</string>
<string name="no_permission_mprisreceiver">Para poder controlar os seus reprodutores de son e vídeo ten que garantir acceso ás notificacións.</string>
<string name="no_permissions">Debe conceder permisos para acceder ás notificacións</string>
<string name="no_permission_mprisreceiver">Para poder controlar os seus reprodutores de son e vídeo ten que garantir acceso ás notificacións</string>
<string name="no_permissions_remotekeyboard">Para recibir presións de tecla ten que activar o teclado remoto de KDE Connect</string>
<string name="send_ping">Enviar un ping</string>
<string name="open_mpris_controls">Control multimedia</string>
<string name="remotekeyboard_editing_only_title">Xestionar teclas remotas só ao editar.</string>
@@ -103,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>
@@ -117,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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Filtro de notificacións</string>
<string name="filter_apps_info">As notificacións sincronizaranse para os seguintes aplicativos.</string>
<string name="sftp_internal_storage">Almacenamento interno</string>
<string name="sftp_all_files">Todos os ficheiros</string>
<string name="sftp_sdcard_num">Tarxeta SD %d</string>
<string name="sftp_sdcard">Tarxeta SD</string>
<string name="sftp_readonly">(só lectura)</string>
<string name="sftp_camera">Imaxes da cámara</string>
<string name="add_device_dialog_title">Engadir o dispositivo</string>
<string name="add_device_hint">Nome de máquina ou enderezo IP</string>
<string name="sftp_preference_detected_sdcards">Tarxetas SD detectadas</string>
<string name="sftp_preference_edit_sdcard_title">Editar a tarxeta SD</string>
<string name="sftp_preference_configured_storage_locations">Lugares de almacenamento configurados</string>
<string name="sftp_preference_add_storage_location_title">Engadir un lugar de almacenamento</string>
<string name="sftp_preference_edit_storage_location">Editar un lugar de almacenamento</string>
<string name="sftp_preference_add_camera_shortcut">Engadir un atallo ao cartafol de cámara</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Engadir un atallo ao cartafol da cámara</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Non engadir un atallo ao cartafol da cámara</string>
<string name="sftp_storage_preference_storage_location">Lugar de almacenamento</string>
<string name="sftp_storage_preference_storage_location_already_configured">Este lugar xa está configurado</string>
<string name="sftp_storage_preference_click_to_select">premer para seleccionar</string>
<string name="sftp_storage_preference_display_name">Nome para mostrar</string>
<string name="sftp_storage_preference_display_name_already_used">Este nome para mostrar xa está a usarse</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">O nome para mostrar non pode estar baleiro</string>
<string name="sftp_action_mode_menu_delete">Eliminar</string>
<string name="sftp_no_sdcard_detected">Non se detectaron tarxetas SD</string>
<string name="sftp_no_storage_locations_configured">Non se configuraron localizacións de almacenamento</string>
<string name="sftp_saf_permission_explanation">Para acceder a ficheiro remotamente ten que configurar lugares de almacenamento</string>
<string name="add_host">Engadir unha nome ou IP</string>
<string name="add_host_hint">Nome de máquina ou IP</string>
<string name="no_players_connected">Non se atoparon reprodutores.</string>
<string name="mpris_player_on_device">%1$s en %2$s</string>
<string name="send_files">Enviar ficheiros</string>
@@ -204,7 +230,7 @@
<string name="on_data_message">Parece que está usando unha conexión de datos de móbil. KDE Connect só funciona en redes locais.</string>
<string name="no_file_browser">Non hai navegadores de ficheiros instalados.</string>
<string name="pref_plugin_telepathy">Enviar unha mensaxe de texto</string>
<string name="pref_plugin_telepathy_desc">Enviar mensaxes de texto desde un computador de escritorio.</string>
<string name="pref_plugin_telepathy_desc">Enviar mensaxes de texto desde o seu escritorio</string>
<string name="plugin_not_supported">O dispositivo non é compatíbel con este complemento.</string>
<string name="findmyphone_title">Atopar o móbil</string>
<string name="findmyphone_title_tablet">Atopar a tableta</string>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Este complemento necesita permisos para funcionar.</string>
<string name="optional_permission_explanation">Ten que conceder permisos adicionais para activar todas as funcións.</string>
<string name="plugins_need_optional_permission">Algúns complementos teñen funcionalidades desactivadas por mor dunha falta de permisos (toque para máis información):</string>
<string name="sftp_permission_explanation">Para acceder aos seus ficheiros desde o computador o aplicativo necesita permiso para acceder ao almacenamento do teléfono.</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>
@@ -273,6 +298,7 @@
<string name="block_contents">Bloquear o contido das notificacións</string>
<string name="block_images">Bloquear as imaxes nas notificacións</string>
<string name="notification_channel_receivenotification">Notificacións desde outros dispositivos</string>
<string name="take_picture">Sacar unha foto</string>
<string name="plugin_photo_desc">Sacar unha foto e enviala a outro dispositivo</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>
@@ -139,11 +133,11 @@
<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>
<string name="sftp_camera">תמונות מצלמה</string>
<string name="add_host">הוסף כתובת או IP</string>
<string name="no_players_connected">לא נמצא נגן</string>
<string name="mpris_player_on_device">%1$s אצל %2$s</string>
<string name="send_files">שלח קובץ</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 clipboard</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>
@@ -172,11 +163,11 @@
<string name="title_activity_notification_filter">Filter notifikasi</string>
<string name="filter_apps_info">Notifikasi akan disinkronkan terhadap apl terpilih.</string>
<string name="sftp_internal_storage">Penyimpanan internal</string>
<string name="sftp_all_files">Semua file</string>
<string name="sftp_sdcard_num">Kartu SD %d</string>
<string name="sftp_sdcard">Kartu SD</string>
<string name="sftp_readonly">(hanya baca)</string>
<string name="sftp_camera">Gambar kamera</string>
<string name="add_host">Tambahkan host/IP</string>
<string name="no_players_connected">Tidak ada player yang ditemukan</string>
<string name="mpris_player_on_device">%1$s pada %2$s</string>
<string name="send_files">Kirim file</string>
@@ -204,10 +195,8 @@
<string name="permission_explanation">Plugin ini perlu perizinan untuk kerja</string>
<string name="optional_permission_explanation">Kamu perlu mengabulkan perizinan extra untuk memfungsikan semua fungsian</string>
<string name="plugins_need_optional_permission">Beberapa plugin yang memiliki fitur dinonfungsikan karena kurangnya perizinan (ketuk untuk info selebihnya):</string>
<string name="sftp_permission_explanation">Untuk mengakses filemu dari PC-mu si apl perlu perizinan untuk mengakses penyimpanan teleponmu</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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Filtro delle notifiche</string>
<string name="filter_apps_info">Le notifiche saranno sincronizzate per le applicazioni selezionate.</string>
<string name="sftp_internal_storage">Archiviazione interna</string>
<string name="sftp_all_files">Tutti i file</string>
<string name="sftp_sdcard_num">Scheda SD %d</string>
<string name="sftp_sdcard">Scheda SD</string>
<string name="sftp_readonly">(sola lettura)</string>
<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>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Questa estensione ha bisogno di permessi per funzionare</string>
<string name="optional_permission_explanation">Devi concedere permessi aggiuntivi per abilitare tutte le funzioni</string>
<string name="plugins_need_optional_permission">Alcune estensioni hanno funzioni disabilitate per una mancanza di permessi (tocca per maggiori informazioni):</string>
<string name="sftp_permission_explanation">Per accedere ai tuoi file dal tuo PC, l\'applicazione ha bisogno dell\'autorizzazione di accesso alla memoria del telefono</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>
@@ -273,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>
@@ -152,11 +183,32 @@
<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">SD 카드 %d</string>
<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>
@@ -184,10 +236,9 @@
<string name="permission_explanation">이 플러그인을 사용하려면 권한이 필요합니다</string>
<string name="optional_permission_explanation">모든 기능을 사용하려면 추가 권한이 필요합니다</string>
<string name="plugins_need_optional_permission">일부 플러그인은 권한이 없어서 비활성화되었습니다(정보를 보려면 누르기):</string>
<string name="sftp_permission_explanation">PC에서 파일에 접근하려면 앱에서 휴대폰 저장소 접근 권한이 필요합니다</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>
@@ -198,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>
@@ -205,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>
@@ -218,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

@@ -87,11 +87,11 @@
<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="sftp_all_files">Visi failai</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="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>

View File

@@ -38,6 +38,7 @@
<string name="open_settings">Instellingen openen</string>
<string name="no_permissions">U moet toestemming geven voor toegang tot meldingen</string>
<string name="no_permission_mprisreceiver">Om in staat te zijn uw mediaspelers te besturen moet u toegan geven tot de meldingen</string>
<string name="no_permissions_remotekeyboard">Om indrukken van toetsen te ontvangen moet u het KDE Connect Remote Keyboard activeren</string>
<string name="send_ping">Ping verzenden</string>
<string name="open_mpris_controls">Bediening van multimedia</string>
<string name="remotekeyboard_editing_only_title">Behandel toetsen op afstand alleen bij bewerken</string>
@@ -103,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>
@@ -117,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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Filter voor meldingen</string>
<string name="filter_apps_info">Meldingen zullen gesynchroniseerd worden voor de geselecteerde apps.</string>
<string name="sftp_internal_storage">Interne opslag</string>
<string name="sftp_all_files">Alle bestanden</string>
<string name="sftp_sdcard_num">SD-kaartje %d</string>
<string name="sftp_sdcard">SD-kaartje</string>
<string name="sftp_readonly">(alleen-lezen)</string>
<string name="sftp_camera">Afbeeldingen van camera</string>
<string name="add_device_dialog_title">Apparaat toevoegen</string>
<string name="add_device_hint">Hostnaam of IP-adres</string>
<string name="sftp_preference_detected_sdcards">SD-kaarten gevonden</string>
<string name="sftp_preference_edit_sdcard_title">SD-kaart bewerken</string>
<string name="sftp_preference_configured_storage_locations">Opslaglocaties geconfigureerd</string>
<string name="sftp_preference_add_storage_location_title">Opslaglocatie toegevoegd</string>
<string name="sftp_preference_edit_storage_location">Opslaglocatie bewerken</string>
<string name="sftp_preference_add_camera_shortcut">Sneltoets voor cameramap toevoegen</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Een sneltoets naar de cameramap toevoegen</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Geen sneltoets naar de cameramap toevoegen</string>
<string name="sftp_storage_preference_storage_location">Opslaglocatie</string>
<string name="sftp_storage_preference_storage_location_already_configured">Deze locatie is al geconfigureerd</string>
<string name="sftp_storage_preference_click_to_select">klik om te selecteren</string>
<string name="sftp_storage_preference_display_name">Schermnaam</string>
<string name="sftp_storage_preference_display_name_already_used">Deze schermnaam wordt al gebruikt</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Schermnaam mag niet leeg zijn</string>
<string name="sftp_action_mode_menu_delete">Verwijderen</string>
<string name="sftp_no_sdcard_detected">Geen SD-kaart gedetecteerd</string>
<string name="sftp_no_storage_locations_configured">Geen opslaglocaties geconfigureerd</string>
<string name="sftp_saf_permission_explanation">Om toegang te hebben tot bestanden op afstand moet u opslaglocaties configureren</string>
<string name="add_host">Host/IP-adres toevoegen</string>
<string name="add_host_hint">Hostnaam of IP-adres</string>
<string name="no_players_connected">Geen spelers gevonden</string>
<string name="mpris_player_on_device">%1$s op %2$s</string>
<string name="send_files">Bestanden verzenden</string>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Deze plug-in heeft toestemming nodig om te werken</string>
<string name="optional_permission_explanation">U moet toestemming geven om alle functies in te schakelen</string>
<string name="plugins_need_optional_permission">Sommige plug-ins hebben functies uitgeschakeld vanwege ontbrekende toestemming (tik voor meer informatie):</string>
<string name="sftp_permission_explanation">"Om toegang tot uw bestanden te krijgen vanuit uw PC heeft de app toestemming nodig voor toegang tot de opslag van uw telefoon "</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>
@@ -273,6 +298,7 @@
<string name="block_contents">Inhoud van meldingen blokkeren</string>
<string name="block_images">Afbeeldingen in meldingen blokkeren</string>
<string name="notification_channel_receivenotification">Meldingen van andere apparaten</string>
<string name="take_picture">Foto nemen</string>
<string name="plugin_photo_desc">Neem een foto en stuur het naar een ander apparaat</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>
@@ -179,11 +191,32 @@
<string name="title_activity_notification_filter">Varslingsfilter</string>
<string name="filter_apps_info">Varslingar vert synkroniserte for dei valde appane.</string>
<string name="sftp_internal_storage">Intern lagring</string>
<string name="sftp_all_files">Alle filer</string>
<string name="sftp_sdcard_num">SD-kort %d</string>
<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,21 +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="sftp_permission_explanation">For å gje tilgang til filene frå datamaskina treng appen leseløyve til lagringsområdet på telefonen</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>
@@ -265,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

@@ -106,14 +106,6 @@
<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_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="other"/>
</plurals>
<plurals name="received_files_title">
<item quantity="one">Odebrano plik od %1$s</item>
<item quantity="few">Odebrano %2$d pliki od %1$s</item>
@@ -128,10 +120,6 @@
</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>
@@ -189,11 +177,11 @@
<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_all_files">Wszystkie pliki</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_host">Dodaj gospodarza/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>
@@ -221,10 +209,8 @@
<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="sftp_permission_explanation">Aby uzyskać dostęp do plików z twojego PC aplikacja ta potrzebuje uprawnień do dostępu do pamięci twojego telefonu</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="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>

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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Filtro de notificações</string>
<string name="filter_apps_info">As notificações dos aplicativos selecionados serão sincronizadas.</string>
<string name="sftp_internal_storage">Armazenamento interno</string>
<string name="sftp_all_files">Todos os arquivos</string>
<string name="sftp_sdcard_num">Cartão SD %d</string>
<string name="sftp_sdcard">Cartão SD</string>
<string name="sftp_readonly">(somente leitura)</string>
<string name="sftp_camera">Imagens da câmera</string>
<string name="add_device_dialog_title">Adicionar dispositivo</string>
<string name="add_device_hint">Nome da máquina ou endereço IP</string>
<string name="sftp_preference_detected_sdcards">Cartões SD detectados</string>
<string name="sftp_preference_edit_sdcard_title">Editar cartão SD</string>
<string name="sftp_preference_configured_storage_locations">Localizações de armazenamento configuradas</string>
<string name="sftp_preference_add_storage_location_title">Adicionar localização de armazenamento</string>
<string name="sftp_preference_edit_storage_location">Editar localização de armazenamento</string>
<string name="sftp_preference_add_camera_shortcut">Adicionar atalho para pasta da câmera</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Adiciona um atalho para a pasta da câmera</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Não adiciona um atalho para a pasta da câmera</string>
<string name="sftp_storage_preference_storage_location">Localização do armazenamento</string>
<string name="sftp_storage_preference_storage_location_already_configured">Esta localização já foi configurada</string>
<string name="sftp_storage_preference_click_to_select">clique para selecionar</string>
<string name="sftp_storage_preference_display_name">Nome de exibição</string>
<string name="sftp_storage_preference_display_name_already_used">Este nome de exibição já está em uso</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">O nome de exibição não pode esta vazio</string>
<string name="sftp_action_mode_menu_delete">Excluir</string>
<string name="sftp_no_sdcard_detected">Nenhum cartão SD detectado</string>
<string name="sftp_no_storage_locations_configured">Nenhuma localização de armazenamento configurada</string>
<string name="sftp_saf_permission_explanation">Para acessar arquivos remotamente você precisa configurar localizações de armazenamento</string>
<string name="add_host">Adicionar máquina/IP</string>
<string name="add_host_hint">Nome da máquina ou IP</string>
<string name="no_players_connected">Nenhum reprodutor encontrado</string>
<string name="mpris_player_on_device">%1$s em %2$s</string>
<string name="send_files">Enviar arquivos</string>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Este plugin precisa de permissões para funcionar</string>
<string name="optional_permission_explanation">Você precisa conceder permissões extras para ativar todas as funções</string>
<string name="plugins_need_optional_permission">Alguns plugins possuem recursos desativados devido à falta de permissões (toque para obter mais informações):</string>
<string name="sftp_permission_explanation">Para acessar os seus arquivos a partir do PC, o aplicativo precisa de permissão para acessar o armazenamento do seu celular</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>
@@ -273,6 +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">Tirar uma foto</string>
<string name="plugin_photo_desc">Tira uma foto e envia ela para outro dispositivo</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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Filtro de notificações</string>
<string name="filter_apps_info">As notificações serão sincronizadas para as aplicações seleccionadas.</string>
<string name="sftp_internal_storage">Armazenamento interno</string>
<string name="sftp_all_files">Todos os ficheiros</string>
<string name="sftp_sdcard_num">Cartão SD %d</string>
<string name="sftp_sdcard">Cartão SD</string>
<string name="sftp_readonly">(apenas para leitura)</string>
<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>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Este \'plugin\' precisa de permissões para funcionar</string>
<string name="optional_permission_explanation">Precisa de dar permissões extra para activar todas as funcionalidades</string>
<string name="plugins_need_optional_permission">Alguns \'plugins\' têm funcionalidades desactivadas devido à falta de permissões (toque para obter mais informações):</string>
<string name="sftp_permission_explanation">Para aceder aos seus ficheiros a partir do seu PC, a aplicação precisa de permissão para aceder ao armazenamento do seu telemóvel</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>
@@ -273,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

@@ -90,8 +90,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 +103,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>
@@ -158,11 +152,11 @@
<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">SD-карта %d</string>
<string name="sftp_sdcard">SD-карта</string>
<string name="sftp_readonly">(только чтение)</string>
<string name="sftp_camera">Фотографии с камеры</string>
<string name="add_host">Добавить хост/IP-адрес</string>
<string name="no_players_connected">Медиапроигрывателей не найдено</string>
<string name="mpris_player_on_device">%1$s на %2$s</string>
<string name="send_files">Отправить файлы</string>
@@ -189,10 +183,8 @@
<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">Чтобы читать и писать SMS с компьютера, вам необходимо дать разрешение на доступ к SMS</string>
<string name="telephony_permission_explanation">Чтобы видеть телефонные звонки и SMS на компьютере, необходимо дать разрешение на телефонные звонки и SMS</string>
<string name="telephony_optional_permission_explanation">Чтобы видеть имя контакта вместо номера телефона, необходимо предоставить доступ к контактам</string>
<string name="dark_theme">Тёмное оформление</string>
<string name="devices">Устройства</string>

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>
@@ -189,11 +177,14 @@
<string name="title_activity_notification_filter">Filter upozornení</string>
<string name="filter_apps_info">Upozornenia budú synchronizované pre vybrané aplikácie.</string>
<string name="sftp_internal_storage">Interné úložisko</string>
<string name="sftp_all_files">Všetky súbory</string>
<string name="sftp_sdcard_num">SD karta %d</string>
<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>
@@ -221,10 +212,8 @@
<string name="permission_explanation">Tento plugin potrebuje oprávnenia aby fungoval</string>
<string name="optional_permission_explanation">Musíte povoliť oprávnenia na povolenie všetkých funkcií</string>
<string name="plugins_need_optional_permission">Niektoré pluginy majú zakázané funkcie pre nedostatok opránení (ťuknite pre viac info):</string>
<string name="sftp_permission_explanation">Na prístup k vaším súborom z PC, aplikácia potrebuje oprávnenie na prístup k vašemu úložisku</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

@@ -38,6 +38,7 @@
<string name="open_settings">Öppna inställningarna</string>
<string name="no_permissions">Du måste ge rättighet att komma åt underrättelser</string>
<string name="no_permission_mprisreceiver">För att kunna styra mediaspelare måste du ge tillgång till underrättelser</string>
<string name="no_permissions_remotekeyboard">KDE-ansluts fjärrtangentbord måste aktiveras för att ta emot tangentnedtryckningar</string>
<string name="send_ping">Skicka ping</string>
<string name="open_mpris_controls">Kontroll av multimedia</string>
<string name="remotekeyboard_editing_only_title">Hantera bara externa tangenter vid redigering</string>
@@ -96,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>
@@ -117,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>
@@ -184,13 +191,32 @@
<string name="title_activity_notification_filter">Underrättelsefilter</string>
<string name="filter_apps_info">Underrättelser synkroniseras för markerade applikationer.</string>
<string name="sftp_internal_storage">Intern lagring</string>
<string name="sftp_all_files">Alla filer</string>
<string name="sftp_sdcard_num">SD-kort %d</string>
<string name="sftp_sdcard">SD-kort</string>
<string name="sftp_readonly">(skrivskyddat)</string>
<string name="sftp_camera">Kamerabilder</string>
<string name="add_device_dialog_title">Lägg till apparat</string>
<string name="add_device_hint">Värddatornamn eller IP-adress</string>
<string name="sftp_preference_detected_sdcards">Detekterade SD-kort</string>
<string name="sftp_preference_edit_sdcard_title">Redigera SD-kort</string>
<string name="sftp_preference_configured_storage_locations">Anpassa lagringsplatser</string>
<string name="sftp_preference_add_storage_location_title">Lägg till lagringsplats</string>
<string name="sftp_preference_edit_storage_location">Redigera lagringsplats</string>
<string name="sftp_preference_add_camera_shortcut">Lägg till genväg till kamerakatalog</string>
<string name="sftp_preference_add_camera_shortcut_summary_on">Lägg till en genväg till kamerakatalogen</string>
<string name="sftp_preference_add_camera_shortcut_summary_off">Lägg inte till en genväg till kamerakatalogen</string>
<string name="sftp_storage_preference_storage_location">Lagringsplats</string>
<string name="sftp_storage_preference_storage_location_already_configured">Platsen har redan ställts in</string>
<string name="sftp_storage_preference_click_to_select">klicka för att välja</string>
<string name="sftp_storage_preference_display_name">Namn att visa</string>
<string name="sftp_storage_preference_display_name_already_used">Namn att visa används redan</string>
<string name="sftp_storage_preference_display_name_cannot_be_empty">Namn att visa kan inte vara tomt</string>
<string name="sftp_action_mode_menu_delete">Ta bort</string>
<string name="sftp_no_sdcard_detected">Inga SD-kort detekterades</string>
<string name="sftp_no_storage_locations_configured">Inga lagringsplatser inställda</string>
<string name="sftp_saf_permission_explanation">För att komma åt filer från en annan apparat måste lagringsplatser ställas in</string>
<string name="add_host">Lägg till värddator/IP-adress</string>
<string name="add_host_hint">Värddatornamn eller IP</string>
<string name="no_players_connected">Inga spelare hittades</string>
<string name="mpris_player_on_device">%1$s på %2$s</string>
<string name="send_files">Skicka filer</string>
@@ -218,10 +244,9 @@
<string name="permission_explanation">Insticksprogrammet behöver rättigheter för att fungera</string>
<string name="optional_permission_explanation">Du måste ge extra rättigheter för att aktivera alla funktioner</string>
<string name="plugins_need_optional_permission">Vissa insticksprogram har inaktiverade funktioner på grund av att rättigheter saknas (rör för mer information):</string>
<string name="sftp_permission_explanation">För att komma åt filerna från din dator behöver applikationen rättighet att komma åt telefonens lagringsutrymme</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>
@@ -273,6 +298,7 @@
<string name="block_contents">Blockera underrättelsernas innehåll</string>
<string name="block_images">Blockera bilder i underrättelser</string>
<string name="notification_channel_receivenotification">Underrättelser från andra apparater</string>
<string name="take_picture">Ta bild</string>
<string name="plugin_photo_desc">Ta en bild och skicka den till en annan apparat</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,20 +175,48 @@
<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>
<string name="sftp_all_files">Tüm dosyalar</string>
<string name="sftp_sdcard_num">SD kart %d</string>
<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>
@@ -170,9 +244,61 @@
<string name="permission_explanation">Bu eklenti, çalışmak için izne ihtiyaç duyuyor</string>
<string name="optional_permission_explanation">Tüm işlevleri etkinleştirmek için daha fazla yetkiye ihtiyacınız var</string>
<string name="plugins_need_optional_permission">Bazı eklentilerin özellikleri, izin yetersizliğinden kapalı gelmektedir (daha fazla bilgi için dokunun):</string>
<string name="sftp_permission_explanation">Bilgisayarınızdaki dosyalara erişmek için, uygulama telefonunuzun depolama alanına erişim izni olmalıdır</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

@@ -38,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>
@@ -107,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>
@@ -127,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>
@@ -194,13 +207,32 @@
<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">Картка SD %d</string>
<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">%1$s на %2$s</string>
<string name="send_files">Надіслати файли</string>
@@ -228,10 +260,9 @@
<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">Щоб читати і писати 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>
@@ -283,6 +314,7 @@
<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="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>
@@ -174,7 +166,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">SD卡%d</string>
<string name="sftp_sdcard">SD卡</string>
<string name="sftp_readonly">(只读)</string>
@@ -206,10 +197,8 @@
<string name="permission_explanation">这个插件需要权限才能工作</string>
<string name="optional_permission_explanation">您需要授予额外权限以启用全部功能</string>
<string name="plugins_need_optional_permission">因缺少权限,某些插件的一些功能已禁用(点击以查看更多信息):</string>
<string name="sftp_permission_explanation">此应用需要手机存储权限才能从您的 PC 访问手机内的文件</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

@@ -38,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 連線遠端鍵盤功能</string>
<string name="send_ping">傳送Ping回應封包</string>
<string name="open_mpris_controls">多媒體控制</string>
<string name="remotekeyboard_editing_only_title">當編輯時只處理遠端按鍵</string>
@@ -101,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>
@@ -112,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>
@@ -179,13 +183,32 @@
<string name="title_activity_notification_filter">通知過濾器</string>
<string name="filter_apps_info">將會以您選擇的App應用程式啟用同步通知</string>
<string name="sftp_internal_storage">內部儲存空間</string>
<string name="sftp_all_files">全部檔案</string>
<string name="sftp_sdcard_num">SD卡 %d</string>
<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">增加 host/IP</string>
<string name="add_host_hint">主機名稱或 IP</string>
<string name="no_players_connected">沒有發現播放器</string>
<string name="mpris_player_on_device">%1$s on %2$s</string>
<string name="send_files">傳送檔案</string>
@@ -213,10 +236,9 @@
<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_permission_explanation">為了要在桌面上檢視手機通話,您需要提供手機通話記錄及手機狀態的權限。</string>
<string name="telephony_optional_permission_explanation">為了要讓聯絡人名稱取代手機號碼,您需要提供手機通訊錄的權限。</string>
<string name="contacts_permission_explanation">為了要與電腦分享您的通訊錄,您必須提供「聯絡人」的權限</string>
<string name="select_ringtone">選擇一個鈴聲</string>
@@ -268,4 +290,7 @@
<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

@@ -39,6 +39,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" translatable="false">remotekeyboard_editing_only</string>
@@ -134,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>
@@ -148,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>
@@ -225,13 +232,37 @@
<string name="title_activity_notification_filter">Notification filter</string>
<string name="filter_apps_info">Notifications will be synchronized for the selected apps.</string>
<string name="sftp_internal_storage">Internal storage</string>
<string name="sftp_all_files">All files</string>
<string name="sftp_sdcard_num">SD card %d</string>
<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_preference_key_preference_category" translatable="false">key_sftp_preference_category</string>
<string name="sftp_preference_key_add_storage" translatable="false">key_sftp_add_storage</string>
<string name="sftp_preference_key_add_camera_shortcut" translatable="false">key_sftp_add_camera_shotcut</string>
<string name="sftp_preference_key_storage_info" translatable="false">key_sftp_storage_info%d"</string>
<string name="sftp_preference_key_storage_info_list" translatable="false">key_sftp_storage_info_list</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>
@@ -262,10 +293,9 @@
<string name="permission_explanation">This plugin needs permissions to work</string>
<string name="optional_permission_explanation">You need to grant extra permissions to enable all functions</string>
<string name="plugins_need_optional_permission">Some plugins have features disabled because of lack of permission (tap for more info):</string>
<string name="sftp_permission_explanation">To access your files from your PC the app needs permission to access your phone\'s storage</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>
@@ -330,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

@@ -18,6 +18,7 @@
<item name="android:textColorPrimary">@android:color/black</item>
<item name="android:textColor">@android:color/black</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
<item name="actionModeStyle">@style/ActionModeStyle</item>
</style>
<style name="KdeConnectThemeBase.NoActionBar" parent="KdeConnectThemeBase">
@@ -42,4 +43,8 @@
<style name="DisableableButton" parent="ThemeOverlay.AppCompat">
<item name="colorButtonNormal">@drawable/disableable_button</item>
</style>
<style name="ActionModeStyle" parent="Widget.AppCompat.ActionMode">
<item name="background">@color/primaryDark</item>
</style>
</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

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<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"
android:persistent="false">
</PreferenceCategory>
<org.kde.kdeconnect.Plugins.SftpPlugin.StoragePreference
android:key="key_sftp_add_storage"
android:icon="@drawable/ic_add"
android:title="@string/sftp_preference_add_storage_location_title"
android:persistent="false"/>
<androidx.preference.SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/sftp_preference_key_add_camera_shortcut"
android:summaryOff="@string/sftp_preference_add_camera_shortcut_summary_off"
android:summaryOn="@string/sftp_preference_add_camera_shortcut_summary_on"
android:title="@string/sftp_preference_add_camera_shortcut"/>
</PreferenceScreen>

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);
}
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) { }
}
}
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);
}
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,10 +198,8 @@ 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);
@@ -236,8 +225,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
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();
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;
@@ -252,8 +240,7 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
sslsocket.startHandshake();
}
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPacket.getString("deviceName"));
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Handshake failed with " + identityPacket.getString("deviceName"), e);
//String[] ciphers = sslsocket.getSupportedCipherSuites();
//for (String cipher : ciphers) {
@@ -261,11 +248,8 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
//}
}
}).start();
} else {
addLink(identityPacket, socket, connectionStarted);
}
} 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

@@ -419,7 +419,7 @@ public class BackgroundService extends Service {
}).start();
}
public static <T extends Plugin> void runWithPlugin(final Context c, final String deviceId, final Class<T> pluginClass, final PluginCallback<T> cb) {
public static <T extends Plugin> void RunWithPlugin(final Context c, final String deviceId, final Class<T> pluginClass, final PluginCallback<T> cb) {
RunCommand(c, service -> {
Device device = service.getDevice(deviceId);

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);
@@ -437,6 +421,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 +445,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 +493,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 +529,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,12 +537,16 @@ 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()) {
// pluginsByIncomingInterface may not be built yet
if(pluginsByIncomingInterface.isEmpty()) {
reloadPluginsFromSettings();
}
//If capabilities are not supported, iterate all plugins
Collection<String> targetPlugins = pluginsByIncomingInterface.get(np.getType());
if (targetPlugins != null && !targetPlugins.isEmpty()) {
@@ -573,8 +555,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 _) { }
}
}
@@ -599,8 +580,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 {
@@ -626,11 +606,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);
}
};
@@ -656,20 +632,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);
}
if (success) break; //If the link didn't call sendSuccess(), try the next one
}
@@ -729,10 +697,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);
@@ -770,8 +735,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;
@@ -791,7 +755,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);
@@ -805,7 +769,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);
@@ -872,17 +835,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);
// 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);
}
lastModified = getLastModifiedTime(context, uri);
} catch (Exception e) {
e.printStackTrace();
Log.e("SendFileActivity", "Could not obtain file name");
}
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) {
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,6 +85,21 @@ 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
*/
@@ -86,6 +113,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 +140,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<>();
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,
Message.smsColumns,
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,17 +249,19 @@ public class SMSHelper {
messageInfo.put(colName, body);
}
Message message = new Message(messageInfo);
ThreadID threadID = new ThreadID(message.m_threadID);
if (transportType == TransportType.SMS) {
parseSMS(context, messageInfo);
} else if (transportType == TransportType.MMS) {
parseMMS(context, messageInfo);
}
if (!toReturn.containsKey(threadID)) {
toReturn.put(threadID, new ArrayList<>());
}
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;
}
@@ -165,16 +272,18 @@ public class SMSHelper {
* @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 +293,226 @@ 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 = Integer.parseInt(messageInfo.getOrDefault(Message.EVENT, "0"));
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 +529,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 +545,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
@@ -260,14 +594,18 @@ public class SMSHelper {
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 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
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 +618,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

@@ -20,7 +20,13 @@
package org.kde.kdeconnect.Helpers;
import android.annotation.TargetApi;
import android.content.Context;
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;
@@ -32,6 +38,8 @@ import java.util.List;
import java.util.Scanner;
import java.util.StringTokenizer;
import androidx.annotation.NonNull;
//Code from http://stackoverflow.com/questions/9340332/how-can-i-get-the-list-of-mounted-external-storage-of-android-device/19982338#19982338
//modified to work on Lollipop and other devices
public class StorageHelper {
@@ -43,7 +51,7 @@ public class StorageHelper {
public final boolean removable;
public final int number;
StorageInfo(String path, boolean readonly, boolean removable, int number) {
public StorageInfo(String path, boolean readonly, boolean removable, int number) {
this.path = path;
this.readonly = readonly;
this.removable = removable;
@@ -77,15 +85,15 @@ public class StorageHelper {
}
File storage = new File("/storage/");
if (storage.exists() && storage.isDirectory()) {
if (storage.exists() && storage.isDirectory() && storage.canRead()) {
String mounts = null;
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()) {
@@ -100,7 +108,7 @@ public class StorageHelper {
if (!path.startsWith("/storage/emulated") || dirs.length == 1) {
if (!paths.contains(path) && !paths.contains(path2)) {
if (mounts == null || mounts.contains(path) || mounts.contains(path2)) {
list.add(0, new StorageInfo(path, false, true, cur_removable_number++));
list.add(0, new StorageInfo(path, dir.canWrite(), true, cur_removable_number++));
paths.add(path);
}
}
@@ -122,7 +130,7 @@ public class StorageHelper {
}
}
} catch (Exception e) {
e.printStackTrace();
Log.e("StorageHelper", "Exception", e);
}
for (String line : entries) {
@@ -153,4 +161,37 @@ public class StorageHelper {
return list;
}
/* treeUri documentId
* ==================================================================================================
* content://com.android.providers.downloads.documents/tree/downloads => downloads
* content://com.android.externalstorage.documents/tree/1715-1D1F: => 1715-1D1F:
* content://com.android.externalstorage.documents/tree/1715-1D1F:My%20Photos => 1715-1D1F:My Photos
* content://com.android.externalstorage.documents/tree/primary: => primary:
* content://com.android.externalstorage.documents/tree/primary:DCIM => primary:DCIM
* content://com.android.externalstorage.documents/tree/primary:Download/bla => primary:Download/bla
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static String getDisplayName(@NonNull Context context, @NonNull Uri treeUri) {
List<String> pathSegments = treeUri.getPathSegments();
if (!pathSegments.get(0).equals("tree")) {
throw new IllegalArgumentException("treeUri is not valid");
}
String documentId = DocumentsContract.getTreeDocumentId(treeUri);
int colonIdx = pathSegments.get(1).indexOf(':');
if (colonIdx >= 0) {
String tree = pathSegments.get(1).substring(0, colonIdx + 1);
if (!documentId.equals(tree)) {
return documentId.substring(tree.length());
} else {
return documentId.substring(0, colonIdx);
}
}
return documentId;
}
}

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;
}
@@ -222,7 +229,7 @@ public class MousePadActivity extends AppCompatActivity implements GestureDetect
mCurrentX = event.getX();
mCurrentY = event.getY();
BackgroundService.runWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> {
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> {
float deltaX = (mCurrentX - mPrevX) * displayDpiMultiplier * mCurrentSensitivity;
float deltaY = (mCurrentY - mPrevY) * displayDpiMultiplier * mCurrentSensitivity;
@@ -293,7 +300,7 @@ public class MousePadActivity extends AppCompatActivity implements GestureDetect
@Override
public void onLongPress(MotionEvent e) {
getWindow().getDecorView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
BackgroundService.runWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendSingleHold);
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendSingleHold);
}
@Override
@@ -303,13 +310,13 @@ public class MousePadActivity extends AppCompatActivity implements GestureDetect
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
BackgroundService.runWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendSingleClick);
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendSingleClick);
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
BackgroundService.runWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendDoubleClick);
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendDoubleClick);
return true;
}
@@ -348,15 +355,15 @@ public class MousePadActivity extends AppCompatActivity implements GestureDetect
private void sendMiddleClick() {
BackgroundService.runWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendMiddleClick);
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendMiddleClick);
}
private void sendRightClick() {
BackgroundService.runWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendRightClick);
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendRightClick);
}
private void sendScroll(final float y) {
BackgroundService.runWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> plugin.sendScroll(0, y));
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> plugin.sendScroll(0, y));
}
//TODO: Does not work on KitKat with or without requestFocus()

View File

@@ -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 -> {
positionSeekUpdateRunnable = () -> BackgroundService.RunCommand(MprisActivity.this, service -> {
if (targetPlayer != null) {
positionSeek.setProgress((int) (targetPlayer.getPosition()));
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,7 +442,7 @@ public class MprisActivity extends AppCompatActivity {
});
findViewById(R.id.now_playing_textview).setSelected(true);
nowPlayingText.setSelected(true);
}
@Override

View File

@@ -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);
}
/**
@@ -326,7 +370,8 @@ public class MprisMediaSession implements SharedPreferences.OnSharedPreferenceCh
.setSmallIcon(R.drawable.ic_play_white)
.setShowWhen(false)
.setColor(service.getResources().getColor(R.color.primary))
.setVisibility(androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC);
.setVisibility(androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC)
.setSubText(service.getDevice(notificationDevice).getName());
if (!notificationPlayer.getTitle().isEmpty()) {
notification.setContentTitle(notificationPlayer.getTitle());
@@ -353,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);
}
@@ -418,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;
}
@@ -432,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,23 +45,12 @@ class MprisReceiverCallback extends MediaController.Callback {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onPlaybackStateChanged(@NonNull PlaybackState state) {
switch (state.getState()) {
case PlaybackState.STATE_PLAYING:
player.setPlaying(true);
plugin.sendPlaying(player);
break;
case PlaybackState.STATE_PAUSED:
player.setPaused(true);
plugin.sendPlaying(player);
break;
}
public void onPlaybackStateChanged(PlaybackState state) {
plugin.sendMetadata(player);
}
@Override
public void onMetadataChanged(@Nullable MediaMetadata metadata) {
if (metadata == null)
return;
plugin.sendMetadata(player);
}

View File

@@ -34,36 +34,59 @@ class MprisReceiverPlayer {
private final String name;
private boolean isPlaying;
MprisReceiverPlayer(MediaController controller, String name) {
this.controller = controller;
this.name = name;
if (controller.getPlaybackState() != null) {
isPlaying = controller.getPlaybackState().getState() == PlaybackState.STATE_PLAYING;
}
}
boolean isPlaying() {
return isPlaying;
PlaybackState state = controller.getPlaybackState();
if (state == null) return false;
return state.getState() == PlaybackState.STATE_PLAYING;
}
void setPlaying(boolean playing) {
isPlaying = playing;
boolean canPlay() {
PlaybackState state = controller.getPlaybackState();
if (state == null) return false;
if (state.getState() == PlaybackState.STATE_PLAYING) return true;
return (state.getActions() & (PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PLAY_PAUSE)) != 0;
}
boolean isPaused() {
return !isPlaying;
boolean canPause() {
PlaybackState state = controller.getPlaybackState();
if (state == null) return false;
if (state.getState() == PlaybackState.STATE_PAUSED) return true;
return (state.getActions() & (PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY_PAUSE)) != 0;
}
void setPaused(boolean paused) {
isPlaying = !paused;
boolean canGoPrevious() {
PlaybackState state = controller.getPlaybackState();
if (state == null) return false;
return (state.getActions() & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0;
}
boolean canGoNext() {
PlaybackState state = controller.getPlaybackState();
if (state == null) return false;
return (state.getActions() & PlaybackState.ACTION_SKIP_TO_NEXT) != 0;
}
boolean canSeek() {
PlaybackState state = controller.getPlaybackState();
if (state == null) return false;
return (state.getActions() & PlaybackState.ACTION_SEEK_TO) != 0;
}
void playPause() {
if (isPlaying) {
if (isPlaying()) {
controller.getTransportControls().pause();
} else {
controller.getTransportControls().play();
@@ -75,24 +98,31 @@ class MprisReceiverPlayer {
}
String getAlbum() {
if (controller.getMetadata() == null)
return "";
String album = controller.getMetadata().getString(MediaMetadata.METADATA_KEY_ALBUM);
MediaMetadata metadata = controller.getMetadata();
if (metadata == null) return "";
String album = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
return album != null ? album : "";
}
String getArtist() {
if (controller.getMetadata() == null)
return "";
MediaMetadata metadata = controller.getMetadata();
if (metadata == null) return "";
String artist = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
if (artist == null || artist.isEmpty()) artist = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST);
if (artist == null || artist.isEmpty()) artist = metadata.getString(MediaMetadata.METADATA_KEY_AUTHOR);
if (artist == null || artist.isEmpty()) artist = metadata.getString(MediaMetadata.METADATA_KEY_WRITER);
String artist = controller.getMetadata().getString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST);
return artist != null ? artist : "";
}
String getTitle() {
if (controller.getMetadata() == null)
return "";
String title = controller.getMetadata().getString(MediaMetadata.METADATA_KEY_TITLE);
MediaMetadata metadata = controller.getMetadata();
if (metadata == null) return "";
String title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
if (title == null || title.isEmpty()) title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
return title != null ? title : "";
}
@@ -104,6 +134,18 @@ class MprisReceiverPlayer {
controller.getTransportControls().skipToNext();
}
void play() {
controller.getTransportControls().play();
}
void pause() {
controller.getTransportControls().pause();
}
void stop() {
controller.getTransportControls().stop();
}
int getVolume() {
if (controller.getPlaybackInfo() == null)
return 0;
@@ -115,4 +157,15 @@ class MprisReceiverPlayer {
return 0;
return controller.getPlaybackState().getPosition();
}
void setPosition(long position) {
controller.getTransportControls().seekTo(position);
}
long getLength() {
MediaMetadata metadata = controller.getMetadata();
if (metadata == null) return 0;
return metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
}
}

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,16 +45,16 @@ import java.util.List;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
//@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() {
@@ -67,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();
@@ -78,6 +81,16 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
MediaSessionManager manager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
if (manager != null && mediaSessionChangeListener != null) {
manager.removeOnActiveSessionsChangedListener(mediaSessionChangeListener);
mediaSessionChangeListener = null;
}
}
private void createPlayers(List<MediaController> sessions) {
for (MediaController controller : sessions) {
createPlayer(controller);
@@ -116,10 +129,21 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
return true;
}
if (np.has("SetPosition")) {
long position = np.getLong("SetPosition", 0);
player.setPosition(position);
}
if (np.has("action")) {
String action = np.getString("action");
switch (action) {
case "Play":
player.play();
break;
case "Pause":
player.pause();
break;
case "PlayPause":
player.playPause();
break;
@@ -128,6 +152,10 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
break;
case "Previous":
player.previous();
break;
case "Stop":
player.stop();
break;
}
}
@@ -144,6 +172,7 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
return new String[]{PACKET_TYPE_MPRIS};
}
private final class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener {
@Override
public void onActiveSessionsChanged(@Nullable List<MediaController> controllers) {
@@ -157,8 +186,12 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
sendPlayerList();
}
}
private void createPlayer(MediaController controller) {
// Skip the media session we created ourselves as KDE Connect
if (controller.getPackageName().equals(context.getPackageName())) return;
MprisReceiverPlayer player = new MprisReceiverPlayer(controller, AppsHelper.appNameLookup(context, controller.getPackageName()));
controller.registerCallback(new MprisReceiverCallback(this, player), new Handler(Looper.getMainLooper()));
players.put(player.getName(), player);
@@ -170,20 +203,12 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
device.sendPacket(np);
}
void sendPlaying(MprisReceiverPlayer player) {
NetworkPacket np = new NetworkPacket(MprisReceiverPlugin.PACKET_TYPE_MPRIS);
np.set("player", player.getName());
np.set("isPlaying", player.isPlaying());
device.sendPacket(np);
}
@Override
public int getMinSdk() {
return Build.VERSION_CODES.LOLLIPOP_MR1;
}
public void sendMetadata(MprisReceiverPlayer player) {
void sendMetadata(MprisReceiverPlayer player) {
NetworkPacket np = new NetworkPacket(MprisReceiverPlugin.PACKET_TYPE_MPRIS);
np.set("player", player.getName());
if (player.getArtist().isEmpty()) {
@@ -196,12 +221,12 @@ public class MprisReceiverPlugin extends Plugin implements MediaSessionManager.O
np.set("album", player.getAlbum());
np.set("isPlaying", player.isPlaying());
np.set("pos", player.getPosition());
device.sendPacket(np);
}
public void sendVolume(MprisReceiverPlayer player) {
NetworkPacket np = new NetworkPacket(MprisReceiverPlugin.PACKET_TYPE_MPRIS);
np.set("player", player.getName());
np.set("length", player.getLength());
np.set("canPlay", player.canPlay());
np.set("canPause", player.canPause());
np.set("canGoPrevious", player.canGoPrevious());
np.set("canGoNext", player.canGoNext());
np.set("canSeek", player.canSeek());
np.set("volume", player.getVolume());
device.sendPacket(np);
}

View File

@@ -35,11 +35,18 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.text.SpannableString;
import android.util.Log;
import android.util.Pair;
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 +62,11 @@ 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.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 +74,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 +127,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
pendingIntents = new HashMap<>();
currentNotifications = new HashSet<>();
actions = new HashMap<>();
appDatabase = new AppDatabase(context, true);
@@ -149,10 +160,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 +194,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 +224,166 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
NetworkPacket np = new NetworkPacket(PACKET_TYPE_NOTIFICATION);
boolean isUpdate = currentNotifications.contains(key);
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 (!isUpdate) {
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);
}
}
currentNotifications.add(key);
Bitmap appIcon = extractIcon(statusBarNotification, notification);
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));
attachIcon(np, appIcon);
}
} catch (Exception e) {
e.printStackTrace();
Log.e("NotificationsPlugin", "Error retrieving icon");
}
} else {
currentNotifications.add(key);
}
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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (conversation.first != null) {
np.set("title", conversation.first);
} else {
np.set("title", notification.extras.getString(Notification.EXTRA_TITLE));
}
np.set("text", extractText(notification, conversation));
}
}
device.sendPacket(np);
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private String extractText(Notification notification, Pair<String, String> conversation) {
if (conversation.second != null) {
return conversation.second;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && notification.extras.containsKey(Notification.EXTRA_BIG_TEXT)) {
return extractStringFromExtra(notification.extras, Notification.EXTRA_BIG_TEXT);
}
return notification.extras.getString(Notification.EXTRA_TEXT);
}
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 = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (notification.extras.containsKey(Notification.EXTRA_IS_GROUP_CONVERSATION)) {
isGroupConversation = notification.extras.getBoolean(Notification.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(m.getString("text"));
messagesBuilder.append("\n");
}
return new Pair<>(title, messagesBuilder.toString());
}
private Bitmap drawableToBitmap(Drawable drawable) {
if (drawable == null) return null;
@@ -298,13 +412,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 +428,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 +437,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) {
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;
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();
}
}
}
return repliableNotification;
}
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;
return null;
}
private static String extractStringFromExtra(Bundle extras, String key) {
Object extra = extras.get(key);
if (extra == null) {
@@ -421,7 +478,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,16 +489,13 @@ 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);
String extraTitle = extractStringFromExtra(extras, Notification.EXTRA_TITLE);
String extraText = extractStringFromExtra(extras, Notification.EXTRA_TEXT);
if (extraTitle != null && extraText != null && !extraText.isEmpty()) {
ticker = extraTitle + ": " + extraText;
@@ -451,30 +505,50 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
ticker = extraText;
}
} catch (Exception e) {
Log.w("NotificationPlugin", "problem parsing notification extras for " + notification.tickerText);
e.printStackTrace();
Log.e(TAG, "problem parsing notification extras for " + notification.tickerText, e);
}
}
if (ticker.isEmpty()) {
ticker = (notification.tickerText != null) ? notification.tickerText.toString() : "";
}
}
return ticker;
}
private void sendCurrentNotifications(NotificationReceiver service) {
StatusBarNotification[] notifications = service.getActiveNotifications();
if (notifications != null) { //Can happen only on API 23 and lower
for (StatusBarNotification notification : notifications) {
sendNotification(notification);
}
}
}
@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);
@@ -510,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
@@ -525,7 +599,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
} 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(':');
@@ -568,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;
}

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