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

Compare commits

...

215 Commits
v1.0 ... v1.5

Author SHA1 Message Date
Albert Vaca
b5e6519efe Increased versio number to release 2016-12-17 15:33:09 +01:00
l10n daemon script
ad2b7678b9 GIT_SILENT made messages (after extraction) 2016-12-16 03:35:24 +01:00
l10n daemon script
9649efcdc4 GIT_SILENT made messages (after extraction) 2016-12-14 02:48:48 +01:00
l10n daemon script
99b7c4f312 GIT_SILENT made messages (after extraction) 2016-12-13 02:39:05 +01:00
l10n daemon script
c80820294c GIT_SILENT made messages (after extraction) 2016-12-12 02:34:13 +01:00
Albert Vaca
90856690fb Increased version number to release in the beta channel 2016-12-11 21:22:13 +01:00
Albert Vaca
1334dae342 Added a Share setting to add received files to the "Downloads" app
Only works for old-school (not-Storage Access Framework) paths, so we have
to keep track of the fact that we are using one or the other.

Also this requires the permission DOWNLOAD_WITHOUT_NOTIFICATION, but
hopefully the play store won't make users confirm this one.

This behaviour is enabled by default.
2016-12-11 21:03:39 +01:00
Albert Vaca
f8dd9bf923 Fixed crash if received file didn't have extension 2016-12-11 20:27:52 +01:00
Albert Vaca
72d8ba647a If transfer failed, don't create a pending intent nor index the file 2016-12-11 19:19:51 +01:00
Albert Vaca
b3449c87f7 Rewritten ShareSettingsActivity and de-duplicated SAF code in SharePlugin.
There was unused code and incorrect comments that were introduced in
commit e2e4086.
2016-12-11 18:57:21 +01:00
Albert Vaca
5c3b5a2737 Fixed comment as MayeulC suggested on IRC 2016-12-11 16:40:11 +01:00
Richard Wagler
e2e40863f8 Implement saving to SD card using SAF 2016-12-11 14:01:46 +01:00
Albert Vaca
036737deae Unused imports 2016-12-11 14:00:55 +01:00
Albert Vaca
8d7337b6b0 Refactored media scanner call into MediaStoreHelper 2016-12-11 13:59:52 +01:00
Albert Vaca
0a4459e8a0 Made Sftp server notify the Android MediaScanner of new/removed files
Plus refactored the code a bit to simplify logic
2016-12-11 13:59:00 +01:00
Albert Vaca
cef8cbd34a Android Studio update 2016-12-11 13:59:00 +01:00
l10n daemon script
3afb8ae8f3 GIT_SILENT made messages (after extraction) 2016-12-11 02:35:14 +01:00
l10n daemon script
85fce32bcb GIT_SILENT made messages (after extraction) 2016-12-10 02:32:33 +01:00
Albert Vaca
d52be109ee Revert "Add support to deal with album arts sent by mpris plugin."
This was very poorly implemented and can't stay as it is right now:
 - Every second or so the art image was being loaded from disk, scaled,
   base64 encoded and sent over the freakin network!
 - The Android interface didn't take into account small screens, and
   adding the image would cut stuff out of the screen.
 - Didn't manage "edge cases" like playing a song without cover after one
   with cover (previous image was still being shown) or changing players.

This reverts commit 24c404400f.

# Conflicts:
#	src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java
#	src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.java
2016-12-08 23:47:17 +01:00
Albert Vaca
66794002f7 Updated version number to release in the beta channel/ 2016-12-08 20:53:40 +01:00
Albert Vaca
d61536ef76 Improved sharing files.
- Refactored the code to separate reading the file, sending it and
  updating the notification. This last part it's now done in a standalone
  sendPackage callback class.
- Added a non-async sendPacakge function to device, so we can properly
  wait for a file to finish before sending the next one without using
  recursive callbacks as before.
- Fixed bug that made it impossible to share more than one file from
  certain apps. This was caused because we can't access content resolvers
  any longer after closing the activity that was launched to "share" to.
  Now we do the content resolutions first and once for all files.
- When sending more than one file, do not spam one notification for each.
- There is no need to update the notifications from the UI thread, now
  we do it from a background thread instead.
2016-12-08 20:50:49 +01:00
Albert Vaca
1dad3c145d Make onProgressChanged updates time-based.
If used to update a notification, calling it too often would lag the UI
2016-12-08 20:50:49 +01:00
Albert Vaca
33f7a4809d Don't call onProgressChanged unless it actually changes 2016-12-08 20:50:49 +01:00
Albert Vaca
d81448ad34 Fixed LanLink always calling onProgressChanged with a progress of 0 2016-12-08 20:50:49 +01:00
Albert Vaca
ed4c0d24df Introduced NotificationHelper 2016-12-08 20:50:49 +01:00
Albert Vaca
fed9bb1eec Not an error 2016-12-08 20:50:49 +01:00
Albert Vaca
b7418c1db6 Socket should be volatile to prevent data-race
CCBUG: 368438
2016-12-08 20:50:49 +01:00
Albert Vaca
e6bc78f013 Some refactoring on sendpackage to reduce allocations of empty callbacks 2016-12-08 20:50:49 +01:00
Albert Vaca
22de200cbf Typo 2016-12-08 20:50:49 +01:00
Albert Vaca
22dde07892 Typo 2016-12-08 20:50:49 +01:00
Albert Vaca
f9bdfb9fbd Not actually an error 2016-12-08 20:50:49 +01:00
Albert Vaca
9d6476a27e Moved the code around to split it in big blocks and add some comments 2016-12-08 20:50:49 +01:00
Albert Vaca
29a8db9c45 On error show an error message, not a success message :) 2016-12-08 20:50:49 +01:00
l10n daemon script
6e1f8e1496 SVN_SILENT made messages (after extraction) 2016-11-29 03:03:55 +01:00
Albert Vaca
82b15292fb Merge branch '1.x' 2016-11-28 14:48:39 +01:00
Albert Vaca
32f9f3b76a Update gradle plugin to make Android Studio happy 2016-11-28 14:48:22 +01:00
Albert Vaca
fc25e42196 Remove FindMyPhoneActivity from the application switcher,
because returning to it would make it to ring again.
2016-11-23 18:34:31 +01:00
l10n daemon script
db3422268f SVN_SILENT made messages (after extraction) 2016-11-05 07:16:39 +00:00
l10n daemon script
71bef30624 SVN_SILENT made messages (after extraction) 2016-10-28 10:31:23 +00:00
l10n daemon script
15e0188538 SVN_SILENT made messages (after extraction) 2016-10-28 06:56:05 +00:00
l10n daemon script
8bc8a47e9d SVN_SILENT made messages (after extraction) 2016-10-17 10:38:49 +00:00
l10n daemon script
ad7769edde SVN_SILENT made messages (after extraction) 2016-10-17 06:58:14 +00:00
l10n daemon script
a2c51a16ee SVN_SILENT made messages (after extraction) 2016-10-14 10:45:01 +00:00
l10n daemon script
788555b69c SVN_SILENT made messages (after extraction) 2016-10-14 07:05:25 +00:00
l10n daemon script
ccdeb3d806 SVN_SILENT made messages (after extraction) 2016-10-07 07:02:29 +00:00
l10n daemon script
00a5ad3c1f SVN_SILENT made messages (after extraction) 2016-09-30 10:42:37 +00:00
Albert Vaca
b35ce0f821 Do not clear values if the player didn't actually change 2016-09-29 14:15:31 +02:00
Albert Vaca
77e38da2d9 Fixed spotify hacks and moved them to a single function in the code 2016-09-29 14:14:55 +02:00
Albert Vaca
8fb942ccdf Nothing changes in this function, no need to update the list. 2016-09-29 13:21:31 +02:00
Albert Vaca
74dc1bcd63 Reduced logging. Renamed variables in camelcase. 2016-09-29 13:21:13 +02:00
Albert Vaca
0f655cf4eb Added a check to see if the dialog is already open.
If tapping fast, two dialogs could be opened. Opening it twice caused the
second dialog to segfault on tapping yes.
2016-09-29 13:20:43 +02:00
Albert Vaca
0de86785f8 Merge branch 'master' into 1.x
# Conflicts:
#	res/values-da/strings.xml
2016-09-29 13:01:25 +02:00
Thomas Posch
5320c3c226 Make commands sorted by name
BUG: 369239
2016-09-29 12:57:22 +02:00
l10n daemon script
7234b34db7 SVN_SILENT made messages (after extraction) 2016-09-28 10:26:51 +00:00
l10n daemon script
428fa947a4 SVN_SILENT made messages (after extraction) 2016-09-25 10:36:05 +00:00
Albert Vaca
b454dec0a0 Merge branch '1.x' 2016-09-24 20:34:03 +02:00
Albert Vaca
2e3fabb3e5 Bumped version to release 2016-09-24 20:28:05 +02:00
Albert Vaca
c7b754a36e Swapped branches in if statement caused settings to not be stored in db 2016-09-24 20:26:23 +02:00
l10n daemon script
347c34927d SVN_SILENT made messages (after extraction) 2016-09-23 10:40:27 +00:00
Albert Vaca
6c3811a0af Merge branch '1.x' 2016-09-20 12:47:50 +02:00
Albert Vaca
e9013e7995 Android Studio 2.2 complains if we use an old version of the gradle plugin 2016-09-20 12:46:59 +02:00
Albert Vaca
3b5a7deebe Rewritten the Notification Filter.
- Now we display every app in the phone.
- We show the app icon next to the name.
- Some apps can now be disabled by default.

Disabled notifications for the default Android messaging app, as SMS
notifications are already handled by the telephony plugin.

BUG: 367930
2016-09-20 12:46:28 +02:00
Albert Vaca
ac04f5a610 Oops 2016-09-20 11:08:59 +02:00
Albert Vaca
efce35366b Bumped version to release 2016-09-20 10:54:44 +02:00
Albert Vaca
1a32f8142d Do not unpair on every handshake error.
Sometimes we get stupid errors like "connection closed by peer".

BUG: 368812
2016-09-20 10:53:10 +02:00
Albert Vaca
103f24c140 Reduce logging 2016-09-20 10:51:37 +02:00
Albert Vaca
66cdb77bbd Fixed margins of notification filter activity 2016-09-20 10:48:06 +02:00
Albert Vaca
86245a697e Print a warning if device name is not in the list of human readable names. 2016-09-19 16:20:35 +02:00
Albert Vaca
041f172195 Merge remote-tracking branch 'origin/1.x' 2016-09-19 16:19:21 +02:00
Albert Vaca
91eefcc65a Fixed margins in the settings activity 2016-09-19 16:16:30 +02:00
Albert Vaca
b25ec2d133 Added cipher list for Android versions between 11 and 21.
BUG: 368172
2016-09-19 16:16:30 +02:00
l10n daemon script
e2e78cb562 SVN_SILENT made messages (after extraction) 2016-09-19 10:16:29 +00:00
l10n daemon script
8b0b65178b SVN_SILENT made messages (after extraction) 2016-09-18 10:38:50 +00:00
l10n daemon script
6c1dcda744 SVN_SILENT made messages (after extraction) 2016-09-18 07:04:53 +00:00
l10n daemon script
34d8a771d7 SVN_SILENT made messages (after extraction) 2016-09-14 10:29:43 +00:00
l10n daemon script
91913664be SVN_SILENT made messages (after extraction) 2016-09-14 06:53:53 +00:00
Albert Vaca
9e680d1494 Merge branch '1.x' 2016-09-13 16:58:41 +02:00
Albert Vaca
7f3afd194b Trying to debug a "no shared ciphers" issue some users are experiencing. 2016-09-13 16:56:23 +02:00
Albert Vaca
4158a14437 Merge branch '1.x' 2016-09-12 17:15:06 +02:00
Albert Vaca
ef79b04d1a Bumped version to release 2016-09-11 21:47:49 +02:00
Albert Vaca
62bf2ccab7 Fixed clipboard propagation loop bug
Now ClipboardListener is a singleton

BUG: 362596
2016-09-11 21:40:07 +02:00
l10n daemon script
28a33172ce SVN_SILENT made messages (after extraction) 2016-09-08 07:23:50 +00:00
Albert Vaca
901440f78a Don't return an empty list of supported plugins
If we don't know it yet return them all.
2016-09-06 10:53:24 +02:00
Albert Vaca
e9a64dc99a Don't show the Encryption Info menu if we are not connected with the device
We don't know the actual encryption used unless we have a device link.
2016-09-06 10:53:24 +02:00
l10n daemon script
086194c629 SVN_SILENT made messages (after extraction) 2016-09-01 07:22:43 +00:00
l10n daemon script
23f7eb90ba SVN_SILENT made messages (after extraction) 2016-08-31 10:22:15 +00:00
Albert Vaca
75eca915dd Merge branch 'master' of git.kde.org:kdeconnect-android 2016-08-29 11:54:08 +02:00
Albert Vaca
cfcf9d97e7 Merge branch '1.x' 2016-08-29 11:53:49 +02:00
Albert Vaca
a8838b57bb What XML file? 2016-08-29 11:49:25 +02:00
Albert Vaca
2989fe5ab3 Bump version to release 2016-08-29 11:49:04 +02:00
Albert Vaca
9c40374b00 Unpair only when there is no certificate
Unpairing on every error was a bit too aggressive.

BUG: 367937
2016-08-29 11:48:30 +02:00
l10n daemon script
4acf931f5a SVN_SILENT made messages (after extraction) 2016-08-27 07:23:55 +00:00
Albert Vaca
1b369d0bf8 Bumped version to release 2016-08-26 19:13:04 +02:00
Albert Vaca
f093326f93 Re-enabled SMS plugin 2016-08-26 19:13:04 +02:00
Saikrishna Arcot
e95265c263 MPRIS: Read and apply the allowed actions for the player.
Read list of allowed actions for the player sent from KDE, and hide
buttons that perform actions that aren't allowed based on MPRIS.

REVIEW: 128273
2016-08-26 11:12:58 +02:00
Albert Vaca
fa689bace6 Merge branch '1.x' 2016-08-26 11:06:43 +02:00
Albert Vaca
b43e0628c2 FindMyPhone: Calling ring twice now stop the ringing
BUG: 366253
2016-08-26 11:04:56 +02:00
Albert Vaca
fdd6de5cee Gradle 2.1.3 fixes a privilege escalation vulnerability in 2.1.2 2016-08-26 11:02:54 +02:00
Albert Vaca
816d47026e Bumped version to release 2016-08-25 18:08:16 +02:00
Albert Vaca
14e97545e4 Unpair on error 2016-08-25 18:06:56 +02:00
l10n daemon script
1d5efe36f0 SVN_SILENT made messages (after extraction) 2016-08-19 10:33:47 +00:00
l10n daemon script
625bdd36e3 SVN_SILENT made messages (after extraction) 2016-08-19 07:02:54 +00:00
l10n daemon script
9478f5bb39 SVN_SILENT made messages (after extraction) 2016-08-15 10:10:03 +00:00
l10n daemon script
1f0e7f9705 SVN_SILENT made messages (after extraction) 2016-08-13 10:45:06 +00:00
l10n daemon script
fe4f099663 SVN_SILENT made messages (after extraction) 2016-08-11 10:52:48 +00:00
l10n daemon script
7272c0abf1 SVN_SILENT made messages (after extraction) 2016-08-10 10:28:10 +00:00
l10n daemon script
cbb1f3d806 SVN_SILENT made messages (after extraction) 2016-08-08 10:28:30 +00:00
l10n daemon script
717bb081a7 SVN_SILENT made messages (after extraction) 2016-08-08 06:58:25 +00:00
l10n daemon script
37669e2be0 SVN_SILENT made messages (after extraction) 2016-08-06 11:10:46 +00:00
l10n daemon script
9d612192e3 SVN_SILENT made messages (after extraction) 2016-08-06 07:27:45 +00:00
l10n daemon script
25527af4ad SVN_SILENT made messages (after extraction) 2016-08-05 10:08:52 +00:00
l10n daemon script
6c7cb63af9 SVN_SILENT made messages (after extraction) 2016-08-04 11:04:58 +00:00
l10n daemon script
4b171e624f SVN_SILENT made messages (after extraction) 2016-07-17 07:34:28 +00:00
Albert Vaca
e49ab67e26 Oops, still one place was missing. 2016-07-14 14:43:39 +02:00
Albert Vaca
edc3075763 Reintroduced hack as we broke compatibility with devices using version 0.9
And bumped version to release
2016-07-14 07:13:50 +02:00
Albert Vaca
fd2102b432 Bump version to release 2016-07-13 18:20:37 +02:00
Albert Vaca
adee9014fd Fixed sending files, we were closing the stream before it was sent 2016-07-13 18:20:15 +02:00
Albert Vaca
2c0d9af64d Unpair also if handshake fails but the callback is not called 2016-07-13 18:12:12 +02:00
Albert Vaca
53117c9a4b Bumped version to release beta 2016-07-13 18:11:48 +02:00
Albert Vaca
47ba298128 Disabled removing links when exiting discovery mode because it was broken.
If both devices are in "discovery mode" (ie: both want to keep links
established) and the last one to create the link goes out of discovery
mode, it will close the link when it shouldn't (because the other end still
was "using" it to display it to the user).
2016-07-12 12:39:51 +02:00
Albert Vaca
c5456fd84e Fixed issues detected in static analysis by Coverity 2016-07-07 17:45:04 +02:00
Albert Vaca
d29868ac04 Fixed npe when receiving payloads
Converting a socket to SSL has to be done after connecting to an address
2016-07-06 19:03:52 +02:00
Albert Vaca
4b7c5fb85a Fixed loopback link 2016-07-06 18:43:41 +02:00
Albert Vaca
de81a20f82 Made capabilities static like in /r/128386/ 2016-07-06 17:45:12 +02:00
l10n daemon script
ed9b804693 SVN_SILENT made messages (after extraction) 2016-07-06 07:16:41 +00:00
l10n daemon script
d9cc1c0ba4 SVN_SILENT made messages (after extraction) 2016-07-04 08:10:10 +00:00
l10n daemon script
92b8238977 SVN_SILENT made messages (after extraction) 2016-07-02 07:39:38 +00:00
l10n daemon script
8567abdfa6 SVN_SILENT made messages (after extraction) 2016-07-01 07:41:23 +00:00
l10n daemon script
d490d53be7 SVN_SILENT made messages (after extraction) 2016-06-30 07:37:33 +00:00
Saikrishna Arcot
7981d952ec Allow any type of list to be passed into NetworkPackage
And change return types to use List interface as well.

REVIEW: 128287
2016-06-28 21:53:26 +02:00
Albert Vaca
4e86eb0dd3 Changed some plugins descriptions to make them more discoverable.
Sorry translators!
2016-06-28 20:00:41 +02:00
l10n daemon script
6f8947377a SVN_SILENT made messages (after extraction) 2016-06-28 08:24:30 +00:00
Albert Vaca
106e651c6c Bumped version number to release 2016-06-26 17:26:57 +02:00
Albert Vaca
5669e3c22c Split notifications plugin in two: send and receive. 2016-06-26 17:08:47 +02:00
Albert Vaca
2d1dbd124c Fixed NPE if playerList was received null (which was due to a bug).
Maybe np.has(key) should return false if the key is set but null?
2016-06-26 17:07:57 +02:00
Albert Vaca
1edcbd2b39 Fixed incorrect packet type 2016-06-26 17:05:17 +02:00
Albert Vaca
c76a32ff05 Unused function 2016-06-26 17:04:54 +02:00
l10n daemon script
c078e45a98 SVN_SILENT made messages (after extraction) 2016-06-22 07:47:39 +00:00
Albert Vaca
163e3c31f4 Bumped version number to release 2016-06-21 17:29:26 +02:00
Albert Vaca
5a5e236710 Update build tools 2016-06-21 17:29:10 +02:00
Albert Vaca
603c87d42d Fixed tests after last refactor of lanlink 2016-06-21 16:45:18 +02:00
Albert Vaca
1a04bfbbea Fixed simple issues detected by lint 2016-06-21 16:44:21 +02:00
Albert Vaca
fec0b34330 Socket can't be null 2016-06-21 13:39:55 +02:00
Albert Vaca
f6c4084746 error -> warning for some logs 2016-06-21 13:38:21 +02:00
Albert Vaca
968d018f41 We were never reusing the existing link 2016-06-21 13:37:49 +02:00
Albert Vaca
4fc6ca8d4f Further simplified lanbackend 2016-06-20 14:26:49 +02:00
Albert Vaca
d3ab18b721 Increased version number to release as beta 2016-06-17 10:03:54 +02:00
Albert Vaca
b2d0e57641 Missing start() call on a thread! 2016-06-17 10:02:22 +02:00
Albert Vaca
66238406d6 Print if trusted when creating an ssl socket 2016-06-17 09:59:46 +02:00
Albert Vaca
010c960680 Check for trusted only once 2016-06-17 09:59:31 +02:00
Albert Vaca
49d4383828 Renamed running -> listening 2016-06-17 09:56:53 +02:00
Albert Vaca
226869e200 Fixed shared urls and text having the wrong package type 2016-06-17 09:43:23 +02:00
Albert Vaca
bbc1113710 Bumped version to release in beta channel 2016-06-17 02:27:18 +02:00
Albert Vaca
b3bacf241c Support both the new and old UDP port 2016-06-17 02:25:52 +02:00
Albert Vaca
ff47313409 Be less verbose 2016-06-17 02:25:10 +02:00
Albert Vaca
ac4f072322 Code cleanup 2016-06-17 02:01:20 +02:00
Albert Vaca
5ed1b80716 Fast compilation 2016-06-16 23:48:16 +02:00
Albert Vaca
8a413bb42e Working :D 2016-06-16 23:48:16 +02:00
Albert Vaca
71dc713578 Getting rid of netty WIP 2016-06-16 23:48:16 +02:00
Albert Vaca
ca3d677db6 Fixed warning 2016-06-16 23:46:24 +02:00
Albert Vaca
79ed37345b Simplified logic to find open ports. 2016-06-16 23:10:08 +02:00
Pinak Ahuja
24c404400f Add support to deal with album arts sent by mpris plugin.
We should probably look into resizing the album art according
 to the DPI of the device.
2016-06-16 10:37:04 +02:00
l10n daemon script
082de423c0 SVN_SILENT made messages (after extraction) 2016-06-16 07:48:15 +00:00
Albert Vaca
b5fb7f73ee Updated netty 2016-06-16 00:53:36 +02:00
Albert Vaca
097d1f5fa5 Removed log 2016-06-16 00:40:37 +02:00
Albert Vaca
5fa43f8979 Removed SO_BACKLOG because of a log message about it being unsupported 2016-06-16 00:40:37 +02:00
Albert Vaca
e5c7adba3a Ignore udp packets on the old port for devices that know about the new port 2016-06-16 00:40:20 +02:00
Kai Uwe Broulik
ced5c71369 [MousePadPlugin] Support mouse wheel events
This allows to scroll using a touch-enabled physical keyboard or a real mouse

BUG: 360006

Reviewed-By: Albert Vaca
2016-06-14 18:42:21 +02:00
Albert Vaca
9bf2adefc4 Commented annoying log message 2016-06-14 17:39:41 +02:00
Albert Vaca
24685348cf Moved hardcoded protocol version numbers to constants 2016-06-14 17:39:20 +02:00
Albert Vaca
7556e1d7fa Allow kdeconnect to broadcast both on UDP port 1714 and 1716 2016-06-14 11:13:20 +02:00
Albert Vaca
c9b852f88c Fix leak on Android < IceCream when workerGroup hasn't been initialized 2016-06-14 11:13:20 +02:00
Albert Vaca
d8169f3787 Reuse channel initializers in LanLinkProvider 2016-06-14 11:13:20 +02:00
l10n daemon script
b71e8562bc SVN_SILENT made messages (after extraction) 2016-06-13 08:19:24 +00:00
Albert Vaca
4d19a7cdc8 Fixed tests 2016-06-12 21:07:01 +02:00
Albert Vaca
ae7a80e262 Removed duplicate code when receiving identity packages via tcp and udp 2016-06-12 21:00:19 +02:00
l10n daemon script
4a269a607c SVN_SILENT made messages (after extraction) 2016-06-12 07:23:58 +00:00
Albert Vaca
a6c23b252c Increased version number to release 2016-06-10 15:47:51 +02:00
Albert Vaca
fa5f584fe4 shrinkResources was deleting our plugin preferences XML! 2016-06-10 15:47:18 +02:00
Albert Vaca
634f71d724 Increased version to release 2016-06-10 11:52:41 +02:00
Albert Vaca
055e0f6454 Added a comment explaining the proguard line we added yesterday. 2016-06-10 11:50:57 +02:00
Albert Vaca
8df2b7b0ee Fix the status bar covering the action bar in Android 4.4
Apparently the status bar handles translucency differently on 4.4 and 5+
2016-06-10 11:50:57 +02:00
l10n daemon script
5f64e94878 SVN_SILENT made messages (after extraction) 2016-06-10 07:25:16 +00:00
Albert Vaca
abbbc9414d Make proguard keep mina, as SSHD uses it.
Mina uses reflection (eg in SimpleIoProcessorPool.java), so proguard was
breaking it.
2016-06-09 16:00:04 +02:00
Albert Vaca
cfa13cb16b Updated to latest stable netty 2016-06-09 15:59:05 +02:00
Albert Vaca
d726fe23bf Increased version to release 2016-06-09 15:00:55 +02:00
Albert Vaca
d920b2659d Trying to fix people not being able to mount sftp 2016-06-09 15:00:42 +02:00
Albert Vaca
0836453c35 Project-wise optimize imports 2016-06-09 13:42:54 +02:00
Albert Vaca
09e1811bd8 Deprecated sendPackageEncrypted
It's only called for compatibility with old devices
2016-06-09 13:42:54 +02:00
Albert Vaca
1ab07d201e Enabled keepalive, increased socket queue size and removed logging handler 2016-06-09 13:42:54 +02:00
Albert Vaca
1c2dfdb795 Variable name 2016-06-09 13:42:54 +02:00
l10n daemon script
dd3df9d19f SVN_SILENT made messages (after extraction) 2016-06-08 07:13:23 +00:00
l10n daemon script
5bfbd90414 SVN_SILENT made messages (after extraction) 2016-06-07 08:29:00 +00:00
Albert Vaca
5eb17f7a9a Do not complain if there is no public key. 2016-06-07 00:35:07 +02:00
Albert Vaca
44be314899 Fixed bug that made file transfers not work.
The socket was not initialized for SSL when it should.
2016-06-07 00:28:37 +02:00
Albert Vaca
728fa0b508 Made SftpImpl contain a single class and renamed the file to match its name
This looks more like the java way.
2016-06-06 23:59:25 +02:00
Albert Vaca
e9e93423f1 Don't complain about our own packet types 2016-06-06 23:56:01 +02:00
Albert Vaca
a1dd4fe2cc Remove some hardcoded left-align 2016-06-06 18:35:47 +02:00
Albert Vaca
2294f30505 We actually work on RTL 2016-06-06 18:06:11 +02:00
Albert Vaca
8c86edcf33 Don't log an empty list 2016-06-06 18:05:48 +02:00
Albert Vaca
113739e1d9 Fixed rare crash 2016-06-06 00:01:59 +02:00
l10n daemon script
48cd5aa29b SVN_SILENT made messages (after extraction) 2016-06-05 07:23:59 +00:00
Albert Vaca
5487c5556b Specify useProgard and shrinkResources for Gradle plugin 2.1
minifyEnabled alone in 2.0+ refers to the new experimental code shrinker
shrinkResources removes unused resources
2016-06-04 15:26:13 +02:00
Albert Vaca
e34de854bf New release. 2016-06-04 13:46:02 +02:00
Albert Vaca
d8aab59d4b Trying to fix a bug some people is experiencing with the sensitivity option 2016-06-04 13:45:33 +02:00
Albert Vaca
835a4f0bc7 This should make the sms plugin work with old kdeconnect-kde git master.
For people using Ubuntu, which packages some random git version.
2016-06-04 13:43:34 +02:00
Albert Vaca
a588c8f787 Revert "SVN_SILENT made messages (after extraction)"
This reverts commit 2497ec2b1c.
2016-06-04 13:40:47 +02:00
Albert Vaca
36d1145df7 Increased support library version 2016-06-04 13:30:04 +02:00
Albert Vaca
5d3ccbe0b0 Incresed build tools version 2016-06-04 13:29:52 +02:00
l10n daemon script
2497ec2b1c SVN_SILENT made messages (after extraction) 2016-06-04 07:28:40 +00:00
Albert Vaca
bb8f015e87 Increased version number 2016-06-04 02:21:45 +02:00
Albert Vaca
f3e5bf917a Removed unneeded whitespaces. 2016-06-04 02:21:28 +02:00
Frederik Schwarzer
45ba30e0ba There should be no space before the colon. 2016-06-03 21:43:53 +02:00
Aleix Pol
f967c2888d Make sure we're not left with the sensitivity uninitialized
Fallback to the same value as default
2016-06-03 15:34:04 +02:00
Albert Vaca
6f0b581c02 Fixed crash 2016-06-03 14:51:31 +02:00
104 changed files with 3196 additions and 2044 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.kde.kdeconnect_tp"
android:versionCode="1000"
android:versionName="1.0">
android:versionCode="1500"
android:versionName="1.5">
<uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="22" />
@@ -23,14 +23,16 @@
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" android:required="false" />
<uses-permission android:name="android.permission.RECEIVE_SMS" android:required="false" />
<!-- <uses-permission android:name="android.permission.SEND_SMS" android:required="false" /> -->
<uses-permission android:name="android.permission.SEND_SMS" android:required="false" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:supportsRtl="true"
android:label="KDE Connect"
android:theme="@style/KdeConnectTheme"
>
@@ -82,6 +84,16 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.SettingsActivity" />
</activity>
<activity
android:name="org.kde.kdeconnect.Plugins.SharePlugin.ShareSettingsActivity"
android:label="@string/device_menu_plugins"
android:parentActivityName="org.kde.kdeconnect.UserInterface.SettingsActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.Plugins.SharePlugin.ShareSettingsActivity" />
</activity>
<receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver" >
<intent-filter>
@@ -117,6 +129,7 @@
<activity
android:name="org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhoneActivity"
android:label="@string/findmyphone_title"
android:excludeFromRecents="true"
android:launchMode="singleInstance">
</activity>
@@ -186,6 +199,7 @@
android:value="org.kde.kdeconnect.UserInterface.PluginSettingsActivity" />
</activity>
</application>
</manifest>

View File

@@ -3,22 +3,23 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
apply plugin: 'com.android.application'
android {
buildToolsVersion '23.0.2'
buildToolsVersion '23.0.3'
compileSdkVersion 23
defaultConfig {
minSdkVersion 9
targetSdkVersion 22 //Bumping to 23 means we have to support the new permissions model
multiDexEnabled true
//multiDexEnabled true
//testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"
}
dexOptions {
javaMaxHeapSize "4g"
javaMaxHeapSize "2g"
}
compileOptions {
// Use Java 1.7, requires minSdk 8
@@ -52,31 +53,35 @@ android {
checkReleaseBuilds false
}
buildTypes {
debug {
minifyEnabled false
useProguard false
}
release { //keep on 'releae', set to 'all' when testing to make sure proguard is not deleting important stuff
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
}
dependencies {
repositories {
mavenCentral()
}
compile 'com.android.support:support-v4:23.2.1'
compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:design:23.2.1'
compile 'com.android.support:support-v4:23.4.0'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'org.apache.sshd:sshd-core:0.8.0' //0.9 seems to fail on Android 6 and 1.+ requires java.nio.file, which doesn't exist in Android
//compile 'org.bouncycastle:bcprov-jdk15on:1.54'
compile 'com.madgag.spongycastle:pkix:1.54.0.0'
compile 'io.netty:netty-handler:4.1.0.CR3'
compile 'com.madgag.spongycastle:pkix:1.54.0.0' //For SSL certificate generation
// Testing
androidTestCompile 'org.mockito:mockito-core:1.10.19'
// Because mockito has some problems with dex environment
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'// Because mockito has some problems with dex environment
androidTestCompile 'org.skyscreamer:jsonassert:1.3.0'
//compile fileTree(include: '*.jar', dir: 'libs')
}

5
proguard-rules.pro vendored
View File

@@ -24,7 +24,6 @@
-keepnames class !android.support.v7.internal.view.menu.**,android.support.v7.** {*;}
-dontwarn org.spongycastle.**
-dontwarn org.bouncycastle.**
-dontwarn org.apache.sshd.**
-dontwarn org.apache.mina.**
-dontwarn org.slf4j.**
@@ -33,6 +32,8 @@
-keepattributes SourceFile,LineNumberTable,Signature,*Annotation*
-keep class org.spongycastle.** {*;}
-keep class org.bouncycastle.** {*;}
# SSHd requires mina, and mina uses reflection so some classes would get deleted
-keep class org.apache.mina.** {*;}
-keep class org.kde.kdeconnect.** {*;}

View File

@@ -26,7 +26,6 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/device_not_paired"
android:id="@+id/pair_message"
android:layout_gravity="left|center_vertical"
/>
<Button

View File

@@ -1,22 +1,32 @@
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
<LinearLayout 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:orientation="vertical"
tools:context="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationFilterActivity">
<TextView
android:text="@string/filter_apps_info"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="5dp"
android:id="@+id/tFilter"/>
<ProgressBar
android:layout_marginTop="64dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/spinner" />
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/lvFilterApps"
android:layout_below="@id/tFilter"/>
android:visibility="gone"
/>
</RelativeLayout>
</LinearLayout>

View File

@@ -151,7 +151,6 @@
android:layout_marginEnd="10dip"
android:id="@+id/imageView"
android:layout_weight="0"
android:layout_gravity="left|center_vertical"
android:contentDescription="@string/mpris_volume"
android:src="@drawable/ic_volume"
/>

View File

@@ -6,14 +6,12 @@
android:orientation="vertical"
android:gravity="bottom">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="KDE Connect"
android:textColor="#FFF"
android:textStyle="bold"
android:layout_above="@+id/device_name"
android:id="@+id/kdeconnect_label"
android:paddingLeft="16dp"
android:paddingStart="16dp"
@@ -29,9 +27,6 @@
android:id="@+id/device_name"
android:layout_marginBottom="0dp"
android:textColor="#fff"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:paddingBottom="16dp"
android:paddingTop="4dp"
android:paddingRight="48dp"

View File

@@ -18,35 +18,29 @@
Preference is able to place a specific widget for its particular
type in the "widget_frame" layout. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingEnd="?android:attr/scrollbarSize"
android:paddingRight="?android:attr/scrollbarSize"
android:background="?attr/selectableItemBackground" >
android:paddingStart="12dip"
android:paddingLeft="12dip"
android:background="?attr/selectableItemBackground">
<!-- Preference should place its actual preference widget here. -->
<!-- Preference will place its actual preference widget here. -->
<LinearLayout android:id="@+android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical" />
<ImageView
android:id="@+android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dip"
android:layout_marginLeft="15dip"
android:layout_marginEnd="15dip"
android:layout_marginStart="12dip"
android:layout_marginLeft="12dip"
android:layout_marginEnd="4dip"
android:layout_marginRight="4dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"

View File

@@ -5,16 +5,23 @@
<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_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_mpris_desc">تحكّم بالصّوت والصّورة من هاتفك</string>
<string name="pref_plugin_mousepad">الدّخل البعيد</string>
<string name="pref_plugin_mousepad_desc">استخدم الهاتف أو اللوحيّ كفأرة ولوحة مفاتيح</string>
<string name="pref_plugin_mpris">تحكّمات الوسائط المتعدّدة</string>
<string name="pref_plugin_mpris_desc">توفّر تحكّمًا بعيدًا لمشغّل الوسائط</string>
<string name="pref_plugin_runcommand">شغّل أمرًا</string>
<string name="pref_plugin_runcommand_desc">تحفّز أوامر عن بعد من الهاتف أو اللوحيّ</string>
<string name="pref_plugin_ping">وخزة</string>
<string name="pref_plugin_ping_desc">أرسل واستقبل وخزات</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">استقبل الإخطارات من الجهاز الآخر واعرضها على أندرويد</string>
<string name="pref_plugin_sharereceiver">شارك واستقبل</string>
<string name="pref_plugin_sharereceiver_desc">شارك الملفّات والعناوين بين الجهازين</string>
<string name="plugin_not_available">هذه الميزة غير متوفّرة في إصدار أندرويد لديك</string>
<string name="device_list_empty">لا أجهزة</string>
<string name="ok">حسنًا</string>
@@ -22,24 +29,33 @@
<string name="open_settings">افتح الإعدادات</string>
<string name="no_permissions">عليك إعطاء التّطبيق صلاحيّات للنّفاذ إلى الإخطارات</string>
<string name="send_ping">أرسل وخزة</string>
<string name="open_mpris_controls">تحكّمات الوسائط المتعدّدة</string>
<string name="open_mousepad">الدّخل البعيد</string>
<string name="mousepad_info">حرّك اصبعًا على الشّاشة لتحريك مؤشّر الفأرة. المس للنّقر واستخدم إصبعين أو ثلاث للزّرّين الأيمن والأوسط. انقر مطوّلًا للسّحب والإفلات.</string>
<string name="mousepad_double_tap_settings_title">اضبط إجراء اللمس بإصبعين</string>
<string name="mousepad_triple_tap_settings_title">اضبط إجراء اللمس بثلاث أصابع</string>
<string name="mousepad_sensitivity_settings_title">اضبط حساسيّة لوحة اللمس</string>
<string name="mousepad_scroll_direction_title">اعكس اتّجاه التّمرير</string>
<string-array name="mousepad_tap_entries">
<item/>
<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>Above Default</item>
<item>Fastest</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="category_remembered_devices">الأجهزة المتذكَّرة</string>
<string name="plugins_failed_to_load">فشل تحميل الملحقات (المس لمعلومات اكثر):</string>
<string name="device_menu_unpair">أزل الاقتران</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>
@@ -49,14 +65,24 @@
<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">لا يستخدم الجهاز الآخر إصدارة حديثة من «كدي المتّصل»، ستُستخدم طريقة التّعمية القديمة.</string>
<string name="my_device_fingerprint">بصمة SHA1 لشهادة جهازك هي:</string>
<string name="remote_device_fingerprint">بصمة SHA1 لشهادة الجهاز البعيد هي:</string>
<string name="pair_requested">طُلب الاقتران</string>
<string name="pairing_request_from">طلب اقتران من %1s</string>
<string name="received_url_title">استُلمت وصلة من %1s</string>
<string name="received_url_text">المس لفتح \'%1s\'</string>
<string name="incoming_file_title">ملفّ وارد من %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="received_file_title">استُلم ملفّ من %1s</string>
<string name="outgoing_file_title">يرسل ملفًّا إلى %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">استُقبل ملفّ من %1s</string>
<string name="received_file_fail_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>
@@ -77,6 +103,8 @@
<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 ثوان</item>
<item>20 ثانية</item>
@@ -95,9 +123,13 @@
<string name="invalid_device_name">اسم جهاز غير صالح</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">أضف أجهزة بميفاق الإنترنت م​إ</string>
<string name="share_notification_preference">إخطارات مزعجة</string>
<string name="share_notification_preference_summary">اهتزّ وشغّل صوتًا عند استقبال ملفّ</string>
<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>
@@ -105,7 +137,26 @@
<string name="sftp_readonly">(للقراءة فقط)</string>
<string name="sftp_camera">صور الكاميرا</string>
<string name="add_host">أضف مضيفًا/م​إ</string>
<string name="add_host_hint">اسم المضيف أو عنوان IP</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>
<string name="pairing_description">الأجهزة الأخرى التي تشغّل «كدي المتّصل» وعلى نفس الشّبكة ستظهر هنا.</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="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_description">يرّن هذا الجهاز لتجده</string>
<string name="findmyphone_found">وُجد</string>
<string name="open">افتح</string>
<string name="close">أغلق</string>
</resources>

View File

@@ -1,23 +1,25 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Notificador telefónicu</string>
<string name="pref_plugin_telephony">Avisador telefónicu</string>
<string name="pref_plugin_telephony_desc">Unvia avisos pa SMS y llamaes</string>
<string name="pref_plugin_battery">Informe de batería</string>
<string name="pref_plugin_battery_desc">Infoma davezu del estáu de la batería</string>
<string name="pref_plugin_sftp">Esposición del sistema de ficheros</string>
<string name="pref_plugin_sftp_desc">Permite restolar remotamente\'l sistema de ficheros del teléfonu</string>
<string name="pref_plugin_sftp_desc">Permite restolar remotamente el 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 como mur y tecláu</string>
<string name="pref_plugin_mousepad_desc">Usa\'l to preséu como panel táutil y tecláu</string>
<string name="pref_plugin_mpris">Controles remotos multimedia</string>
<string name="pref_plugin_mpris_desc">Controla l\'audiu/videu dende\'l to teléfonu</string>
<string name="pref_plugin_mpris_desc">Forne control multimedia pal to reproductor</string>
<string name="pref_plugin_runcommand">Executar comandu</string>
<string name="pref_plugin_runcommand_desc">Executa un comandu nel to sistema</string>
<string name="pref_plugin_runcommand_desc">Aiciona comandos remotos dende\'l to preséu</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 dende otros preseos</string>
<string name="pref_plugin_receive_notifications">Recibir avisos</string>
<string name="pref_plugin_receive_notifications_desc">Recibe avisos d\'otru preséu 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>
@@ -25,7 +27,7 @@
<string name="ok">Aceutar</string>
<string name="cancel">Encaboxar</string>
<string name="open_settings">Abrir axustes</string>
<string name="no_permissions">Necesites garantizar l\'accesu a los avisos</string>
<string name="no_permissions">Precises dar laude p\'acceder a los avisos</string>
<string name="send_ping">Unviar ping</string>
<string name="open_mpris_controls">Controles multimedia</string>
<string name="open_mousepad">Entrada remota</string>
@@ -43,8 +45,8 @@
<string name="mousepad_triple_default">d\'en mediu</string>
<string name="mousepad_sensitivity_default">por defeutu</string>
<string-array name="mousepad_sensitivity_entries">
<item>Perlenta</item>
<item>La más lenta</item>
<item>Above Slowest</item>
<item>Predeterminada</item>
<item>Penriba lo predeterminao</item>
<item>La más rápida</item>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Encaboxáu pol usuariu</string>
<string name="error_canceled_by_other_peer">Encaboxáu pola otra parte</string>
<string name="error_invalid_key">Recibióse una clave non válida</string>
<string name="encryption_info_title">Información de cifráu</string>
<string name="encryption_info_msg_no_ssl">L\'otru preséu nun usa una versión recién de KDE Connect. Usando\'l métodu de cifráu heredáu.</string>
<string name="my_device_fingerprint">La buelga SHA1 del certificáu del to preséu ye:</string>
<string name="remote_device_fingerprint">La buelga SHA1 del certificáu del preséu remotu ye:</string>
<string name="pair_requested">Solicitóse l\'empareyamientu</string>
<string name="pairing_request_from">Solicitú d\'empareyamientu de %1s</string>
<string name="received_url_title">Recibióse l\'enllaz de %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">Ficheru entrante de %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Unviando ficheru a %1s</string>
<string name="outgoing_files_title">Unviando ficheros a %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Unviáronse %1$d de %2$d ficheros</string>
<string name="received_file_title">Recibióse\'l ficheru de %1s</string>
<string name="received_file_fail_title">Fallu al recibir el ficheru de %1s</string>
<string name="received_file_text">Tap to open \'%1s\'</string>
<string name="received_file_text">Calca p\'abrir «%1s»</string>
<string name="sent_file_title">Unvióse\'l ficheru a %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Fallu al unviar el ficheru %1s</string>
<string name="sent_file_failed_title">Falló l\'unviu del ficheru a %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Calca pa responder</string>
<string name="reconnect">Reconeutar</string>
@@ -120,12 +128,16 @@
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Nome de preséu non válidu</string>
<string name="shareplugin_text_saved">Recibióse\'l testu y guardóse nel cartafueyu</string>
<string name="custom_devices_settings">Llista de preseos personalizada</string>
<string name="custom_devices_settings">Llistáu personalizáu de preseos</string>
<string name="pair_device_action">Empareyar con un preséu nuevu</string>
<string name="unpair_device_action">Desempareyar %s</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">Fai vibrar y reproduz un soníu al recibir un ficheru</string>
<string name="share_destination_customize">Personalizar direutoriu destín</string>
<string name="share_destination_customize_summary_disabled">Los ficheros recibíos apaecerán en Descargues</string>
<string name="share_destination_customize_summary_enabled">Los ficheros atroxaránse nel direutoriu d\'embaxo</string>
<string name="share_destination_folder_preference">Direutoriu destín</string>
<string name="title_activity_notification_filter">Peñera d\'avisos</string>
<string name="filter_apps_info">Los avisos sincronizaránse coles aplicaciones esbillaes.</string>
<string name="sftp_internal_storage">Almacenamientu internu</string>
@@ -137,7 +149,7 @@
<string name="add_host">Amestar agospiu/IP</string>
<string name="add_host_hint">Nome d\'agospiu o IP</string>
<string name="no_players_connected">Nun s\'alcontraron reproductores</string>
<string name="custom_dev_list_help">Usa esta opción namái si\'l to preséu nun se deteuta automáticamente. Introduz la direición o\'l nome d\'agospiu y toca\'l botón p\'amestalu a la llista. Toca un elementu esistente pa desanicialu de la llista.</string>
<string name="custom_dev_list_help">Usa esta opción namái si\'l to preséu nun se deteuta automáticamente. Introduz la direición o\'l nome d\'agospiu y toca\'l botón p\'amestalu al llistáu. Toca un elementu esistente pa desanicialu de la llistáu.</string>
<string name="mpris_player_on_device">%1$s en %2$s</string>
<string name="send_files">Unviar ficheros</string>
<string name="pairing_title">Preseos KDE Connect</string>
@@ -151,8 +163,9 @@
<string name="pref_plugin_telepathy">Unviar SMS</string>
<string name="pref_plugin_telepathy_desc">Unvia mensaxes de testu dende\'l to escritoriu</string>
<string name="plugin_not_supported">Esti complementu nun ta sofitáu pol preséu</string>
<string name="findmyphone_title">Alcuéntra\'l mio teléfonu</string>
<string name="findmyphone_description">Fai sonar esti teléfonu pa que pueas alcontralu.</string>
<string name="findmyphone_title">Alcuentra\'l mio teléfonu</string>
<string name="findmyphone_title_tablet">Alcuentra la mio tableta</string>
<string name="findmyphone_description">Fai sonar esti preséu pa que pueas alcontralu.</string>
<string name="findmyphone_found">Alcontrar</string>
<string name="open">Abrir</string>
<string name="close">Zarrar</string>

View File

@@ -5,10 +5,8 @@
<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_mpris_desc">Управление на звук/видео от телефона</string>
<string name="pref_plugin_ping">Пинг</string>
<string name="pref_plugin_ping_desc">Изпращане и получаване на пинг</string>
<string name="pref_plugin_notifications">Синхронизиране на уведомленията</string>

View File

@@ -5,12 +5,9 @@
<string name="pref_plugin_battery">Baterijski izvještaj</string>
<string name="pref_plugin_battery_desc">Periodično javi baterijski status</string>
<string name="pref_plugin_sftp">Otkrivanje datotečnog sistema</string>
<string name="pref_plugin_sftp_desc">Dopušta daljinsko pretraživanje datotečnog sistema telefona</string>
<string name="pref_plugin_clipboard">Sinhronizovanje sandučića</string>
<string name="pref_plugin_clipboard_desc">Podijeli sadržaj sandučića</string>
<string name="pref_plugin_mousepad">Daljinska kontrola ulaza</string>
<string name="pref_plugin_mousepad_desc">Koristi telofon kao miš i tastaturu</string>
<string name="pref_plugin_mpris_desc">Upravljajte zvukom/slikom sa vašeg telefona</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Šalji i primaj ping-ove</string>
<string name="pref_plugin_notifications">Sinhronizovano obavještenje</string>
@@ -67,7 +64,6 @@
<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_title">Neuspjelo slanje datoteke %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>

View File

@@ -1,23 +1,25 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Notificador de telefonia</string>
<string name="pref_plugin_telephony">Notificador de la telefonia</string>
<string name="pref_plugin_telephony_desc">Envia notificacions pels SMS i les trucades</string>
<string name="pref_plugin_battery">Informe 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 telèfon</string>
<string name="pref_plugin_sftp_desc">Permet navegar de forma remota pel sistema de fitxers del dispositiu</string>
<string name="pref_plugin_clipboard">Sincronitza el porta-retalls</string>
<string name="pref_plugin_clipboard_desc">Comparteix el contingut del porta-retalls</string>
<string name="pref_plugin_mousepad">Entrada remota</string>
<string name="pref_plugin_mousepad_desc">Usa el vostre telèfon com un ratolí i un teclat</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_mpris">Controls multimèdia</string>
<string name="pref_plugin_mpris_desc">Controla l\'àudio i el vídeo del vostre telèfon</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>
<string name="pref_plugin_runcommand_desc">Executa una ordre al vostre sistema</string>
<string name="pref_plugin_runcommand_desc">Activa les ordres remotes des del telèfon o tauleta</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envia i rep els pings</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_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>
<string name="plugin_not_available">Aquesta característica no està disponible en la vostra versió d\'Android</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Cancel·lat per l\'usuari</string>
<string name="error_canceled_by_other_peer">Cancel·lat per l\'altre parell</string>
<string name="error_invalid_key">S\'ha rebut una clau no vàlida</string>
<string name="encryption_info_title">Informació de l\'encriptatge</string>
<string name="encryption_info_msg_no_ssl">L\'altre dispositiu no usa una versió recent del KDE Connect, s\'utilitzarà el mètode d\'encriptatge antic.</string>
<string name="my_device_fingerprint">L\'empremta digital SHA1 del certificat del vostre dispositiu és:</string>
<string name="remote_device_fingerprint">L\'empremta digital SHA1 del certificat del dispositiu remot és:</string>
<string name="pair_requested">S\'ha demanat aparellar</string>
<string name="pairing_request_from">S\'ha demanat aparellar des de %1s</string>
<string name="received_url_title">S\'ha rebut un vincle des de %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">Fitxer entrant des de %1s</string>
<string name="incoming_file_text">%1s</string>
<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>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">S\'ha enviat %1$d de %2$d fitxers</string>
<string name="received_file_title">Fitxer rebut des de %1s</string>
<string name="received_file_fail_title">Ha fallat en rebre el fitxer des de %1s</string>
<string name="received_file_text">Puntegeu per obrir «%1s»</string>
<string name="sent_file_title">Envia el fitxer a %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Ha fallat en enviar el fitxer %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>
@@ -126,6 +134,10 @@
<string name="custom_device_list">Afegeix dispositius per la IP</string>
<string name="share_notification_preference">Rebombori de les notificacions</string>
<string name="share_notification_preference_summary">Vibra i reprodueix un so en rebre un fitxer</string>
<string name="share_destination_customize">Personalitza el directori de destinació</string>
<string name="share_destination_customize_summary_disabled">Els fitxers rebuts apareixeran a Baixades</string>
<string name="share_destination_customize_summary_enabled">Els fitxers seran emmagatzemats al directori de a sota</string>
<string name="share_destination_folder_preference">Directori de destinació</string>
<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>
@@ -151,8 +163,9 @@
<string name="pref_plugin_telepathy">Envia un SMS</string>
<string name="pref_plugin_telepathy_desc">Envia missatges de text des de l\'escriptori</string>
<string name="plugin_not_supported">Aquest connector no és admès pel dispositiu</string>
<string name="findmyphone_title">Troba el meu telèfon</string>
<string name="findmyphone_description">Fa sonar aquest telèfon perquè el pugueu trobar.</string>
<string name="findmyphone_title">Cerca el meu telèfon</string>
<string name="findmyphone_title_tablet">Cerca la meva tauleta</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>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Hlášení baterie</string>
<string name="pref_plugin_battery_desc">Periodicky hlásit stav baterky</string>
<string name="pref_plugin_sftp">Přístup k souborovému systému</string>
<string name="pref_plugin_sftp_desc">Umožní vám vzdáleně prohlížet souborový systém telefonu</string>
<string name="pref_plugin_sftp_desc">Umožní vám vzdáleně prohlížet souborový systém tohoto zařízení</string>
<string name="pref_plugin_clipboard">Synchronizace schránky</string>
<string name="pref_plugin_clipboard_desc">Sdílet obsah schránky</string>
<string name="pref_plugin_mousepad">Vzdálený vstup</string>
<string name="pref_plugin_mousepad_desc">Používejte svůj telefon jako myš a klávesnici</string>
<string name="pref_plugin_mousepad_desc">Používejte svůj telefon nebo tablet jako touchpad a klávesnici</string>
<string name="pref_plugin_mpris">Ovládání multimédií</string>
<string name="pref_plugin_mpris_desc">Ovládejte audio/video z vašeho telefonu</string>
<string name="pref_plugin_mpris_desc">Poskytuje dálkové ovládání přehrávačů médií</string>
<string name="pref_plugin_runcommand">Spustit příkaz</string>
<string name="pref_plugin_runcommand_desc">Spustí příkaz na vašem počítači</string>
<string name="pref_plugin_runcommand_desc">Spouštějte příkazy vzdáleně z vašeho telefonu</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Posílat a přijímat ping</string>
<string name="pref_plugin_notifications">Synchronizace upozornění</string>
<string name="pref_plugin_notifications_desc">Zpřístupněte si upozornění z jiných zařízení</string>
<string name="pref_plugin_receive_notifications">Přijímat oznámení</string>
<string name="pref_plugin_receive_notifications_desc">Přijímat oznámení z jiného zařízení a zobrazovat je v Androidu</string>
<string name="pref_plugin_sharereceiver">Sdílet s přijmnout</string>
<string name="pref_plugin_sharereceiver_desc">Sdílet soubory a odkazy mezi zařízeními</string>
<string name="plugin_not_available">Tato vlastnost není pro vaši verzi Androidu platná</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Přerušeno uživatelem</string>
<string name="error_canceled_by_other_peer">Přerušeno druhým uživatelem</string>
<string name="error_invalid_key">Byl přijat neplatný klíč</string>
<string name="encryption_info_title">Informace o šifrování</string>
<string name="encryption_info_msg_no_ssl">Druhé zařízení nepoužívá poslední verzi KDE connect. Bude použita stará metoda šifrování.</string>
<string name="my_device_fingerprint">Otisk SHA1 certifikátu vašeho zařízení je:</string>
<string name="remote_device_fingerprint">Otisk SHA1 certifikátu vzdáleného zařízení je:</string>
<string name="pair_requested">Bylo vyžádáno párování</string>
<string name="pairing_request_from">Požadavek o párování z %1s</string>
<string name="received_url_title">Přijat odkaz od %1s</string>
@@ -79,7 +85,6 @@
<string name="received_file_text">Ťukněte pro otevření \'%1s\'</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">Nelze odeslat soubor %1s</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>
@@ -152,7 +157,8 @@
<string name="pref_plugin_telepathy_desc">Posílejte zprávy ze své pracovní plochy</string>
<string name="plugin_not_supported">Tento modul zařízení nepodporuje</string>
<string name="findmyphone_title">Najít můj telefon</string>
<string name="findmyphone_description">Prozvoní tento telefon, takže jej můžete najít.</string>
<string name="findmyphone_title_tablet">Najít můj tablet</string>
<string name="findmyphone_description">Prozvoní toto zařízení, takže jej můžete najít</string>
<string name="findmyphone_found">Nalezeno</string>
<string name="open">Otevřít</string>
<string name="close">Zavřít</string>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Batterirapport</string>
<string name="pref_plugin_battery_desc">Rapportér batteristatus periodisk</string>
<string name="pref_plugin_sftp">Filsystem-expose</string>
<string name="pref_plugin_sftp_desc">Muliggør at gennemsøge telefonens filsystem eksternt</string>
<string name="pref_plugin_sftp_desc">Muliggør at gennemsøge denne enheds filsystem eksternt</string>
<string name="pref_plugin_clipboard">Synk. af udklipsholder</string>
<string name="pref_plugin_clipboard_desc">Del indholdet af udklipsholderen</string>
<string name="pref_plugin_mousepad">Eksternt input</string>
<string name="pref_plugin_mousepad_desc">Brug din telefon som mus og tastatur</string>
<string name="pref_plugin_mousepad_desc">Brug din telefon eller tablet som mus og tastatur</string>
<string name="pref_plugin_mpris">Multimediekontroller</string>
<string name="pref_plugin_mpris_desc">Styr lyd og video fra din telefon</string>
<string name="pref_plugin_mpris_desc">Giver en fjernbetjening til din medieafspiller</string>
<string name="pref_plugin_runcommand">Kør kommando</string>
<string name="pref_plugin_runcommand_desc">Kører en kommando på dit system</string>
<string name="pref_plugin_runcommand_desc">Kør eksterne kommandoer fra din telefon eller tablet</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send og modtag ping</string>
<string name="pref_plugin_notifications">Synk. af bekendtgørelser</string>
<string name="pref_plugin_notifications_desc">Tilgå dine bekendtgørelser fra andre enheder</string>
<string name="pref_plugin_receive_notifications">Modtag bekendtgørelser</string>
<string name="pref_plugin_receive_notifications_desc">Modtag bekendtgørelser fra den anden enhed og vis dem på Android</string>
<string name="pref_plugin_sharereceiver">Del og modtag</string>
<string name="pref_plugin_sharereceiver_desc">Del filer og URL\'er mellem enheder</string>
<string name="plugin_not_available">Denne funktion er ikke tilgængelig i din Android-version</string>
@@ -32,6 +34,8 @@
<string name="mousepad_info">Bevæg en finger på skærmen for at flytte musemarkøren. Tap for at klikke og brug to/tre-fingre for højre og midterste museknap. Brug et langt tryk til at trække og slippe.</string>
<string name="mousepad_double_tap_settings_title">Angiv handling for tap med to fingre</string>
<string name="mousepad_triple_tap_settings_title">Angiv handling for tap med tre fingre</string>
<string name="mousepad_sensitivity_settings_title">Angiv følsomhed for touchpad</string>
<string name="mousepad_scroll_direction_title">Omvendt rulleretning</string>
<string-array name="mousepad_tap_entries">
<item>Højreklik</item>
<item>Midterklik</item>
@@ -39,12 +43,13 @@
</string-array>
<string name="mousepad_double_default">højre</string>
<string name="mousepad_triple_default">midter</string>
<string name="mousepad_sensitivity_default">standard</string>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
<item>Mest langsom</item>
<item>Over mest langsom</item>
<item>Standard</item>
<item>Over standard</item>
<item>Hurtigst</item>
</string-array>
<string name="category_connected_devices">Forbundne enheder</string>
<string name="category_not_paired_devices">Tilgængelig enheder</string>
@@ -63,6 +68,10 @@
<string name="error_canceled_by_user">Annulleret af brugeren</string>
<string name="error_canceled_by_other_peer">Annulleret af modpart</string>
<string name="error_invalid_key">Ugyldige nøgle modtaget</string>
<string name="encryption_info_title">Krypteringsinfo</string>
<string name="encryption_info_msg_no_ssl">Den anden enhed bruger ikke en nylig version af KDE Connect, og bruger dermed den forældede krypteringsmetode.</string>
<string name="my_device_fingerprint">SHA1-fingeraftrykket for dit enhedscertifikat er:</string>
<string name="remote_device_fingerprint">SHA1-fingeraftrykket for det eksterne enhedscertifikat er:</string>
<string name="pair_requested">Anmodet om parring</string>
<string name="pairing_request_from">Parringsanmodning fra %1s</string>
<string name="received_url_title">Modtog link fra %1s</string>
@@ -70,13 +79,15 @@
<string name="incoming_file_title">Indkommende fil fra %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Sender fil til %1s</string>
<string name="outgoing_files_title">Sender filer til %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Sendte %1$d ud af %2$d filer</string>
<string name="received_file_title">Modtog fil fra %1s</string>
<string name="received_file_fail_title">Kunne ikke modtage fil fra %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 %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>
@@ -123,6 +134,10 @@
<string name="custom_device_list">Tilføj enheder via IP</string>
<string name="share_notification_preference">Støjende bekendtgørelser</string>
<string name="share_notification_preference_summary">Vibrér og afspil en lyd når en fil modtages</string>
<string name="share_destination_customize">Tilpas destinationsmappe</string>
<string name="share_destination_customize_summary_disabled">Modtagne filer vil dukke op i Downloads</string>
<string name="share_destination_customize_summary_enabled">Filer vil blive gemt i mappen nedenfor</string>
<string name="share_destination_folder_preference">Destinationsmappe</string>
<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>
@@ -149,7 +164,8 @@
<string name="pref_plugin_telepathy_desc">Send SMS-beskeder fra din desktop</string>
<string name="plugin_not_supported">Dette plugin er ikke understøttet af enheden</string>
<string name="findmyphone_title">Find min telefon</string>
<string name="findmyphone_description">Ringer til denne telefon, så du kan finde den.</string>
<string name="findmyphone_title_tablet">Find min tablet</string>
<string name="findmyphone_description">Ringer til denne enhed, så du kan finde den.</string>
<string name="findmyphone_found">Fundet</string>
<string name="open">Åbn</string>
<string name="close">Luk</string>

View File

@@ -5,19 +5,19 @@
<string name="pref_plugin_battery">Akkubericht</string>
<string name="pref_plugin_battery_desc">Akkustatus periodisch berichten</string>
<string name="pref_plugin_sftp">Dateisystem zeigen</string>
<string name="pref_plugin_sftp_desc">Erlaubt das Browsen des Dateisystems auf dem entfernten Handy</string>
<string name="pref_plugin_sftp_desc">Erlaubt das Browsen des Dateisystems auf diesem Gerät aus der Ferne</string>
<string name="pref_plugin_clipboard">Abgleich der Zwischenablage</string>
<string name="pref_plugin_clipboard_desc">Inhalt der Zwischenablage freigeben</string>
<string name="pref_plugin_mousepad">Ferneingabe</string>
<string name="pref_plugin_mousepad_desc">Verwendet Ihr Handy als Maus und Tastatur</string>
<string name="pref_plugin_mousepad_desc">Verwendet Ihr Handy als Tablett, Touchpad und Tastatur</string>
<string name="pref_plugin_mpris">Multimedia-Bedienung</string>
<string name="pref_plugin_mpris_desc">Audio und Video mit Ihrem Telefon steuern</string>
<string name="pref_plugin_runcommand">Befehl ausführen</string>
<string name="pref_plugin_runcommand_desc">Führt einen Befehl auf Ihrem System aus</string>
<string name="pref_plugin_runcommand_desc">Von Ihrem Telefon oder Tablett Befehle auf anderen Geräten ausführen</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Senden und Empfangen von Pings</string>
<string name="pref_plugin_notifications">Benachrichtigungs-Abgleich</string>
<string name="pref_plugin_notifications_desc">Zugriff auf Ihre Benachrichtigungen von anderen Geräten</string>
<string name="pref_plugin_receive_notifications">Benachrichtigungen empfangen</string>
<string name="pref_plugin_sharereceiver">Veröffentlichen und Empfangen</string>
<string name="pref_plugin_sharereceiver_desc">Dateien und Adressen (URLs) zwischen Geräten teilen</string>
<string name="plugin_not_available">Diese Funktion ist in Ihrer Android-Version nicht verfügbar</string>
@@ -29,6 +29,9 @@
<string name="send_ping">Ping senden</string>
<string name="open_mpris_controls">Multimedia-Bedienung</string>
<string name="open_mousepad">Ferneingabe</string>
<string name="mousepad_double_tap_settings_title">Aktionsausführung bei Berührung mit zwei Fingern einstellen</string>
<string name="mousepad_triple_tap_settings_title">Aktionsausführung bei Berührung mit drei Fingern einstellen</string>
<string name="mousepad_sensitivity_settings_title">Empfindlichkeit des Touchpads einstellen</string>
<string name="mousepad_scroll_direction_title">Bildlaufrichtung umkehren</string>
<string-array name="mousepad_tap_entries">
<item>Rechtsklick</item>
@@ -62,6 +65,9 @@
<string name="error_canceled_by_user">Abbruch durch Benutzer</string>
<string name="error_canceled_by_other_peer">Abbruch durch Gegenstelle</string>
<string name="error_invalid_key">Ungültiger Schlüssel empfangen</string>
<string name="encryption_info_title">Verschlüsselungsinformationen</string>
<string name="my_device_fingerprint">Der SHA1-Fingerabdruck Ihres Gerätezertifikats lautet:</string>
<string name="remote_device_fingerprint">Der SHA1-Fingerabdruck des Gerätezertifikats der Gegenstelle lautet:</string>
<string name="pair_requested">Verbindung angefordert</string>
<string name="pairing_request_from">Verbindungsanfrage von %1s</string>
<string name="received_url_title">Verknüpfung von %1s erhalten</string>
@@ -73,7 +79,6 @@
<string name="received_file_fail_title">Der Empfang der Datei %1s ist fehlgeschlagen</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>
@@ -95,6 +100,7 @@
<string name="mpris_next">Weiter</string>
<string name="mpris_volume">Lautstärke</string>
<string name="mpris_settings">Multimedia-Einstellungen</string>
<string name="mpris_time_settings_title">Knöpfe Vorwärts/Rückwärts</string>
<string-array name="mpris_time_entries">
<item>10 Sekunden</item>
<item>20 Sekunden</item>
@@ -141,8 +147,9 @@
<string name="pref_plugin_telepathy">SMS senden</string>
<string name="pref_plugin_telepathy_desc">Text-Nachrichten von Ihrer Arbeitsfläche senden</string>
<string name="plugin_not_supported">Dieses Modul wird durch das Gerät nicht unterstützt</string>
<string name="findmyphone_title">Telefon suchen</string>
<string name="findmyphone_description">Ruft dieses Telefon an, um es zu suchen.</string>
<string name="findmyphone_title">Mein Telefon suchen</string>
<string name="findmyphone_title_tablet">Mein Tablett suchen</string>
<string name="findmyphone_description">Ruft dieses Gerät an, um es zu suchen.</string>
<string name="findmyphone_found">Gefunden</string>
<string name="open">Öffnen</string>
<string name="close">Schließen</string>

165
res/values-el/strings.xml Normal file
View File

@@ -0,0 +1,165 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Ειδοποιήσεις τηλεφωνίας</string>
<string name="pref_plugin_telephony_desc">Αποστολή ειδοποιήσεων για SMS και κλήσεις</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_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_ping">Ping</string>
<string name="pref_plugin_ping_desc">Αποστολή και λήψη pings</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="ok">Εντάξει</string>
<string name="cancel">Ακύρωση</string>
<string name="open_settings">Ρυθμίσεις ανοίγματος</string>
<string name="no_permissions">Απαιτείται παραχώρηση δικαιωμάτων για την πρόσβαση σε ειδοποιήσεις</string>
<string name="send_ping">Αποστολή ping</string>
<string name="open_mpris_controls">Έλεγχος πολυμέσων</string>
<string name="open_mousepad">Απομακρυσμένη είσοδος στοιχείων</string>
<string name="mousepad_info">Μετακινείστε το δάκτυλο στην οθόνη για να μετακινηθεί ο δρομέας του ποντικιού. Χτυπήστε για κλικ και χρησιμοποιήστε δύο/τρία δάκτυλα για δεξί και μεσαίο κλικ. Πιέστε με διάρκεια για μετακίνηση και απόθεση.</string>
<string name="mousepad_double_tap_settings_title">Ρύθμιση δύο δακτύλων για την ενέργεια χτυπήματος</string>
<string name="mousepad_triple_tap_settings_title">Ρύθμιση τριών δακτύλων για την ενέργεια χτυπήματος</string>
<string name="mousepad_sensitivity_settings_title">Ρύθμιση ευαισθησίας της οθόνης αφής</string>
<string name="mousepad_scroll_direction_title">Κατεύθυνση ανάστροφης κύλησης</string>
<string-array name="mousepad_tap_entries">
<item>Δεξί κλικ</item>
<item>Μεσαίο κλικ</item>
<item>Τίποτα</item>
</string-array>
<string name="mousepad_double_default">δεξί</string>
<string name="mousepad_triple_default">μεσαίο</string>
<string name="mousepad_sensitivity_default">προκαθορισμένο</string>
<string-array name="mousepad_sensitivity_entries">
<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="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>
<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_could_not_send_package">Αδυναμία αποστολής πακέτου</string>
<string name="error_timed_out">Τέλος χρονικού ορίου</string>
<string name="error_canceled_by_user">Ακυρώθηκε από τον χρήστη</string>
<string name="error_canceled_by_other_peer">Ακυρώθηκε από άλλον χρήστη</string>
<string name="error_invalid_key">Ελήφθη μη έγκυρο κλειδί</string>
<string name="encryption_info_title">Πληροφορίες κρυπτογράφησης</string>
<string name="encryption_info_msg_no_ssl">Η άλλη συσκευή δεν χρησιμοποιεί μια πρόσφατη έκδοση του KDE Connect, θα χρησιμοποιηθεί η παλαιά μέθοδος κρυπτογράφησης.</string>
<string name="my_device_fingerprint">Το ίχνος SHA1 του πιστοποιητικού της συσκευής σας είναι:</string>
<string name="remote_device_fingerprint">Το ίχνος SHA1 του πιστοποιητικού της απομακρυσμένης συσκευής είναι:</string>
<string name="pair_requested">Ζητήθηκε σύζευξη</string>
<string name="pairing_request_from">Αίτημα σύζευξης από %1s</string>
<string name="received_url_title">Ελήφθη σύνδεσμος από %1s</string>
<string name="received_url_text">Χτυπήστε για άνοιγμα \'%1s\'</string>
<string name="incoming_file_title">Εισερχόμενο αρχείο από %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Αποστολή αρχείου σε %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">Ελήφθη αρχείο από %1s</string>
<string name="received_file_fail_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>
<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="remote_control">Απομακρυσμένος έλεγχος</string>
<string name="settings">Ρυθμίσεις KDE Connect</string>
<string name="mpris_play">Αναπαραγωγή</string>
<string name="mpris_previous">Προηγούμενο</string>
<string name="mpris_rew">Ταχεία ώθηση όπισθεν</string>
<string name="mpris_ff">Ταχεία προώθηση</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 λεπτό</item>
<item>2 λεπτά</item>
</string-array>
<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="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Μη έγκυρο όνομα συσκευής</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="share_notification_preference">Θορυβώδεις ειδοποιήσεις</string>
<string name="share_notification_preference_summary">Δόνηση και ηχητική ένδειξη με τη λήψη αρχείου</string>
<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="add_host_hint">Όνομα υπολογιστή ή IP</string>
<string name="no_players_connected">Δεν βρέθηκαν συσκευές αναπαραγωγής</string>
<string name="custom_dev_list_help">Χρησιμοποιήστε την επιλογή αυτή μόνο αν η συσκευή σας δεν έχει εντοπιστεί αυτόματα. Δώστε την IP διεύθυνση ή το όνομα του υπολογιστή και αγγίξτε το κουμπί για να προστεθεί στη λίστα. Αγγίξτε ένα αντικείμενο της λίστας για να το αφαιρέσετε.</string>
<string name="mpris_player_on_device">%1$s σε %2$s</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="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_description">Καλεί αυτή τη συσκευή ώστε να την εντοπίσετε</string>
<string name="findmyphone_found">Βρέθηκε</string>
<string name="open">Άνοιγμα</string>
<string name="close">Κλείσιμο</string>
</resources>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Battery report</string>
<string name="pref_plugin_battery_desc">Periodically report battery status</string>
<string name="pref_plugin_sftp">Filesystem expose</string>
<string name="pref_plugin_sftp_desc">Allows to browse the phone\'s filesystem remotely</string>
<string name="pref_plugin_sftp_desc">Allows to browse this device\'s filesystem remotely</string>
<string name="pref_plugin_clipboard">Clipboard sync</string>
<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 as a mouse and keyboard</string>
<string name="pref_plugin_mousepad_desc">Use your phone or tablet as a touchpad and keyboard</string>
<string name="pref_plugin_mpris">Multimedia controls</string>
<string name="pref_plugin_mpris_desc">Control audio/video from your phone</string>
<string name="pref_plugin_mpris_desc">Provides a remote control for your media player</string>
<string name="pref_plugin_runcommand">Run Command</string>
<string name="pref_plugin_runcommand_desc">Runs a command on your system</string>
<string name="pref_plugin_runcommand_desc">Trigger remote commands from your phone or tablet</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send and receive pings</string>
<string name="pref_plugin_notifications">Notification sync</string>
<string name="pref_plugin_notifications_desc">Access your notifications from other devices</string>
<string name="pref_plugin_receive_notifications">Receive notifications</string>
<string name="pref_plugin_receive_notifications_desc">Receive notifications from the other device and display them on Android</string>
<string name="pref_plugin_sharereceiver">Share and receive</string>
<string name="pref_plugin_sharereceiver_desc">Share files and URLs between devices</string>
<string name="plugin_not_available">This feature is not available in your Android version</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Cancelled by user</string>
<string name="error_canceled_by_other_peer">Cancelled by other peer</string>
<string name="error_invalid_key">Invalid key received</string>
<string name="encryption_info_title">Encryption Info</string>
<string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string>
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is:</string>
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is:</string>
<string name="pair_requested">Pair requested</string>
<string name="pairing_request_from">Pairing request from %1s</string>
<string name="received_url_title">Received link from %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">Incoming file from %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Sending file to %1s</string>
<string name="outgoing_files_title">Sending files to %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Sent %1$d out of %2$d files</string>
<string name="received_file_title">Received file from %1s</string>
<string name="received_file_fail_title">Failed receiving file from %1s</string>
<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 %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>
@@ -151,8 +159,9 @@
<string name="pref_plugin_telepathy">Send SMS</string>
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
<string name="plugin_not_supported">This plugin is not supported by the device</string>
<string name="findmyphone_title">Find My Phone</string>
<string name="findmyphone_description">Rings this phone so you can find it.</string>
<string name="findmyphone_title">Find my phone</string>
<string name="findmyphone_title_tablet">Find my tablet</string>
<string name="findmyphone_description">Rings this device so you can find it</string>
<string name="findmyphone_found">Found</string>
<string name="open">Open</string>
<string name="close">Close</string>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Informe de la batería</string>
<string name="pref_plugin_battery_desc">Informar periódicamente del estado de la batería</string>
<string name="pref_plugin_sftp">Mostrar el sistema de archivos</string>
<string name="pref_plugin_sftp_desc">Permite examinar de forma remota el sistema de archivos del teléfono</string>
<string name="pref_plugin_sftp_desc">Permite examinar de forma remota el sistema de archivos de este dispositivo</string>
<string name="pref_plugin_clipboard">Sincronización del portapapeles</string>
<string name="pref_plugin_clipboard_desc">Compartir el contenido del portapapeles</string>
<string name="pref_plugin_mousepad">Entrada remota</string>
<string name="pref_plugin_mousepad_desc">Usar su teléfono como ratón y teclado</string>
<string name="pref_plugin_mousepad_desc">Usar su teléfono o tableta como teclado y teclado táctil</string>
<string name="pref_plugin_mpris">Controles multimedia</string>
<string name="pref_plugin_mpris_desc">Controlar audio y vídeo desde el teléfono</string>
<string name="pref_plugin_mpris_desc">Proporciona un control remoto para su reproductor de medios</string>
<string name="pref_plugin_runcommand">Ejecutar orden</string>
<string name="pref_plugin_runcommand_desc">Ejecuta una orden en su sistema</string>
<string name="pref_plugin_runcommand_desc">Desencadenar órdenes remotas desde su teléfono o tableta</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Enviar y recibir pings</string>
<string name="pref_plugin_notifications">Sincronizar notificaciones</string>
<string name="pref_plugin_notifications_desc">Acceder a las notificaciones desde otros dispositivos</string>
<string name="pref_plugin_receive_notifications">Recibir notificaciones</string>
<string name="pref_plugin_receive_notifications_desc">Recibir notificaciones desde el otro dispositivo y mostrarlas en Android</string>
<string name="pref_plugin_sharereceiver">Compartir y recibir</string>
<string name="pref_plugin_sharereceiver_desc">Compartir archivos y URL entre dispositivos</string>
<string name="plugin_not_available">Esta funcionalidad no está disponible en su versión de Android</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Cancelado por el usuario</string>
<string name="error_canceled_by_other_peer">Cancelado por la otra parte</string>
<string name="error_invalid_key">Se ha recibido una clave no valida</string>
<string name="encryption_info_title">Información de cifrado</string>
<string name="encryption_info_msg_no_ssl">El otro dispositivo no dispone de una versión reciente de KDE Connect, se usará un método de cifrado antiguo.</string>
<string name="my_device_fingerprint">La huella digital SHA1 del certificado de su dispositivo es:</string>
<string name="remote_device_fingerprint">La huella digital SHA1 del certificado del dispositivo remoto es:</string>
<string name="pair_requested">Vinculación solicitada</string>
<string name="pairing_request_from">Solicitud de vinculación de %1s</string>
<string name="received_url_title">Enlace recibido desde %1s</string>
@@ -79,7 +85,6 @@
<string name="received_file_text">Pulse para abrir «%1s»</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 %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Pulse para responder</string>
<string name="reconnect">Reconectar</string>
@@ -152,7 +157,8 @@
<string name="pref_plugin_telepathy_desc">Enviar mensajes de texto desde el escritorio</string>
<string name="plugin_not_supported">Este complemento no está permitido por el dispositivo</string>
<string name="findmyphone_title">Encontrar mi teléfono</string>
<string name="findmyphone_description">Hace sonar este teléfono para que pueda encontrarlo.</string>
<string name="findmyphone_title_tablet">Encontrar mi tableta</string>
<string name="findmyphone_description">Hace sonar este dispositivo para que pueda encontrarlo</string>
<string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string>
<string name="close">Cerrar</string>

165
res/values-et/strings.xml Normal file
View File

@@ -0,0 +1,165 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Telefoni märguanded</string>
<string name="pref_plugin_telephony_desc">SMS-ide ja kõnede märguannete saatmine</string>
<string name="pref_plugin_battery">Aku aruanne</string>
<string name="pref_plugin_battery_desc">Perioodiline aku olekust teavitamine</string>
<string name="pref_plugin_sftp">Failisüsteemi sirvimine</string>
<string name="pref_plugin_sftp_desc">Seadme failisüsteemi eemalt sirvimise võimaldamine</string>
<string name="pref_plugin_clipboard">Lõikepuhvri sünkroonimine</string>
<string name="pref_plugin_clipboard_desc">Lõikepuhvri sisu jagamine</string>
<string name="pref_plugin_mousepad">Kaugsisestus</string>
<string name="pref_plugin_mousepad_desc">Telefoni või tahvli kasutamine puuteplaadi ja klaviatuurina</string>
<string name="pref_plugin_mpris">Multimeedia juhtimine</string>
<string name="pref_plugin_mpris_desc">Meediamängija kaugjuhtimise võimaldamine</string>
<string name="pref_plugin_runcommand">Käsu käivitamine</string>
<string name="pref_plugin_runcommand_desc">Telefonist või tahvlist kaugkäskude käivitamine</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Pingide saatmine ja vastuvõtmine</string>
<string name="pref_plugin_notifications">Märguannete sünkroonimine</string>
<string name="pref_plugin_notifications_desc">Märguannete nägemine teistes seadmetes</string>
<string name="pref_plugin_receive_notifications">Märguannete vastuvõtmine</string>
<string name="pref_plugin_receive_notifications_desc">Märguannete vastuvõtmine teisest seadmest ja nende näitamine Androidis</string>
<string name="pref_plugin_sharereceiver">Jagamine ja vastuvõtmine</string>
<string name="pref_plugin_sharereceiver_desc">Failide ja URL-ide jagamine seadmete vahel</string>
<string name="plugin_not_available">See omadus ei ole sinu Androidi versioonis veel saadaval</string>
<string name="device_list_empty">Seadmed puuduvad</string>
<string name="ok">OK</string>
<string name="cancel">Loobu</string>
<string name="open_settings">Ava seadistused</string>
<string name="no_permissions">Märguannete nägemiseks tuleb anda vastavad õigused</string>
<string name="send_ping">Saada ping</string>
<string name="open_mpris_controls">Multimeedia juhtimine</string>
<string name="open_mousepad">Kaugsisestus</string>
<string name="mousepad_info">Hiirekursori liigutamiseks liiguta sõrme ekraanil. Koputa klõpsamiseks ja kasuta kaht või kolme sõrme parema ja keskmise nupu jaoks. Pika vajutusega saab lohistada.</string>
<string name="mousepad_double_tap_settings_title">Kahe sõrmega koputamise toimingu määramine</string>
<string name="mousepad_triple_tap_settings_title">Kolme sõrmega koputamise toimingu määramine</string>
<string name="mousepad_sensitivity_settings_title">Puutepadja tundlikkuse määramine</string>
<string name="mousepad_scroll_direction_title">Vastupidi kerimise suund</string>
<string-array name="mousepad_tap_entries">
<item>Paremklõps</item>
<item>Keskklõps</item>
<item>Ei tee midagi</item>
</string-array>
<string name="mousepad_double_default">parem</string>
<string name="mousepad_triple_default">keskmine</string>
<string name="mousepad_sensitivity_default">vaikimisi</string>
<string-array name="mousepad_sensitivity_entries">
<item>Kõige aeglasem</item>
<item>Kõige aeglasemast kiirem</item>
<item>Vaikimisi</item>
<item>Keskmisest kiirem</item>
<item>Kõige kiirem</item>
</string-array>
<string name="category_connected_devices">Ühendatud seadmed</string>
<string name="category_not_paired_devices">Saadaolevad seadmed</string>
<string name="category_remembered_devices">Meeldejäetud seadmed</string>
<string name="plugins_failed_to_load">Pluginate laadimine nurjus (koputa rohkema teabe saamiseks)</string>
<string name="device_menu_plugins">Plugina seadistused</string>
<string name="device_menu_unpair">Eemalda paardumine</string>
<string name="device_not_reachable">Paardunud seade pole saadaval</string>
<string name="pair_new_device">Paarita uus seade</string>
<string name="unknown_device">Tundmatu seade</string>
<string name="error_not_reachable">Seade pole saadaval</string>
<string name="error_already_requested">Paardumist on juba soovitud</string>
<string name="error_already_paired">Seade on juba paaritatud</string>
<string name="error_could_not_send_package">Paketi saatmine nurjus</string>
<string name="error_timed_out">Ajaületus</string>
<string name="error_canceled_by_user">Kasutaja katkestas</string>
<string name="error_canceled_by_other_peer">Teine pool katkestas</string>
<string name="error_invalid_key">Saadi vigane võti</string>
<string name="encryption_info_title">Krüptimise teave</string>
<string name="encryption_info_msg_no_ssl">Teine seade ei kasuta KDE Connecti uusimat versiooni ja tarvitab krüptimisel pärandmeetodit.</string>
<string name="my_device_fingerprint">Sinu seadme sertifikaadi SHA1 sõrmejälg on:</string>
<string name="remote_device_fingerprint">Kaugseadme sertifikaadi SHA1 sõrmejälg on:</string>
<string name="pair_requested">Paardumise soov</string>
<string name="pairing_request_from">Paardumise soov seadmest %1s</string>
<string name="received_url_title">Lingi saamine seadmest %1s</string>
<string name="received_url_text">Koputa \"%1s\" avamiseks</string>
<string name="incoming_file_title">Saabuv fail seadmest %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Faili saatmine seadmesse %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">Faili saamine seadmest %1s</string>
<string name="received_file_fail_title">Faili saamine seadmest %1s nurjus</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_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>
<string name="middle_click">Saada keskklõps</string>
<string name="show_keyboard">Näita klaviatuuri</string>
<string name="device_not_paired">Seadmed pole paaritatud</string>
<string name="request_pairing">Soovi paardumist</string>
<string name="pairing_accept">Nõustu</string>
<string name="pairing_reject">Keeldu</string>
<string name="device">Seade</string>
<string name="pair_device">Paarita seade</string>
<string name="remote_control">Kaugjuhtimine</string>
<string name="settings">KDE Connecti seadistused</string>
<string name="mpris_play">Esita</string>
<string name="mpris_previous">Eelmine</string>
<string name="mpris_rew">Keri tagasi</string>
<string name="mpris_ff">Kiiresti edasi</string>
<string name="mpris_next">Järgmine</string>
<string name="mpris_volume">Helitugevus</string>
<string name="mpris_settings">Multimeedia seadistused</string>
<string name="mpris_time_settings_title">Edasi-tagasikerimise nupud</string>
<string name="mpris_time_settings_summary">Kiire edasi-tagasikerimise aeg vajutamisel</string>
<string-array name="mpris_time_entries">
<item>10 sekundit</item>
<item>20 sekundit</item>
<item>30 sekundit</item>
<item>1 minut</item>
<item>2 minutit</item>
</string-array>
<string name="share_to">Jaga...</string>
<string name="protocol_version_older">See seade kasutab vana protokolli versiooni</string>
<string name="protocol_version_newer">See seade kasutab uuemat protokolli versiooni</string>
<string name="general_settings">Üldised seadistused</string>
<string name="plugin_settings">Seadistused</string>
<string name="plugin_settings_with_name">%s seadistused</string>
<string name="device_name">Seadme nimi</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Vigane seadme nimi</string>
<string name="shareplugin_text_saved">Saadi tekst, salvestati lõikepuhvrisse</string>
<string name="custom_devices_settings">Kohandatud seadmete loend</string>
<string name="pair_device_action">Paarita uus seade</string>
<string name="unpair_device_action">Eemalda %s paardumine</string>
<string name="custom_device_list">Lisa seadmeid IP järgi</string>
<string name="share_notification_preference">Mürarikkad märguanded</string>
<string name="share_notification_preference_summary">Vibreerimine ja heli esitamine faili saamisel</string>
<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="add_host_hint">Masinanimi või IP</string>
<string name="no_players_connected">Ühtegi mängijat ei leitud</string>
<string name="custom_dev_list_help">Seda tuleks kasutada ainult siis, kui sinu seadet ei tuvastatud automaatselt. Sisesta allpool IP-aadress või masinanimi ja puuduta nuppu selle lisamiseks loendisse. Puuduta olemasolevat kirjet selle eemaldamiseks loendist.</string>
<string name="mpris_player_on_device">%1$s seadmes %2$s</string>
<string name="send_files">Saada faile</string>
<string name="pairing_title">KDE Connecti seadmed</string>
<string name="pairing_description">Siin näeb teisi seadmeid samas võrgus, kus töötab KDE Connect</string>
<string name="device_paired">Seade on paaritatud</string>
<string name="device_rename_title">Muuda seadme nime</string>
<string name="device_rename_confirm">Muuda nime</string>
<string name="refresh">Värskenda</string>
<string name="unreachable_description">See paardunud seade pole saadaval. Kontrolli, kas see on ikka ühendatud sinuga samasse võrku.</string>
<string name="no_file_browser">Ühtegi failisirvijat pole paigaldatud</string>
<string name="pref_plugin_telepathy">Saada SMS</string>
<string name="pref_plugin_telepathy_desc">Tekstisõnumite saatmine oma töölaualt</string>
<string name="plugin_not_supported">Seade ei toeta seda pluginat</string>
<string name="findmyphone_title">Leia mu telefon</string>
<string name="findmyphone_title_tablet">Leia mu tahvel</string>
<string name="findmyphone_description">Seadmele helistamine, et see üles leida</string>
<string name="findmyphone_found">Leitud</string>
<string name="open">Ava</string>
<string name="close">Sulge</string>
</resources>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Akkuraportti</string>
<string name="pref_plugin_battery_desc">Raportoi akun tila säännöllisesti</string>
<string name="pref_plugin_sftp">Tiedostojärjestelmän paljastaminen</string>
<string name="pref_plugin_sftp_desc">Mahdollistaa puhelimen tiedostojärjestelmän etäselaamisen</string>
<string name="pref_plugin_sftp_desc">Mahdollistaa tämän laitteen tiedostojärjestelmän etäselaamisen</string>
<string name="pref_plugin_clipboard">Leikepöydän synkronointi</string>
<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ä puhelintasi hiirenä ja näppäimistönä</string>
<string name="pref_plugin_mousepad_desc">Käytä puhelinta tai tablettia hiirenä ja näppäimistönä</string>
<string name="pref_plugin_mpris">Multimedian ohjaus</string>
<string name="pref_plugin_mpris_desc">Ohjaa ääntä ja videota puhelimestasi</string>
<string name="pref_plugin_mpris_desc">Mahdollistaa mediasoittimen etäohjauksen</string>
<string name="pref_plugin_runcommand">Suorita komento</string>
<string name="pref_plugin_runcommand_desc">Suorita komento järjestelmässäsi</string>
<string name="pref_plugin_runcommand_desc">Suorita komentoja etänä puhelimelta tai tabletilta</string>
<string name="pref_plugin_ping">Tiedustelupaketti</string>
<string name="pref_plugin_ping_desc">Lähetä ja vastaanota tiedustelupaketteja</string>
<string name="pref_plugin_notifications">Ilmoitusten synkronointi</string>
<string name="pref_plugin_notifications_desc">Näe ilmoituksesi muissa laitteissa</string>
<string name="pref_plugin_receive_notifications">Vastaanota ilmoituksia</string>
<string name="pref_plugin_receive_notifications_desc">Vastaanota ilmoituksia toiselta laitteelta ja näytä ne Androidissa</string>
<string name="pref_plugin_sharereceiver">Jako ja vastaanottaminen</string>
<string name="pref_plugin_sharereceiver_desc">Jaa tiedostoja ja osoitteita laitteiden välillä</string>
<string name="plugin_not_available">Piirre ei ole käytettävissä Android-versiossasi</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Käyttäjä perui</string>
<string name="error_canceled_by_other_peer">Vertaiskäyttäjä perui</string>
<string name="error_invalid_key">Vastaanotettiin väärä avain</string>
<string name="encryption_info_title">Salaustiedot</string>
<string name="encryption_info_msg_no_ssl">Toinen laite ei käytä KDE Connectin uudehkoa versiota, joten käytetään vanhaa salausmenetelmää.</string>
<string name="my_device_fingerprint">Laitteen varmenteen SHA1-sormenjälki on:</string>
<string name="remote_device_fingerprint">Etälaitteen varmenteen SHA1-sormenjälki on:</string>
<string name="pair_requested">Paripyyntö</string>
<string name="pairing_request_from">Paripyyntö laitteesta %1s</string>
<string name="received_url_title">Vastaanotettiin linkki lähettäjältä %1s</string>
@@ -79,7 +85,6 @@
<string name="received_file_text">Avaa ”%1s” napauttamalla</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 epäonnistui: %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Vastaa napauttamalla</string>
<string name="reconnect">Yhdistä uudelleen</string>
@@ -152,7 +157,8 @@
<string name="pref_plugin_telepathy_desc">Lähetä tekstiviestejä työpöydältäsi</string>
<string name="plugin_not_supported">Laite ei tue tätä liitännäistä</string>
<string name="findmyphone_title">Löydä puhelimeni</string>
<string name="findmyphone_description">Laittaa puhelimen soimaan, jotta voit löytää sen.</string>
<string name="findmyphone_title_tablet">Löydä tablettini</string>
<string name="findmyphone_description">Laittaa laitteen soimaan, jotta voit löytää sen.</string>
<string name="findmyphone_found">Löytyi</string>
<string name="open">Avaa</string>
<string name="close">Sulje</string>

View File

@@ -5,13 +5,10 @@
<string name="pref_plugin_battery">Rapport sur la batterie</string>
<string name="pref_plugin_battery_desc">Rapport périodique sur l\'état de la batterie</string>
<string name="pref_plugin_sftp">Exposer le système de fichiers</string>
<string name="pref_plugin_sftp_desc">Permettre de parcourir le système de fichiers du téléphone à distance</string>
<string name="pref_plugin_clipboard">Synchronisation avec le presse-papiers</string>
<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 comme une souris et un clavier</string>
<string name="pref_plugin_mpris">Contrôles multimédia</string>
<string name="pref_plugin_mpris_desc">Contrôle l\'audio / la vidéo depuis votre téléphone</string>
<string name="pref_plugin_ping">Commande « Ping »</string>
<string name="pref_plugin_ping_desc">Envoie et reçoit des commandes « Ping »</string>
<string name="pref_plugin_notifications">Synchronisation des notifications</string>
@@ -73,7 +70,6 @@
<string name="received_file_text">Appuyez pour ouvrir %1s</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>

View File

@@ -5,15 +5,11 @@
<string name="pref_plugin_battery">Informe da batería</string>
<string name="pref_plugin_battery_desc">Envíe periodicamente un informe sobre o estado da batería.</string>
<string name="pref_plugin_sftp">Revelador do sistema de ficheiros</string>
<string name="pref_plugin_sftp_desc">Permite examinar o sistema de ficheiros do teléfono remotamente.</string>
<string name="pref_plugin_clipboard">Sincronización do portapapeis</string>
<string name="pref_plugin_clipboard_desc">Comparta o contido do portapapeis.</string>
<string name="pref_plugin_mousepad">Entrada remota</string>
<string name="pref_plugin_mousepad_desc">Use o teléfono como rato e teclado.</string>
<string name="pref_plugin_mpris">Controis multimedia</string>
<string name="pref_plugin_mpris_desc">Controle o son e o vídeo desde o teléfono.</string>
<string name="pref_plugin_runcommand">Executar unha orde</string>
<string name="pref_plugin_runcommand_desc">Executa unha orde no sistema.</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envíe e reciba pings.</string>
<string name="pref_plugin_notifications">Sincronización de notificacións</string>
@@ -79,7 +75,6 @@
<string name="received_file_text">Toque para abrir «%1s».</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 foi posíbel enviar o ficheiro %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>
@@ -151,8 +146,6 @@
<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="plugin_not_supported">O dispositivo non é compatíbel con este complemento.</string>
<string name="findmyphone_title">Atopar o móbil</string>
<string name="findmyphone_description">Reproduce un son de chamada no móbil para que poida atopalo.</string>
<string name="findmyphone_found">Atopado</string>
<string name="open">Abrir</string>
<string name="close">Pechar</string>

157
res/values-he/strings.xml Normal file
View File

@@ -0,0 +1,157 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<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_clipboard">סנכנרון לוח העתקה</string>
<string name="pref_plugin_clipboard_desc">שתף בין המחשבים את מה שמועתק</string>
<string name="pref_plugin_mousepad">שליטה מרחוק</string>
<string name="pref_plugin_mpris">שליטה במדיה</string>
<string name="pref_plugin_runcommand">הרץ פקודה</string>
<string name="pref_plugin_ping">פינג</string>
<string name="pref_plugin_ping_desc">שלח וקבל פינגים</string>
<string name="pref_plugin_notifications">סנכרון התראות</string>
<string name="pref_plugin_notifications_desc">הראה את ההתראות מהפלאפון בהתקן אחר</string>
<string name="pref_plugin_sharereceiver">שתף וקבל קבצים וכתובות</string>
<string name="pref_plugin_sharereceiver_desc">שתף וקבל קבצים וכתובת אינטרנט בין התקנים</string>
<string name="plugin_not_available">אפשרות זו אינה זמינה בגרסת האנדרואיד שלך</string>
<string name="device_list_empty">אין התקנים</string>
<string name="ok">אישור</string>
<string name="cancel">בטל</string>
<string name="open_settings">פתח הגדרות</string>
<string name="no_permissions">אתה צריך לתת הרשאות לגישה להתראות</string>
<string name="send_ping">שלח פינג</string>
<string name="open_mpris_controls">שליטה על המדיה</string>
<string name="open_mousepad">שלוט על המחשב</string>
<string name="mousepad_info">הזז את האצבע על המסך כדי להזיז את סמן העכבר במחשב. לחץ כדי ללחוץ במחשב, השתמש בשנים או שלוש אצבעות כדי ללחוץ על המקש הימני או האמצעי. השתמש בליחצה ארוכה לגרירה ושחרור.</string>
<string name="mousepad_double_tap_settings_title">הגדר פעולה ללחיצת שתי אצבעות</string>
<string name="mousepad_triple_tap_settings_title">הגדר פעולה ללחיצת שלוש אצבעות</string>
<string name="mousepad_sensitivity_settings_title">הגדר רגישות משטח המגע</string>
<string name="mousepad_scroll_direction_title">הפוך את כיוון הגלילה</string>
<string-array name="mousepad_tap_entries">
<item>"לחיצה ימנית "</item>
<item>לחיצה אצמעית (גלגלת)</item>
<item>שום דבר</item>
</string-array>
<string name="mousepad_double_default">ימין</string>
<string name="mousepad_triple_default">אמצע</string>
<string name="mousepad_sensitivity_default">ברירת מחדל</string>
<string-array name="mousepad_sensitivity_entries">
<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="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>
<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_could_not_send_package">לא יכול לשלוח חבילה</string>
<string name="error_timed_out">נגמר הזמן</string>
<string name="error_canceled_by_user">בוטל ע\"י המשתמש</string>
<string name="error_canceled_by_other_peer">בוטל ע\"י מישהו אחר</string>
<string name="error_invalid_key">התקבל מפתח לא חוקי</string>
<string name="encryption_info_title">פרטי הצפנה</string>
<string name="encryption_info_msg_no_ssl">ההתקן השני אינו משתמש בגרסה האחרונה של KDE Connect, משתמש בשיטת ההצפנה הישנה.</string>
<string name="my_device_fingerprint">טביעת האצבע SHA1 של ההתקן היא:</string>
<string name="remote_device_fingerprint">"טביעת האצבע SHA1 של ההתקן המרוחק היא: "</string>
<string name="pair_requested">בקשת התאמה</string>
<string name="pairing_request_from">בוקשה התאמה מ־%1s</string>
<string name="received_url_title">התקבל קישור מ־%1s</string>
<string name="received_url_text">לחץ כדי לפתוח את \'%1s\'</string>
<string name="incoming_file_title">התקבל קובץ ־%1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">שולח קובץ ל־%1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">התקבל קובץ מ־%1s</string>
<string name="received_file_fail_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>
<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="remote_control">שלוט מרחוק</string>
<string name="settings">הגדרות KDE Connect</string>
<string name="mpris_play">נגן</string>
<string name="mpris_previous">הקודם</string>
<string name="mpris_rew">דילוג אחורה</string>
<string name="mpris_ff">דילוג קדימה</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 שניות</item>
<item>20 שניות</item>
<item>חצי דקה</item>
<item>דקה</item>
<item>שתי דקות</item>
</string-array>
<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="device_name_preference_summary">%s</string>
<string name="invalid_device_name">שם המכשיר לא תקין</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="share_notification_preference">התראות רועשות</string>
<string name="share_notification_preference_summary">רטוט ונגן צליל בעת קבלת קובץ</string>
<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="add_host_hint">כתבות שרת או IP</string>
<string name="no_players_connected">לא נמצא נגן</string>
<string name="custom_dev_list_help">השתמש באפשרות זו רק אם המכשיר שלך לא מזוהה באופן אוטומטי. הקלד את כתובת הIP או את כינוי ההתקן למטה ולחץ על הכפתור כדי להוסיף לרשימה. לחץ על פריט קיים כדי להסיר אותו מהרשימה.</string>
<string name="mpris_player_on_device">%1$s ב־%2$s</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="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_found">נמצא</string>
<string name="open">פתח</string>
<string name="close">סגור</string>
</resources>

View File

@@ -5,10 +5,8 @@
<string name="pref_plugin_battery">Akkumulátor jelentés</string>
<string name="pref_plugin_battery_desc">Akkumulátorállapot időszakos jelentése</string>
<string name="pref_plugin_sftp">Fájlrendszer kifejtés</string>
<string name="pref_plugin_sftp_desc">Lehetővé teszi a telefon fájlrendszerének távolról történő tallózását</string>
<string name="pref_plugin_clipboard">Vágólap szinkronizáció</string>
<string name="pref_plugin_clipboard_desc">A vágólap tartalmának megosztása</string>
<string name="pref_plugin_mpris_desc">Hang vagy videó vezérlése a telefonról</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Pingek küldése és fogadása</string>
<string name="pref_plugin_notifications">Értesítés szinkronizáció</string>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Livello batteria</string>
<string name="pref_plugin_battery_desc">Comunica periodicamente lo stato della batteria</string>
<string name="pref_plugin_sftp">Esposizione filesystem</string>
<string name="pref_plugin_sftp_desc">Consente di navigare da remoto il filesystem del telefono</string>
<string name="pref_plugin_sftp_desc">Consente di navigare da remoto il filesystem del dispositivo</string>
<string name="pref_plugin_clipboard">Sincronizzazione appunti</string>
<string name="pref_plugin_clipboard_desc">Condividi il contenuto degli appunti</string>
<string name="pref_plugin_mousepad">Impulso remoto</string>
<string name="pref_plugin_mousepad_desc">Usa il tuo telefono come mouse e tastiera</string>
<string name="pref_plugin_mousepad_desc">Usa il tuo telefono o il tablet come touchpad e tastiera</string>
<string name="pref_plugin_mpris">Controlli multimediali</string>
<string name="pref_plugin_mpris_desc">Controlla la riproduzione audio/video dal telefono</string>
<string name="pref_plugin_mpris_desc">Fornisce un controllo remoto per il tuo lettore multimediale</string>
<string name="pref_plugin_runcommand">Esegui comando</string>
<string name="pref_plugin_runcommand_desc">Esegue un comando sul tuo sistema</string>
<string name="pref_plugin_runcommand_desc">Esegui comandi remoti dal tuo telefono o tablet</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Invia e ricevi ping</string>
<string name="pref_plugin_notifications">Sincronizzazione notifiche</string>
<string name="pref_plugin_notifications_desc">Consenti l\'accesso alle notifiche dagli altri dispositivi</string>
<string name="pref_plugin_receive_notifications">Ricevi notifiche</string>
<string name="pref_plugin_receive_notifications_desc">Ricevi notifiche da altri dispositivi e le visualizza su Android</string>
<string name="pref_plugin_sharereceiver">Condividi e ricevi</string>
<string name="pref_plugin_sharereceiver_desc">Condividi file e URL tra i dispositivi</string>
<string name="plugin_not_available">Questa funzionalità non è disponibile sulla tua versione di Android</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Annullata dall\'utente</string>
<string name="error_canceled_by_other_peer">Annullata dal dispositivo remoto</string>
<string name="error_invalid_key">Ricevuta chiave non valida</string>
<string name="encryption_info_title">Informazioni di cifratura</string>
<string name="encryption_info_msg_no_ssl">L\'altro dispositivo non utilizza una versione recente di KDE Connect, utilizzando il metodo di cifratura precedente.</string>
<string name="my_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo è:</string>
<string name="remote_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo remoto è:</string>
<string name="pair_requested">Richiesta di associazione</string>
<string name="pairing_request_from">Richiesta associazione da %1s</string>
<string name="received_url_title">Collegamento ricevuto da %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">File in ingresso da %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Invio file a %1s</string>
<string name="outgoing_files_title">Invio file a %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Inviati %1$d di %2$d file</string>
<string name="received_file_title">File ricevuto da %1s</string>
<string name="received_file_fail_title">Ricezione file da %1s non riuscita</string>
<string name="received_file_text">Tocca per aprire «%1s»</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 %1s non riuscito</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>
@@ -126,6 +134,10 @@
<string name="custom_device_list">Aggiungi dispositivi per IP</string>
<string name="share_notification_preference">Notifiche rumorose</string>
<string name="share_notification_preference_summary">Vibra e riproduci un suono alla ricezione di un file</string>
<string name="share_destination_customize">Personalizza la cartella di destinazione</string>
<string name="share_destination_customize_summary_disabled">I file ricevuti saranno visualizzati in Downloads</string>
<string name="share_destination_customize_summary_enabled">I file saranno memorizzati nella cartella seguente</string>
<string name="share_destination_folder_preference">Cartella di destinazione</string>
<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>
@@ -152,7 +164,8 @@
<string name="pref_plugin_telepathy_desc">Invia messaggi di testo dal tuo desktop</string>
<string name="plugin_not_supported">Questa estensione non è supportata dal dispositivo</string>
<string name="findmyphone_title">Trova il mio telefono</string>
<string name="findmyphone_description">Fa squillare questo telefono per trovarlo.</string>
<string name="findmyphone_title_tablet">Trova il mio tablet</string>
<string name="findmyphone_description">Fa squillare questo dispositivo per trovarlo</string>
<string name="findmyphone_found">Trovato</string>
<string name="open">Apri</string>
<string name="close">Chiudi</string>

View File

@@ -5,15 +5,11 @@
<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_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_ping"></string>
<string name="pref_plugin_ping_desc">핑 보내고 받기</string>
<string name="pref_plugin_notifications">알림 동기화</string>
@@ -75,7 +71,6 @@
<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>
@@ -147,8 +142,6 @@
<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_description">소리를 울려서 휴대폰을 찾는 데 도움을 줍니다.</string>
<string name="findmyphone_found">찾았음</string>
<string name="open">열기</string>
<string name="close">닫기</string>

View File

@@ -101,8 +101,6 @@
<string name="unreachable_description">Šis suporuotas įrenginys nepasiekiamas. Patikrinkite, ar jis prisijungęs prie to paties tinklo.</string>
<string name="pref_plugin_telepathy">Siųsti SMS</string>
<string name="plugin_not_supported">Telefonas nepalaiko šio papildinio</string>
<string name="findmyphone_title">Rasti telefoną</string>
<string name="findmyphone_description">Telefonas skambės, tad galėsite jį rasti.</string>
<string name="findmyphone_found">Radau</string>
<string name="open">Atverti</string>
<string name="close">Užverti</string>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Batterijrapportage</string>
<string name="pref_plugin_battery_desc">Rapporteer periodiek de status van de batterij</string>
<string name="pref_plugin_sftp">Bestandssysteem blootstellen</string>
<string name="pref_plugin_sftp_desc">Het bladeren in het bestandssysteem op afstand in de telefoon toestaan</string>
<string name="pref_plugin_sftp_desc">Het bladeren in het bestandssysteem van het apparaat op afstand toestaan</string>
<string name="pref_plugin_clipboard">Klembordsynchronisatie</string>
<string name="pref_plugin_clipboard_desc">De inhoud van het klembord delen</string>
<string name="pref_plugin_mousepad">Invoer op afstand</string>
<string name="pref_plugin_mousepad_desc">Uw telefoon gebruiken als een muis en toetsenbord</string>
<string name="pref_plugin_mousepad_desc">Uw telefoon of tablet gebruiken als een touchpad en toetsenbord</string>
<string name="pref_plugin_mpris">Bediening van multimedia</string>
<string name="pref_plugin_mpris_desc">Bedien de audio/video vanaf uw telefoon</string>
<string name="pref_plugin_mpris_desc">Biedt een afstandsbediening voor uw mediaspeler</string>
<string name="pref_plugin_runcommand">Commando uitvoeren</string>
<string name="pref_plugin_runcommand_desc">Voert een commando uit op uw systeem</string>
<string name="pref_plugin_runcommand_desc">Commando\'s afvuren op afstand vanaf uw telefoon of tablet</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Pings verzenden en ontvangen</string>
<string name="pref_plugin_notifications">Synchronisatie van meldingen</string>
<string name="pref_plugin_notifications_desc">Bekijk uw meldingen van andere apparaten</string>
<string name="pref_plugin_receive_notifications">Meldingen ontvangen</string>
<string name="pref_plugin_receive_notifications_desc">Meldingen van het andere apparaat ontvangen en tonen op Android</string>
<string name="pref_plugin_sharereceiver">Delen en Ontvangen</string>
<string name="pref_plugin_sharereceiver_desc">Bestanden en URL\'s delen tussen apparaten</string>
<string name="plugin_not_available">Deze functie is niet beschikbaar in uw versie van Android</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Geannuleerd door gebruiker</string>
<string name="error_canceled_by_other_peer">Geannuleerd door andere kant</string>
<string name="error_invalid_key">Ongeldige sleutel ontvangen</string>
<string name="encryption_info_title">Versleutelde informatie</string>
<string name="encryption_info_msg_no_ssl">Het andere apparaat gebruikt geen recente versie van KDE Connect, de verouderde versleutelingsmethode zal worden gebruikt.</string>
<string name="my_device_fingerprint">De SHA1 vingerafdruk van het certificaat van uw apparaat is:</string>
<string name="remote_device_fingerprint">De SHA1 vingerafdruk van het certificaat van het apparaat op afstand is:</string>
<string name="pair_requested">Paar gevraagd</string>
<string name="pairing_request_from">Verzoek om een paar te maken van %1s</string>
<string name="received_url_title">Ontvangen koppeling van %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">Inkomend bestand van %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Bezig bestand te verzenden naar %1s</string>
<string name="outgoing_files_title">Bezig bestanden te verzenden naar %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Verzonden %1$d uit %2$d bestanden</string>
<string name="received_file_title">Bestand ontvangen van %1s</string>
<string name="received_file_fail_title">Bestand ontvangen van %1s is mislukt</string>
<string name="received_file_text">Tap om \'%1s\' te openen</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 %1s is mislukt</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>
@@ -126,6 +134,10 @@
<string name="custom_device_list">Voeg apparaten toe per IP-adres</string>
<string name="share_notification_preference">Luidruchtige meldingen</string>
<string name="share_notification_preference_summary">Vibreer en speel een geluidje bij ontvangen van een bestand</string>
<string name="share_destination_customize">De bestemmingsmap aanpassen</string>
<string name="share_destination_customize_summary_disabled">Ontvangen bestanden zullen in Downloads verschijnen</string>
<string name="share_destination_customize_summary_enabled">Bestanden zullen opgeslagen worden in de onderstaande map</string>
<string name="share_destination_folder_preference">Bestemmingsmap</string>
<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>
@@ -152,7 +164,8 @@
<string name="pref_plugin_telepathy_desc">Stuur tekstberichten van uw bureaublad</string>
<string name="plugin_not_supported">Deze plug-in wordt niet ondersteund door het apparaat</string>
<string name="findmyphone_title">Zoek mijn telefoon</string>
<string name="findmyphone_description">Laat deze telefoon bellen zodat u het kunt vinden.</string>
<string name="findmyphone_title_tablet">Zoek mijn tablet</string>
<string name="findmyphone_description">Laat dit apparaat bellen zodat u het kunt vinden</string>
<string name="findmyphone_found">Gevonden</string>
<string name="open">Openen</string>
<string name="close">Sluiten</string>

165
res/values-nn/strings.xml Normal file
View File

@@ -0,0 +1,165 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Telefonvarsling</string>
<string name="pref_plugin_telephony_desc">Send varsling ved SMS og oppringing</string>
<string name="pref_plugin_battery">Batterirapport</string>
<string name="pref_plugin_battery_desc">Meld regelmessig frå om batteristatusen</string>
<string name="pref_plugin_sftp">Vis filsystem</string>
<string name="pref_plugin_sftp_desc">Tillat lesing av filsystemet frå datamaskina</string>
<string name="pref_plugin_clipboard">Synkroniser utklippstavle</string>
<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 styrepute og tastatur</string>
<string name="pref_plugin_mpris">Mediekontrollar</string>
<string name="pref_plugin_mpris_desc">Gjev fjernkontroll til mediespelarar</string>
<string name="pref_plugin_runcommand">Køyr kommando</string>
<string name="pref_plugin_runcommand_desc">Utløys fjernkommandoar frå telefonen eller nettbrettet</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send og ta imot ping-signal</string>
<string name="pref_plugin_notifications">Varslingssynkronisering</string>
<string name="pref_plugin_notifications_desc">Få tilgang til varslingar frå andre einingar</string>
<string name="pref_plugin_receive_notifications">Få varslingar</string>
<string name="pref_plugin_receive_notifications_desc">Få 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>
<string name="device_list_empty">Ingen einingar</string>
<string name="ok">OK</string>
<string name="cancel">Avbryt</string>
<string name="open_settings">Opna innstillingar</string>
<string name="no_permissions">Du må gje tilgang til lesing av varslingar</string>
<string name="send_ping">Send pingsignal</string>
<string name="open_mpris_controls">Mediekontroll</string>
<string name="open_mousepad">Fjernstyring</string>
<string name="mousepad_info">Dra ein finger over skjermen for å flytta peikaren på datamaskina. Trykk for å klikka, og bruk to eller tre fingrar for høvesvis høgre- og midtknappen. Trykk og hald for å dra og sleppa.</string>
<string name="mousepad_double_tap_settings_title">Vel handling for tofingertrykk</string>
<string name="mousepad_triple_tap_settings_title">Vel handling for trefingertrykk</string>
<string name="mousepad_sensitivity_settings_title">Vel følsemd for styrepute</string>
<string name="mousepad_scroll_direction_title">Omvend rulleretning</string>
<string-array name="mousepad_tap_entries">
<item>Høgreklikk</item>
<item>Midtklikk</item>
<item>Ingenting</item>
</string-array>
<string name="mousepad_double_default">høgre</string>
<string name="mousepad_triple_default">midt</string>
<string name="mousepad_sensitivity_default">standard</string>
<string-array name="mousepad_sensitivity_entries">
<item>Saktast</item>
<item>Raskare enn saktast</item>
<item>Standard</item>
<item>Raskare enn standard</item>
<item>Raskast</item>
</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="plugins_failed_to_load">Klarte ikkje lasta programtillegg (trykk for meir informasjon):</string>
<string name="device_menu_plugins">Innstillingar for programtillegg</string>
<string name="device_menu_unpair">Løys paring</string>
<string name="device_not_reachable">Får ikkje kontakt med para eining</string>
<string name="pair_new_device">Par ny eining</string>
<string name="unknown_device">Ukjend eining</string>
<string name="error_not_reachable">Får ikkje kontakt med eininga</string>
<string name="error_already_requested">Paringsførespurnad er alt send</string>
<string name="error_already_paired">Eininga er alt para</string>
<string name="error_could_not_send_package">Klarte ikkje senda pakke</string>
<string name="error_timed_out">Tidsavbrot</string>
<string name="error_canceled_by_user">Avbroten av brukar</string>
<string name="error_canceled_by_other_peer">Avbroten av den andre eininga</string>
<string name="error_invalid_key">Fekk ugyldig nøkkel</string>
<string name="encryption_info_title">Krypteringsinfo</string>
<string name="encryption_info_msg_no_ssl">Den andre eininga brukar ein gammal versjon av KDE Connect, med ein utdatert krypteringsmetode.</string>
<string name="my_device_fingerprint">SHA-fingeravtrykk av einingssertifikatet er:</string>
<string name="remote_device_fingerprint">SHA-fingeravtrykk av fjerneiningssertifikatet er:</string>
<string name="pair_requested">Paringsførespurnad</string>
<string name="pairing_request_from">Paringsførespurnad frå %1s</string>
<string name="received_url_title">Fekk lenkje frå %1s</string>
<string name="received_url_text">Trykk for å opna «%1s»</string>
<string name="incoming_file_title">Får fil frå %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Sender fil til %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">Fekk fil frå %1s</string>
<string name="received_file_fail_title">Klarte ikkje ta imot fil frå %1s</string>
<string name="received_file_text">Trykk for å opna «%1s»</string>
<string name="sent_file_title">Send fil til %1s</string>
<string name="sent_file_text">%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>
<string name="middle_click">Send midtklikk</string>
<string name="show_keyboard">Vis tastatur</string>
<string name="device_not_paired">Eininga er ikkje para</string>
<string name="request_pairing">Be om paring</string>
<string name="pairing_accept">Godta</string>
<string name="pairing_reject">Avvis</string>
<string name="device">Eining</string>
<string name="pair_device">Par ny eining</string>
<string name="remote_control">Fjernstyring</string>
<string name="settings">Innstillingar for KDE Connect</string>
<string name="mpris_play">Spel</string>
<string name="mpris_previous">Førre</string>
<string name="mpris_rew">Spol tilbake</string>
<string name="mpris_ff">Spol fram</string>
<string name="mpris_next">Neste</string>
<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-array name="mpris_time_entries">
<item>10 sekund</item>
<item>20 sekund</item>
<item>30 sekund</item>
<item>1 minutt</item>
<item>2 minutt</item>
</string-array>
<string name="share_to">Del til </string>
<string name="protocol_version_older">Denne eininga brukar ein gammal protokollversjon</string>
<string name="protocol_version_newer">Denne eininga brukar ein nyare protokollversjon</string>
<string name="general_settings">Generelle innstillingar</string>
<string name="plugin_settings">Innstillingar</string>
<string name="plugin_settings_with_name">%s-innstillingar</string>
<string name="device_name">Einingsnamn</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Ugyldig einingsnamn</string>
<string name="shareplugin_text_saved">Fekk tekst, som vart lagra på utklippstavla</string>
<string name="custom_devices_settings">Tilpassa einingsliste</string>
<string name="pair_device_action">Par ny eining</string>
<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="share_notification_preference">Lydvarsling</string>
<string name="share_notification_preference_summary">Vibrer og spel ein lyd ved mottak av fil</string>
<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_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="custom_dev_list_help">Bruk berre dette valet viss eininga ikkje vert kjend att automatisk. Skriv inn IP-adressa eller vertsnamnet nedanfor, og trykk på knappen for å leggja eininga til oversikta. Trykk på element i oversikta for å fjerna dei.</string>
<string name="mpris_player_on_device">%1$s på %2$s</string>
<string name="send_files">Send filer</string>
<string name="pairing_title">KDE Connect-einingar</string>
<string name="pairing_description">Andre einingar i nettverket som køyrer KDE Connect vil dukka opp her.</string>
<string name="device_paired">Eininga er para</string>
<string name="device_rename_title">Endra namn på eining</string>
<string name="device_rename_confirm">Endra namn</string>
<string name="refresh">Oppdater</string>
<string name="unreachable_description">Får ikkje kontakt med den para eininga. Sjå til at ho er kopla til same nettverk.</string>
<string name="no_file_browser">Ingen filhandsamarar er installerte.</string>
<string name="pref_plugin_telepathy">Send SMS</string>
<string name="pref_plugin_telepathy_desc">Send tekstmeldingar frå datamaskina</string>
<string name="plugin_not_supported">Dette tillegget er ikkje støtta av eininga</string>
<string name="findmyphone_title">Finn telefonen min</string>
<string name="findmyphone_title_tablet">Finn nettbrettet mitt</string>
<string name="findmyphone_description">Ring til eininga, slik at du kan finna ho</string>
<string name="findmyphone_found">Fann</string>
<string name="open">Opna</string>
<string name="close">Lukk</string>
</resources>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Zgłaszanie baterii</string>
<string name="pref_plugin_battery_desc">Okresowo zgłaszaj stan baterii</string>
<string name="pref_plugin_sftp">Eksponowanie systemu plików</string>
<string name="pref_plugin_sftp_desc">Zezwala na zdalne przeglądanie systemu plików telefonu</string>
<string name="pref_plugin_sftp_desc">Zezwala na zdalne przeglądanie systemu plików tego urządzenia</string>
<string name="pref_plugin_clipboard">Synchronizacja schowka</string>
<string name="pref_plugin_clipboard_desc">Udostępnij zawartość schowka</string>
<string name="pref_plugin_mousepad">Zdalne sterowanie</string>
<string name="pref_plugin_mousepad_desc">Użyj swojego telefonu jako myszy i klawiatury</string>
<string name="pref_plugin_mousepad_desc">Użyj swojego telefonu lub tabletu jako myszy i klawiatury</string>
<string name="pref_plugin_mpris">Sterowanie multimediami</string>
<string name="pref_plugin_mpris_desc">Steruj dźwiękiem/obrazem ze swojego telefonu</string>
<string name="pref_plugin_mpris_desc">Zapewnia zdalne sterowanie twoim odtwarzaczem multimedialnym</string>
<string name="pref_plugin_runcommand">Wykonaj polecenie</string>
<string name="pref_plugin_runcommand_desc">Wykonuje polecenie na twoim systmie</string>
<string name="pref_plugin_runcommand_desc">Wyzwalaj zdalne polecenia z twojego telefonu lub tabletu</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Wysyłaj i otrzymuj pingi</string>
<string name="pref_plugin_notifications">Powiadomienia synchronizacji</string>
<string name="pref_plugin_notifications_desc">Uzyskaj dostęp do swoich powiadomień z innego urządzenia</string>
<string name="pref_plugin_receive_notifications">Otrzymuj powiadomienia</string>
<string name="pref_plugin_receive_notifications_desc">Otrzymuj powiadomienia z urządzeń i wyświetlaj je na Androidzie</string>
<string name="pref_plugin_sharereceiver">Udostępniaj i otrzymuj</string>
<string name="pref_plugin_sharereceiver_desc">Współdziel pliki i adresy URL pomiędzy urządzeniami</string>
<string name="plugin_not_available">Funkcja ta nie jest dostępna w twojej wersji Androida</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Anulowane przez użytkownika</string>
<string name="error_canceled_by_other_peer">Anulowane przez innego partnera</string>
<string name="error_invalid_key">Otrzymano nieprawidłowy klucz</string>
<string name="encryption_info_title">Zaszyfrowane informacje</string>
<string name="encryption_info_msg_no_ssl">Drugie urządzenie nie używa ostatniej wersji KDE Connect, użyto przestarzałego szyfrowania.</string>
<string name="my_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego urządzenia to:</string>
<string name="remote_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego zdalnego urządzenia to:</string>
<string name="pair_requested">Zażądano parowania</string>
<string name="pairing_request_from">Żądanie parowania z %1s</string>
<string name="received_url_title">Odebrano odsyłacz od %1s</string>
@@ -79,7 +85,6 @@
<string name="received_file_text">Znacznik do otwarcia \'%1s\'</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">Nieudane wysyłanie pliku %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>
@@ -152,7 +157,8 @@
<string name="pref_plugin_telepathy_desc">Wyślij wiadomość tekstową z komputera</string>
<string name="plugin_not_supported">Ta wtyczka nie jest obsługiwana przez to urządzenie</string>
<string name="findmyphone_title">Znajdź mój telefon</string>
<string name="findmyphone_description">Dzwoni na ten telefon, tak abyś mógł go znaleźć.</string>
<string name="findmyphone_title_tablet">Znajdź mój tablet</string>
<string name="findmyphone_description">Dzwoni na dane urządzenie, tak abyś mógł je znaleźć.</string>
<string name="findmyphone_found">Znaleziony</string>
<string name="open">Otwórz</string>
<string name="close">Zamknij</string>

View File

@@ -5,19 +5,19 @@
<string name="pref_plugin_battery">Relatório da bateria</string>
<string name="pref_plugin_battery_desc">Informação periódica do status da bateria</string>
<string name="pref_plugin_sftp">Exposição do sistema de arquivos</string>
<string name="pref_plugin_sftp_desc">Navegação remota pelo sistema de arquivos do telefone</string>
<string name="pref_plugin_sftp_desc">Permite navegar no sistema de arquivos deste telefone remotamente</string>
<string name="pref_plugin_clipboard">Sincronização da área de transferência</string>
<string name="pref_plugin_clipboard_desc">Compartilha o conteúdo da área de transferência</string>
<string name="pref_plugin_mousepad">Introdução de dados remota</string>
<string name="pref_plugin_mousepad_desc">Use seu telefone como mouse ou teclado</string>
<string name="pref_plugin_mousepad_desc">Use seu telefone ou tablet como mouse e teclado</string>
<string name="pref_plugin_mpris">Controle multimídia</string>
<string name="pref_plugin_mpris_desc">Controla áudio e vídeo a partir do seu telefone</string>
<string name="pref_plugin_mpris_desc">Fornece um controle remoto de seu reprodutor de mídia</string>
<string name="pref_plugin_runcommand">Executar comando</string>
<string name="pref_plugin_runcommand_desc">Executa um comando no seu sistema</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envia e recebe pings</string>
<string name="pref_plugin_notifications">Sincronização de notificações</string>
<string name="pref_plugin_notifications_desc">Acesse suas notificações a partir de outros dispositivos</string>
<string name="pref_plugin_receive_notifications">Receber notificações</string>
<string name="pref_plugin_sharereceiver">Compartilhar e receber</string>
<string name="pref_plugin_sharereceiver_desc">Compartilha arquivos e URLs entre os dispositivos</string>
<string name="plugin_not_available">Esta funcionalidade não está disponível na sua versão do Android</string>
@@ -66,6 +66,7 @@
<string name="error_canceled_by_user">Cancelado pelo usuário</string>
<string name="error_canceled_by_other_peer">Cancelado pelo outro dispositivo</string>
<string name="error_invalid_key">Chave inválida recebida</string>
<string name="encryption_info_title">Informação da criptografia</string>
<string name="pair_requested">Solicitação de emparelhamento</string>
<string name="pairing_request_from">Emparelhamento solicitado por %1s</string>
<string name="received_url_title">Link recebido de %1s</string>
@@ -79,7 +80,6 @@
<string name="received_file_text">Toque para abrir o \'%1s\'</string>
<string name="sent_file_title">Enviar arquivo para %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Falha ao enviar o arquivo %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Toque para responder</string>
<string name="reconnect">Reconectar</string>
@@ -152,7 +152,7 @@
<string name="pref_plugin_telepathy_desc">Enviar mensagens de texto do seu Desktop</string>
<string name="plugin_not_supported">Este plugin não é suportado pelo dispositivo</string>
<string name="findmyphone_title">Encontrar meu telefone</string>
<string name="findmyphone_description">Faça este telefone tocar para encontrá-lo.</string>
<string name="findmyphone_title_tablet">Encontrar meu tablet</string>
<string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string>
<string name="close">Fechar</string>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Relatório da bateria</string>
<string name="pref_plugin_battery_desc">Comunicar periodicamente o estado da bateria</string>
<string name="pref_plugin_sftp">Exposição do sistema de ficheiros</string>
<string name="pref_plugin_sftp_desc">Permite navegar pelo sistema de ficheiros remoto do telefone</string>
<string name="pref_plugin_sftp_desc">Permite navegar pelo sistema de ficheiros remoto deste dispositivo</string>
<string name="pref_plugin_clipboard">Sincronização da área de transferência</string>
<string name="pref_plugin_clipboard_desc">Partilhar o conteúdo da área de transferência</string>
<string name="pref_plugin_mousepad">Introdução remota de dados</string>
<string name="pref_plugin_mousepad_desc">Usar o seu telefone como um rato ou teclado</string>
<string name="pref_plugin_mousepad_desc">Usar o seu telefone ou \'tablet\' como um rato ou teclado</string>
<string name="pref_plugin_mpris">Comandos multimédia</string>
<string name="pref_plugin_mpris_desc">Controlar o áudio/vídeo a partir do seu telefone</string>
<string name="pref_plugin_mpris_desc">Oferece um comando à distância para o seu leitor multimédia</string>
<string name="pref_plugin_runcommand">Executar um Comando</string>
<string name="pref_plugin_runcommand_desc">Executa um comando no seu sistema</string>
<string name="pref_plugin_runcommand_desc">Despoletar comandos remotos a partir do seu telefone ou \'tablet\'</string>
<string name="pref_plugin_ping">Contacto</string>
<string name="pref_plugin_ping_desc">Enviar e receber pedidos de contacto (\'ping\')</string>
<string name="pref_plugin_notifications">Sincronização da notificação</string>
<string name="pref_plugin_notifications_desc">Aceder às suas notificações a partir de outros dispositivos</string>
<string name="pref_plugin_receive_notifications">Receber as notificações</string>
<string name="pref_plugin_receive_notifications_desc">Receber as notificações de outros dispositivos e mostrá-las no Android</string>
<string name="pref_plugin_sharereceiver">Partilhar e receber</string>
<string name="pref_plugin_sharereceiver_desc">Partilhar ficheiros e URL\'s entre dispositivos</string>
<string name="plugin_not_available">Esta funcionalidade não está disponível na sua versão do Android</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Cancelado pelo utilizador</string>
<string name="error_canceled_by_other_peer">Cancelado pela outra máquina</string>
<string name="error_invalid_key">Chave inválida recebida</string>
<string name="encryption_info_title">Dados de Encriptação</string>
<string name="encryption_info_msg_no_ssl">O outro dispositivo não usa uma versão recente do KDE Connect; será usado o método antigo de encriptação.</string>
<string name="my_device_fingerprint">A impressão digital SHA1 do certificado do seu dispositivo é:</string>
<string name="remote_device_fingerprint">A impressão digital SHA1 do certificado do dispositivo remoto é:</string>
<string name="pair_requested">Emparelhamento pedido</string>
<string name="pairing_request_from">Pedido de emparelhamento de %1s</string>
<string name="received_url_title">Ligação recebida de %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">Ficheiro recebido de %1s</string>
<string name="incoming_file_text">%1s</string>
<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>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Foram enviados %1$d de %2$d ficheiros</string>
<string name="received_file_title">Ficheiro recebido de %1s</string>
<string name="received_file_fail_title">Falhou a recepção do ficheiro de %1s</string>
<string name="received_file_text">Toque para abrir o \'%1s\'</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 %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>
@@ -126,6 +134,10 @@
<string name="custom_device_list">Adicionar dispositivos pelo IP</string>
<string name="share_notification_preference">Notificações com ruído</string>
<string name="share_notification_preference_summary">Vibrar e tocar um som ao receber um ficheiro</string>
<string name="share_destination_customize">Personalizar a pasta de destino</string>
<string name="share_destination_customize_summary_disabled">Os ficheiros recebidos irão aparecer em \'Transferências\'</string>
<string name="share_destination_customize_summary_enabled">Os ficheiros serão guardados na pasta abaixo</string>
<string name="share_destination_folder_preference">Pasta de destino</string>
<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>
@@ -151,8 +163,9 @@
<string name="pref_plugin_telepathy">Enviar um SMS</string>
<string name="pref_plugin_telepathy_desc">Enviar mensagens de texto a partir do seu ambiente de trabalho</string>
<string name="plugin_not_supported">Este \'plugin\' não é suportado pelo dispositivo</string>
<string name="findmyphone_title">Descobrir o Meu Telefone</string>
<string name="findmyphone_description">Toca este telefone para que o possa encontrar.</string>
<string name="findmyphone_title">Descobrir o meu telefone</string>
<string name="findmyphone_title_tablet">Descobrir o meu \'tablet\'</string>
<string name="findmyphone_description">Toca este dispositivo para que o possa encontrar</string>
<string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string>
<string name="close">Fechar</string>

View File

@@ -5,10 +5,8 @@
<string name="pref_plugin_battery">Raport acumulator</string>
<string name="pref_plugin_battery_desc">Raportează periodic starea acumulatorului</string>
<string name="pref_plugin_sftp">Expunere sistem de fișiere</string>
<string name="pref_plugin_sftp_desc">Vă permite să răsfoiți de la distanță sistemul de fișiere al dispozitivului</string>
<string name="pref_plugin_clipboard">Sincronizare clipboard</string>
<string name="pref_plugin_clipboard_desc">Partajează conținutul clipboardului</string>
<string name="pref_plugin_mpris_desc">Controlați audio/video de pe telefon</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Trimite și primește ping-uri</string>
<string name="pref_plugin_notifications">Sincronizare notificări</string>

View File

@@ -5,15 +5,11 @@
<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_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_ping">Пинг</string>
<string name="pref_plugin_ping_desc">Отправка и получение тестовых сигналов</string>
<string name="pref_plugin_notifications">Синхронизация уведомлений</string>
@@ -75,7 +71,6 @@
<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>
@@ -147,8 +142,6 @@
<string name="pref_plugin_telepathy">Отправка SMS</string>
<string name="pref_plugin_telepathy_desc">Отправка SMS-сообщений с вашего компьютера</string>
<string name="plugin_not_supported">Этот модуль не поддерживается устройством</string>
<string name="findmyphone_title">Поиск телефона</string>
<string name="findmyphone_description">Подача звукового сигнала на телефоне, чтобы вы могли его найти.</string>
<string name="findmyphone_found">Найден</string>
<string name="open">Открыть</string>
<string name="close">Закрыть</string>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Oznam o batérii</string>
<string name="pref_plugin_battery_desc">Periodicky oznamovať stav batérie</string>
<string name="pref_plugin_sftp">Odhaliť súborový systém</string>
<string name="pref_plugin_sftp_desc">Umožní prístup na súborový systém telefónu vzdialene</string>
<string name="pref_plugin_sftp_desc">Umožní prehliadač súborový systém zariadenia vzdialene</string>
<string name="pref_plugin_clipboard">Synchronizácia schránky</string>
<string name="pref_plugin_clipboard_desc">Zdieľať obsah schránky</string>
<string name="pref_plugin_mousepad">Vzdialený vstup</string>
<string name="pref_plugin_mousepad_desc">Použiť váš telefón ako myš a klávesnicu</string>
<string name="pref_plugin_mousepad_desc">Použiť váš telefón alebo tablet ako touchpad a klávesnicu</string>
<string name="pref_plugin_mpris">Multimediálne ovládače</string>
<string name="pref_plugin_mpris_desc">Ovládať audio/video z vášho telefónu</string>
<string name="pref_plugin_mpris_desc">Poskytuje vzdialené ovládanie pre váš prehrávač médií</string>
<string name="pref_plugin_runcommand">Spustiť príkaz</string>
<string name="pref_plugin_runcommand_desc">Spustí príkazy na vašom systéme</string>
<string name="pref_plugin_runcommand_desc">Vyvolať vzdialené príkazy z vášho mobilu alebo tabletu</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Poslať a prijať pingy</string>
<string name="pref_plugin_notifications">Synchronizácia pripomienok</string>
<string name="pref_plugin_notifications_desc">Prístup k vašim pripomienkam z iných zariadení</string>
<string name="pref_plugin_receive_notifications">Prijať upozornenia</string>
<string name="pref_plugin_receive_notifications_desc">Prístup k pripomienkam z iných zariadení a zobrazenie ich na Androide</string>
<string name="pref_plugin_sharereceiver">Zdieľať a prijať</string>
<string name="pref_plugin_sharereceiver_desc">Zdieľať súbory a medzi zariadeniami</string>
<string name="plugin_not_available">Táto funkcia nie je dostupná vo vašej verzii Androidu</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Zrušené používateľom</string>
<string name="error_canceled_by_other_peer">Zrušené iným klientom</string>
<string name="error_invalid_key">Získaný nesprávny kľúč</string>
<string name="encryption_info_title">Informácia o šifrovaní</string>
<string name="encryption_info_msg_no_ssl">Ďalšie zariadenie nepoužíva aktuálnu verziu KDE Connect, používam klasickú metódu šifrovania.</string>
<string name="my_device_fingerprint">Odtlačok SHA1 vášho certifikátu zariadenia je:</string>
<string name="remote_device_fingerprint">Odtlačok SHA1 vzdialeného certifikátu zariadenia je:</string>
<string name="pair_requested">Spárovanie vyžiadané</string>
<string name="pairing_request_from">Požiadavka na spárovanie od %1s</string>
<string name="received_url_title">Prijatý odkaz od %1s</string>
@@ -79,7 +85,6 @@
<string name="received_file_text">Ťuknite na otvorenie \'%1s\'</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 %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>
@@ -152,7 +157,8 @@
<string name="pref_plugin_telepathy_desc">Posielať textové správy z vášho počítača</string>
<string name="plugin_not_supported">Tento plugin nie je podporovaný zariadením</string>
<string name="findmyphone_title">Nájsť môj telefón</string>
<string name="findmyphone_description">Prezvoní vaše zariadenie, aby ste ho našli.</string>
<string name="findmyphone_title_tablet">Nájsť môj tablet</string>
<string name="findmyphone_description">Prezvoní vaše zariadenie, aby ste ho našli</string>
<string name="findmyphone_found">Nájdené</string>
<string name="open">Otvoriť</string>
<string name="close">Zavrieť</string>

View File

@@ -5,19 +5,21 @@
<string name="pref_plugin_battery">Batterirapport</string>
<string name="pref_plugin_battery_desc">Rapportera periodiskt batteriets status</string>
<string name="pref_plugin_sftp">Exponera filsystem</string>
<string name="pref_plugin_sftp_desc">Gör det möjligt att bläddra i telefonens filsystem från annan apparat</string>
<string name="pref_plugin_sftp_desc">Gör det möjligt att bläddra i apparatens filsystem från annan apparat</string>
<string name="pref_plugin_clipboard">Synkronisera klippbord</string>
<string name="pref_plugin_clipboard_desc">Dela klippbordets innehåll</string>
<string name="pref_plugin_mousepad">Fjärrinmatning</string>
<string name="pref_plugin_mousepad_desc">Använd telefonen som mus och tangentbord</string>
<string name="pref_plugin_mousepad_desc">Använd telefonen eller surfplattan som mus och tangentbord</string>
<string name="pref_plugin_mpris">Multimediakontroller</string>
<string name="pref_plugin_mpris_desc">Styr ljud och video från telefonen</string>
<string name="pref_plugin_mpris_desc">Tillhandahåller en fjärrkontroll för mediaspelaren</string>
<string name="pref_plugin_runcommand">Kör kommando</string>
<string name="pref_plugin_runcommand_desc">Kör ett kommando på systemet</string>
<string name="pref_plugin_runcommand_desc">Utlös fjärrkommandon från din telefon eller surfplatta</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Skicka och ta emot ping</string>
<string name="pref_plugin_notifications">Synkronisering av underrättelser</string>
<string name="pref_plugin_notifications_desc">Kom åt underrättelser från andra apparater</string>
<string name="pref_plugin_receive_notifications">Ta emot underrättelser</string>
<string name="pref_plugin_receive_notifications_desc">Ta emot underrättelser från andra apparater och visa dem på Android</string>
<string name="pref_plugin_sharereceiver">Dela och ta emot</string>
<string name="pref_plugin_sharereceiver_desc">Dela filer och webbadresser mellan apparater</string>
<string name="plugin_not_available">Funktionen är inte tillgänglig i Android-versionen</string>
@@ -66,6 +68,10 @@
<string name="error_canceled_by_user">Avbruten av användaren</string>
<string name="error_canceled_by_other_peer">Avbruten av motparten</string>
<string name="error_invalid_key">Ogiltig nyckel mottagen</string>
<string name="encryption_info_title">Krypteringsinformation</string>
<string name="encryption_info_msg_no_ssl">Den andra apparaten använder inte en aktuell version av KDE-anslut. Använder den föråldrade krypteringsmetoden.</string>
<string name="my_device_fingerprint">SHA1-fingeravtryck för din apparats certifikat är:</string>
<string name="remote_device_fingerprint">SHA1-fingeravtryck för den andra apparatens certifikat är:</string>
<string name="pair_requested">Ihopparning begärd</string>
<string name="pairing_request_from">Begäran om ihopparning från %1s</string>
<string name="received_url_title">Tog emot länk från %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">Inkommande fil från %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Skickar fil till %1s</string>
<string name="outgoing_files_title">Skickar filer till %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Skickade %1$d av %2$d filer</string>
<string name="received_file_title">Tog emot fil från %1s</string>
<string name="received_file_fail_title">Misslyckades ta emot fil från %1s</string>
<string name="received_file_text">Rör för att öppna \'%1s\'</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 %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>
@@ -126,6 +134,10 @@
<string name="custom_device_list">Lägg till apparater enligt IP-adress</string>
<string name="share_notification_preference">Ljudliga underrättelser</string>
<string name="share_notification_preference_summary">Vibrera och spela ett ljud när en fil tas emot</string>
<string name="share_destination_customize">Anpassa målkatalog</string>
<string name="share_destination_customize_summary_disabled">Mottagna filer hamnar i Nerladdningar</string>
<string name="share_destination_customize_summary_enabled">Filer lagras i katalogen nedan</string>
<string name="share_destination_folder_preference">Målkatalog</string>
<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>
@@ -152,7 +164,8 @@
<string name="pref_plugin_telepathy_desc">Skicka textmeddelanden från skrivbordet</string>
<string name="plugin_not_supported">Insticksprogrammet stöds inte av apparaten</string>
<string name="findmyphone_title">Hitta min telefon</string>
<string name="findmyphone_description">Ringer till telefonen så att du kan hitta den.</string>
<string name="findmyphone_title_tablet">Hitta min surfplatta</string>
<string name="findmyphone_description">Ringer till apparaten så att du kan hitta den</string>
<string name="findmyphone_found">Hittade den</string>
<string name="open">Öppna</string>
<string name="close">Stäng</string>

View File

@@ -5,19 +5,21 @@
<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_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_mousepad_desc">Скористайтеся телефоном або планшетом як замінником сенсорної панелі і клавіатури</string>
<string name="pref_plugin_mpris">Керування відтворенням</string>
<string name="pref_plugin_mpris_desc">Керування відтворенням звуку та відео з вашого телефону</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_runcommand_desc">Віддалені команди з вашого телефону або планшета</string>
<string name="pref_plugin_ping">Підтримання зв’язку</string>
<string name="pref_plugin_ping_desc">Надсилання і отримання сигналів підтримання зв’язку</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">Спільне використання файлів і адрес між пристроями</string>
<string name="plugin_not_available">Ця можливість є недоступною для вашої версії для Android</string>
@@ -66,6 +68,10 @@
<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="my_device_fingerprint">Відбиток SHA1 сертифіката пристрою:</string>
<string name="remote_device_fingerprint">Відбиток SHA1 сертифіката віддаленого пристрою:</string>
<string name="pair_requested">Запит щодо пов’язування</string>
<string name="pairing_request_from">Запит щодо пов’язування від %1s</string>
<string name="received_url_title">Отримано посилання від %1s</string>
@@ -73,13 +79,15 @@
<string name="incoming_file_title">Вхідний файл з %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Надсилаємо файл до %1s</string>
<string name="outgoing_files_title">Надсилаємо файли на %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Надіслано %1$d з %2$d файлів</string>
<string name="received_file_title">Отримано файл з %1s</string>
<string name="received_file_fail_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_title">Не вдалося надіслати файл на %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Натисніть, щоб відповісти</string>
<string name="reconnect">З\'єднати знову</string>
@@ -126,6 +134,10 @@
<string name="custom_device_list">Додати пристрої за IP</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">Отримані файли зберігатимуться до каталогу «Завантаження»</string>
<string name="share_destination_customize_summary_enabled">Файли зберігатимуться у вказаному нижче каталозі</string>
<string name="share_destination_folder_preference">Каталог призначення</string>
<string name="title_activity_notification_filter">Фільтр сповіщень</string>
<string name="filter_apps_info">Сповіщення буде синхронізовано для позначених програм.</string>
<string name="sftp_internal_storage">Вбудоване сховище даних</string>
@@ -152,7 +164,8 @@
<string name="pref_plugin_telepathy_desc">Надсилати текстові повідомлення з вашої робочої станції</string>
<string name="plugin_not_supported">Підтримки цього додатка не передбачено на пристрої</string>
<string name="findmyphone_title">Знайти телефон</string>
<string name="findmyphone_description">Відтворити дзвінок, щоб телефон було простіше знайти.</string>
<string name="findmyphone_title_tablet">Знайти планшет</string>
<string name="findmyphone_description">Відтворити дзвінок, щоб цей пристрій було простіше знайти</string>
<string name="findmyphone_found">Знайдено</string>
<string name="open">Відкрити</string>
<string name="close">Закрити</string>

View File

@@ -5,15 +5,11 @@
<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_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_ping">Ping</string>
<string name="pref_plugin_ping_desc">发送和接受ping</string>
<string name="pref_plugin_notifications">通知同步</string>
@@ -76,7 +72,6 @@
<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>
@@ -148,8 +143,6 @@
<string name="pref_plugin_telepathy">发送短消息</string>
<string name="pref_plugin_telepathy_desc">从桌面发送短消息</string>
<string name="plugin_not_supported">设备不支持此插件</string>
<string name="findmyphone_title">找到我的手机</string>
<string name="findmyphone_description">让手机响铃从而找到它</string>
<string name="findmyphone_found">找到</string>
<string name="open">打开</string>
<string name="close">关闭</string>

View File

@@ -0,0 +1,165 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<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_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_ping">Ping回應封包</string>
<string name="pref_plugin_ping_desc">傳送與接收Ping回應封包</string>
<string name="pref_plugin_notifications">同步通知</string>
<string name="pref_plugin_notifications_desc">存取其他設備上的通知</string>
<string name="pref_plugin_receive_notifications">接收通知</string>
<string name="pref_plugin_receive_notifications_desc">在Android上顯示從其他裝置收到的通知</string>
<string name="pref_plugin_sharereceiver">分享與接收</string>
<string name="pref_plugin_sharereceiver_desc">在兩個設備當中互相分享URL網址與檔案</string>
<string name="plugin_not_available">這個功能無法在您的Android版本上執行。</string>
<string name="device_list_empty">沒有裝置</string>
<string name="ok">OK</string>
<string name="cancel">取消</string>
<string name="open_settings">開啟設定</string>
<string name="no_permissions">您需要授予存取通知的權限</string>
<string name="send_ping">傳送Ping回應封包</string>
<string name="open_mpris_controls">多媒體控制</string>
<string name="open_mousepad">遠端輸入</string>
<string name="mousepad_info">在您的智慧型手機的螢幕上移動手指頭,用來控制電腦螢幕的鼠標。點擊表示滑鼠的左鍵,使用兩隻/三隻手指頭點擊來表示滑鼠的右鍵/中鍵。長按則表示要拖拉。</string>
<string name="mousepad_double_tap_settings_title">設定兩隻手指頭點擊的動作</string>
<string name="mousepad_triple_tap_settings_title">設定三隻手指頭點擊的動作</string>
<string name="mousepad_sensitivity_settings_title">設定觸碰板的靈敏度</string>
<string name="mousepad_scroll_direction_title">滾動方向相反</string>
<string-array name="mousepad_tap_entries">
<item>右鍵點擊</item>
<item>中鍵點擊</item>
<item></item>
</string-array>
<string name="mousepad_double_default"></string>
<string name="mousepad_triple_default"></string>
<string name="mousepad_sensitivity_default">預設</string>
<string-array name="mousepad_sensitivity_entries">
<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="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>
<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_could_not_send_package">無法傳送封包</string>
<string name="error_timed_out">逾時</string>
<string name="error_canceled_by_user">使用者中斷</string>
<string name="error_canceled_by_other_peer">被其他同等功能應用中斷</string>
<string name="error_invalid_key">接收的密鑰無效</string>
<string name="encryption_info_title">加密資訊</string>
<string name="encryption_info_msg_no_ssl">其他的設備沒有使用新版本的KDE連線使用傳統的加密模式。</string>
<string name="my_device_fingerprint">您設備上的SHA1指紋辨識認證是</string>
<string name="remote_device_fingerprint">您遠端設備上的SHA1指紋辨識認證是</string>
<string name="pair_requested">已請求配對</string>
<string name="pairing_request_from">從 %1s 來的配對請求</string>
<string name="received_url_title">已從 %1s 連線接收</string>
<string name="received_url_text">點擊開啟 \'%1s\'</string>
<string name="incoming_file_title">從 %1s 傳來的檔案</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">正在將檔案發送到 %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">已從 %1s 接收檔案</string>
<string name="received_file_fail_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>
<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="remote_control">遠端控制</string>
<string name="settings">KDE連線設定</string>
<string name="mpris_play">播放</string>
<string name="mpris_previous">往前</string>
<string name="mpris_rew">往後</string>
<string name="mpris_ff">快轉</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秒鐘</item>
<item>20秒鐘</item>
<item>30秒鐘</item>
<item>1分鐘</item>
<item>2分鐘</item>
</string-array>
<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="device_name_preference_summary">%s</string>
<string name="invalid_device_name">無效的設備名稱</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="share_notification_preference">通知方式</string>
<string name="share_notification_preference_summary">當接收檔案時發出振動以及播放聲音</string>
<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_host">增加 host/IP</string>
<string name="add_host_hint">Hostname或者IP</string>
<string name="no_players_connected">沒有發現播放器</string>
<string name="custom_dev_list_help">能夠在您的裝置沒有自動偵測到裝置時使用這個選項。輸入IP位址或者是Hostname在下面並且按下按鈕來增加進入列表當中。觸碰以從列表中刪除現有項目。</string>
<string name="mpris_player_on_device">%1$s on %2$s</string>
<string name="send_files">傳送檔案</string>
<string name="pairing_title">KDE連線裝置</string>
<string name="pairing_description">在您相同網域當中有其他有執行KDE連線的裝置會出現在這裡。</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="no_file_browser">沒有安裝此檔案的瀏覽程式</string>
<string name="pref_plugin_telepathy">傳送簡訊</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_description">讓這個裝置發出聲響讓您能找到它</string>
<string name="findmyphone_found">找到</string>
<string name="open">開啟</string>
<string name="close">關閉</string>
</resources>

View File

@@ -6,19 +6,21 @@
<string name="pref_plugin_battery">Battery report</string>
<string name="pref_plugin_battery_desc">Periodically report battery status</string>
<string name="pref_plugin_sftp">Filesystem expose</string>
<string name="pref_plugin_sftp_desc">Allows to browse the phone\'s filesystem remotely</string>
<string name="pref_plugin_sftp_desc">Allows to browse this device\'s filesystem remotely</string>
<string name="pref_plugin_clipboard">Clipboard sync</string>
<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 as a mouse and keyboard</string>
<string name="pref_plugin_mousepad_desc">Use your phone or tablet as a touchpad and keyboard</string>
<string name="pref_plugin_mpris">Multimedia controls</string>
<string name="pref_plugin_mpris_desc">Control audio/video from your phone</string>
<string name="pref_plugin_mpris_desc">Provides a remote control for your media player</string>
<string name="pref_plugin_runcommand">Run Command</string>
<string name="pref_plugin_runcommand_desc">Trigger remote commands from your phone</string>
<string name="pref_plugin_runcommand_desc">Trigger remote commands from your phone or tablet</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send and receive pings</string>
<string name="pref_plugin_notifications">Notification sync</string>
<string name="pref_plugin_notifications_desc">Access your notifications from other devices</string>
<string name="pref_plugin_receive_notifications">Receive notifications</string>
<string name="pref_plugin_receive_notifications_desc">Receive notifications from the other device and display them on Android</string>
<string name="pref_plugin_sharereceiver">Share and receive</string>
<string name="pref_plugin_sharereceiver_desc">Share files and URLs between devices</string>
<string name="plugin_not_available">This feature is not available in your Android version</string>
@@ -85,8 +87,8 @@
<string name="error_invalid_key">Invalid key received</string>
<string name="encryption_info_title">Encryption Info</string>
<string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string>
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is : </string>
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is : </string>
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is:</string>
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is:</string>
<string name="pair_requested">Pair requested</string>
<string name="pairing_request_from">Pairing request from %1s</string>
<string name="received_url_title">Received link from %1s</string>
@@ -94,13 +96,15 @@
<string name="incoming_file_title">Incoming file from %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Sending file to %1s</string>
<string name="outgoing_files_title">Sending files to %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="outgoing_files_text">Sent %1$d out of %2$d files</string>
<string name="received_file_title">Received file from %1s</string>
<string name="received_file_fail_title">Failed receiving file from %1s</string>
<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 %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>
@@ -156,6 +160,10 @@
<string name="custom_device_list">Add devices by IP</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">Customize destination directory</string>
<string name="share_destination_customize_summary_disabled">Received files will appear in Downloads</string>
<string name="share_destination_customize_summary_enabled">Files will be stored in the directory below</string>
<string name="share_destination_folder_preference">Destination directory</string>
<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>
@@ -183,9 +191,11 @@
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
<string name="plugin_not_supported">This plugin is not supported by the device</string>
<string name="findmyphone_title">Find my phone</string>
<string name="findmyphone_description">Rings this phone so you can find it.</string>
<string name="findmyphone_title_tablet">Find my tablet</string>
<string name="findmyphone_description">Rings this device so you can find it</string>
<string name="findmyphone_found">Found</string>
<string name="open">Open</string>
<string name="close">Close</string>
</resources>

View File

@@ -3,6 +3,19 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBoxPreference
android:id="@+id/share_destination_customize"
android:key="share_destination_custom"
android:title="@string/share_destination_customize"
android:summaryOff="@string/share_destination_customize_summary_disabled"
android:summaryOn="@string/share_destination_customize_summary_enabled"
android:defaultValue="false" />
<Preference
android:id="@+id/share_destination_folder_preference"
android:key="share_destination_folder_preference"
android:title="@string/share_destination_folder_preference" />
<CheckBoxPreference
android:id="@+id/share_notification_preference"
android:key="share_notification_preference"

View File

@@ -89,6 +89,7 @@ public abstract class BaseLink {
}
//TO OVERRIDE, should be sync
public abstract void sendPackage(NetworkPackage np,Device.SendPackageStatusCallback callback);
public abstract void sendPackageEncrypted(NetworkPackage np,Device.SendPackageStatusCallback callback, PublicKey key);
public abstract boolean sendPackage(NetworkPackage np,Device.SendPackageStatusCallback callback);
@Deprecated
public abstract boolean sendPackageEncrypted(NetworkPackage np,Device.SendPackageStatusCallback callback, PublicKey key);
}

View File

@@ -20,12 +20,8 @@
package org.kde.kdeconnect.Backends;
import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.NetworkPackage;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class BaseLinkProvider {

View File

@@ -23,8 +23,6 @@ package org.kde.kdeconnect.Backends;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
import java.util.Timer;
/**
* This class separates the pairing interface for each type of link.
* Since different links can pair via different methods, like for LanLink certificate and public key should be shared,
@@ -54,20 +52,14 @@ public abstract class BasePairingHandler {
protected Device mDevice;
protected BaseLink mBaseLink;
protected PairStatus mPairStatus;
protected PairingHandlerCallback mCallback;
protected Timer mPairingTimer;
public BasePairingHandler(Device device, PairingHandlerCallback callback) {
this.mDevice = device;
this.mCallback = callback;
}
public void setLink(BaseLink baseLink) {
this.mBaseLink = baseLink;
}
public boolean isPaired() {
return mPairStatus == PairStatus.Paired;
}
@@ -81,12 +73,10 @@ public abstract class BasePairingHandler {
}
/* To be implemented by respective pairing handler */
public abstract NetworkPackage createPairPackage();
public abstract void packageReceived(NetworkPackage np) throws Exception;
public abstract void requestPairing();
public abstract void acceptPairing();
public abstract void rejectPairing();
public abstract void pairingDone();
public abstract void unpair();
}

View File

@@ -21,73 +21,113 @@
package org.kde.kdeconnect.Backends.LanBackend;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.BackgroundService;
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.NetworkPackage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
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.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import javax.net.ssl.SSLSocket;
public class LanLink extends BaseLink {
public interface LinkDisconnectedCallback {
void linkDisconnected(LanLink brokenLink);
}
public enum ConnectionStarted {
Locally, Remotely;
};
protected ConnectionStarted connectionSource; // If the other device sent me a broadcast,
// I should not close the connection with it
private ConnectionStarted connectionSource; // If the other device sent me a broadcast,
// I should not close the connection with it
// because it's probably trying to find me and
// potentially ask for pairing.
private Channel channel = null;
private boolean onSsl = false;
private volatile Socket socket = null;
private LinkDisconnectedCallback callback;
@Override
public void disconnect() {
closeSocket();
}
//Returns the old channel
public Channel reset(Channel channel, ConnectionStarted connectionSource, boolean onSsl) {
Channel oldChannel = this.channel;
this.channel = channel;
this.connectionSource = connectionSource;
return oldChannel;
}
public void closeSocket() {
if (channel == null) {
Log.e("KDE/LanLink", "Not yet connected");
return;
Log.i("LanLink/Disconnect","socket:"+ socket.hashCode());
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
channel.close();
}
public LanLink(Context context, String deviceId, BaseLinkProvider linkProvider, Channel channel, ConnectionStarted connectionSource, boolean onSsl) {
//Returns the old socket
public Socket reset(final Socket newSocket, ConnectionStarted connectionSource) throws IOException {
Socket oldSocket = socket;
socket = newSocket;
this.connectionSource = connectionSource;
if (oldSocket != null) {
oldSocket.close(); //This should cancel the readThread
}
//Log.e("LanLink", "Start listening");
//Create a thread to take care of incoming data for the new socket
new Thread(new Runnable() {
@Override
public void run() {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(newSocket.getInputStream(), StringsHelper.UTF8));
while (true) {
String packet;
try {
packet = reader.readLine();
} catch (SocketTimeoutException e) {
continue;
}
if (packet == null) {
throw new IOException("End of stream");
}
if (packet.isEmpty()) {
continue;
}
NetworkPackage np = NetworkPackage.unserialize(packet);
receivedNetworkPackage(np);
}
} catch (Exception e) {
Log.i("LanLink", "Socket closed: " + newSocket.hashCode() + ". Reason: " + e.getMessage());
try { Thread.sleep(300); } catch (InterruptedException ignored) {} // Wait a bit because we might receive a new socket meanwhile
boolean thereIsaANewSocket = (newSocket != socket);
if (!thereIsaANewSocket) {
callback.linkDisconnected(LanLink.this);
}
}
}
}).start();
return oldSocket;
}
public LanLink(Context context, String deviceId, LanLinkProvider linkProvider, Socket socket, ConnectionStarted connectionSource) throws IOException {
super(context, deviceId, linkProvider);
reset(channel, connectionSource, onSsl);
callback = linkProvider;
reset(socket, connectionSource);
}
@@ -101,26 +141,12 @@ public class LanLink extends BaseLink {
return new LanPairingHandler(device, callback);
}
@Override
public void addPackageReceiver(PackageReceiver pr) {
super.addPackageReceiver(pr);
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(getDeviceId());
if (device == null) return;
if (!device.isPaired()) return;
// If the device is already paired due to other link, just send a pairing request to get required attributes for this link
}
});
}
//Blocking, do not call from main thread
private void sendPackageInternal(NetworkPackage np, final Device.SendPackageStatusCallback callback, PublicKey key) {
if (channel == null) {
private boolean sendPackageInternal(NetworkPackage np, final Device.SendPackageStatusCallback callback, PublicKey key) {
if (socket == null) {
Log.e("KDE/sendPackage", "Not yet connected");
callback.sendFailure(new NotYetConnectedException());
return;
callback.onFailure(new NotYetConnectedException());
return false;
}
try {
@@ -128,7 +154,7 @@ public class LanLink extends BaseLink {
//Prepare socket for the payload
final ServerSocket server;
if (np.hasPayload()) {
server = openTcpSocketOnFreePort(context, getDeviceId(), onSsl);
server = LanLinkProvider.openServerSocketOnFreePort(LanLinkProvider.PAYLOAD_TRANSFER_MIN_PORT);
JSONObject payloadTransferInfo = new JSONObject();
payloadTransferInfo.put("port", server.getLocalPort());
np.setPayloadTransferInfo(payloadTransferInfo);
@@ -141,58 +167,73 @@ public class LanLink extends BaseLink {
np = RsaHelper.encrypt(np, key);
}
//Send body of the network package
ChannelFuture future = channel.writeAndFlush(np.serialize()).sync();
if (!future.isSuccess()) {
Log.e("KDE/sendPackage", "!future.isWritten()");
callback.sendFailure(future.cause());
return;
}
//Log.e("LanLink/sendPackage", np.getType());
//Send body of the network package
try {
OutputStream writer = socket.getOutputStream();
writer.write(np.serialize().getBytes(StringsHelper.UTF8));
writer.flush();
} catch (Exception e) {
disconnect(); //main socket is broken, disconnect
throw e;
}
//Send payload
if (server != null) {
OutputStream socket = null;
Socket payloadSocket = null;
OutputStream outputStream = null;
InputStream inputStream = null;
try {
//Wait a maximum of 10 seconds for the other end to establish a connection with our socket, close it afterwards
server.setSoTimeout(10*1000);
socket = server.accept().getOutputStream();
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();
Log.i("KDE/LanLink", "Beginning to send payload");
byte[] buffer = new byte[4096];
int bytesRead;
long size = np.getPayloadSize();
long progress = 0;
InputStream stream = np.getPayload();
while ((bytesRead = stream.read(buffer)) != -1) {
long timeSinceLastUpdate = -1;
while ((bytesRead = inputStream.read(buffer)) != -1) {
//Log.e("ok",""+bytesRead);
progress += bytesRead;
socket.write(buffer, 0, bytesRead);
if (np.getPayloadSize() > 0) {
callback.sendProgress((int)(progress / np.getPayloadSize()));
outputStream.write(buffer, 0, bytesRead);
if (size > 0) {
if (timeSinceLastUpdate + 500 < System.currentTimeMillis()) { //Report progress every half a second
long percent = ((100 * progress) / size);
callback.onProgressChanged((int) percent);
timeSinceLastUpdate = System.currentTimeMillis();
}
}
}
socket.flush();
stream.close();
outputStream.flush();
outputStream.close();
Log.i("KDE/LanLink", "Finished sending payload ("+progress+" bytes written)");
} catch (Exception e) {
Log.e("KDE/sendPackage", "Exception: "+e);
callback.sendFailure(e);
return;
} finally {
if (socket != null) {
try { socket.close(); } catch (Exception e) { }
}
try { server.close(); } catch (Exception e) { }
try { payloadSocket.close(); } catch (Exception e) { }
try { inputStream.close(); } catch (Exception e) { }
try { outputStream.close(); } catch (Exception e) { }
}
}
callback.sendSuccess();
callback.onSuccess();
return true;
} catch (Exception e) {
if (callback != null) {
callback.sendFailure(e);
callback.onFailure(e);
}
return false;
} finally {
//Make sure we close the payload stream, if any
InputStream stream = np.getPayload();
@@ -203,22 +244,17 @@ public class LanLink extends BaseLink {
//Blocking, do not call from main thread
@Override
public void sendPackage(NetworkPackage np,Device.SendPackageStatusCallback callback) {
sendPackageInternal(np, callback, null);
public boolean sendPackage(NetworkPackage np,Device.SendPackageStatusCallback callback) {
return sendPackageInternal(np, callback, null);
}
//Blocking, do not call from main thread
@Override
public void sendPackageEncrypted(NetworkPackage np, Device.SendPackageStatusCallback callback, PublicKey key) {
if (onSsl) {
sendPackageInternal(np, callback, null); // No need to encrypt
}else {
sendPackageInternal(np, callback, key);
}
public boolean sendPackageEncrypted(NetworkPackage np, Device.SendPackageStatusCallback callback, PublicKey key) {
return sendPackageInternal(np, callback, key);
}
public void injectNetworkPackage(NetworkPackage np) {
private void receivedNetworkPackage(NetworkPackage np) {
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
try {
@@ -227,27 +263,22 @@ public class LanLink extends BaseLink {
e.printStackTrace();
Log.e("KDE/onPackageReceived","Exception decrypting the package");
}
}
if (np.hasPayloadTransferInfo()) {
Socket socket = null;
Socket payloadSocket = new Socket();
try {
// Use ssl if existing link is on ssl
if (onSsl) {
SSLContext sslContext = SslHelper.getSslContext(context, getDeviceId(), true);
socket = sslContext.getSocketFactory().createSocket();
} else {
socket = new Socket();
}
int tcpPort = np.getPayloadTransferInfo().getInt("port");
InetSocketAddress address = (InetSocketAddress)channel.remoteAddress();
socket.connect(new InetSocketAddress(address.getAddress(), tcpPort));
np.setPayload(socket.getInputStream(), np.getPayloadSize());
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(payloadSocket.getInputStream(), np.getPayloadSize());
} catch (Exception e) {
try { socket.close(); } catch(Exception ignored) { }
try { payloadSocket.close(); } catch(Exception ignored) { }
e.printStackTrace();
Log.e("KDE/LanLink", "Exception connecting to payload remote socket");
}
@@ -257,79 +288,14 @@ public class LanLink extends BaseLink {
packageReceived(np);
}
static ServerSocket openTcpSocketOnFreePort(Context context, String deviceId, boolean useSsl) throws IOException {
if (useSsl) {
return openSecureServerSocket(context, deviceId);
} else {
return openUnsecureSocketOnFreePort();
}
}
static ServerSocket openUnsecureSocketOnFreePort() throws IOException {
boolean success = false;
int tcpPort = 1739;
ServerSocket candidateServer = null;
while(!success) {
try {
candidateServer = new ServerSocket();
candidateServer.bind(new InetSocketAddress(tcpPort));
success = true;
Log.i("KDE/LanLink", "Using port "+tcpPort);
} catch(IOException e) {
//Log.e("LanLink", "Exception openning serversocket: "+e);
tcpPort++;
if (tcpPort >= 1764) {
Log.e("KDE/LanLink", "No more ports available");
throw e;
}
}
}
return candidateServer;
}
static ServerSocket openSecureServerSocket(Context context, String deviceId) throws IOException{
boolean success = false;
int tcpPort = 1739;
SSLContext tlsContext = SslHelper.getSslContext(context, deviceId, true);
SSLServerSocketFactory sslServerSocketFactory = tlsContext.getServerSocketFactory();
ServerSocket candidateServer = null;
while(!success) {
try {
candidateServer = sslServerSocketFactory.createServerSocket();
candidateServer.bind(new InetSocketAddress(tcpPort));
success = true;
Log.i("LanLink", "Using port "+tcpPort);
} catch(IOException e) {
//Log.e("LanLink", "Exception opening serversocket: "+e);
tcpPort++;
if (tcpPort >= 1764) {
Log.e("LanLink", "No more ports available");
throw e;
}
}
}
return candidateServer;
}
@Override
public boolean linkShouldBeKeptAlive() {
return true; //FIXME: Current implementation is broken, so for now we will keep links always established
//We keep the remotely initiated connections, since the remotes require them if they want to request
//pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing
if (connectionSource == ConnectionStarted.Remotely) {
return true;
}
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
if (preferences.contains(getDeviceId())) {
return true; //Already paired
}
return false;
//pairing to us, or connections that are already paired.
//return (connectionSource == ConnectionStarted.Remotely);
}
}

View File

@@ -21,9 +21,9 @@
package org.kde.kdeconnect.Backends.LanBackend;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.v4.util.LongSparseArray;
import android.util.Base64;
import android.util.Log;
@@ -32,435 +32,341 @@ import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.StringsHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLHandshakeException;
import javax.net.SocketFactory;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLSocket;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDisconnectedCallback {
public class LanLinkProvider extends BaseLinkProvider {
public static final int MIN_VERSION_WITH_SSL_SUPPORT = 6;
public static final int MIN_VERSION_WITH_NEW_PORT_SUPPORT = 7;
public static final String KEY_CUSTOM_DEVLIST_PREFERENCE = "device_list_preference";
private final static int port = 1714;
private static final int MIN_VERSION_WITH_SSL_SUPPORT = 6;
final static int MIN_PORT_LEGACY = 1714;
final static int MIN_PORT = 1716;
final static int MAX_PORT = 1764;
final static int PAYLOAD_TRANSFER_MIN_PORT = 1739;
private final Context context;
private final HashMap<String, LanLink> visibleComputers = new HashMap<>(); //Links by device id
private final LongSparseArray<LanLink> nioLinks = new LongSparseArray<>(); //Links by channel id
private EventLoopGroup bossGroup, workerGroup, udpGroup, clientGroup;
private TcpHandler tcpHandler = new TcpHandler();
private UdpHandler udpHandler = new UdpHandler();
private ServerSocket tcpServer;
private DatagramSocket udpServer;
private DatagramSocket udpServerOldPort;
// To prevent infinte loop if both device can only broadcast identity package but cannot connect via TCO
private ArrayList<String> reverseConnectionBlackList = new ArrayList<>();
private Timer reverseConnectionTimer;
private boolean listening = false;
// To prevent infinte loop between Android < IceCream because both device can only broadcast identity package but cannot connect via TCP
private ArrayList<InetAddress> reverseConnectionBlackList = new ArrayList<>();
@ChannelHandler.Sharable
private class TcpHandler extends SimpleChannelInboundHandler<String>{
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
// Close channel for any sudden exception
ctx.channel().close();
@Override // SocketClosedCallback
public void linkDisconnected(LanLink brokenLink) {
String deviceId = brokenLink.getDeviceId();
visibleComputers.remove(deviceId);
connectionLost(brokenLink);
}
//They received my UDP broadcast and are connecting to me. The first thing they sned should be their identity.
public void tcpPackageReceived(Socket socket) throws Exception {
NetworkPackage networkPackage;
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String message = reader.readLine();
networkPackage = NetworkPackage.unserialize(message);
//Log.e("TcpListener","Received TCP package: "+networkPackage.serialize());
} catch (Exception e) {
e.printStackTrace();
return;
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// Called after a long time if remote device closes session unexpectedly, like wifi off
try {
Channel channel = ctx.channel();
final LanLink brokenLink = nioLinks.get(channel.hashCode());
if (brokenLink != null) {
nioLinks.remove(channel.hashCode());
//Log.i("KDE/LanLinkProvider", "nioLinks.size(): " + nioLinks.size() + " (-)");
try {
brokenLink.closeSocket();
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Exception. Already disconnected?");
}
//Log.i("KDE/LanLinkProvider", "Disconnected!");
String deviceId = brokenLink.getDeviceId();
if (visibleComputers.get(deviceId) == brokenLink) {
visibleComputers.remove(deviceId);
}
new Thread(new Runnable() {
@Override
public void run() {
//Wait a bit before emitting connectionLost, in case the same device re-appears
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
connectionLost(brokenLink);
if (!networkPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
Log.e("KDE/LanLinkProvider", "Expecting an identity package instead of " + networkPackage.getType());
return;
}
}
}).start();
Log.i("KDE/LanLinkProvider", "Identity package received from a TCP connection from " + networkPackage.getString("deviceName"));
identityPackageReceived(networkPackage, socket, LanLink.ConnectionStarted.Locally);
}
//I've received their broadcast and should connect to their TCP socket and send my identity.
protected void udpPacketReceived(DatagramPacket packet) throws Exception {
final InetAddress address = packet.getAddress();
try {
String message = new String(packet.getData(), StringsHelper.UTF8);
final NetworkPackage identityPackage = NetworkPackage.unserialize(message);
final String deviceId = identityPackage.getString("deviceId");
if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
Log.e("KDE/LanLinkProvider", "Expecting an UDP identity package");
return;
} else {
String myId = DeviceHelper.getDeviceId(context);
if (deviceId.equals(myId)) {
//Ignore my own broadcast
return;
}
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "channelInactive exception");
}
}
@Override
public void channelRead0(ChannelHandlerContext ctx, String message) throws Exception {
//Log.e("KDE/LanLinkProvider", "Received a TCP packet from " + ctx.channel().remoteAddress() + ":" + message);
if (message.isEmpty()) {
Log.e("KDE/LanLinkProvider", "Empty package received");
if (identityPackage.getInt("protocolVersion") >= MIN_VERSION_WITH_NEW_PORT_SUPPORT && identityPackage.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;
}
final Channel channel = ctx.channel();
final NetworkPackage np = NetworkPackage.unserialize(message);
Log.i("KDE/LanLinkProvider", "Broadcast identity package received from " + identityPackage.getString("deviceName"));
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
int tcpPort = identityPackage.getInt("tcpPort", MIN_PORT);
String myId = DeviceHelper.getDeviceId(context);
if (np.getString("deviceId").equals(myId)) {
Log.e("KDE/LanLinkProvider", "Somehow I'm connected to myself, ignoring. This should not happen.");
return;
}
SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket(address, tcpPort);
configureSocket(socket);
Log.i("KDE/LanLinkProvider", "Identity package received from a stablished TCP connection from " + np.getString("deviceName"));
OutputStream out = socket.getOutputStream();
NetworkPackage myIdentity = NetworkPackage.createIdentityPackage(context);
out.write(myIdentity.serialize().getBytes());
out.flush();
final LanLink.ConnectionStarted connectionStarted = LanLink.ConnectionStarted.Locally;
identityPackageReceived(identityPackage, socket, LanLink.ConnectionStarted.Remotely);
// Add ssl handler if device uses new protocol
try {
if (np.getInt("protocolVersion") >= MIN_VERSION_WITH_SSL_SUPPORT) {
final SSLEngine sslEngine = SslHelper.getSslEngine(context, np.getString("deviceId"), SslHelper.SslMode.Client);
SslHandler sslHandler = new SslHandler(sslEngine);
channel.pipeline().addFirst(sslHandler);
sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<? super Channel>>() {
@Override
public void operationComplete(Future<? super Channel> future) throws Exception {
if (future.isSuccess()) {
Log.i("KDE/LanLinkProvider","Handshake successful with " + np.getString("deviceName") + " secured with " + sslEngine.getSession().getCipherSuite());
//Log.e("KDE/LanLinkProvider", "Channel" + channel.hashCode());
Certificate certificate = sslEngine.getSession().getPeerCertificates()[0];
np.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
addLink(np, channel, connectionStarted, true);
} else {
// Unpair if handshake failed
Log.e("KDE/LanLinkProvider", "Handshake as server failed with " + np.getString("deviceName"));
future.cause().printStackTrace();
if (future.cause() instanceof SSLHandshakeException) {
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(np.getString("deviceId"));
if (device == null) return;
device.unpair();
}
});
}
}
}
});
} else {
addLink(np, channel, connectionStarted, false);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Cannot connect to " + address);
e.printStackTrace();
if (!reverseConnectionBlackList.contains(address)) {
Log.w("KDE/LanLinkProvider","Blacklisting "+address);
reverseConnectionBlackList.add(address);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
reverseConnectionBlackList.remove(address);
}
} catch (Exception e) {
e.printStackTrace();
}
}, 5*1000);
} else {
LanLink link = nioLinks.get(channel.hashCode());
if (link== null) {
Log.e("KDE/LanLinkProvider","Expecting an identity package instead of " + np.getType());
//Log.e("KDE/LanLinkProvider", "Channel" + channel.hashCode());
} else {
link.injectNetworkPackage(np);
}
// Try to cause a reverse connection
onNetworkChange();
}
}
}
@ChannelHandler.Sharable
private class UdpHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private void configureSocket(Socket socket) {
try {
socket.setKeepAlive(true);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
protected void channelRead0(final ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
try {
String theMessage = packet.content().toString(CharsetUtil.UTF_8);
private void identityPackageReceived(final NetworkPackage identityPackage, final Socket socket, final LanLink.ConnectionStarted connectionStarted) {
final NetworkPackage identityPackage = NetworkPackage.unserialize(theMessage);
final String deviceId = identityPackage.getString("deviceId");
String myId = DeviceHelper.getDeviceId(context);
final String deviceId = identityPackage.getString("deviceId");
if (deviceId.equals(myId)) {
Log.e("KDE/LanLinkProvider", "Somehow I'm connected to myself, ignoring. This should not happen.");
return;
}
if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
Log.e("KDE/LanLinkProvider", "Expecting an UDP identity package");
return;
} else {
String myId = DeviceHelper.getDeviceId(context);
if (deviceId.equals(myId)) {
Log.i("KDE/LanLinkProvider", "Ignoring my own broadcast");
return;
}
}
// If I'm the TCP server I will be the SSL client and viceversa.
final boolean clientMode = (connectionStarted == LanLink.ConnectionStarted.Locally);
//Log.i("KDE/LanLinkProvider", "Identity package received, creating link");
// Add ssl handler if device uses new protocol
try {
if (identityPackage.getInt("protocolVersion") >= MIN_VERSION_WITH_SSL_SUPPORT) {
try{
Bootstrap b = new Bootstrap();
b.group(clientGroup);
b.channel(NioSocketChannel.class);
b.handler(new TcpInitializer());
int tcpPort = identityPackage.getInt("tcpPort", port);
final ChannelFuture channelFuture = b.connect(packet.sender().getAddress(), tcpPort);
channelFuture.addListener(new ChannelFutureListener() {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
boolean isDeviceTrusted = preferences.getBoolean(deviceId, false);
if (isDeviceTrusted && !SslHelper.isCertificateStored(context, deviceId)) {
//Device paired with and old version, we can't use it as we lack the certificate
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
final Channel channel = channelFuture.channel();
if (!future.isSuccess()) {
Log.e("KDE/LanLinkProvider", "Cannot connect to " + deviceId);
if (!reverseConnectionBlackList.contains(deviceId)) {
Log.w("KDE/LanLinkProvider","Blacklisting "+deviceId);
reverseConnectionBlackList.add(deviceId);
reverseConnectionTimer = new Timer();
reverseConnectionTimer.schedule(new TimerTask() {
@Override
public void run() {
reverseConnectionBlackList.remove(deviceId);
}
}, 5*1000);
// Try to cause a reverse connection
onNetworkChange();
}
return;
}
//Log.e("KDE/LanLinkProvider", "Connection successful: " + channel.isActive());
// Add ssl handler if device supports new protocol
if (identityPackage.getInt("protocolVersion") >= MIN_VERSION_WITH_SSL_SUPPORT) {
// add ssl handler with start tls true
SSLEngine sslEngine = SslHelper.getSslEngine(context, deviceId, SslHelper.SslMode.Server);
SslHandler sslHandler = new SslHandler(sslEngine, true);
channel.pipeline().addFirst(sslHandler);
}
final LanLink.ConnectionStarted connectionStarted = LanLink.ConnectionStarted.Remotely;
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
ChannelFuture future2 = channel.writeAndFlush(np2.serialize()).sync();
if (!future2.isSuccess()) {
Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back");
return;
}
// If ssl handler is in channel, add link after handshake is completed
final SslHandler sslHandler = channel.pipeline().get(SslHandler.class);
if (sslHandler != null) {
//Log.e("KDE/LanLinkProvider", "Initiating SSL handshake");
sslHandler.handshakeFuture().addListener(new GenericFutureListener<Future<? super Channel>>() {
@Override
public void operationComplete(Future<? super Channel> future) throws Exception {
if (future.isSuccess()) {
try {
Log.i("KDE/LanLinkProvider", "Handshake successfully completed with " + identityPackage.getString("deviceName") + ", session secured with " + sslHandler.engine().getSession().getCipherSuite());
Certificate certificate = sslHandler.engine().getSession().getPeerCertificates()[0];
identityPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
addLink(identityPackage, channel, connectionStarted, true);
} catch (Exception e){
Log.e("KDE/LanLinkProvider", "Exception in addLink");
e.printStackTrace();
}
} else {
// Unpair if handshake failed
// Any exception or handshake exception ?
Log.e("KDE/LanLinkProvider", "Handshake as client failed with " + identityPackage.getString("deviceName"));
future.cause().printStackTrace();
if (future.cause() instanceof SSLHandshakeException) {
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.unpair();
}
});
}
}
}
});
} else {
Log.w("KDE/LanLinkProvider", "Not using SSL");
addLink(identityPackage, channel, connectionStarted, false);
}
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.unpair();
//Retry as unpaired
identityPackageReceived(identityPackage, socket, connectionStarted);
}
});
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
Log.e("KDE/LanLinkProvider","Exception receiving udp package!!");
e.printStackTrace();
Log.i("KDE/LanLinkProvider","Starting SSL handshake with " + identityPackage.getString("deviceName") + " trusted:"+isDeviceTrusted);
final SSLSocket sslsocket = SslHelper.convertToSslSocket(context, socket, deviceId, isDeviceTrusted, clientMode);
sslsocket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
@Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
String mode = clientMode? "client" : "server";
try {
Certificate certificate = event.getPeerCertificates()[0];
identityPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
Log.i("KDE/LanLinkProvider","Handshake as " + mode + " successful with " + identityPackage.getString("deviceName") + " secured with " + event.getCipherSuite());
addLink(identityPackage, sslsocket, connectionStarted);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider","Handshake as " + mode + " failed with " + identityPackage.getString("deviceName"));
e.printStackTrace();
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.unpair();
}
});
}
}
});
//Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection
new Thread(new Runnable() {
@Override
public void run() {
try {
sslsocket.startHandshake();
} catch (Exception e) {
Log.e("KDE/LanLinkProvider","Handshake failed with " + identityPackage.getString("deviceName"));
e.printStackTrace();
//String[] ciphers = sslsocket.getSupportedCipherSuites();
//for (String cipher : ciphers) {
// Log.i("SupportedCiphers","cipher: " + cipher);
//}
}
}
}).start();
} else {
addLink(identityPackage, socket, connectionStarted);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public class TcpInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
ch.config().setAllowHalfClosure(false); // Not sure how it will work, but we certainly don't want half closure
ch.config().setKeepAlive(true);
pipeline.addLast(new DelimiterBasedFrameDecoder(512 * 1024, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(tcpHandler);
}
}
private void addLink(final NetworkPackage identityPackage, Socket socket, LanLink.ConnectionStarted connectionOrigin) throws IOException {
private void addLink(NetworkPackage identityPackage, Channel channel, LanLink.ConnectionStarted connectionOrigin, boolean useSsl) {
String deviceId = identityPackage.getString("deviceId");
Log.i("KDE/LanLinkProvider","addLink to "+deviceId);
LanLink currentLink = visibleComputers.get(deviceId);
if (currentLink != null) {
//Update old link
Log.i("KDE/LanLinkProvider", "Reusing same link for device " + deviceId);
final Channel oldChannel = currentLink.reset(channel, connectionOrigin, useSsl);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
nioLinks.remove(oldChannel.hashCode());
//Log.e("KDE/LanLinkProvider", "Forgetting about channel " + channel.hashCode());
}
}, 500); //Stop accepting messages from the old channel after 500ms
nioLinks.put(channel.hashCode(), currentLink);
//Log.e("KDE/LanLinkProvider", "Replacing channel. old: "+ oldChannel.hashCode() + " - new: "+ channel.hashCode());
final Socket oldSocket = currentLink.reset(socket, connectionOrigin);
//Log.e("KDE/LanLinkProvider", "Replacing socket. old: "+ oldSocket.hashCode() + " - new: "+ socket.hashCode());
} else {
Log.i("KDE/LanLinkProvider", "Creating a new link for device " + deviceId);
//Let's create the link
LanLink link = new LanLink(context, deviceId, this, channel, connectionOrigin, useSsl);
nioLinks.put(channel.hashCode(), link);
LanLink link = new LanLink(context, deviceId, this, socket, connectionOrigin);
visibleComputers.put(deviceId, link);
connectionAccepted(identityPackage, link);
}
}
public LanLinkProvider(Context context) {
this.context = context;
udpGroup = new NioEventLoopGroup();
try {
Bootstrap udpBootstrap = new Bootstrap();
udpBootstrap.group(udpGroup);
udpBootstrap.channel(NioDatagramChannel.class);
udpBootstrap.option(ChannelOption.SO_BROADCAST, true);
udpBootstrap.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(512 * 1024, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(udpHandler);
}
});
udpBootstrap.bind(new InetSocketAddress(port)).sync();
}catch (Exception e){
Log.e("KDE/LanLinkProvider","Exception setting up UDP server");
e.printStackTrace();
}
clientGroup = new NioEventLoopGroup();
// Due to certificate request from SSL server to client, the certificate request message from device with latest android version to device with
// old android version causes a FATAL ALERT message stating that incorrect certificate request
// Server is disabled on these devices and using a reverse connection strategy. This works well for connection of these devices with kde
// and newer android versions. Although devices with android version less than ICS cannot connect to other devices who also have android version less
// than ICS because server is disabled on both
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Log.w("KDE/LanLinkProvider","Not starting a TCP server because it's not supported on Android < 14. Operating only as client.");
return;
}
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap tcpBootstrap = new ServerBootstrap();
tcpBootstrap.group(bossGroup, workerGroup);
tcpBootstrap.channel(NioServerSocketChannel.class);
tcpBootstrap.option(ChannelOption.SO_BACKLOG, 100);
tcpBootstrap.handler(new LoggingHandler(LogLevel.INFO));
tcpBootstrap.option(ChannelOption.SO_REUSEADDR, true);
tcpBootstrap.childHandler(new TcpInitializer());
tcpBootstrap.bind(new InetSocketAddress(port)).sync();
}catch (Exception e) {
Log.e("KDE/LanLinkProvider","Exception setting up TCP server");
e.printStackTrace();
}
}
@Override
public void onStart() {
private DatagramSocket setupUdpListener(int udpPort) {
final DatagramSocket server;
try {
server = new DatagramSocket(udpPort);
server.setReuseAddress(true);
server.setBroadcast(true);
} catch (SocketException e) {
Log.e("LanLinkProvider", "Error creating udp server");
e.printStackTrace();
return null;
}
new Thread(new Runnable() {
@Override
public void run() {
while (listening) {
final int bufferSize = 1024 * 512;
byte[] data = new byte[bufferSize];
DatagramPacket packet = new DatagramPacket(data, bufferSize);
try {
server.receive(packet);
udpPacketReceived(packet);
} catch (Exception e) {
e.printStackTrace();
Log.e("LanLinkProvider", "UdpReceive exception");
}
}
Log.w("UdpListener","Stopping UDP listener");
}
}).start();
return server;
}
Log.i("KDE/LanLinkProvider", "onStart");
private void setupTcpListener() {
try {
tcpServer = openServerSocketOnFreePort(MIN_PORT);
new Thread(new Runnable() {
@Override
public void run() {
while (listening) {
try {
Socket socket = tcpServer.accept();
configureSocket(socket);
tcpPackageReceived(socket);
} catch (Exception e) {
e.printStackTrace();
Log.e("LanLinkProvider", "TcpReceive exception");
}
}
Log.w("TcpListener", "Stopping TCP listener");
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
static ServerSocket openServerSocketOnFreePort(int minPort) throws IOException {
int tcpPort = minPort;
while(tcpPort < MAX_PORT) {
try {
ServerSocket candidateServer = new ServerSocket();
candidateServer.bind(new InetSocketAddress(tcpPort));
Log.i("KDE/LanLink", "Using port "+tcpPort);
return candidateServer;
} catch(IOException e) {
tcpPort++;
}
}
Log.e("KDE/LanLink", "No ports available");
throw new IOException("No ports available");
}
void broadcastUdpPackage() {
new Thread(new Runnable() {
@Override
public void run() {
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(
KEY_CUSTOM_DEVLIST_PREFERENCE, "");
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(CustomDevicesActivity.KEY_CUSTOM_DEVLIST_PREFERENCE, "");
ArrayList<String> iplist = new ArrayList<>();
if (!deviceListPrefs.isEmpty()) {
iplist = CustomDevicesActivity.deserializeIpList(deviceListPrefs);
@@ -468,14 +374,14 @@ public class LanLinkProvider extends BaseLinkProvider {
iplist.add("255.255.255.255"); //Default: broadcast.
NetworkPackage identity = NetworkPackage.createIdentityPackage(context);
identity.set("tcpPort", port);
identity.set("tcpPort", MIN_PORT);
DatagramSocket socket = null;
byte[] bytes = null;
try {
socket = new DatagramSocket();
socket.setReuseAddress(true);
socket.setBroadcast(true);
bytes = identity.serialize().getBytes("UTF-8");
bytes = identity.serialize().getBytes(StringsHelper.UTF8);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider","Failed to create DatagramSocket");
@@ -486,9 +392,9 @@ public class LanLinkProvider extends BaseLinkProvider {
for (String ipstr : iplist) {
try {
InetAddress client = InetAddress.getByName(ipstr);
java.net.DatagramPacket packet = new java.net.DatagramPacket(bytes, bytes.length, client, port);
socket.send(packet);
//Log.i("KDE/LanLinkProvider","Udp identity package sent to address "+packet.getAddress());
socket.send(new DatagramPacket(bytes, bytes.length, client, MIN_PORT));
socket.send(new DatagramPacket(bytes, bytes.length, client, MIN_PORT_LEGACY));
//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 + ")");
@@ -503,23 +409,53 @@ public class LanLinkProvider extends BaseLinkProvider {
}
}).start();
}
@Override
public void onStart() {
//Log.i("KDE/LanLinkProvider", "onStart");
if (!listening) {
listening = true;
udpServer = setupUdpListener(MIN_PORT);
udpServerOldPort = setupUdpListener(MIN_PORT_LEGACY);
// Due to certificate request from SSL server to client, the certificate request message from device with latest android version to device with
// old android version causes a FATAL ALERT message stating that incorrect certificate request
// Server is disabled on these devices and using a reverse connection strategy. This works well for connection of these devices with kde
// and newer android versions. Although devices with android version less than ICS cannot connect to other devices who also have android version less
// than ICS because server is disabled on both
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Log.w("KDE/LanLinkProvider","Not starting a TCP server because it's not supported on Android < 14. Operating only as client.");
} else {
setupTcpListener();
}
broadcastUdpPackage();
}
}
@Override
public void onNetworkChange() {
//FilesHelper.LogOpenFileCount();
onStart();
//FilesHelper.LogOpenFileCount();
broadcastUdpPackage();
}
@Override
public void onStop() {
//Log.i("KDE/LanLinkProvider", "onStop");
listening = false;
try {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
udpGroup.shutdownGracefully();
clientGroup.shutdownGracefully();
}catch (Exception e){
tcpServer.close();
} catch (Exception e){
e.printStackTrace();
}
try {
udpServer.close();
} catch (Exception e){
e.printStackTrace();
}
try {
udpServerOldPort.close();
} catch (Exception e){
e.printStackTrace();
}
}
@@ -529,6 +465,4 @@ public class LanLinkProvider extends BaseLinkProvider {
return "LanLinkProvider";
}
}

View File

@@ -39,6 +39,8 @@ import java.util.TimerTask;
public class LanPairingHandler extends BasePairingHandler {
private Timer mPairingTimer;
public LanPairingHandler(Device device, final PairingHandlerCallback callback) {
super(device, callback);
@@ -49,8 +51,7 @@ public class LanPairingHandler extends BasePairingHandler {
}
}
@Override
public NetworkPackage createPairPackage() {
private NetworkPackage createPairPackage() {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
np.set("pair", true);
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(mDevice.getContext());
@@ -141,7 +142,7 @@ public class LanPairingHandler extends BasePairingHandler {
Device.SendPackageStatusCallback statusCallback = new Device.SendPackageStatusCallback() {
@Override
protected void onSuccess() {
public void onSuccess() {
hidePairingNotification(); //Will stop the pairingTimer if it was running
mPairingTimer = new Timer();
mPairingTimer.schedule(new TimerTask() {
@@ -156,7 +157,12 @@ public class LanPairingHandler extends BasePairingHandler {
}
@Override
protected void onFailure(Throwable e) {
public void onFailure(Throwable e) {
if (e != null) {
e.printStackTrace();
} else {
Log.e("LanPairing/onFailure", "Unknown (null) exception");
}
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_could_not_send_package));
}
};
@@ -175,12 +181,17 @@ public class LanPairingHandler extends BasePairingHandler {
hidePairingNotification();
Device.SendPackageStatusCallback statusCallback = new Device.SendPackageStatusCallback() {
@Override
protected void onSuccess() {
public void onSuccess() {
pairingDone();
}
@Override
protected void onFailure(Throwable e) {
public void onFailure(Throwable e) {
if (e != null) {
e.printStackTrace();
} else {
Log.e("LanPairing/onFailure", "Unknown (null) exception");
}
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_not_reachable));
}
};
@@ -196,17 +207,18 @@ public class LanPairingHandler extends BasePairingHandler {
mDevice.sendPackage(np);
}
@Override
public void pairingDone() {
// Store device information needed to create a Device object in a future
//Log.e("KDE/PairingDone", "Pairing Done");
SharedPreferences.Editor editor = mDevice.getContext().getSharedPreferences(mDevice.getDeviceId(), Context.MODE_PRIVATE).edit();
try {
String encodedPublicKey = Base64.encodeToString(mDevice.publicKey.getEncoded(), 0);
editor.putString("publicKey", encodedPublicKey);
} catch (Exception e) {
Log.e("KDE/PairingDone", "Error encoding public key");
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 {

View File

@@ -26,11 +26,9 @@ import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.NetworkPackage;
import java.security.PublicKey;
import java.util.ArrayList;
public class LoopbackLink extends BaseLink {
@@ -49,18 +47,19 @@ public class LoopbackLink extends BaseLink {
}
@Override
public void sendPackage(NetworkPackage in, Device.SendPackageStatusCallback callback) {
public boolean sendPackage(NetworkPackage in, Device.SendPackageStatusCallback callback) {
packageReceived(in);
if (in.hasPayload()) {
callback.sendProgress(0);
callback.onProgressChanged(0);
in.setPayload(in.getPayload(), in.getPayloadSize());
callback.sendProgress(100);
callback.onProgressChanged(100);
}
callback.sendSuccess();
callback.onSuccess();
return true;
}
@Override
public void sendPackageEncrypted(NetworkPackage np, Device.SendPackageStatusCallback callback, PublicKey key) {
sendPackage(np, callback);
public boolean sendPackageEncrypted(NetworkPackage np, Device.SendPackageStatusCallback callback, PublicKey key) {
return sendPackage(np, callback);
}
}

View File

@@ -20,13 +20,45 @@
package org.kde.kdeconnect.Backends.LoopbackBackend;
import org.kde.kdeconnect.Backends.LanBackend.LanPairingHandler;
import org.kde.kdeconnect.Device;
import android.util.Log;
public class LoopbackPairingHandler extends LanPairingHandler{
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
public class LoopbackPairingHandler extends BasePairingHandler {
public LoopbackPairingHandler(Device device, PairingHandlerCallback callback) {
super(device, callback);
}
// Extending from LanPairingHandler, as it is similar to it
@Override
public void packageReceived(NetworkPackage np) throws Exception {
}
@Override
public void requestPairing() {
Log.i("LoopbackPairing", "requestPairing");
mCallback.pairingDone();
}
@Override
public void acceptPairing() {
Log.i("LoopbackPairing", "acceptPairing");
mCallback.pairingDone();
}
@Override
public void rejectPairing() {
Log.i("LoopbackPairing", "rejectPairing");
mCallback.unpaired();
}
@Override
public void unpair() {
Log.i("LoopbackPairing", "unpair");
mCallback.unpaired();
}
}

View File

@@ -20,7 +20,6 @@
package org.kde.kdeconnect;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -28,7 +27,6 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLink;
@@ -64,11 +62,13 @@ public class BackgroundService extends Service {
if (wasEmpty) {
onNetworkChange();
}
//Log.e("acquireDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
return wasEmpty;
}
public void releaseDiscoveryMode(Object key) {
boolean removed = discoveryModeAcquisitions.remove(key);
//Log.e("releaseDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
if (removed && discoveryModeAcquisitions.isEmpty()) {
cleanDevices();
}
@@ -141,15 +141,9 @@ public class BackgroundService extends Service {
private void registerLinkProviders() {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
//linkProviders.add(new LoopbackLinkProvider(this));
//if (settings.getBoolean("loopback_link", true)) {
// linkProviders.add(new LoopbackLinkProvider(this));
//}
if (settings.getBoolean("lan_link", true)) {
linkProviders.add(new LanLinkProvider(this));
}
linkProviders.add(new LanLinkProvider(this));
}
@@ -162,11 +156,16 @@ public class BackgroundService extends Service {
}
private void cleanDevices() {
for(Device d : devices.values()) {
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
d.disconnect();
new Thread(new Runnable() {
@Override
public void run() {
for(Device d : devices.values()) {
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
d.disconnect();
}
}
}
}
}).start();
}
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {

View File

@@ -28,18 +28,16 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.util.Base64;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Helpers.ObjectsHelper;
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;
import org.kde.kdeconnect.Plugins.PluginFactory;
@@ -53,13 +51,12 @@ import java.security.cert.Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -78,29 +75,17 @@ public class Device implements BaseLink.PackageReceiver {
private PairStatus pairStatus;
private final CopyOnWriteArrayList<PairingCallback> pairingCallback = new CopyOnWriteArrayList<>();
private Map<String, BasePairingHandler> pairingHandlers = new HashMap<String, BasePairingHandler>();
private Map<String, BasePairingHandler> pairingHandlers = new HashMap<>();
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
private ArrayList<String> incomingCapabilities = new ArrayList<>();
private ArrayList<String> outgoingCapabilities = new ArrayList<>();
private List<String> m_supportedPlugins = new ArrayList<>();
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Plugin> failedPlugins = new ConcurrentHashMap<>();
private ArrayList<String> unsupportedPlugins = new ArrayList<>();
private HashSet<String> supportedIncomingInterfaces = new HashSet<>();
private HashSet<String> supportedOutgoingInterfaces = new HashSet<>();
private HashMap<String, ArrayList<String>> pluginsByIncomingInterface;
private HashMap<String, ArrayList<String>> pluginsByOutgoingInterface;
private Map<String, ArrayList<String>> pluginsByIncomingInterface;
private final SharedPreferences settings;
public ArrayList<String> getUnsupportedPlugins() {
return unsupportedPlugins;
}
private final CopyOnWriteArrayList<PluginsChangedListener> pluginsChangedListeners = new CopyOnWriteArrayList<>();
public interface PluginsChangedListener {
@@ -162,7 +147,11 @@ public class Device implements BaseLink.PackageReceiver {
Log.e("KDE/Device","Exception deserializing stored public key for device");
}
reloadPluginsFromSettings();
//Assume every plugin is supported until addLink is called and we can get the actual list
m_supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
//Do not load plugins yet, the device is not present
//reloadPluginsFromSettings();
}
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
@@ -299,7 +288,6 @@ public class Device implements BaseLink.PackageReceiver {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
preferences.edit().remove(deviceId).apply();
// FIXME : We delete all device info here, but the xml file still persists
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
devicePreferences.edit().clear().apply();
@@ -395,14 +383,9 @@ public class Device implements BaseLink.PackageReceiver {
.build();
final NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationHelper.notifyCompat(notificationManager, notificationId, noti);
try {
BackgroundService.addGuiInUseCounter(context);
notificationManager.notify(notificationId, noti);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
BackgroundService.addGuiInUseCounter(context);
}
public void hidePairingNotification() {
@@ -492,22 +475,19 @@ public class Device implements BaseLink.PackageReceiver {
};
pairingHandlers.put(link.getName(), link.getPairingHandler(this, callback));
}
pairingHandlers.get(link.getName()).setLink(link);
/*
Collections.sort(links, new Comparator<BaseLink>() {
@Override
public int compare(BaseLink o, BaseLink o2) {
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
}
});
*/
Set<String> outgoingCapabilities = identityPackage.getStringSet("outgoingCapabilities", null);
Set<String> incomingCapabilities = identityPackage.getStringSet("incomingCapabilities", null);
if (incomingCapabilities != null && outgoingCapabilities != null) {
m_supportedPlugins = new Vector<>(PluginFactory.pluginsForCapabilities(context, incomingCapabilities, outgoingCapabilities));
} else {
m_supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins());
}
link.addPackageReceiver(this);
if (links.size() == 1) {
reloadPluginsFromSettings();
}
reloadPluginsFromSettings();
}
public void removeLink(BaseLink link) {
@@ -550,16 +530,6 @@ public class Device implements BaseLink.PackageReceiver {
Log.e("PairingPackageReceived","Exception");
}
}
} else if (NetworkPackage.PACKAGE_TYPE_CAPABILITIES.equals(np.getType())) {
ArrayList<String> newIncomingCapabilities = np.getStringList("IncomingCapabilities");
ArrayList<String> newOutgoingCapabilities = np.getStringList("OutgoingCapabilities");
if (!ObjectsHelper.equals(newIncomingCapabilities, incomingCapabilities) ||
!ObjectsHelper.equals(newOutgoingCapabilities, outgoingCapabilities)) {
incomingCapabilities = newIncomingCapabilities;
outgoingCapabilities = newOutgoingCapabilities;
reloadPluginsFromSettings();
}
} else if (isPaired()) {
//If capabilities are not supported, iterate all plugins
@@ -572,10 +542,11 @@ public class Device implements BaseLink.PackageReceiver {
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception in " + plugin.getPluginKey() + "'s onPackageReceived()");
//try { Log.e("KDE/Device", "NetworkPackage:" + np.serialize()); } catch (Exception _) { }
}
}
} else {
Log.e("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it");
Log.w("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it");
}
} else {
@@ -607,78 +578,74 @@ public class Device implements BaseLink.PackageReceiver {
}
public static abstract class SendPackageStatusCallback {
protected abstract void onSuccess();
protected abstract void onFailure(Throwable e);
protected void onProgressChanged(int percent) { }
public abstract void onSuccess();
public abstract void onFailure(Throwable e);
public void onProgressChanged(int percent) { }
}
private boolean success = false;
public void sendSuccess() {
success = true;
onSuccess();
}
public void sendFailure(Throwable e) {
private SendPackageStatusCallback defaultCallback = new SendPackageStatusCallback() {
@Override
public void onSuccess() { }
@Override
public void onFailure(Throwable e) {
if (e != null) {
e.printStackTrace();
Log.e("KDE/sendPackage", "Exception: " + e.getMessage());
} else {
Log.e("KDE/sendPackage", "Unknown (null) exception");
}
onFailure(e);
}
public void sendProgress(int percent) { onProgressChanged(percent); }
}
};
public void sendPackage(NetworkPackage np) {
sendPackage(np,new SendPackageStatusCallback() {
@Override
protected void onSuccess() { }
@Override
protected void onFailure(Throwable e) { }
});
sendPackage(np, defaultCallback);
}
public boolean sendPackageBlocking(NetworkPackage np) {
return sendPackageBlocking(np, defaultCallback);
}
//Async
public void sendPackage(final NetworkPackage np, final SendPackageStatusCallback callback) {
hackToMakeRetrocompatiblePacketTypes(np);
if (protocolVersion >= 6 && !supportedOutgoingInterfaces.contains(np.getType())) {
Log.e("Device/sendPackage", "Plugin tried to send an unsupported package: " + np.getType());
Log.e("Device/sendPackage", "Supported package types: " + Arrays.toString(supportedOutgoingInterfaces.toArray()));
}
//Log.e("sendPackage", "Sending package...");
//Log.e("sendPackage", np.serialize());
final Throwable backtrace = new Throwable();
new Thread(new Runnable() {
@Override
public void run() {
boolean useEncryption = (protocolVersion < 6 && (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired()));
//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) {
link.sendPackageEncrypted(np, callback, publicKey);
} else {
link.sendPackage(np, callback);
}
if (callback.success) break; //If the link didn't call sendSuccess(), try the next one
}
if (!callback.success) {
Log.e("KDE/sendPackage", "No device link (of "+links.size()+" available) could send the package. Package "+np.getType()+" to " + name + " lost!");
backtrace.printStackTrace();
}
sendPackageBlocking(np, callback);
}
}).start();
}
public boolean sendPackageBlocking(final NetworkPackage np, final SendPackageStatusCallback callback) {
/*
if (!m_outgoingCapabilities.contains(np.getType()) && !NetworkPackage.protocolPackageTypes.contains(np.getType())) {
Log.e("Device/sendPackage", "Plugin tried to send an undeclared package: " + np.getType());
Log.w("Device/sendPackage", "Declared outgoing package types: " + Arrays.toString(m_outgoingCapabilities.toArray()));
}
*/
hackToMakeRetrocompatiblePacketTypes(np);
boolean useEncryption = (protocolVersion < LanLinkProvider.MIN_VERSION_WITH_SSL_SUPPORT && (!np.getType().equals(NetworkPackage.PACKAGE_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.sendPackageEncrypted(np, callback, publicKey);
} else {
success = link.sendPackage(np, callback);
}
if (success) break; //If the link didn't call sendSuccess(), try the next one
}
if (!success) {
Log.e("KDE/sendPackage", "No device link (of "+links.size()+" available) could send the package. Package "+np.getType()+" to " + name + " lost!");
}
return success;
}
//
// Plugin-related functions
//
@@ -713,7 +680,8 @@ public class Device implements BaseLink.PackageReceiver {
final Plugin plugin = PluginFactory.instantiatePluginForDevice(context, pluginKey, this);
if (plugin == null) {
Log.e("KDE/addPlugin","could not instantiate plugin: "+pluginKey);
failedPlugins.put(pluginKey, null);
//Can't put a null
//failedPlugins.put(pluginKey, null);
return false;
}
@@ -774,29 +742,15 @@ public class Device implements BaseLink.PackageReceiver {
return enabled;
}
public boolean hasPluginsLoaded() {
return !plugins.isEmpty() || !failedPlugins.isEmpty();
}
public void reloadPluginsFromSettings() {
failedPlugins.clear();
Set<String> availablePlugins = PluginFactory.getAvailablePlugins();
ArrayList<String> newUnsupportedPlugins = new ArrayList<>();
HashSet<String> newSupportedIncomingInterfaces = new HashSet<>();
HashSet<String> newSupportedOutgoingInterfaces = new HashSet<>();
HashMap<String, ArrayList<String>> newPluginsByIncomingInterface = new HashMap<>();
HashMap<String, ArrayList<String>> newPluginsByOutgoingInterface = new HashMap<>();
final boolean supportsCapabilities = (protocolVersion >= 6);
for (String pluginKey : availablePlugins) {
for (String pluginKey : m_supportedPlugins) {
PluginFactory.PluginInfo pluginInfo = PluginFactory.getPluginInfo(context, pluginKey);
Set<String> incomingInterfaces = pluginInfo.getSupportedPackageTypes();
Set<String> outgoingInterfaces = pluginInfo.getOutgoingPackageTypes();
boolean pluginEnabled = false;
boolean listenToUnpaired = pluginInfo.listenToUnpaired();
@@ -804,48 +758,16 @@ public class Device implements BaseLink.PackageReceiver {
pluginEnabled = isPluginEnabled(pluginKey);
}
//TODO: Check for plugins that will fail to load before checking the capabilities
if (supportsCapabilities && (!incomingInterfaces.isEmpty() || !outgoingInterfaces.isEmpty())) {
HashSet<String> supportedOut = new HashSet<>(outgoingInterfaces);
supportedOut.retainAll(incomingCapabilities); //Intersection
HashSet<String> supportedIn = new HashSet<>(incomingInterfaces);
supportedIn.retainAll(outgoingCapabilities);
if (supportedOut.isEmpty() && supportedIn.isEmpty()) {
newUnsupportedPlugins.add(pluginKey);
if (pluginEnabled) {
//We still want to announce this capability, to prevent a deadlock
newSupportedOutgoingInterfaces.addAll(outgoingInterfaces);
newSupportedIncomingInterfaces.addAll(incomingInterfaces);
pluginEnabled = false;
}
}
}
if (pluginEnabled) {
boolean success = addPlugin(pluginKey);
if (success) {
newSupportedIncomingInterfaces.addAll(incomingInterfaces);
newSupportedOutgoingInterfaces.addAll(outgoingInterfaces);
for (String packageType : incomingInterfaces) {
for (String packageType : pluginInfo.getSupportedPackageTypes()) {
packageType = hackToMakeRetrocompatiblePacketTypes(packageType);
ArrayList<String> plugins = newPluginsByIncomingInterface.get(packageType);
if (plugins == null) plugins = new ArrayList<>();
plugins.add(pluginKey);
newPluginsByIncomingInterface.put(packageType, plugins);
}
for (String packageType : outgoingInterfaces) {
packageType = hackToMakeRetrocompatiblePacketTypes(packageType);
ArrayList<String> plugins = newPluginsByOutgoingInterface.get(packageType);
if (plugins == null) plugins = new ArrayList<>();
plugins.add(pluginKey);
newPluginsByOutgoingInterface.put(packageType, plugins);
}
}
} else {
removePlugin(pluginKey);
@@ -853,30 +775,9 @@ public class Device implements BaseLink.PackageReceiver {
}
boolean capabilitiesChanged = false;
if (!newSupportedIncomingInterfaces.equals(supportedIncomingInterfaces) ||
!newSupportedOutgoingInterfaces.equals(pluginsByOutgoingInterface)) {
capabilitiesChanged = true;
}
pluginsByOutgoingInterface = newPluginsByOutgoingInterface;
pluginsByIncomingInterface = newPluginsByIncomingInterface;
supportedIncomingInterfaces = newSupportedIncomingInterfaces;
supportedOutgoingInterfaces = newSupportedOutgoingInterfaces;
unsupportedPlugins = newUnsupportedPlugins;
Log.i("ReloadPlugins", "not loading " + Arrays.toString(unsupportedPlugins.toArray()) + " because of unmatched capabilities");
onPluginsChanged();
//Only send capabilities to devices using protocol version 6 or later
if (capabilitiesChanged && isReachable() && isPaired() && protocolVersion >= 6) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_CAPABILITIES);
np.set("IncomingCapabilities", new ArrayList<>(newSupportedIncomingInterfaces));
np.set("OutgoingCapabilities", new ArrayList<>(newSupportedOutgoingInterfaces));
sendPackage(np);
}
}
public void onPluginsChanged() {
@@ -908,6 +809,13 @@ public class Device implements BaseLink.PackageReceiver {
}
public boolean deviceShouldBeKeptAlive() {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
if (preferences.contains(getDeviceId())) {
//Log.e("DeviceShouldBeKeptAlive", "because it's a paired device");
return true; //Already paired
}
for(BaseLink l : links) {
if (l.linkShouldBeKeptAlive()) {
return true;
@@ -916,14 +824,17 @@ public class Device implements BaseLink.PackageReceiver {
return false;
}
public List<String> getSupportedPlugins() {
return m_supportedPlugins;
}
public void hackToMakeRetrocompatiblePacketTypes(NetworkPackage np) {
if (protocolVersion >= 6) return;
np.mType = np.getType().replace(".request","");
}
public String hackToMakeRetrocompatiblePacketTypes(String type) {
if (protocolVersion >= 6) return type;
return type.replace(".request","");
return type.replace(".request", "");
}
}

View File

@@ -20,10 +20,11 @@
package org.kde.kdeconnect.Helpers;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.util.Base64;
@@ -37,11 +38,13 @@ import java.util.Map;
public class ContactsHelper {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static Map<String, String> phoneNumberLookup(Context context, String number) {
//Log.e("PhoneNumberLookup", number);
Map<String, String> contactInfo = new HashMap<String, String>();
Map<String, String> contactInfo = new HashMap<>();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
Cursor cursor = null;
@@ -72,8 +75,9 @@ public class ContactsHelper {
contactInfo.put("photoID", cursor.getString(nameIndex));
}
try { cursor.close(); } catch (Exception e) {}
if (!contactInfo.isEmpty()) {
cursor.close();
return contactInfo;
}
}
@@ -82,25 +86,29 @@ public class ContactsHelper {
}
public static String photoId64Encoded(Context context, String photoId) {
if (photoId == null) {
return "";
}
Uri photoUri = Uri.parse(photoId);
Uri displayPhotoUri = Uri.withAppendedPath(photoUri, ContactsContract.Contacts.Photo.DISPLAY_PHOTO);
byte[] buffer = null;
Base64OutputStream out = null;
ByteArrayOutputStream encodedPhoto = null;
InputStream input = null;
Base64OutputStream output= null;
try {
encodedPhoto = new ByteArrayOutputStream();
out = new Base64OutputStream(encodedPhoto, Base64.DEFAULT);
InputStream fd2 = context.getContentResolver().openInputStream(photoUri);
buffer = new byte[1024];
ByteArrayOutputStream encodedPhoto = new ByteArrayOutputStream();
output = new Base64OutputStream(encodedPhoto, Base64.DEFAULT);
input = context.getContentResolver().openInputStream(photoUri);
byte[] buffer = new byte[1024];
int len;
while ((len = fd2.read(buffer)) != -1) {
out.write(buffer, 0, len);
while ((len = input.read(buffer)) != -1) {
output.write(buffer, 0, len);
}
return encodedPhoto.toString();
} catch (Exception ex) {
Log.e("ContactsHelper", ex.toString());
return new String();
return "";
} finally {
try { input.close(); } catch(Exception ignored) { };
try { output.close(); } catch(Exception ignored) { };
}
}
}

View File

@@ -450,13 +450,17 @@ public class DeviceHelper {
public static String getAndroidDeviceName() {
String deviceName = null;
try {
String dictName = humanReadableNames.get(Build.MODEL.replace(' ', '_'));
String internalName = Build.MODEL.replace(' ', '_');
String dictName = humanReadableNames.get(internalName);
if (dictName != null) {
deviceName = dictName;
} else if (Build.BRAND.equalsIgnoreCase("samsung")) {
deviceName = "Samsung " + Build.MODEL;
} else {
deviceName = Build.BRAND;
Log.w("getAndroidDeviceName", "Not found human readable name for device '" + internalName + "'");
if (Build.BRAND.equalsIgnoreCase("samsung")) {
deviceName = "Samsung " + Build.MODEL;
} else {
deviceName = Build.BRAND;
}
}
} catch (Exception e) {
//Some phones might not define BRAND or MODEL, ignore exceptions

View File

@@ -27,17 +27,35 @@ import java.io.File;
public class FilesHelper {
public static String getFileExt(String fileName) {
//return MimeTypeMap.getFileExtensionFromUrl(fileName);
return fileName.substring((fileName.lastIndexOf(".") + 1), fileName.length());
public static String getFileExt(String filename) {
//return MimeTypeMap.getFileExtensionFromUrl(filename);
return filename.substring((filename.lastIndexOf(".") + 1));
}
public static String getFileNameWithoutExt(String filename) {
int dot = filename.lastIndexOf(".");
return (dot < 0)? filename : filename.substring(0, dot);
}
public static String getMimeTypeFromFile(String file) {
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(getFileExt(file));
if (mime == null) mime = "*/*";
return mime;
}
public static String findNonExistingNameForNewFile(String path, String filename) {
int dot = filename.lastIndexOf(".");
String name = (dot < 0)? filename : filename.substring(0, dot);
String ext = (dot < 0)? "" : filename.substring(filename.lastIndexOf("."));
int num = 1;
while (new File(path+"/"+filename).exists()) {
filename = name+" ("+num+")"+ext;
num++;
}
return filename;
}
//Following code from http://activemq.apache.org/maven/5.7.0/kahadb/apidocs/src-html/org/apache/kahadb/util/IOHelper.html
/**
* Converts any string into a string that is safe to use as a file name.

View File

@@ -0,0 +1,17 @@
package org.kde.kdeconnect.Helpers;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
public class MediaStoreHelper {
//Maybe this class could batch successive calls together
public static void indexFile(Context context, Uri path) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(path);
context.sendBroadcast(mediaScanIntent);
}
}

View File

@@ -0,0 +1,25 @@
package org.kde.kdeconnect.Helpers;
import android.app.Notification;
import android.app.NotificationManager;
public class NotificationHelper {
public static void notifyCompat(NotificationManager notificationManager, int notificationId, Notification notification) {
try {
notificationManager.notify(notificationId, notification);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
public static void notifyCompat(NotificationManager notificationManager, String tag, int notificationId, Notification notification) {
try {
notificationManager.notify(tag, notificationId, notification);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
}

View File

@@ -1,12 +0,0 @@
package org.kde.kdeconnect.Helpers;
import java.util.concurrent.atomic.AtomicInteger;
public class NotificationsHelper {
private final static AtomicInteger c = new AtomicInteger((int)System.currentTimeMillis());
public static int getUniqueId() {
return c.incrementAndGet();
}
}

View File

@@ -1,8 +1,6 @@
package org.kde.kdeconnect.Helpers;
import android.util.Log;
import java.security.SecureRandom;
public class RandomHelper {

View File

@@ -23,7 +23,6 @@ package org.kde.kdeconnect.Helpers.SecurityHelpers;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.util.Base64;
import android.util.Log;

View File

@@ -24,11 +24,11 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.RandomHelper;
import org.spongycastle.asn1.x500.X500NameBuilder;
import org.spongycastle.asn1.x500.style.BCStyle;
import org.spongycastle.cert.X509CertificateHolder;
@@ -38,10 +38,10 @@ import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import org.kde.kdeconnect.Helpers.RandomHelper;
import java.io.IOException;
import java.math.BigInteger;
import java.net.Socket;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.PrivateKey;
@@ -50,14 +50,14 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Formatter;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
@@ -131,7 +131,14 @@ public class SslHelper {
}
}
public static boolean isCertificateStored(Context context, String deviceId) {
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
String cert = devicePreferences.getString("certificate", "");
return !cert.isEmpty();
}
public static SSLContext getSslContext(Context context, String deviceId, boolean isDeviceTrusted) {
//TODO: Cache
try {
// Get device private key
PrivateKey privateKey = RsaHelper.getPrivateKey(context);
@@ -141,7 +148,6 @@ public class SslHelper {
if (isDeviceTrusted){
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0);
Log.e("DeviceCertificate", "bytes:"+ Arrays.toString(certificateBytes));
X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes);
remoteDeviceCertificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
}
@@ -196,48 +202,45 @@ public class SslHelper {
}
public static SSLEngine getSslEngine(final Context context, final String deviceId, SslMode sslMode) {
public static void configureSslSocket(SSLSocket socket, boolean isDeviceTrusted, boolean isClient) {
try{
socket.setEnabledProtocols(new String[]{ "TLSv1" }); //Newer TLS versions are only supported on API 16+
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
final boolean isDeviceTrusted = preferences.getBoolean(deviceId, false);
SSLContext tlsContext = getSslContext(context, deviceId, isDeviceTrusted);
SSLEngine sslEngine = tlsContext.createSSLEngine();
sslEngine.setEnabledProtocols(new String[]{ "TLSv1" }); //Newer TLS versions are only supported on API 16+
// These cipher suites are most common of them that are accepted by kde and android during handshake
ArrayList<String> supportedCiphers = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
}
// These cipher suites are most common of them that are accepted by kde and android during handshake
ArrayList<String> supportedCiphers = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"); // API 20+
supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"); // API 20+
supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); // API 11+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"); // API 11+
supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA"); // API 9+
supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5"); // API 9+
} else {
// Following ciphers are for and due to old devices
supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA");
supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5");
sslEngine.setEnabledCipherSuites(supportedCiphers.toArray(new String[supportedCiphers.size()]));
if (sslMode == SslMode.Client){
sslEngine.setUseClientMode(true);
}else{
sslEngine.setUseClientMode(false);
if (isDeviceTrusted) {
sslEngine.setNeedClientAuth(true);
}else {
sslEngine.setWantClientAuth(true);
}
}
return sslEngine;
}catch (Exception e){
e.printStackTrace();
Log.e("SslHelper", "Error creating ssl filter");
supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA"); // API 9+
supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5"); // API 9+
}
return null;
socket.setEnabledCipherSuites(supportedCiphers.toArray(new String[supportedCiphers.size()]));
if (isClient){
socket.setUseClientMode(true);
}else{
socket.setUseClientMode(false);
if (isDeviceTrusted) {
socket.setNeedClientAuth(true);
} else {
socket.setWantClientAuth(true);
}
}
}
public static SSLSocket convertToSslSocket(Context context, Socket socket, String deviceId, boolean isDeviceTrusted, boolean clientMode) throws IOException {
SSLSocketFactory sslsocketFactory = SslHelper.getSslContext(context, deviceId, isDeviceTrusted).getSocketFactory();
SSLSocket sslsocket = (SSLSocket)sslsocketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
SslHelper.configureSslSocket(sslsocket, isDeviceTrusted, clientMode);
return sslsocket;
}
public static String getCertificateHash(Certificate certificate) {
@@ -253,7 +256,6 @@ public class SslHelper {
} catch (Exception e) {
return null;
}
}
public static Certificate parseCertificate(byte[] certificateBytes) throws IOException, CertificateException {

View File

@@ -0,0 +1,16 @@
package org.kde.kdeconnect.Helpers;
import java.nio.charset.Charset;
public class StringsHelper {
public static final Charset UTF8 = Charset.forName("UTF-8");
public static int compare(String a, String b) {
if (a == b) return 0;
if (a == null) return -1;
if (b == null) return 1;
return a.compareToIgnoreCase(b);
}
}

View File

@@ -21,27 +21,34 @@
package org.kde.kdeconnect;
import android.content.Context;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Plugins.PluginFactory;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class NetworkPackage {
public final static int ProtocolVersion = 6;
public final static int ProtocolVersion = 7;
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted";
public static final String PACKAGE_TYPE_CAPABILITIES = "kdeconnect.capabilities";
public static Set<String> protocolPackageTypes = new HashSet<String>() {{
add(PACKAGE_TYPE_IDENTITY);
add(PACKAGE_TYPE_PAIR);
add(PACKAGE_TYPE_ENCRYPTED);
}};
private long mId;
String mType;
@@ -89,40 +96,61 @@ public class NetworkPackage {
public JSONArray getJSONArray(String key) { return mBody.optJSONArray(key); }
public void set(String key, JSONArray value) { try { mBody.put(key,value); } catch(Exception e) { } }
public ArrayList<String> getStringList(String key) {
public Set<String> getStringSet(String key) {
JSONArray jsonArray = mBody.optJSONArray(key);
if (jsonArray == null) return null;
ArrayList<String> list = new ArrayList<>();
Set<String> list = new HashSet<>();
int length = jsonArray.length();
for (int i = 0; i < length; i++) {
try {
String str = jsonArray.getString(i);
list.add(str);
} catch(Exception e) {
}
} catch(Exception e) { }
}
return list;
}
public ArrayList<String> getStringList(String key, ArrayList<String> defaultValue) {
if (mBody.has(key)) return getStringList(key);
public Set<String> getStringSet(String key, Set<String> defaultValue) {
if (mBody.has(key)) return getStringSet(key);
else return defaultValue;
}
public void set(String key, ArrayList<String> value) {
public void set(String key, Set<String> value) {
try {
JSONArray jsonArray = new JSONArray();
for(String str : value) {
jsonArray.put(str);
}
mBody.put(key,jsonArray);
} catch(Exception e) {
} catch(Exception e) { }
}
public List<String> getStringList(String key) {
JSONArray jsonArray = mBody.optJSONArray(key);
if (jsonArray == null) return null;
List<String> list = new ArrayList<>();
int length = jsonArray.length();
for (int i = 0; i < length; i++) {
try {
String str = jsonArray.getString(i);
list.add(str);
} catch(Exception e) { }
}
return list;
}
public List<String> getStringList(String key, List<String> defaultValue) {
if (mBody.has(key)) return getStringList(key);
else return defaultValue;
}
public void set(String key, List<String> value) {
try {
JSONArray jsonArray = new JSONArray();
for(String str : value) {
jsonArray.put(str);
}
mBody.put(key,jsonArray);
} catch(Exception e) { }
}
public boolean has(String key) { return mBody.has(key); }
public boolean isEncrypted() { return mType.equals(PACKAGE_TYPE_ENCRYPTED); }
public String serialize() throws JSONException {
JSONObject jo = new JSONObject();
jo.put("id", mId);
@@ -164,6 +192,8 @@ public class NetworkPackage {
np.mBody.put("deviceName", DeviceHelper.getDeviceName(context));
np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion);
np.mBody.put("deviceType", DeviceHelper.isTablet()? "tablet" : "phone");
np.mBody.put("incomingCapabilities", new JSONArray(PluginFactory.getIncomingCapabilities(context)));
np.mBody.put("outgoingCapabilities", new JSONArray(PluginFactory.getOutgoingCapabilities(context)));
} catch (Exception e) {
e.printStackTrace();
Log.e("NetworkPacakge","Exception on createIdentityPackage");

View File

@@ -22,18 +22,21 @@ package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.Context;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
import java.util.HashSet;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ClipboardListener {
public interface ClipboardObserver {
void clipboardChanged(String content);
}
private HashSet<ClipboardObserver> observers = new HashSet<>();
private final Context context;
private String currentContent;
@@ -41,9 +44,26 @@ public class ClipboardListener {
private ClipboardManager cm = null;
private ClipboardManager.OnPrimaryClipChangedListener listener;
ClipboardListener(final Context ctx, final Device device) {
private static ClipboardListener _instance = null;
public static ClipboardListener instance(Context context) {
if (_instance == null) {
_instance = new ClipboardListener(context);
}
return _instance;
}
public void registerObserver(ClipboardObserver observer) {
observers.add(observer);
}
public void removeObserver(ClipboardObserver observer) {
observers.remove(observer);
}
ClipboardListener(final Context ctx) {
context = ctx;
if(android.os.Build.VERSION.SDK_INT < 11) {
if(android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return;
}
@@ -59,11 +79,14 @@ public class ClipboardListener {
ClipData.Item item = cm.getPrimaryClip().getItemAt(0);
String content = item.coerceToText(context).toString();
if (!content.equals(currentContent)) {
NetworkPackage np = new NetworkPackage(ClipboardPlugin.PACKAGE_TYPE_CLIPBOARD);
np.set("content", content);
device.sendPackage(np);
currentContent = content;
if (content.equals(currentContent)) {
return;
}
currentContent = content;
for (ClipboardObserver observer : observers) {
observer.clipboardChanged(content);
}
} catch (Exception e) {
@@ -76,18 +99,10 @@ public class ClipboardListener {
});
}
public void stop() {
if(android.os.Build.VERSION.SDK_INT < 11) {
return;
}
cm.removePrimaryClipChangedListener(listener);
}
@SuppressWarnings("deprecation")
public void setText(String text) {
currentContent = text;
if(android.os.Build.VERSION.SDK_INT < 11) {
if(android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(text);
}

View File

@@ -20,11 +20,7 @@
package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.widget.Button;
import android.os.Build;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
@@ -47,28 +43,34 @@ public class ClipboardPlugin extends Plugin {
@Override
public boolean isEnabledByDefault() {
//Disabled by default due to just one direction sync(incoming clipboard change) in early version of android.
return (android.os.Build.VERSION.SDK_INT >= 11);
return (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB);
}
private ClipboardListener listener;
@Override
public boolean onPackageReceived(NetworkPackage np) {
String content = np.getString("content");
ClipboardListener.instance(context).setText(content);
return true;
}
private ClipboardListener.ClipboardObserver observer = new ClipboardListener.ClipboardObserver() {
@Override
public void clipboardChanged(String content) {
NetworkPackage np = new NetworkPackage(ClipboardPlugin.PACKAGE_TYPE_CLIPBOARD);
np.set("content", content);
device.sendPackage(np);
}
};
@Override
public boolean onCreate() {
listener = new ClipboardListener(context, device);
ClipboardListener.instance(context).registerObserver(observer);
return true;
}
@Override
public void onDestroy() {
listener.stop();
}
@Override
public boolean onPackageReceived(NetworkPackage np) {
String content = np.getString("content");
listener.setText(content);
return true;
ClipboardListener.instance(context).removeObserver(observer);
}
@Override

View File

@@ -1,6 +1,7 @@
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
import android.app.Activity;
import android.content.Intent;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Ringtone;
@@ -16,6 +17,12 @@ import org.kde.kdeconnect_tp.R;
public class FindMyPhoneActivity extends Activity {
Ringtone ringtone;
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
finish(); //If this activity was already open and we received the ring packet again, just finish it
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@@ -1,12 +1,8 @@
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.Button;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
@@ -23,7 +19,7 @@ public class FindMyPhonePlugin extends Plugin {
@Override
public String getDisplayName() {
return context.getString(R.string.findmyphone_title);
return DeviceHelper.isTablet() ? context.getString(R.string.findmyphone_title_tablet) : context.getString(R.string.findmyphone_title);
}
@Override

View File

@@ -1,10 +1,6 @@
package org.kde.kdeconnect.Plugins.MousePadPlugin;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.InputConnection;
public class KeyInputConnection extends BaseInputConnection {
private KeyListenerView view;

View File

@@ -28,12 +28,12 @@ import android.preference.PreferenceManager;
import android.support.v7.app.ActionBarActivity;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
@@ -42,7 +42,8 @@ import org.kde.kdeconnect_tp.R;
public class MousePadActivity extends ActionBarActivity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, MousePadGestureDetector.OnGestureListener {
String deviceId;
private final static float MinDistanceToSendScroll = 2.5f;
private final static float MinDistanceToSendScroll = 2.5f; // touch gesture scroll
private final static float MinDistanceToSendGenericScroll = 0.1f; // real mouse scroll wheel event
private float mPrevX;
private float mPrevY;
@@ -122,6 +123,7 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
mCurrentSensitivity = 2.0f;
break;
default:
mCurrentSensitivity = 1.0f;
return;
}
@@ -237,6 +239,25 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
return false;
}
@Override
public boolean onGenericMotionEvent(MotionEvent e)
{
if (android.os.Build.VERSION.SDK_INT >= 12) { // MotionEvent.getAxisValue is >= 12
if (e.getAction() == MotionEvent.ACTION_SCROLL) {
final float distanceY = e.getAxisValue(MotionEvent.AXIS_VSCROLL);
accumulatedDistanceY += distanceY;
if (accumulatedDistanceY > MinDistanceToSendGenericScroll || accumulatedDistanceY < -MinDistanceToSendGenericScroll) {
sendScroll(accumulatedDistanceY);
accumulatedDistanceY = 0;
}
}
}
return super.onGenericMotionEvent(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, final float distanceX, final float distanceY) {
// If only one thumb is used then cancel the scroll gesture
@@ -249,17 +270,7 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
accumulatedDistanceY += distanceY;
if (accumulatedDistanceY > MinDistanceToSendScroll || accumulatedDistanceY < -MinDistanceToSendScroll)
{
final float scrollToSendY = accumulatedDistanceY;
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = device.getPlugin(MousePadPlugin.class);
if (mousePadPlugin == null) return;
mousePadPlugin.sendScroll(0, scrollDirection * scrollToSendY);
}
});
sendScroll(scrollDirection * accumulatedDistanceY);
accumulatedDistanceY = 0;
}
@@ -386,6 +397,18 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
});
}
private void sendScroll(final float y) {
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = device.getPlugin(MousePadPlugin.class);
if (mousePadPlugin == null) return;
mousePadPlugin.sendScroll(0, y);
}
});
}
private void showKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInputFromWindow(keyListenerView.getWindowToken(), 0, 0);

View File

@@ -84,6 +84,10 @@ public class MousePadPlugin extends Plugin {
public void sendMouseDelta(float dx, float dy, float sensitivity) {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
if (sensitivity <= 0.0001f) {
sensitivity = 1.0f;
}
np.set("dx", dx*sensitivity);
np.set("dy", dy*sensitivity);

View File

@@ -38,13 +38,13 @@ import android.widget.Spinner;
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.Backends.BaseLinkProvider;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.List;
public class MprisActivity extends ActionBarActivity {
@@ -95,13 +95,14 @@ public class MprisActivity extends ActionBarActivity {
nowPlaying.setText(song);
}
if (mpris.getLength() > -1 && mpris.getPosition() > -1 && !"spotify".equals(mpris.getPlayer().toLowerCase())) {
((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(mpris.getLength()));
//Hacks for Spotify because it reports incorrect info about what it supports
boolean isSpotify = "spotify".equals(mpris.getPlayer().toLowerCase());
if (mpris.getLength() > -1 && mpris.getPosition() > -1 && !isSpotify) {
((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(mpris.getLength()));
SeekBar positionSeek = (SeekBar)findViewById(R.id.positionSeek);
positionSeek.setMax((int)(mpris.getLength()));
positionSeek.setProgress((int)(mpris.getPosition()));
findViewById(R.id.progress_slider).setVisibility(View.VISIBLE);
} else {
findViewById(R.id.progress_slider).setVisibility(View.GONE);
@@ -113,9 +114,23 @@ public class MprisActivity extends ActionBarActivity {
boolean isPlaying = mpris.isPlaying();
if (isPlaying) {
((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_pause);
findViewById(R.id.play_button).setVisibility(mpris.isPauseAllowed() ? View.VISIBLE : View.GONE);
} else {
((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_play);
findViewById(R.id.play_button).setVisibility(mpris.isPlayAllowed() ? View.VISIBLE : View.GONE);
}
if (isSpotify) {
findViewById(R.id.volume_layout).setVisibility(View.INVISIBLE);
findViewById(R.id.rew_button).setVisibility(View.GONE);
findViewById(R.id.ff_button).setVisibility(View.GONE);
} else {
findViewById(R.id.volume_layout).setVisibility(View.VISIBLE);
findViewById(R.id.rew_button).setVisibility(mpris.isSeekAllowed() ? View.VISIBLE : View.GONE);
findViewById(R.id.ff_button).setVisibility(mpris.isSeekAllowed() ? View.VISIBLE : View.GONE);
}
findViewById(R.id.next_button).setVisibility(mpris.isGoNextAllowed() ? View.VISIBLE : View.GONE);
findViewById(R.id.prev_button).setVisibility(mpris.isGoPreviousAllowed() ? View.VISIBLE : View.GONE);
}
});
}
@@ -124,7 +139,7 @@ public class MprisActivity extends ActionBarActivity {
mpris.setPlayerListUpdatedHandler("activity", new Handler() {
@Override
public void handleMessage(Message msg) {
final ArrayList<String> playerList = mpris.getPlayerList();
final List<String> playerList = mpris.getPlayerList();
final ArrayAdapter<String> adapter = new ArrayAdapter<>(MprisActivity.this,
android.R.layout.simple_spinner_item,
playerList.toArray(new String[playerList.size()])
@@ -153,23 +168,16 @@ public class MprisActivity extends ActionBarActivity {
if (pos >= playerList.size()) return;
((TextView) findViewById(R.id.now_playing_textview)).setText("");
String player = playerList.get(pos);
mpris.setPlayer(player);
//Spotify doesn't support changing the volume yet...
//Also doesn't support seeking and telling actual position...
if (player.toLowerCase().equals("spotify")) {
findViewById(R.id.volume_layout).setVisibility(View.INVISIBLE);
findViewById(R.id.rew_button).setVisibility(View.GONE);
findViewById(R.id.ff_button).setVisibility(View.GONE);
findViewById(R.id.positionSeek).setVisibility(View.INVISIBLE);
findViewById(R.id.progress_slider).setVisibility(View.GONE);
} else {
findViewById(R.id.volume_layout).setVisibility(View.VISIBLE);
findViewById(R.id.rew_button).setVisibility(View.VISIBLE);
findViewById(R.id.ff_button).setVisibility(View.VISIBLE);
findViewById(R.id.positionSeek).setVisibility(View.VISIBLE);
if (player.equals(mpris.getPlayer())) {
return; //Player hasn't actually changed
}
mpris.setPlayer(player);
//Clear values from previous player
((TextView) findViewById(R.id.now_playing_textview)).setText("");
((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(0));
((SeekBar)findViewById(R.id.positionSeek)).setMax(0);
}
@Override

View File

@@ -34,6 +34,7 @@ import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class MprisPlugin extends Plugin {
@@ -47,9 +48,14 @@ public class MprisPlugin extends Plugin {
private long length = -1;
private long lastPosition;
private long lastPositionTime;
private boolean playAllowed = true;
private boolean pauseAllowed = true;
private boolean goNextAllowed = true;
private boolean goPreviousAllowed = true;
private boolean seekAllowed = true;
private HashMap<String,Handler> playerStatusUpdated = new HashMap<>();
private ArrayList<String> playerList = new ArrayList<>();
private List<String> playerList = new ArrayList<>();
private HashMap<String,Handler> playerListUpdated = new HashMap<>();
@Override
@@ -130,6 +136,11 @@ public class MprisPlugin extends Plugin {
lastPositionTime = System.currentTimeMillis();
}
playing = np.getBoolean("isPlaying", playing);
playAllowed = np.getBoolean("canPlay", playAllowed);
pauseAllowed = np.getBoolean("canPause", pauseAllowed);
goNextAllowed = np.getBoolean("canGoNext", goNextAllowed);
goPreviousAllowed = np.getBoolean("canGoPrevious", goPreviousAllowed);
seekAllowed = np.getBoolean("canSeek", seekAllowed);
for (String key : playerStatusUpdated.keySet()) {
try {
playerStatusUpdated.get(key).dispatchMessage(new Message());
@@ -142,9 +153,8 @@ public class MprisPlugin extends Plugin {
}
}
if (np.has("playerList")) {
ArrayList<String> newPlayerList = np.getStringList("playerList");
List<String> newPlayerList = np.getStringList("playerList");
if (newPlayerList != null) {
boolean equals = false;
if (newPlayerList.size() == playerList.size()) {
equals = true;
@@ -210,6 +220,11 @@ public class MprisPlugin extends Plugin {
currentSong = "";
volume = 50;
playing = false;
playAllowed = true;
pauseAllowed = true;
goNextAllowed = true;
goPreviousAllowed = true;
seekAllowed = true;
for (String key : playerStatusUpdated.keySet()) {
try {
playerStatusUpdated.get(key).dispatchMessage(new Message());
@@ -222,7 +237,7 @@ public class MprisPlugin extends Plugin {
requestPlayerStatus();
}
public ArrayList<String> getPlayerList() {
public List<String> getPlayerList() {
return playerList;
}
@@ -244,6 +259,26 @@ public class MprisPlugin extends Plugin {
return playing;
}
public boolean isPlayAllowed() {
return playAllowed;
}
public boolean isPauseAllowed() {
return pauseAllowed;
}
public boolean isGoNextAllowed() {
return goNextAllowed;
}
public boolean isGoPreviousAllowed() {
return goPreviousAllowed;
}
public boolean isSeekAllowed() {
return seekAllowed;
}
public long getPosition(){
if(playing) {
return lastPosition + (System.currentTimeMillis() - lastPositionTime);

View File

@@ -26,26 +26,32 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.HashSet;
public class AppDatabase {
public static final String KEY_ROW_ID = "id";
public static final String KEY_NAME = "app";
public static final String KEY_PACKAGE_NAME = "packageName";
public static final String KEY_IS_ENABLED = "isEnabled";
static final private HashSet<String> disabledByDefault = new HashSet<>();
static {
disabledByDefault.add("com.android.messaging"); //We already have sms notifications in the telephony plugin
disabledByDefault.add("com.google.android.googlequicksearchbox"); //Google Now notifications re-spawn every few minutes
}
private static final String DATABASE_NAME = "Applications";
private static final String DATABASE_TABLE = "Applications";
private static final int DATABASE_VERSION = 1;
static final String KEY_PACKAGE_NAME = "packageName";
static final String KEY_IS_ENABLED = "isEnabled";
private final Context ourContext;
private SQLiteDatabase ourDatabase;
private DbHelper ourHelper;
static final String DATABASE_NAME = "Applications";
static final String DATABASE_TABLE = "Applications";
static final int DATABASE_VERSION = 2;
final Context ourContext;
SQLiteDatabase ourDatabase;
DbHelper ourHelper;
public AppDatabase(Context c) {
ourContext = c;
}
private static class DbHelper extends SQLiteOpenHelper{
private static class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -53,73 +59,57 @@ public class AppDatabase {
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + DATABASE_TABLE + "(" + KEY_ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ KEY_NAME + " TEXT NOT NULL, " + KEY_PACKAGE_NAME + " TEXT NOT NULL, " + KEY_IS_ENABLED + " TEXT NOT NULL); ");
db.execSQL("CREATE TABLE " + DATABASE_TABLE + "(" + KEY_PACKAGE_NAME + " TEXT PRIMARY KEY NOT NULL, " + KEY_IS_ENABLED + " TEXT NOT NULL); ");
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i2) {
db.execSQL("DROP TABLE IF EXISTS "+ DATABASE_TABLE);
onCreate(db);
}
}
public void open(){
public void open() {
ourHelper = new DbHelper(ourContext);
ourDatabase = ourHelper.getWritableDatabase();
}
public void close(){
public void close() {
ourHelper.close();
}
public Cursor getAllApplications()
{
String[] columns = new String []{KEY_ROW_ID,KEY_NAME,KEY_PACKAGE_NAME,KEY_IS_ENABLED};
Cursor res = ourDatabase.query(DATABASE_TABLE,columns,null,null,null,null,KEY_NAME);
return res;
}
public void setEnabled(String packageName, boolean isEnabled) {
String[] columns = new String []{KEY_IS_ENABLED};
Cursor res = ourDatabase.query(DATABASE_TABLE, columns, KEY_PACKAGE_NAME + " =? ",new String[]{packageName},null,null,null);
public long create(String appName, String packageName, boolean isEnabled) {
ContentValues cv = new ContentValues();
cv.put(KEY_NAME, appName);
cv.put(KEY_PACKAGE_NAME, packageName);
cv.put(KEY_IS_ENABLED, isEnabled?"true":"false");
return ourDatabase.insert(DATABASE_TABLE, null, cv);
}
public long update(String packageName, boolean isEnabled) {
ContentValues cvUpdate = new ContentValues();
cvUpdate.put(KEY_IS_ENABLED, isEnabled?"true":"false");
return ourDatabase.update(DATABASE_TABLE,cvUpdate,KEY_PACKAGE_NAME + "=?",new String[]{packageName});
}
public boolean exists(String packageName) {
String[] columns = new String []{KEY_ROW_ID};
Cursor res = ourDatabase.query(DATABASE_TABLE,columns,KEY_PACKAGE_NAME + " =? ",new String[]{packageName},null,null,null);
int count = res.getCount();
if (res.getCount() > 0) {
ourDatabase.update(DATABASE_TABLE, cv, KEY_PACKAGE_NAME + "=?",new String[]{packageName});
} else {
cv.put(KEY_PACKAGE_NAME, packageName);
ourDatabase.insert(DATABASE_TABLE, null, cv);
}
res.close();
return (count != 0);
}
public boolean isEnabled(String packageName){
public boolean isEnabled(String packageName) {
String[] columns = new String []{KEY_IS_ENABLED};
Cursor res = ourDatabase.query(DATABASE_TABLE,columns,KEY_PACKAGE_NAME + " =? ",new String[]{packageName},null,null,null);
boolean result = true; //Apps are enabled by default
boolean result;
if (res.getCount() > 0) {
res.moveToFirst();
result = (res.getString(res.getColumnIndex(KEY_IS_ENABLED))).equals("true");
} else {
result = getDefaultStatus(packageName);
}
res.close();
return result;
}
public void delete(String packageName){
ourDatabase.delete(DATABASE_TABLE,KEY_PACKAGE_NAME + " =? ",new String[]{packageName} );
private boolean getDefaultStatus(String packageName) {
return !disabledByDefault.contains(packageName);
}
}

View File

@@ -22,142 +22,144 @@ package org.kde.kdeconnect.Plugins.NotificationsPlugin;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.support.v7.app.ActionBarActivity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.CheckedTextView;
import android.widget.ListView;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Helpers.StringsHelper;
import org.kde.kdeconnect_tp.R;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class NotificationFilterActivity extends ActionBarActivity {
private AppDatabase appDatabase;
AppDatabase appDatabase;
static class AppListInfo {
String pkg;
String name;
Drawable icon;
boolean isEnabled;
}
AppListInfo[] apps;
class AppListAdapter extends BaseAdapter {
@Override
public int getCount() {
return apps.length;
}
@Override
public AppListInfo getItem(int position) {
return apps[position];
}
@Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View view, ViewGroup parent) {
if (view == null) {
LayoutInflater inflater = getLayoutInflater();
view = inflater.inflate(android.R.layout.simple_list_item_multiple_choice, null, true);
}
CheckedTextView checkedTextView = (CheckedTextView)view;
checkedTextView.setText(apps[position].name);
checkedTextView.setCompoundDrawablesWithIntrinsicBounds(apps[position].icon, null, null, null);
checkedTextView.setCompoundDrawablePadding((int)(8*getResources().getDisplayMetrics().density));
return view;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification_filter);
final ListView listView = (ListView)findViewById(R.id.lvFilterApps);
appDatabase = new AppDatabase(this);
appDatabase = new AppDatabase(NotificationFilterActivity.this);
deleteUninstalledApps();
addNewlyInstalledApps();
new Thread(new Runnable() {
@Override
public void run() {
appDatabase.open();
Cursor res = appDatabase.getAllApplications();
res.moveToFirst();
PackageManager packageManager = getPackageManager();
List<ApplicationInfo> appList = packageManager.getInstalledApplications(0);
int count = appList.size();
String[] appName = new String[res.getCount()];
final String[] pkgName = new String[res.getCount()];
Boolean[] isFiltered = new Boolean[res.getCount()];
apps = new AppListInfo[count];
appDatabase.open();
for (int i = 0; i < count; i++) {
ApplicationInfo appInfo = appList.get(i);
apps[i] = new AppListInfo();
apps[i].pkg = appInfo.packageName;
apps[i].name = appInfo.loadLabel(packageManager).toString();
apps[i].icon = resizeIcon(appInfo.loadIcon(packageManager), 48);
apps[i].isEnabled = appDatabase.isEnabled(appInfo.packageName);
}
appDatabase.close();
int i = 0;
while(!res.isAfterLast()){
appName[i] = res.getString(res.getColumnIndex(AppDatabase.KEY_NAME));
pkgName[i] = res.getString(res.getColumnIndex(AppDatabase.KEY_PACKAGE_NAME));
isFiltered[i] = res.getString(res.getColumnIndex(AppDatabase.KEY_IS_ENABLED)).equals("true");
res.moveToNext();
i++;
}
res.close();
appDatabase.close();
Arrays.sort(apps, new Comparator<AppListInfo>() {
@Override
public int compare(AppListInfo lhs, AppListInfo rhs) {
return StringsHelper.compare(lhs.name, rhs.name);
}
});
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_multiple_choice, android.R.id.text1, appName);
runOnUiThread(new Runnable() {
@Override
public void run() {
displayAppList();
}
});
}
}).start();
}
void displayAppList() {
final ListView listView = (ListView) findViewById(R.id.lvFilterApps);
AppListAdapter adapter = new AppListAdapter();
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
for (i = 0 ; i < isFiltered.length; i++){
if (isFiltered[i]) {
listView.setItemChecked(i, true);
}
}
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
boolean checked = listView.isItemChecked(i);
//Log.e("NotificationFilterActivity", pkgName[i] + ":" + checked);
appDatabase.open();
appDatabase.update(pkgName[i], checked);
appDatabase.setEnabled(apps[i].pkg, checked);
appDatabase.close();
apps[i].isEnabled = checked;
}
});
}
// Delete apps from database which are uninstalled
private void deleteUninstalledApps(){
Cursor res;
appDatabase.open();
res = appDatabase.getAllApplications();
if (res != null) {
res.moveToFirst();
while (!res.isAfterLast()) {
String packageName = res.getString(res.getColumnIndex(AppDatabase.KEY_PACKAGE_NAME));
if (!isPackageInstalled(packageName)) {
appDatabase.delete(packageName);
}
res.moveToNext();
}
res.close();
}
appDatabase.close();
}
// Adding newly installed apps in database
private void addNewlyInstalledApps() {
List<ApplicationInfo> PackList = getPackageManager().getInstalledApplications(0);
appDatabase.open();
for (int i=0; i < PackList.size(); i++)
{
ApplicationInfo PackInfo = PackList.get(i);
String appName = PackInfo.loadLabel(getPackageManager()).toString();
String packageName = PackInfo.packageName;
if ( (PackInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 ) {
if (!appDatabase.exists(packageName)) {
appDatabase.create(appName, packageName, true);
}
//Log.e("App FLAG_UPDATED_SYSTEM_APP: " + Integer.toString(i), appName);
} else if ( (PackInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
//ignore these apps
} else {
if (!appDatabase.exists(packageName)) {
appDatabase.create(appName, packageName, true);
}
//Log.e("App : " + Integer.toString(i), appName);
}
for (int i = 0 ; i < apps.length; i++) {
listView.setItemChecked(i, apps[i].isEnabled);
}
appDatabase.close();
listView.setVisibility(View.VISIBLE);
findViewById(R.id.spinner).setVisibility(View.GONE);
}
private boolean isPackageInstalled(String packageName){
PackageManager pm = getPackageManager();
try {
pm.getPackageInfo(packageName, PackageManager.GET_META_DATA);
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onStart() {
super.onStart();
@@ -170,4 +172,19 @@ public class NotificationFilterActivity extends ActionBarActivity {
BackgroundService.removeGuiInUseCounter(this);
}
Drawable resizeIcon(Drawable icon, int maxSize) {
Resources res = getResources();
//Convert to display pixels
maxSize = (int)(maxSize*res.getDisplayMetrics().density);
Bitmap bitmap = Bitmap.createBitmap(maxSize, maxSize, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
icon.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
icon.draw(canvas);
return new BitmapDrawable(res, bitmap);
}
}

View File

@@ -24,30 +24,21 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.UserInterface.SettingsActivity;
import org.kde.kdeconnect_tp.R;
import java.io.InputStream;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
@@ -111,11 +102,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
return false;
}
}
// request all existing notifications
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION_REQUEST);
np.set("request", true);
device.sendPackage(np);
return true;
}
@@ -135,7 +121,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
String id = getNotificationKeyCompat(statusBarNotification);
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION_REQUEST);
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION);
np.set("id", id);
np.set("isCancel", true);
device.sendPackage(np);
@@ -184,11 +170,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
return;
}
if (packageName.equals("com.google.android.googlequicksearchbox")) {
//HACK: Hide Google Now notifications that keep constantly popping up (and without text because we don't know how to read them properly)
return;
}
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION);
if (packageName.equals("org.kde.kdeconnect_tp"))
@@ -324,58 +305,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
}
});
} else {
if (!np.has("ticker") || !np.has("appName") || !np.has("id")) {
Log.e("NotificationsPlugin", "Received notification package lacks properties");
} else {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MaterialActivity.class);
stackBuilder.addNextIntent(new Intent(context, MaterialActivity.class));
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
Bitmap largeIcon = null;
if (np.hasPayload()) {
int width = 64; // default icon dimensions
int height = 64;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
width = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
height = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
}
final InputStream input = np.getPayload();
largeIcon = BitmapFactory.decodeStream(np.getPayload());
try { input.close(); } catch (Exception e) { }
if (largeIcon != null) {
//Log.i("NotificationsPlugin", "hasPayload: size=" + largeIcon.getWidth() + "/" + largeIcon.getHeight() + " opti=" + width + "/" + height);
if (largeIcon.getWidth() > width || largeIcon.getHeight() > height) {
// older API levels don't scale notification icons automatically, therefore:
largeIcon = Bitmap.createScaledBitmap(largeIcon, width, height, false);
}
}
}
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(np.getString("appName"))
.setContentText(np.getString("ticker"))
.setContentIntent(resultPendingIntent)
.setTicker(np.getString("ticker"))
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(largeIcon)
.setAutoCancel(true)
.setLocalOnly(true) // to avoid bouncing the notification back to other kdeconnect nodes
.setDefaults(Notification.DEFAULT_ALL)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
try {
// tag all incoming notifications
notificationManager.notify("kdeconnectId:" + np.getString("id", "0"), np.getInt("id", 0), noti);
} catch (Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
}
return true;
@@ -419,12 +348,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION, PACKAGE_TYPE_NOTIFICATION_REQUEST};
return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION, PACKAGE_TYPE_NOTIFICATION_REQUEST};
return new String[]{PACKAGE_TYPE_NOTIFICATION};
}
//For compat with API<21, because lollipop changed the way to cancel notifications
@@ -433,6 +362,10 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
service.cancelNotification(compatKey);
} else {
int first = compatKey.indexOf(':');
if (first == -1) {
Log.e("cancelNotificationCompa","Not formated like a notification key: "+ compatKey);
return;
}
int last = compatKey.lastIndexOf(':');
String packageName = compatKey.substring(0, first);
String tag = compatKey.substring(first + 1, last);

View File

@@ -30,9 +30,10 @@ import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect_tp.R;
@@ -89,12 +90,7 @@ public class PingPlugin extends Plugin {
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
try {
notificationManager.notify(id, noti);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
NotificationHelper.notifyCompat(notificationManager, id, noti);
return true;

View File

@@ -161,8 +161,9 @@ public abstract class Plugin {
public void onDestroy() { }
/**
* If onCreate returns false, should create a dialog explaining
* the problem (and how to fix it, if possible) to the user.
* Called when a plugin receives a package. By convention we return true
* when we have done something in response to the package or false
* otherwise, even though that value is unused as of now.
*/
public boolean onPackageReceived(NetworkPackage np) { return false; }

View File

@@ -26,25 +26,24 @@ import android.util.Log;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardPlugin;
import org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhonePlugin;
import org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadPlugin;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardPlugin;
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin;
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin;
import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin;
import org.kde.kdeconnect.Plugins.ReceiveNotificationsPlugin.ReceiveNotificationsPlugin;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
import org.kde.kdeconnect.Plugins.SharePlugin.SharePlugin;
import org.kde.kdeconnect.Plugins.TelepathyPlugin.TelepathyPlugin;
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.lang.reflect.Method;
public class PluginFactory {
@@ -109,10 +108,9 @@ public class PluginFactory {
}
private static final Map<String, Class> availablePlugins = new TreeMap<>();
private static final Map<String, PluginInfo> availablePluginsInfo = new TreeMap<>();
private static final Map<String, PluginInfo> pluginInfoCache = new TreeMap<>();
static {
//TODO: Use reflection to find all subclasses of Plugin, instead of adding them manually
PluginFactory.registerPlugin(TelephonyPlugin.class);
PluginFactory.registerPlugin(PingPlugin.class);
PluginFactory.registerPlugin(MprisPlugin.class);
@@ -120,16 +118,17 @@ public class PluginFactory {
PluginFactory.registerPlugin(BatteryPlugin.class);
PluginFactory.registerPlugin(SftpPlugin.class);
PluginFactory.registerPlugin(NotificationsPlugin.class);
PluginFactory.registerPlugin(ReceiveNotificationsPlugin.class);
PluginFactory.registerPlugin(MousePadPlugin.class);
PluginFactory.registerPlugin(SharePlugin.class);
//PluginFactory.registerPlugin(TelepathyPlugin.class);
PluginFactory.registerPlugin(TelepathyPlugin.class);
PluginFactory.registerPlugin(FindMyPhonePlugin.class);
PluginFactory.registerPlugin(RunCommandPlugin.class);
}
public static PluginInfo getPluginInfo(Context context, String pluginKey) {
PluginInfo info = availablePluginsInfo.get(pluginKey); //Is it cached?
PluginInfo info = pluginInfoCache.get(pluginKey); //Is it cached?
if (info != null) {
return info;
}
@@ -140,7 +139,7 @@ public class PluginFactory {
info = new PluginInfo(p.getDisplayName(), p.getDescription(), p.getIcon(),
p.isEnabledByDefault(), p.hasSettings(), p.listensToUnpairedDevices(),
p.getSupportedPackageTypes(), p.getOutgoingPackageTypes());
availablePluginsInfo.put(pluginKey, info); //Cache it
pluginInfoCache.put(pluginKey, info); //Cache it
return info;
} catch(Exception e) {
Log.e("PluginFactory","getPluginInfo exception");
@@ -183,4 +182,39 @@ public class PluginFactory {
}
}
public static Set<String> getIncomingCapabilities(Context context) {
HashSet<String> capabilities = new HashSet<>();
for (String pluginId : availablePlugins.keySet()) {
PluginInfo plugin = getPluginInfo(context, pluginId);
capabilities.addAll(plugin.getSupportedPackageTypes());
}
return capabilities;
}
public static Set<String> getOutgoingCapabilities(Context context) {
HashSet<String> capabilities = new HashSet<>();
for (String pluginId : availablePlugins.keySet()) {
PluginInfo plugin = getPluginInfo(context, pluginId);
capabilities.addAll(plugin.getOutgoingPackageTypes());
}
return capabilities;
}
public static Set<String> pluginsForCapabilities(Context context, Set<String> incoming, Set<String> outgoing) {
HashSet<String> plugins = new HashSet<>();
for (String pluginId : availablePlugins.keySet()) {
PluginInfo plugin = getPluginInfo(context, pluginId);
//Check incoming against outgoing
if (Collections.disjoint(outgoing, plugin.getSupportedPackageTypes())
&& Collections.disjoint(incoming, plugin.getOutgoingPackageTypes())) {
Log.i("PluginFactory", "Won't load " + pluginId + " because of unmatched capabilities");
continue; //No capabilities in common, do not load this plugin
}
plugins.add(pluginId);
}
return plugins;
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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.Plugins.ReceiveNotificationsPlugin;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect_tp.R;
import java.io.InputStream;
public class ReceiveNotificationsPlugin extends Plugin {
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
public final static String PACKAGE_TYPE_NOTIFICATION_REQUEST = "kdeconnect.notification.request";
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_receive_notifications);
}
@Override
public String getDescription() {
return context.getResources().getString(R.string.pref_plugin_receive_notifications_desc);
}
@Override
public boolean isEnabledByDefault() {
return false;
}
@Override
public boolean onCreate() {
// request all existing notifications
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION_REQUEST);
np.set("request", true);
device.sendPackage(np);
return true;
}
@Override
public boolean onPackageReceived(final NetworkPackage np) {
if (!np.has("ticker") || !np.has("appName") || !np.has("id")) {
Log.e("NotificationsPlugin", "Received notification package lacks properties");
} else {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MaterialActivity.class);
stackBuilder.addNextIntent(new Intent(context, MaterialActivity.class));
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
Bitmap largeIcon = null;
if (np.hasPayload()) {
int width = 64; // default icon dimensions
int height = 64;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
width = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
height = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
}
final InputStream input = np.getPayload();
largeIcon = BitmapFactory.decodeStream(np.getPayload());
try { input.close(); } catch (Exception e) { }
if (largeIcon != null) {
//Log.i("NotificationsPlugin", "hasPayload: size=" + largeIcon.getWidth() + "/" + largeIcon.getHeight() + " opti=" + width + "/" + height);
if (largeIcon.getWidth() > width || largeIcon.getHeight() > height) {
// older API levels don't scale notification icons automatically, therefore:
largeIcon = Bitmap.createScaledBitmap(largeIcon, width, height, false);
}
}
}
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(np.getString("appName"))
.setContentText(np.getString("ticker"))
.setContentIntent(resultPendingIntent)
.setTicker(np.getString("ticker"))
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(largeIcon)
.setAutoCancel(true)
.setLocalOnly(true) // to avoid bouncing the notification back to other kdeconnect nodes
.setDefaults(Notification.DEFAULT_ALL)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationHelper.notifyCompat(notificationManager, "kdeconnectId:" + np.getString("id", "0"), np.getInt("id", 0), noti);
}
return true;
}
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST};
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2016 Thomas Posch <kdeconnect@online.posch.name>
*
* 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.Plugins.RunCommandPlugin;
import org.kde.kdeconnect.UserInterface.List.EntryItem;
public class CommandEntry extends EntryItem {
private final String key;
public CommandEntry(String name, String cmd, String key) {
super(name, cmd);
this.key = key;
}
public String getKey() {
return key;
}
public String getName() {
return title;
}
}

View File

@@ -32,11 +32,12 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.UserInterface.List.EntryItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class RunCommandActivity extends ActionBarActivity {
@@ -59,28 +60,33 @@ public class RunCommandActivity extends ActionBarActivity {
public void run() {
ListView view = (ListView) findViewById(R.id.listView1);
final ArrayList<JSONObject> commands = plugin.getCommandList();
ArrayList<ListAdapter.Item> commandItems = new ArrayList<>();
for (JSONObject obj : commands) {
final ArrayList<ListAdapter.Item> commandItems = new ArrayList<>();
for (JSONObject obj : plugin.getCommandList()) {
try {
commandItems.add(new EntryItem(obj.getString("name"), obj.getString("command")));
commandItems.add(new CommandEntry(obj.getString("name"),
obj.getString("command"), obj.getString("key")));
} catch (JSONException e) {
e.printStackTrace();
}
}
Collections.sort(commandItems, new Comparator<ListAdapter.Item>() {
@Override
public int compare(ListAdapter.Item lhs, ListAdapter.Item rhs) {
String lName = ((CommandEntry) lhs).getName();
String rName = ((CommandEntry) rhs).getName();
return lName.compareTo(rName);
}
});
ListAdapter adapter = new ListAdapter(RunCommandActivity.this, commandItems);
view.setAdapter(adapter);
view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
try {
plugin.runCommand(commands.get(i).getString("key"));
} catch (JSONException e) {
e.printStackTrace();
}
CommandEntry entry = (CommandEntry) commandItems.get(i);
plugin.runCommand(entry.getKey());
}
});
}

View File

@@ -68,9 +68,9 @@ public class SftpPlugin extends Plugin {
NetworkPackage np2 = new NetworkPackage(PACKAGE_TYPE_SFTP);
np2.set("ip", server.getLocalIpAddress());
np2.set("port", server.port);
np2.set("user", server.passwordAuth.getUser());
np2.set("password", server.passwordAuth.getPassword());
np2.set("port", server.getPort());
np2.set("user", SimpleSftpServer.USER);
np2.set("password", server.getPassword());
//Kept for compatibility, in case "multiPaths" is not possible or the other end does not support it
np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath());

View File

@@ -21,10 +21,10 @@
package org.kde.kdeconnect.Plugins.SftpPlugin;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.KeyExchange;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.Session;
import org.apache.sshd.common.util.SecurityUtils;
@@ -35,110 +35,71 @@ import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.SshFile;
import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.kex.DHG1;
import org.apache.sshd.server.kex.DHG14;
import org.apache.sshd.server.filesystem.NativeFileSystemView;
import org.apache.sshd.server.filesystem.NativeSshFile;
import org.apache.sshd.server.kex.DHG1;
import org.apache.sshd.server.kex.DHG14;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.sftp.SftpSubsystem;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.MediaStoreHelper;
import org.kde.kdeconnect.Helpers.RandomHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import java.io.File;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.PublicKey;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
class SimplePasswordAuthenticator implements PasswordAuthenticator {
public void setUser(String user) {this.user = user;}
public String getUser() {return this.user;}
public void setPassword(String password) {this.password = password;}
public String getPassword() {return this.password;}
@Override
public boolean authenticate(String user, String password, ServerSession session) {
return user.equals(this.user) && password.equals(this.password);
}
private String user;
private String password;
}
class SimplePublicKeyAuthenticator implements PublickeyAuthenticator {
private final List<PublicKey> keys = new ArrayList<>();
public void addKey(PublicKey key) {
keys.add(key);
}
@Override
public boolean authenticate(String user, PublicKey key, ServerSession session) {
for (PublicKey k : keys) {
if (key.equals(k)) {
return true;
}
}
return false;
}
}
class SimpleSftpServer {
private static final int STARTPORT = 1739;
private static final int ENDPORT = 1764;
private static final String USER = "kdeconnect";
static final String USER = "kdeconnect";
public static int port = -1;
private static boolean started = false;
private int port = -1;
private boolean started = false;
public final SimplePasswordAuthenticator passwordAuth = new SimplePasswordAuthenticator();
public final SimplePublicKeyAuthenticator keyAuth = new SimplePublicKeyAuthenticator();
private final SimplePasswordAuthenticator passwordAuth = new SimplePasswordAuthenticator();
private final SimplePublicKeyAuthenticator keyAuth = new SimplePublicKeyAuthenticator();
static {
Security.insertProviderAt( SslHelper.BC, 1);
Security.insertProviderAt(SslHelper.BC, 1);
SecurityUtils.setRegisterBouncyCastle(false);
}
private final SshServer sshd = SshServer.setUpDefaultServer();
public void init(Context ctx, Device device) {
public void init(Context context, Device device) {
sshd.setKeyExchangeFactories(Arrays.asList(
new DHG14.Factory(),
new DHG1.Factory()));
passwordAuth.setUser(USER);
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(ctx.getFilesDir() + "/sftpd.ser"));
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(context.getFilesDir() + "/sftpd.ser"));
sshd.setFileSystemFactory(new SecureFileSystemFactory());
sshd.setFileSystemFactory(new AndroidFileSystemFactory(context));
sshd.setCommandFactory(new ScpCommandFactory());
sshd.setSubsystemFactories(Collections.singletonList((NamedFactory<Command>)new SftpSubsystem.Factory()));
if (device.publicKey != null) {
keyAuth.addKey(device.publicKey);
keyAuth.deviceKey = device.publicKey;
sshd.setPublickeyAuthenticator(keyAuth);
} else {
sshd.setPasswordAuthenticator(passwordAuth);
}
sshd.setPasswordAuthenticator(passwordAuth);
}
public boolean start() {
if (!started) {
String password = RandomHelper.randomString(28);
passwordAuth.setPassword(password);
passwordAuth.password = RandomHelper.randomString(28);
port = STARTPORT;
while(!started) {
@@ -170,6 +131,14 @@ class SimpleSftpServer {
}
}
public String getPassword() {
return passwordAuth.password;
}
public int getPort() {
return port;
}
public String getLocalIpAddress() {
String ip6 = null;
try {
@@ -192,57 +161,91 @@ class SimpleSftpServer {
return ip6;
}
}
static class AndroidFileSystemFactory implements FileSystemFactory {
class SecureFileSystemFactory implements FileSystemFactory {
final private Context context;
public SecureFileSystemFactory() {}
public AndroidFileSystemFactory(Context context) {
this.context = context;
}
@Override
@Override
public FileSystemView createFileSystemView(final Session username) {
final String base = "/";
return new SecureFileSystemView(base, username.getUsername());
return new AndroidFileSystemView(username.getUsername(), context);
}
}
class SecureFileSystemView extends NativeFileSystemView {
// the first and the last character will always be '/'
// It is always with respect to the root directory.
private String currDir = "/";
private String rootDir = "/";
private String userName;
//
public SecureFileSystemView(final String rootDir, final String userName) {
super(userName);
this.rootDir = NativeSshFile.normalizeSeparateChar(rootDir);
static class AndroidFileSystemView extends NativeFileSystemView {
final private String userName;
final private Context context;
public AndroidFileSystemView(final String userName, Context context) {
super(userName, true);
this.userName = userName;
}
//
@Override
public SshFile getFile(final String file) {
return getFile(currDir, file);
this.context = context;
}
@Override
public SshFile getFile(final SshFile baseDir, final String file) {
return getFile(baseDir.getAbsolutePath(), file);
}
//
protected SshFile getFile(final String dir, final String file) {
// get actual file object
final boolean caseInsensitive = false;
String physicalName = NativeSshFile.getPhysicalName("/", dir, file, caseInsensitive);
File fileObj = new File(rootDir, physicalName); // chroot
// strip the root directory and return
String userFileName = physicalName.substring("/".length() - 1);
return new SecureSshFile(userFileName, fileObj, userName);
File fileObj = new File(dir, file);
return new AndroidSshFile(fileObj, userName, context);
}
}
class SecureSshFile extends NativeSshFile {
public SecureSshFile(final String fileName, final File file, final String userName) {
super(fileName, file, userName);
static class AndroidSshFile extends NativeSshFile {
final private Context context;
final private File file;
public AndroidSshFile(final File file, final String userName, Context context) {
super(file.getAbsolutePath(), file, userName);
this.context = context;
this.file = file;
}
@Override
public boolean delete() {
//Log.e("Sshd", "deleting file");
boolean ret = super.delete();
if (ret) {
MediaStoreHelper.indexFile(context, Uri.fromFile(file));
}
return ret;
}
@Override
public boolean create() throws IOException {
//Log.e("Sshd", "creating file");
boolean ret = super.create();
if (ret) {
MediaStoreHelper.indexFile(context, Uri.fromFile(file));
}
return ret;
}
}
static class SimplePasswordAuthenticator implements PasswordAuthenticator {
public String password;
@Override
public boolean authenticate(String user, String password, ServerSession session) {
return user.equals(SimpleSftpServer.USER) && password.equals(this.password);
}
}
static class SimplePublicKeyAuthenticator implements PublickeyAuthenticator {
public PublicKey deviceKey;
@Override
public boolean authenticate(String user, PublicKey key, ServerSession session) {
return deviceKey.equals(key);
}
}
}

View File

@@ -0,0 +1,122 @@
package org.kde.kdeconnect.Plugins.SharePlugin;
import android.app.NotificationManager;
import android.content.Context;
import android.content.res.Resources;
import android.support.v4.app.NotificationCompat;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
class NotificationUpdateCallback extends Device.SendPackageStatusCallback {
final Context context;
final Resources res;
final Device device;
final NotificationManager notificationManager;
final NotificationCompat.Builder builder;
final ArrayList<NetworkPackage> toSend;
final int notificationId;
int sentFiles = 0;
final int numFiles;
NotificationUpdateCallback(Context context, Device device, ArrayList<NetworkPackage> toSend) {
this.context = context;
this.toSend = toSend;
this.device = device;
this.res = context.getResources();
String title;
if (toSend.size() > 1) {
title = res.getString(R.string.outgoing_files_title, device.getName());
} else {
title = res.getString(R.string.outgoing_file_title, device.getName());
}
notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(context)
.setSmallIcon(android.R.drawable.stat_sys_upload)
.setAutoCancel(true)
.setProgress(100, 0, false)
.setContentTitle(title)
.setTicker(title);
notificationId = (int)System.currentTimeMillis();
numFiles = toSend.size();
}
@Override
public void onProgressChanged(int progress) {
builder.setProgress(100 * numFiles, (100 * sentFiles) + progress, false);
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
@Override
public void onSuccess() {
sentFiles++;
if (sentFiles == numFiles) {
updateDone(true);
} else {
updateText();
}
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
@Override
public void onFailure(Throwable e) {
updateDone(false);
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
private void updateText() {
String text;
if (numFiles > 1) {
text = res.getString(R.string.outgoing_files_text, sentFiles, numFiles);
} else {
text = res.getString(R.string.outgoing_file_text, device.getName());
}
builder.setContentText(text);
}
private void updateDone(boolean successful) {
int icon;
String title;
String text;
int progress;
if (successful) {
progress = 1;
if (numFiles > 1) {
text = res.getString(R.string.outgoing_files_text, sentFiles, numFiles);
} else {
final String filename = toSend.get(0).getString("filename");
text = res.getString(R.string.sent_file_text, filename);
}
title = res.getString(R.string.sent_file_title, device.getName());
icon = android.R.drawable.stat_sys_upload_done;
} else {
progress = 0;
final String filename = toSend.get(sentFiles).getString("filename");
title = res.getString(R.string.sent_file_failed_title, device.getName());
text = res.getString(R.string.sent_file_failed_text, filename);
icon = android.R.drawable.stat_notify_error;
}
builder.setOngoing(false)
.setTicker(title)
.setContentTitle(title)
.setContentText(text)
.setSmallIcon(icon)
.setProgress(progress, progress, false); //setting progress to 0 out of 0 remove the progress bar
}
}

View File

@@ -21,45 +21,18 @@
package org.kde.kdeconnect.Plugins.SharePlugin;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.List.EntryItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.SectionItem;
import org.kde.kdeconnect_tp.R;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
public class SendFileActivity extends ActionBarActivity {
@@ -90,13 +63,15 @@ public class SendFileActivity extends ActionBarActivity {
case Activity.RESULT_FIRST_USER:
if (resultCode == RESULT_OK) {
final Uri uri = data.getData();
Log.e("SendFileActivity", "File Uri: " + uri.toString());
Log.i("SendFileActivity", "File Uri: " + uri.toString());
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(mDeviceId);
if (device == null) {
Log.e("SendFileActivity", "Device is null");
finish();
return;
}
ArrayList<Uri> uris = new ArrayList<>();
uris.add(uri);

View File

@@ -170,7 +170,7 @@ public class ShareActivity extends ActionBarActivity {
} catch (Exception e) {
isUrl = false;
}
NetworkPackage np = new NetworkPackage(SharePlugin.PACKAGE_TYPE_SHARE);
NetworkPackage np = new NetworkPackage(SharePlugin.PACKAGE_TYPE_SHARE_REQUEST);
if (isUrl) {
np.set("url", text);
} else {
@@ -198,6 +198,7 @@ public class ShareActivity extends ActionBarActivity {
ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
setContentView(R.layout.activity_list);
}

View File

@@ -21,6 +21,7 @@
package org.kde.kdeconnect.Plugins.SharePlugin;
import android.app.Activity;
import android.app.DownloadManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -33,32 +34,32 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.content.ContextCompat;
import android.support.v4.provider.DocumentFile;
import android.util.Log;
import android.widget.Toast;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.FilesHelper;
import org.kde.kdeconnect.Helpers.MediaStoreHelper;
import org.kde.kdeconnect.Helpers.NotificationHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.SettingsActivity;
import org.kde.kdeconnect_tp.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public class SharePlugin extends Plugin {
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
//public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
public final static String PACKAGE_TYPE_SHARE_REQUEST = "kdeconnect.share.request";
final static boolean openUrlsDirectly = true;
@@ -110,23 +111,21 @@ public class SharePlugin extends Plugin {
final InputStream input = np.getPayload();
final long fileLength = np.getPayloadSize();
final String filename = np.getString("filename", Long.toString(System.currentTimeMillis()));
final String originalFilename = np.getString("filename", Long.toString(System.currentTimeMillis()));
String deviceDir = FilesHelper.toFileSystemSafeName(device.getName());
//Get the external storage and append "/kdeconnect/DEVICE_NAME/"
String destinationDir = Environment.getExternalStorageDirectory().getPath();
destinationDir = new File(destinationDir, "kdeconnect").getPath();
destinationDir = new File(destinationDir, deviceDir).getPath();
//We need to check for already existing files only when storing in the default path.
//User-defined paths use the new Storage Access Framework that already handles this.
final boolean customDestination = ShareSettingsActivity.isCustomDestinationEnabled(context);
final String defaultPath = ShareSettingsActivity.getDefaultDestinationDirectory().getAbsolutePath();
final String filename = customDestination? originalFilename : FilesHelper.findNonExistingNameForNewFile(defaultPath, originalFilename);
//Create directories if needed
new File(destinationDir).mkdirs();
final String nameWithoutExtension = FilesHelper.getFileNameWithoutExt(filename);
final String mimeType = FilesHelper.getMimeTypeFromFile(filename);
//Append filename to the destination path
final File destinationFullPath = new File(destinationDir, filename);
//Log.e("SharePlugin", "destinationFullPath:" + destinationFullPath);
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
final DocumentFile destinationFolderDocument = ShareSettingsActivity.getDestinationDirectory(context);
final DocumentFile destinationDocument = destinationFolderDocument.createFile(mimeType, nameWithoutExtension);
final OutputStream destinationOutput = context.getContentResolver().openOutputStream(destinationDocument.getUri());
final Uri destinationUri = destinationDocument.getUri();
final int notificationId = (int)System.currentTimeMillis();
Resources res = context.getResources();
@@ -139,103 +138,84 @@ public class SharePlugin extends Plugin {
.setOngoing(true)
.setProgress(100,0,true);
try {
notificationManager.notify(notificationId,builder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationHelper.notifyCompat(notificationManager,notificationId, builder.build());
new Thread(new Runnable() {
@Override
public void run() {
OutputStream output = null;
boolean successul = true;
boolean successful = true;
try {
output = new FileOutputStream(destinationFullPath.getPath());
byte data[] = new byte[1024];
long progress = 0, prevProgressPercentage = 0;
int count;
while ((count = input.read(data)) >= 0) {
progress += count;
output.write(data, 0, count);
destinationOutput.write(data, 0, count);
if (fileLength > 0) {
if (progress >= fileLength) break;
long progressPercentage = (progress * 100 / fileLength);
if (progressPercentage != prevProgressPercentage) {
prevProgressPercentage = progressPercentage;
builder.setProgress(100, (int) progressPercentage, false);
try {
notificationManager.notify(notificationId,builder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
}
}
//else Log.e("SharePlugin", "Infinite loop? :D");
}
output.flush();
destinationOutput.flush();
} catch (Exception e) {
successul = false;
successful = false;
Log.e("SharePlugin", "Receiver thread exception");
e.printStackTrace();
} finally {
try { output.close(); } catch (Exception e) {}
try { destinationOutput.close(); } catch (Exception e) {}
try { input.close(); } catch (Exception e) {}
}
try {
Log.i("SharePlugin", "Transfer finished");
//Make sure it is added to the Android Gallery
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(Uri.fromFile(destinationFullPath));
context.sendBroadcast(mediaScanIntent);
Log.i("SharePlugin", "Transfer finished: "+destinationUri.getPath());
//Update the notification and allow to open the file from it
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(destinationFullPath), FilesHelper.getMimeTypeFromFile(destinationFullPath.getPath()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntent(intent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
Resources res = context.getResources();
String message = successul? res.getString(R.string.received_file_title, device.getName()) : res.getString(R.string.received_file_fail_title, device.getName());
String message = successful? res.getString(R.string.received_file_title, device.getName()) : res.getString(R.string.received_file_fail_title, device.getName());
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentTitle(message)
.setTicker(message)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setAutoCancel(true);
if (successul) {
builder.setContentText(res.getString(R.string.received_file_text, filename))
if (successful) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(destinationUri, mimeType);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntent(intent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentText(res.getString(R.string.received_file_text, destinationDocument.getName()))
.setContentIntent(resultPendingIntent);
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("share_notification_preference", true)) {
builder.setDefaults(Notification.DEFAULT_ALL);
}
NotificationHelper.notifyCompat(notificationManager, notificationId, builder.build());
try {
notificationManager.notify(notificationId,builder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
if (successful) {
if (!customDestination) {
Log.i("SharePlugin","Adding to downloads");
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
manager.addCompletedDownload(destinationUri.getLastPathSegment(), device.getName(), true, mimeType, destinationUri.getPath(), fileLength, false);
} else {
//Make sure it is added to the Android Gallery anyway
MediaStoreHelper.indexFile(context, destinationUri);
}
}
} catch (Exception e) {
Log.e("SharePlugin", "Receiver thread exception");
e.printStackTrace();
}
}
}).start();
@@ -283,12 +263,7 @@ public class SharePlugin extends Plugin {
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
try {
notificationManager.notify((int) System.currentTimeMillis(), noti);
} catch (Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
NotificationHelper.notifyCompat(notificationManager, (int) System.currentTimeMillis(), noti);
}
} else {
Log.e("SharePlugin", "Error: Nothing attached!");
@@ -303,37 +278,57 @@ public class SharePlugin extends Plugin {
return true;
}
@Override
public void startPreferencesActivity(SettingsActivity parentActivity) {
Intent intent = new Intent(parentActivity, ShareSettingsActivity.class);
intent.putExtra("plugin_display_name", getDisplayName());
intent.putExtra("plugin_key", getPluginKey());
parentActivity.startActivity(intent);
}
static void queuedSendUriList(Context context, final Device device, final ArrayList<Uri> uriList) {
//Read all the data early, as we only have permissions to do it while the activity is alive
final ArrayList<NetworkPackage> toSend = new ArrayList<>();
for (Uri uri : uriList) {
toSend.add(uriToNetworkPackage(context, uri));
}
//Callback that shows a progress notification
final NotificationUpdateCallback notificationUpdateCallback = new NotificationUpdateCallback(context, device, toSend);
//Do the sending in background
new Thread(new Runnable() {
@Override
public void run() {
//Actually send the files
try {
for (NetworkPackage np : toSend) {
boolean success = device.sendPackageBlocking(np, notificationUpdateCallback);
if (!success) {
Log.e("SharePlugin","Error sending files");
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
//Create the network package from the URI
private static NetworkPackage uriToNetworkPackage(final Context context, final Uri uri) {
static void queuedSendUriList(final Context context, final Device device, final ArrayList<Uri> uriList) {
try {
Uri uri = uriList.remove(0);
ContentResolver cr = context.getContentResolver();
InputStream inputStream = cr.openInputStream(uri);
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_SHARE_REQUEST);
long size = -1;
final NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
final int notificationId = (int)System.currentTimeMillis();
final NotificationCompat.Builder builder ;
Resources res = context.getResources();
builder = new NotificationCompat.Builder(context)
.setContentTitle(res.getString(R.string.outgoing_file_title, device.getName()))
.setTicker(res.getString(R.string.outgoing_file_title, device.getName()))
.setSmallIcon(android.R.drawable.stat_sys_upload)
.setAutoCancel(true)
.setOngoing(true)
.setProgress(100,0,true);
try {
notificationManager.notify(notificationId,builder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
final Handler progressBarHandler = new Handler(Looper.getMainLooper());
if (uri.getScheme().equals("file")) {
// file:// is a non media uri, so we cannot query the ContentProvider
@@ -346,8 +341,6 @@ public class SharePlugin extends Plugin {
e.printStackTrace();
}
np.setPayload(inputStream, size);
}else{
// Probably a content:// uri, so we query the Media content provider
@@ -362,7 +355,7 @@ public class SharePlugin extends Plugin {
size = new File(path).length();
} catch(Exception unused) {
Log.e("SendFileActivity", "Could not resolve media to a file, trying to get info as media");
Log.w("SendFileActivity", "Could not resolve media to a file, trying to get info as media");
try {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
@@ -384,115 +377,22 @@ public class SharePlugin extends Plugin {
e.printStackTrace();
}
} finally {
cursor.close();
try { cursor.close(); } catch (Exception e) { }
}
np.setPayload(inputStream, size);
}
final String filename = np.getString("filename");
builder.setContentText(res.getString(R.string.outgoing_file_text,filename));
try {
notificationManager.notify(notificationId,builder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
device.sendPackage(np, new Device.SendPackageStatusCallback() {
int prevProgress = 0;
@Override
public void onProgressChanged(final int progress) {
if (progress != prevProgress) {
prevProgress = progress;
progressBarHandler.post(new Runnable() {
@Override
public void run() {
builder.setProgress(100, progress, false);
try {
notificationManager.notify(notificationId,builder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
});
}
}
@Override
public void onSuccess() {
progressBarHandler.post(new Runnable() {
@Override
public void run() {
Resources res = context.getResources();
NotificationCompat.Builder anotherBuilder = new NotificationCompat.Builder(context)
.setContentTitle(res.getString(R.string.sent_file_title, device.getName()))
.setContentText(res.getString(R.string.sent_file_text, filename))
.setTicker(res.getString(R.string.sent_file_title, device.getName()))
.setSmallIcon(android.R.drawable.stat_sys_upload_done)
.setOngoing(false)
.setAutoCancel(true);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("share_notification_preference", true)) {
anotherBuilder.setDefaults(Notification.DEFAULT_ALL);
}
try {
notificationManager.notify(notificationId,anotherBuilder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
});
if (!uriList.isEmpty()) queuedSendUriList(context, device, uriList);
else Log.i("SendFileActivity", "All files sent");
}
@Override
public void onFailure(Throwable e) {
progressBarHandler.post(new Runnable() {
@Override
public void run() {
Resources res = context.getResources();
NotificationCompat.Builder anotherBuilder = new NotificationCompat.Builder(context)
.setContentTitle(res.getString(R.string.sent_file_failed_title, device.getName()))
.setContentText(res.getString(R.string.sent_file_failed_text, filename))
.setTicker(res.getString(R.string.sent_file_title, device.getName()))
.setSmallIcon(android.R.drawable.stat_notify_error)
.setOngoing(false)
.setAutoCancel(true);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("share_notification_preference", true)) {
anotherBuilder.setDefaults(Notification.DEFAULT_ALL);
}
try {
notificationManager.notify(notificationId,anotherBuilder.build());
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
});
Log.e("SendFileActivity", "Failed to send file");
}
});
np.setPayload(inputStream, size);
return np;
} catch (Exception e) {
Log.e("SendFileActivity", "Exception sending files");
e.printStackTrace();
return null;
}
}
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_SHARE_REQUEST};

View File

@@ -0,0 +1,120 @@
package org.kde.kdeconnect.Plugins.SharePlugin;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.support.v4.provider.DocumentFile;
import android.util.Log;
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
import java.io.File;
public class ShareSettingsActivity extends PluginSettingsActivity {
private final static String PREFERENCE_CUSTOMIZE_DESTINATION = "share_destination_custom";
private final static String PREFERENCE_DESTINATION = "share_destination_folder_uri";
private static final int RESULT_PICKER = Activity.RESULT_FIRST_USER;
private Preference filePicker;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final CheckBoxPreference customDownloads = (CheckBoxPreference) findPreference("share_destination_custom");
filePicker = findPreference("share_destination_folder_preference");
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)) {
customDownloads.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
updateFilePickerStatus((Boolean) newValue);
return true;
}
});
filePicker.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, RESULT_PICKER);
return true;
}
});
} else {
customDownloads.setEnabled(false);
filePicker.setEnabled(false);
}
boolean customized = PreferenceManager.getDefaultSharedPreferences(this).getBoolean(PREFERENCE_CUSTOMIZE_DESTINATION, false);
updateFilePickerStatus(customized);
}
void updateFilePickerStatus(boolean enabled) {
filePicker.setEnabled(enabled);
String path = PreferenceManager.getDefaultSharedPreferences(this).getString(PREFERENCE_DESTINATION, null);
if (enabled && path != null) {
filePicker.setSummary(Uri.parse(path).getPath());
} else {
filePicker.setSummary(getDefaultDestinationDirectory().getAbsolutePath());
}
}
public static File getDefaultDestinationDirectory() {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
}
public static boolean isCustomDestinationEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREFERENCE_CUSTOMIZE_DESTINATION, false);
}
//Will return the appropriate directory, whether it is customized or not
public static DocumentFile getDestinationDirectory(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(PREFERENCE_CUSTOMIZE_DESTINATION, false)) {
String path = PreferenceManager.getDefaultSharedPreferences(context).getString(PREFERENCE_DESTINATION, null);
if (path != null) {
//There should be no way to enter here on api level < kitkat
DocumentFile treeDocumentFile = DocumentFile.fromTreeUri(context, Uri.parse(path));
if (treeDocumentFile.canWrite()) { //Checks for FLAG_DIR_SUPPORTS_CREATE on directories
return treeDocumentFile;
} else {
//Maybe permission was revoked
Log.w("SharePlugin", "Share destination is not writable, falling back to default path.");
}
}
}
return DocumentFile.fromFile(getDefaultDestinationDirectory());
}
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == RESULT_PICKER
&& resultCode == Activity.RESULT_OK
&& resultData != null) {
Uri uri = resultData.getData();
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
Preference filePicker = findPreference("share_destination_folder_preference");
filePicker.setSummary(uri.getPath());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.edit().putString(PREFERENCE_DESTINATION, uri.toString()).apply();
}
}
}

View File

@@ -20,20 +20,14 @@
package org.kde.kdeconnect.Plugins.TelepathyPlugin;
import android.database.Cursor;
import android.telephony.SmsManager;
import android.util.Log;
import android.widget.Toast;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import static android.provider.ContactsContract.CommonDataKinds;
import static android.provider.ContactsContract.Contacts;
public class TelepathyPlugin extends Plugin {
@@ -174,7 +168,7 @@ public class TelepathyPlugin extends Plugin {
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_SMS_REQUEST};
return new String[]{PACKAGE_TYPE_SMS_REQUEST, TelephonyPlugin.PACKAGE_TYPE_TELEPHONY_REQUEST};
}
@Override

View File

@@ -116,7 +116,18 @@ public class TelephonyPlugin extends Plugin {
}
if (contactInfo.containsKey("photoID")) {
np.set("phoneThumbnail", ContactsHelper.photoId64Encoded(context, contactInfo.get("photoID")));
String photoUri = contactInfo.get("photoID");
if (photoUri != null) {
try {
String base64photo = ContactsHelper.photoId64Encoded(context, photoUri);
if (base64photo != null && !base64photo.isEmpty()) {
np.set("phoneThumbnail", base64photo);
}
} catch (Exception e) {
Log.e("TelephonyPlugin", "Failed to get contact photo");
}
}
}
switch (state) {

View File

@@ -23,10 +23,10 @@ package org.kde.kdeconnect.UserInterface;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
@@ -82,10 +82,15 @@ public class CustomDevicesActivity extends ActionBarActivity {
});
}
boolean dialogAlreadyShown = false;
private AdapterView.OnItemClickListener onClickListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, final int position, final long id) {
Log.i(LOG_ID, "Item clicked pos: " + position + " id: " + id);
if (dialogAlreadyShown) {
return;
}
// remove touched item after confirmation
DialogInterface.OnClickListener confirmationListener = new DialogInterface.OnClickListener() {
@Override
@@ -93,7 +98,6 @@ public class CustomDevicesActivity extends ActionBarActivity {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
ipAddressList.remove(position);
Log.i(LOG_ID, "Removed item pos: " + position + " id: " + id);
saveList();
break;
case DialogInterface.BUTTON_NEGATIVE:
@@ -101,12 +105,23 @@ public class CustomDevicesActivity extends ActionBarActivity {
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(CustomDevicesActivity.this);
builder.setMessage("Delete " + ipAddressList.get(position) + " ?");
builder.setPositiveButton("Yes", confirmationListener);
builder.setNegativeButton("No", confirmationListener);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { //DismissListener
dialogAlreadyShown = true;
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
dialogAlreadyShown = false;
}
});
}
builder.show();
((ArrayAdapter) list.getAdapter()).notifyDataSetChanged();
}
};
@@ -143,37 +158,32 @@ public class CustomDevicesActivity extends ActionBarActivity {
}
public static String serializeIpList(ArrayList<String> iplist) {
public static String serializeIpList(ArrayList<String> ipList) {
String serialized = "";
for (String ipaddr : iplist) {
serialized += IP_DELIM+ipaddr;
for (String ipAddress : ipList) {
serialized += IP_DELIM+ipAddress;
}
// remove first delimiter
serialized = serialized.substring(IP_DELIM.length());
Log.d(LOG_ID, serialized);
return serialized;
}
public static ArrayList<String> deserializeIpList(String serialized) {
ArrayList<String> iplist = new ArrayList<>();
Log.d(LOG_ID, serialized);
for (String ipaddr : serialized.split(IP_DELIM)) {
iplist.add(ipaddr);
Log.d(LOG_ID, ipaddr);
ArrayList<String> ipList = new ArrayList<>();
for (String ipAddress : serialized.split(IP_DELIM)) {
ipList.add(ipAddress);
}
return iplist;
return ipList;
}
private void initializeDeviceList(Context context){
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(
KEY_CUSTOM_DEVLIST_PREFERENCE, "");
if(deviceListPrefs.isEmpty()){
Log.i(LOG_ID, "Initialising empty custom device list");
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(
KEY_CUSTOM_DEVLIST_PREFERENCE,
deviceListPrefs).commit();
} else {
Log.i(LOG_ID, "Populating device list");
ipAddressList = deserializeIpList(deviceListPrefs);
}
}

View File

@@ -22,7 +22,6 @@ package org.kde.kdeconnect.UserInterface;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -42,17 +41,17 @@ import android.widget.TextView;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.UserInterface.List.PluginItem;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.List.CustomItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.PluginItem;
import org.kde.kdeconnect.UserInterface.List.SmallEntryItem;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -123,9 +122,10 @@ public class DeviceFragment extends Fragment {
refreshUI();
if (!device.hasPluginsLoaded()) {
device.reloadPluginsFromSettings();
}
//TODO: Is this needed?
//if (!device.hasPluginsLoaded() && device.isReachable()) {
// device.reloadPluginsFromSettings();
//}
}
});
@@ -242,7 +242,7 @@ public class DeviceFragment extends Fragment {
}
});
if (device.isPaired()) {
if (device.isPaired() && device.isReachable()) {
menu.add(R.string.encryption_info_title).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
@@ -259,13 +259,16 @@ public class DeviceFragment extends Fragment {
if (device.certificate == null) {
builder.setMessage(R.string.encryption_info_msg_no_ssl);
} else {
builder.setMessage(context.getResources().getString(R.string.my_device_fingerprint) + "\n " + SslHelper.getCertificateHash(SslHelper.certificate) + "\n\n"
+ context.getResources().getString(R.string.remote_device_fingerprint) + "\n " + SslHelper.getCertificateHash(device.certificate));
builder.setMessage(context.getResources().getString(R.string.my_device_fingerprint) + "\n" + SslHelper.getCertificateHash(SslHelper.certificate) + "\n\n"
+ context.getResources().getString(R.string.remote_device_fingerprint) + "\n" + SslHelper.getCertificateHash(device.certificate));
}
builder.create().show();
return true;
}
});
}
if (device.isPaired()) {
menu.add(R.string.device_menu_unpair).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
@@ -285,17 +288,21 @@ public class DeviceFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
//TODO: Is this needed?
/*
BackgroundService.RunCommand(mActivity, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
if (mDeviceId != null) {
Device device = service.getDevice(mDeviceId);
if (device != null) {
if (device != null && device.isReachable()) {
device.reloadPluginsFromSettings();
}
}
}
});
*/
getView().setFocusableInTouchMode(true);
getView().requestFocus();
@@ -375,15 +382,16 @@ public class DeviceFragment extends Fragment {
errorHeader.setText(getResources().getString(R.string.plugins_failed_to_load));
}
items.add(new CustomItem(errorHeader));
for (String s : failed.keySet()) {
final Plugin p = failed.get(s);
if (p == null) {
items.add(new SmallEntryItem(s));
for (Map.Entry<String, Plugin> entry : failed.entrySet()) {
String pluginKey = entry.getKey();
final Plugin plugin = entry.getValue();
if (plugin == null) {
items.add(new SmallEntryItem(pluginKey));
} else {
items.add(new SmallEntryItem(p.getDisplayName(), new View.OnClickListener() {
items.add(new SmallEntryItem(plugin.getDisplayName(), new View.OnClickListener() {
@Override
public void onClick(View v) {
p.getErrorDialog(mActivity).show();
plugin.getErrorDialog(mActivity).show();
}
}));
}

View File

@@ -28,8 +28,8 @@ import org.kde.kdeconnect_tp.R;
public class EntryItem implements ListAdapter.Item {
private final String title;
private final String subtitle;
protected final String title;
protected final String subtitle;
public EntryItem(String title) {
this.title = title;

View File

@@ -36,8 +36,6 @@ public class PairingDeviceItem implements ListAdapter.Item {
private final Callback callback;
private final Device device;
private TextView titleView;
private ImageView icon;
public PairingDeviceItem(Device device, Callback callback) {
this.device = device;
@@ -52,10 +50,10 @@ public class PairingDeviceItem implements ListAdapter.Item {
public View inflateView(LayoutInflater layoutInflater) {
final View v = layoutInflater.inflate(R.layout.list_item_with_icon_entry, null);
icon = (ImageView)v.findViewById(R.id.list_item_entry_icon);
ImageView icon = (ImageView) v.findViewById(R.id.list_item_entry_icon);
icon.setImageDrawable(device.getIcon());
titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
TextView titleView = (TextView) v.findViewById(R.id.list_item_entry_title);
titleView.setText(device.getName());
if (device.compareProtocolVersion() != 0) {
@@ -65,7 +63,7 @@ public class PairingDeviceItem implements ListAdapter.Item {
summaryView.setText(R.string.protocol_version_newer);
summaryView.setVisibility(View.VISIBLE);
} else {
//FIXME: Uncoment
//FIXME: Uncoment when we decide old versions are old enough to notify the user.
summaryView.setVisibility(View.GONE);
/*
summaryView.setText(R.string.protocol_version_older);

View File

@@ -20,7 +20,6 @@
package org.kde.kdeconnect.UserInterface.List;
import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;

View File

@@ -40,8 +40,6 @@ public class MaterialActivity extends AppCompatActivity {
private NavigationView mNavigationView;
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
private View mDrawerHeader;
private String mCurrentDevice;
@@ -55,14 +53,14 @@ public class MaterialActivity extends AppCompatActivity {
setContentView(R.layout.activity_main);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mNavigationView = (NavigationView) findViewById(R.id.navigation_drawer);
mDrawerHeader = mNavigationView.getHeaderView(0);
View mDrawerHeader = mNavigationView.getHeaderView(0);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.string.open, /* "open drawer" description */
R.string.close /* "close drawer" description */

View File

@@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -37,8 +36,8 @@ import android.widget.TextView;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem;
import org.kde.kdeconnect.UserInterface.List.SectionItem;
import org.kde.kdeconnect_tp.R;
@@ -214,6 +213,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
String deviceId = data.getStringExtra("deviceId");
mActivity.onDeviceSelected(deviceId);
}
break;
default:
super.onActivityResult(requestCode, resultCode, data);
}

View File

@@ -1,14 +1,11 @@
package org.kde.kdeconnect.UserInterface;
import android.graphics.Color;
import android.preference.CheckBoxPreference;
import android.view.View;
import android.widget.TextView;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.SettingsActivity;
import org.kde.kdeconnect_tp.R;
public class PluginPreference extends CheckBoxPreference {
@@ -54,11 +51,7 @@ public class PluginPreference extends CheckBoxPreference {
super.onBindView(root);
final View button = root.findViewById(R.id.settingsButton);
if (device.getUnsupportedPlugins().contains(pluginKey)) {
((TextView)root.findViewById(android.R.id.title)).setTextColor(Color.GRAY);
((TextView)root.findViewById(android.R.id.summary)).setText(R.string.plugin_not_supported);
button.setVisibility(View.GONE);
} else if (listener == null) {
if (listener == null) {
button.setVisibility(View.GONE);
} else {
button.setEnabled(isChecked());

View File

@@ -22,14 +22,12 @@ package org.kde.kdeconnect.UserInterface;
import android.os.Bundle;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.MenuItem;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.PluginFactory;
import java.util.Set;
import java.util.List;
public class SettingsActivity extends AppCompatPreferenceActivity {
@@ -59,7 +57,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
});
return;
}
Set<String> plugins = PluginFactory.getAvailablePlugins();
List<String> plugins = device.getSupportedPlugins();
for (final String pluginKey : plugins) {
PluginPreference pref = new PluginPreference(SettingsActivity.this, pluginKey, device);
preferenceScreen.addPreference(pref);

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