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

Compare commits

..

183 Commits

Author SHA1 Message Date
Albert Vaca
debbe4fe2c Increased version number to release 2014-10-20 01:31:54 -07:00
Albert Vaca
5724a66561 Added some extra safety checks 2014-10-20 01:30:02 -07:00
Albert Vaca
91fe9d63b0 Removed incorrect translation. 2014-10-20 01:14:45 -07:00
Albert Vaca
a90928188a Fixed crash. 2014-10-20 01:13:41 -07:00
Albert Vaca
256ca60036 Updated version number to release. 2014-10-18 21:19:15 -07:00
Albert Vaca
05fd6fe5b0 Extra safety check 2014-10-18 21:19:15 -07:00
Albert Vaca
6b9a691f24 Fixed warning 2014-10-18 21:19:15 -07:00
l10n daemon script
d18b61f2a1 SVN_SILENT made messages (after extraction) 2014-10-19 01:58:23 +00:00
Albert Vaca
6448258648 Wrong translations sneaked in again
Revert "SVN_SILENT made messages (after extraction)"

This reverts commit f3fbe4d035.
2014-10-17 19:12:36 -07:00
Albert Vaca
e5e9490f19 Resize window when the keyboard appears, so text is always centered. 2014-10-17 19:11:38 -07:00
Albert Vaca
9f7c5be533 Do not restart the activity when orientation changes. 2014-10-17 19:11:38 -07:00
Albert Vaca
f690105181 "Show keyboard" button now actually toggles it. 2014-10-17 19:11:38 -07:00
Albert Vaca
dafe9eb023 Keyboard won't use the full screen when in landscape mode. 2014-10-17 19:11:38 -07:00
Albert Vaca
cfbe6775d0 Linefeed was sent instead of return when return was pressed. 2014-10-17 19:11:38 -07:00
Albert Vaca
6365143c2c Added icon to open the keyboard from the action bar 2014-10-17 19:11:38 -07:00
l10n daemon script
f3fbe4d035 SVN_SILENT made messages (after extraction) 2014-10-15 01:56:22 +00:00
Albert Vaca
2862e5164e Oops 2014-10-14 01:31:51 -07:00
Albert Vaca
6fae1d350d Android Studio decided to change the order of some lines 2014-10-14 01:12:39 -07:00
Albert Vaca
647f71bf98 Removed incorrect translations to release 2014-10-13 21:56:21 -07:00
Albert Vaca
343b2ed5e8 Increased version number to release 2014-10-13 21:56:07 -07:00
l10n daemon script
eb7351a803 SVN_SILENT made messages (after extraction) 2014-10-14 02:00:37 +00:00
l10n daemon script
7e7d6c4d82 SVN_SILENT made messages (after extraction) 2014-10-13 01:59:04 +00:00
l10n daemon script
59d01763c7 SVN_SILENT made messages (after extraction) 2014-10-12 01:56:58 +00:00
Albert Vaca
5b2756b7f8 Added support for devices with more than one external storage
BUG: 336043
2014-10-10 15:49:27 -07:00
Albert Vaca
5912611aab Fixed onPluginsChanged not being always called after calling addPlugin 2014-10-10 12:38:11 -07:00
l10n daemon script
54d24848e5 SVN_SILENT made messages (after extraction) 2014-10-10 02:11:27 +00:00
l10n daemon script
638c9df328 SVN_SILENT made messages (after extraction) 2014-10-07 01:58:52 +00:00
Albert Vaca
9943c36877 Minor layout changes 2014-10-04 21:13:12 -07:00
Albert Vaca
03bcfecb88 Fixed previous commit to work on Android API < 19 2014-10-04 21:07:57 -07:00
Albert Vaca
1b4ade0ae1 MPRIS controls now adapt to tablet resolutions
BUG: 339684
2014-10-04 20:58:14 -07:00
l10n daemon script
b9890210ad SVN_SILENT made messages (after extraction) 2014-10-05 01:58:53 +00:00
l10n daemon script
1509bfaf6d SVN_SILENT made messages (after extraction) 2014-10-04 01:53:54 +00:00
Albert Vaca
a22ffd611a Simplified device activity layout to use a single ListView
This fixes some layout problems that prevented us of having the height of
the lists adapted automatically to the content and had to use fixed height.
2014-09-29 19:03:31 -07:00
Albert Vaca
adb88560b5 Minor changes 2014-09-29 19:03:31 -07:00
Albert Vaca
03ede77bd1 Highlight device when selected from the main activity list.
I had to include the style files, made with http://android-holo-colors.com
because, from kitkat on, the background color of highlighted elements is
gray instead of blue, but that's not reflected in the resources included
in the support library v7 appcompat jar, that are still blue.
2014-09-29 19:03:31 -07:00
Albert Vaca
6afbe93c2d Made error list header not selectable 2014-09-29 19:03:31 -07:00
l10n daemon script
dd4421dce2 SVN_SILENT made messages (after extraction) 2014-09-30 01:55:45 +00:00
Albert Vaca
262f811bba Implemented custom ping messages. 2014-09-26 20:45:28 -07:00
Albert Vaca
0b52cbe36f Fixed bug where plugin errors were not shown if all enabled plugins fail. 2014-09-26 20:45:28 -07:00
Albert Vaca
8542aaf4fa Fixed bug were list of enabled plugins didn't refresh itself.
Removed PreferenceListAdapter
2014-09-26 20:45:27 -07:00
l10n daemon script
c9d11f650c SVN_SILENT made messages (after extraction) 2014-09-27 02:00:12 +00:00
l10n daemon script
6c5ed3094b SVN_SILENT made messages (after extraction) 2014-09-26 02:01:30 +00:00
l10n daemon script
5aaec15e79 SVN_SILENT made messages (after extraction) 2014-09-24 01:50:30 +00:00
Albert Vaca
b0a632fe10 Fixed error list always empty the second time it is loaded. 2014-09-23 16:42:26 +02:00
Albert Vaca
4d1464484e Commented priority sort of links because we don't use it and causes crashes 2014-09-23 16:41:57 +02:00
Albert Vaca
d6cc84ccfb Fixed crash 2014-09-23 16:40:54 +02:00
Albert Vaca
624cee5d87 Workaround for bug where plugins are not loaded for a device 2014-09-23 16:40:34 +02:00
Albert Vaca
a8664d01ec Updated project files 2014-09-23 16:39:28 +02:00
l10n daemon script
bd68f71af6 SVN_SILENT made messages (after extraction) 2014-09-22 01:59:00 +00:00
Albert Vaca
8ec828d6e4 Reload plugins list when user gives permission to access notifications.
This way the error message disappears without having to reload the app.
2014-09-20 10:52:03 +02:00
Albert Vaca
a44d48d527 Separated arrays.xml and strings.xml were causing confusion to the script
that extracts the translations, because it expects a single output file.
2014-09-20 10:24:01 +02:00
Albert Vaca
804642c5fc Fixed bug causing some plugin settings having checkbox when they shouldn't
and viceversa.
2014-09-17 16:06:23 +02:00
Albert Vaca
8e0daa69cc Added preference to disable incoming file notification sound. 2014-09-17 16:05:54 +02:00
Albert Vaca
400b35216d Minor changes 2014-09-17 16:05:16 +02:00
Albert Vaca
4a5545529a Those static will never work
Because class statics are called the first time the class is instantiated.
2014-09-17 10:36:45 +02:00
Ronny Yabar Aizcorbe
b2fa8ab506 Plugin-specific settings
Added support for specific plugin settings.
Added interval time preference to fast forward or rewind a multimedia file
instead of hardcording the time.

REVIEW: 120005
2014-09-16 15:45:58 +02:00
l10n daemon script
b9a0b3d2f0 SVN_SILENT made messages (after extraction) 2014-09-11 01:59:39 +00:00
l10n daemon script
e8c6784ce0 SVN_SILENT made messages (after extraction) 2014-09-03 01:58:03 +00:00
l10n daemon script
e3b8bb5ffc SVN_SILENT made messages (after extraction) 2014-08-28 02:01:02 +00:00
l10n daemon script
103bc69347 SVN_SILENT made messages (after extraction) 2014-08-25 02:00:05 +00:00
l10n daemon script
2b45c82a2e SVN_SILENT made messages (after extraction) 2014-08-22 02:05:13 +00:00
l10n daemon script
0d3232a5a1 SVN_SILENT made messages (after extraction) 2014-08-20 01:58:04 +00:00
l10n daemon script
ed5ddc37c8 SVN_SILENT made messages (after extraction) 2014-08-18 02:03:00 +00:00
l10n daemon script
48bc56f52a SVN_SILENT made messages (after extraction) 2014-08-17 02:00:27 +00:00
l10n daemon script
5de10b05e7 SVN_SILENT made messages (after extraction) 2014-08-16 01:56:50 +00:00
Albert Vaca
1fe2590ec8 Changes to the detection of keyboard input.
Now it detects more special keys.
It does not detect modifiers anymore.
Moved to a view that keeps the focus and prevents focusing the back button.
Changed bitmasks for a hashmap to make it more readable with lots of keys.
Separated sendSpecialKey from sendKey for non-representable keys.

CCMAIL: saiarcot895@gmail.com
2014-08-14 21:50:19 +02:00
Saikrishna Arcot
d02684c714 Add support for using the keyboard.
This patch adds a menu entry to display the keyboard in the mousepad plugin
and allow the user to send keystrokes. Dead keys are not yet supported, and
behavior is unknown if entered.

REVIEW: 119255
2014-08-14 21:50:19 +02:00
Job Noorman
80ed8a58d5 Properly restore the selected MPRIS player after a plugin reconnect.
Whenever the link with a device goes down (the easiest way to test this is to
lock the phone's screen), the connection with the MPRIS plugin is
re-established when the link goes up again. This patch makes sure that the
currently selected player (the one stored on the MprisPlugin instance) is also
selected in the MprisActivity. Currently, the first player in the list is
always selected.

REVIEW: 119692
2014-08-13 22:52:52 +02:00
l10n daemon script
962501fe38 SVN_SILENT made messages (after extraction) 2014-08-07 01:48:38 +00:00
l10n daemon script
60c1e67e66 SVN_SILENT made messages (after extraction) 2014-07-25 02:14:34 +00:00
l10n daemon script
318b82941f SVN_SILENT made messages (after extraction) 2014-07-16 02:16:36 +00:00
Albert Vaca
acdf8d467e This has to be public for some reason 2014-07-12 20:03:08 +02:00
Albert Vaca
ceee1ee1f6 Increased version number to release 2014-07-12 20:03:08 +02:00
Albert Vaca
071f71cb9c Silly changes proposed by lint 2014-07-12 20:03:08 +02:00
Albert Vaca
4adf2cfbbd Updated project to new Android Build Tools. 2014-07-12 20:03:08 +02:00
l10n daemon script
de4fa31b2a SVN_SILENT made messages (after extraction) 2014-07-07 02:01:50 +00:00
l10n daemon script
4deba4ea4f SVN_SILENT made messages (after extraction) 2014-07-05 01:59:05 +00:00
l10n daemon script
07a8b107eb SVN_SILENT made messages (after extraction) 2014-07-03 01:51:49 +00:00
Ahmed I. Khalil
3b750c78ab Added Triple and Double finger gestures.
For right and middle clicking.

REVIEW: 119072
2014-07-02 17:19:44 +02:00
l10n daemon script
47790a710a SVN_SILENT made messages (after extraction) 2014-07-02 02:04:42 +00:00
l10n daemon script
92451df32f SVN_SILENT made messages (after extraction) 2014-07-01 02:06:59 +00:00
l10n daemon script
55eada9dfc SVN_SILENT made messages (after extraction) 2014-06-30 02:05:57 +00:00
Albert Vaca
0ed73f6729 Ooops, added missing file that was untracked! 2014-06-29 19:44:30 +02:00
Albert Vaca
c8e24bf07d Increased version number to release 2014-06-29 18:52:18 +02:00
Albert Vaca
f016a06c32 Notify the system when we receive a file, so it gets added to the gallery, etc. 2014-06-29 18:50:50 +02:00
Ahmed I. Khalil
23463afc33 Added Right and Middle Clicks as Menu Items.
I've made them as "never" to be shown on the ActionBar,
until hopefully we could get icons for them.

REVIEW: 119016
2014-06-29 18:37:28 +02:00
Albert Vaca
45fd3f88e8 Added methods for right and middle click, not yet used 2014-06-29 17:58:33 +02:00
Albert Vaca
b54e2ea36e Made scrolling less sensitive
CCMAIL: ahmedibrahimkhali@gmail.com
2014-06-29 17:58:21 +02:00
Albert Vaca
3043ca5d05 Increased the version number to release 2014-06-29 17:17:43 +02:00
Albert Vaca
47368a27fd Trying to fix a "jumpy mouse" issue when scrolling
CCMAIL: ahmedibrahimkhali@gmail.com
2014-06-29 17:17:22 +02:00
Albert Vaca
046a1cb676 Implemented two finger scrolling on Android, by handling the onScroll method
that it is called from the GestureDetector.

REVIEW: 119007
2014-06-29 17:17:22 +02:00
Albert Vaca
43c6742e21 Device names could contain characters not allowed for a dir name, fixed. 2014-06-29 17:17:22 +02:00
Albert Vaca
5b2f863e0e Added padding to text 2014-06-29 17:17:22 +02:00
Albert Vaca
a12e64ca59 Renamed mousepad -> touchpad 2014-06-29 17:17:22 +02:00
Albert Vaca
e99ba38eac Removed unnecesary import 2014-06-29 17:17:22 +02:00
Albert Vaca
0730c52741 Started implementing receiving shared files 2014-06-29 17:17:22 +02:00
l10n daemon script
093b722f00 SVN_SILENT made messages (after extraction) 2014-06-29 01:58:07 +00:00
Albert Vaca
162fc8b8dd Increased version number to release 2014-06-27 17:33:03 +02:00
Antoni Bella Pérez
c648185ffa Added Sony Xperia Z2 human readable name 2014-06-27 17:29:13 +02:00
Albert Vaca
af915b5756 I like the Nexus phones being called only "Nexus" :P 2014-06-27 17:28:50 +02:00
Albert Vaca
dc60968a35 Merge branch 'feature/mousepad' 2014-06-27 17:22:52 +02:00
Albert Vaca
c122a0d2f3 Removed two error dialogs that were not used 2014-06-27 15:55:22 +02:00
Ahmed I. Khalil
ce597f2e51 Android Implementation of MousePad feature
CCMAIL: ahmedibrahimkhali@gmail.com
2014-06-27 15:48:11 +02:00
Albert Vaca
7ae687ae11 Explicitly capturing plugins exceptions to avoid them being lost magically 2014-06-27 14:47:10 +02:00
Albert Vaca
53bb376990 More projet updates 2014-06-27 14:47:10 +02:00
Albert Vaca
cc10538dcb Added more device names 2014-06-27 14:47:10 +02:00
Albert Vaca
9c7aaf2ed9 Merge https://github.com/R4md4c/kdeconnect-android into feature/mousepad 2014-06-27 14:45:05 +02:00
l10n daemon script
0283b0112e SVN_SILENT made messages (after extraction) 2014-06-24 01:52:25 +00:00
l10n daemon script
906fc35abb SVN_SILENT made messages (after extraction) 2014-06-21 01:56:44 +00:00
l10n daemon script
8bc3e69ce6 SVN_SILENT made messages (after extraction) 2014-06-20 02:08:24 +00:00
l10n daemon script
020d2bca2a SVN_SILENT made messages (after extraction) 2014-06-19 01:48:31 +00:00
Albert Vaca
50682935e0 Explicitly capturing plugins exceptions to avoid them being lost magically 2014-06-18 22:53:52 +02:00
Albert Vaca
1da74a6c68 More projet updates 2014-06-18 22:53:11 +02:00
Aleix Pol
aa33f4cd08 Submit .reviewboardrc file
Simplifies reviewboard patches submision
2014-06-18 02:43:41 +02:00
l10n daemon script
0c142928f9 SVN_SILENT made messages (after extraction) 2014-06-16 01:57:09 +00:00
Albert Vaca
24aefebb0e Added more device names 2014-06-14 19:08:25 +02:00
l10n daemon script
7a47bd0b9c SVN_SILENT made messages (after extraction) 2014-06-14 01:52:05 +00:00
l10n daemon script
cbb3787a15 SVN_SILENT made messages (after extraction) 2014-06-12 01:48:42 +00:00
l10n daemon script
61048e5df4 SVN_SILENT made messages (after extraction) 2014-06-11 01:54:17 +00:00
Albert Vaca
8644f7ee15 Paired unreachable devices show an "unpair" button instead of an empty view 2014-06-09 13:25:50 +02:00
Albert Vaca
77f9a30bd3 Fixed bug causing plugins to load for unreachable devices 2014-06-09 13:25:50 +02:00
Albert Vaca
1dbc93ec73 Inform other devices we don't trust them when they talk to us 2014-06-09 13:25:50 +02:00
Albert Vaca
64c5362b1c Updated android build tools 2014-06-09 13:25:50 +02:00
l10n daemon script
0c4ae62061 SVN_SILENT made messages (after extraction) 2014-06-06 02:02:50 +00:00
l10n daemon script
727c845624 SVN_SILENT made messages (after extraction) 2014-05-31 01:52:48 +00:00
l10n daemon script
577d9a5dc8 SVN_SILENT made messages (after extraction) 2014-05-18 01:54:52 +00:00
Albert Vaca
0311163829 Updated version number to release 2014-05-14 01:23:34 +02:00
Albert Vaca
76fd6d1e08 Better exception handling in sendMessage 2014-05-14 01:21:28 +02:00
l10n daemon script
a79e8f83ce SVN_SILENT made messages (after extraction) 2014-05-05 01:59:18 +00:00
Albert Vaca
3f6ec6a2d7 Broadcast device name after changing it 2014-04-12 20:50:19 +02:00
Albert Vaca
726f623eea Project settings: changed from release to debug 2014-04-12 20:44:39 +02:00
l10n daemon script
0db8f7c1d4 SVN_SILENT made messages (after extraction) 2014-03-29 01:59:41 +00:00
Albert Vaca
7a289fbe38 Fixed silly stuff reported by the lint analyzer 2014-03-29 01:47:15 +01:00
Albert Vaca
41bb2ed916 Increased version number to release 2014-03-27 23:19:58 +01:00
Sven Nobis
4598f080a2 Read notifications ticker text from extras Bundle.
Many Apps don't set a useful ticker text. On Android version KitKat or
newer, the title and text of a notification is stored in the extras Bundle.
This patch make use of it, to provide a better ticker text.
2014-03-27 23:18:56 +01:00
l10n daemon script
ecf22e889b SVN_SILENT made messages (after extraction) 2014-03-26 01:55:42 +00:00
l10n daemon script
8598cf5f2e SVN_SILENT made messages (after extraction) 2014-03-24 01:49:14 +00:00
l10n daemon script
1def9cc9ce SVN_SILENT made messages (after extraction) 2014-03-20 01:52:22 +00:00
l10n daemon script
0772a37f4e SVN_SILENT made messages (after extraction) 2014-03-19 04:23:04 +00:00
l10n daemon script
16edb35a0e SVN_SILENT made messages (after extraction) 2014-03-18 01:46:41 +00:00
Albert Vaca
8f9bad7a0f Increased version number to release 2014-03-15 17:47:50 +01:00
Mariusz Fik
4fd96062f6 Fix crash when user tries to unload plugin, which is not supported by platform. 2014-03-15 17:32:16 +01:00
l10n daemon script
a0a9eca060 SVN_SILENT made messages (after extraction) 2014-03-10 01:56:44 +00:00
Albert Vaca
5999ef7c8e Increased version number to release 2014-03-08 00:21:17 +01:00
Albert Vaca
9542597424 Updated project to latest version of Android build tools 2014-03-08 00:21:06 +01:00
Mariusz Fik
11e5563d9a Use hardware buttons to control multimedia player volume.
REVIEW: 116652
2014-03-07 23:27:01 +01:00
Albert Vaca
f31afdb6d0 Increased version number to release 2014-03-07 23:25:42 +01:00
l10n daemon script
27ec7c5bfb SVN_SILENT made messages (after extraction) 2014-03-03 01:48:29 +00:00
l10n daemon script
49360816a3 SVN_SILENT made messages (after extraction) 2014-02-24 01:51:22 +00:00
Albert Vaca
dd364bd562 New icon by Malcer <malcer@gmx.com> 2014-02-19 21:20:15 +01:00
Albert Vaca
809f37a541 More project changes 2014-02-19 17:03:47 +01:00
Albert Vaca
36c3df7a07 Version should be 0.5 instead of 0.6! 2014-02-19 16:33:08 +01:00
l10n daemon script
34aec7120e SVN_SILENT made messages (after extraction) 2014-02-19 02:37:41 +00:00
l10n daemon script
0a494b002d SVN_SILENT made messages (after extraction) 2014-02-18 01:46:47 +00:00
l10n daemon script
7298247dfc SVN_SILENT made messages (after extraction) 2014-02-17 02:14:15 +00:00
l10n daemon script
72a97d9e1c SVN_SILENT made messages (after extraction) 2014-02-16 03:52:01 +00:00
l10n daemon script
7cc80baf16 SVN_SILENT made messages (after extraction) 2014-02-15 01:46:20 +00:00
Albert Vaca
0600b1ee2e Oops, duplicate library 2014-02-14 20:11:22 +01:00
Albert Vaca
2f0059938d Increased version number to tag and release 2014-02-14 17:08:30 +01:00
Albert Vaca
a023316609 Renamed sftp plugin to "filesystem browser" 2014-02-14 17:08:30 +01:00
Albert Vaca
d94ed56c4e oops 2014-02-14 17:08:30 +01:00
Samoilenko Yuri
15a11cce02 Publick key auth for sftp plugin 2014-02-14 17:08:07 +01:00
Albert Vaca
36530b9580 Prefer IPv4 over IPv6 2014-02-14 17:07:42 +01:00
Samoilenko Yuri
f54acd7308 fix 2014-02-14 17:07:42 +01:00
Samoilenko Yuri
cfe9ccdc31 WORKAROUND new gradle android plugin bug 2014-02-14 17:07:42 +01:00
Samoilenko Yuri
52c7581f42 moved to updated gradle and Android Studio 2014-02-14 17:07:12 +01:00
Albert Vaca
a57dc71ad1 Libs are now included from Maven repo 2014-02-14 17:05:31 +01:00
Samoilenko Yuri
77d06cfe77 sftp implementations splits from sftpplugin 2014-02-14 17:04:36 +01:00
Samoilenko Yuri
13a7c1e5fe first draft SFTP implementation 2014-02-14 17:04:30 +01:00
l10n daemon script
5ccf215a54 SVN_SILENT made messages (after extraction) 2014-02-06 01:38:48 +00:00
l10n daemon script
fddf3c64b5 SVN_SILENT made messages (after extraction) 2014-02-01 01:41:08 +00:00
l10n daemon script
fb02b9bdf1 SVN_SILENT made messages (after extraction) 2014-01-29 01:40:11 +00:00
l10n daemon script
8453156d16 SVN_SILENT made messages (after extraction) 2014-01-23 01:37:44 +00:00
Albert Vaca
9bbe2908ed We will only sync actual notifications from now on
BUG: 325668
2014-01-10 19:29:50 +01:00
Albert Vaca
fb843f8789 Notifications with FLAG_FOREGROUND_SERVICE are not shown anymore
Actually, they are not 'notifications'.
2014-01-10 18:00:26 +01:00
Albert Vaca
c85619dfd2 Phone number shown next to contact name for calls/sms
BUG: 328197
2014-01-10 17:59:50 +01:00
Albert Vaca
de9f78adf3 Gradle upgrade to make Android Studio 4.2 happy 2014-01-10 17:07:00 +01:00
Samoilenko Yuri
9021ab9454 Partial impl of clipboard sync in early Android
Syncronization works only in one direction - incoming
2014-01-08 02:17:37 +04:00
Samoilenko Yuri
10210b3972 Fixed Ping notification on old phones
On old phones(LG P698) notification does not shown ultil all
fields are filles correctly: setContentIntent.
Added pendingIntent to notification as describved into official documentation for Notifications, to
fit support library requirements.
2014-01-08 01:20:24 +04:00
l10n daemon script
f83b9cbe0c SVN_SILENT made messages (after extraction) 2013-12-29 01:40:33 +00:00
l10n daemon script
7d821e7c88 SVN_SILENT made messages (after extraction) 2013-12-10 01:38:14 +00:00
l10n daemon script
02070343fe SVN_SILENT made messages (after extraction) 2013-11-28 01:42:19 +00:00
l10n daemon script
416a000dd3 SVN_SILENT made messages (after extraction) 2013-11-25 01:41:50 +00:00
124 changed files with 4609 additions and 424 deletions

3
.reviewboardrc Normal file
View File

@@ -0,0 +1,3 @@
REVIEWBOARD_URL = "https://git.reviewboard.kde.org"
REPOSITORY = 'git://anongit.kde.org/kdeconnect-android'
TARGET_GROUPS = 'kdeconnect'

View File

@@ -1,29 +1,44 @@
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:0.6.+'
classpath 'com.android.tools.build:gradle:0.12.+'
}
}
apply plugin: 'android'
apply plugin: 'com.android.application'
android {
compileSdkVersion 19
buildToolsVersion "19"
buildToolsVersion '20.0.0'
defaultConfig {
minSdkVersion 9
targetSdkVersion 19
}
packagingOptions {
exclude "META-INF/DEPENDENCIES"
exclude "META-INF/NOTICE"
exclude "META-INF/LICENSE"
exclude "META-INF/LICENSE.txt"
exclude "META-INF/NOTICE.txt"
}
lintOptions {
abortOnError false
}
buildTypes {
}
}
dependencies {
repositories {
mavenCentral()
}
compile "com.android.support:support-v4:19.0.+"
compile "com.android.support:appcompat-v7:19.0.+"
compile "org.apache.mina:mina-core:2.0.+"
compile 'com.android.support:support-v4:19.1.+'
compile 'com.android.support:appcompat-v7:19.1.+'
compile 'org.apache.mina:mina-core:2.0.+'
compile 'org.bouncycastle:bcprov-jdk16:1.45'
compile 'org.apache.sshd:sshd-core:0.8.0'
compile 'tomcat:tomcat-apr:5.5.+'
//compile fileTree(dir: 'libs', include: '*.jar')
}

View File

@@ -1,6 +1,6 @@
#Tue Oct 29 15:58:14 CET 2013
#Mon Jun 09 09:51:48 CEST 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=http\://services.gradle.org/distributions/gradle-1.8-all.zip
distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip

1067
icon.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="Debug" />
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebug" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleTest" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugJava" />
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugTest" />
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
<option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugTestSources" />
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
@@ -22,61 +23,70 @@
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/classes/debug" />
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/source/r/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/aidl/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/rs/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/build/source/buildConfig/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/build/res/rs/test" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/test/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/test/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/assets" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/instrumentTest/resources" type="java-test-resource" />
<excludeFolder url="file://$MODULE_DIR$/.git" />
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/build/apk" />
<excludeFolder url="file://$MODULE_DIR$/build/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/res" />
<excludeFolder url="file://$MODULE_DIR$/build/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
</content>
<orderEntry type="jdk" jdkName="Android API 19 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="support-v4-19.1.0" level="project" />
<orderEntry type="library" exported="" name="sshd-core-0.8.0" level="project" />
<orderEntry type="library" exported="" name="bcprov-jdk16-1.45" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-19.1.0" level="project" />
<orderEntry type="library" exported="" name="mina-core-2.0.8" level="project" />
<orderEntry type="library" exported="" name="tomcat-apr-5.5.15" level="project" />
<orderEntry type="library" exported="" name="slf4j-api-1.6.6" level="project" />
<orderEntry type="library" exported="" name="support-v4-19.0.0" level="project" />
<orderEntry type="library" exported="" name="mina-core-2.0.7" level="project" />
<orderEntry type="library" exported="" name="ComAndroidSupportAppcompatV71900.aar" level="project" />
</component>
</module>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.kde.kdeconnect_tp"
android:versionCode="11"
android:versionName="0.4.2">
android:versionCode="732"
android:versionName="0.7.3.2">
<uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="18" />
android:targetSdkVersion="19" />
<supports-screens
android:smallScreens="true"
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.RECEIVE_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"/>
<application
android:allowBackup="true"
@@ -87,6 +88,15 @@
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
</activity>
<activity
android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
android:label="@string/mpris_settings"
android:parentActivityName="org.kde.kdeconnect.UserInterface.SettingsActivity"
>
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.SettingsActivity" />
</activity>
<receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
@@ -124,6 +134,19 @@
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
</activity>
<activity
android:theme="@style/Theme.AppCompat"
android:name="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity"
android:label="@string/remote_control"
android:windowSoftInputMode="stateHidden|adjustResize"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="fullSensor"
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity"
>
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
</activity>
<activity
android:theme="@style/Theme.AppCompat"
android:name="org.kde.kdeconnect.UserInterface.ShareToReceiver"

View File

@@ -1,23 +1,17 @@
package org.kde.kdeconnect.Backends;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Base64;
import org.kde.kdeconnect.NetworkPackage;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
public abstract class BaseLink {
private BaseLinkProvider linkProvider;
private String deviceId;
private ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
private final BaseLinkProvider linkProvider;
private final String deviceId;
private final ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
protected PrivateKey privateKey;
protected BaseLink(String deviceId, BaseLinkProvider linkProvider) {

View File

@@ -9,7 +9,7 @@ import java.util.ArrayList;
public abstract class BaseLinkProvider {
private ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
private final ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
public interface ConnectionReceiver {
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link);
@@ -43,7 +43,7 @@ public abstract class BaseLinkProvider {
public abstract void onStop();
public abstract void onNetworkChange();
public abstract int getPriority();
//public abstract int getPriority();
public abstract String getName();
}

View File

@@ -3,6 +3,7 @@ package org.kde.kdeconnect.Backends.LanBackend;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.support.v4.util.LongSparseArray;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFuture;
@@ -30,9 +31,9 @@ public class LanLinkProvider extends BaseLinkProvider {
private final static int port = 1714;
private Context context;
private HashMap<String, LanLink> visibleComputers = new HashMap<String, LanLink>();
private HashMap<Long, LanLink> nioSessions = new HashMap<Long, LanLink>();
private final Context context;
private final HashMap<String, LanLink> visibleComputers = new HashMap<String, LanLink>();
private final LongSparseArray<LanLink> nioSessions = new LongSparseArray<LanLink>();
private NioSocketAcceptor tcpAcceptor = null;
private NioDatagramAcceptor udpAcceptor = null;
@@ -41,8 +42,9 @@ public class LanLinkProvider extends BaseLinkProvider {
@Override
public void sessionClosed(IoSession session) throws Exception {
LanLink brokenLink = nioSessions.remove(session.getId());
LanLink brokenLink = nioSessions.get(session.getId());
if (brokenLink != null) {
nioSessions.remove(session.getId());
connectionLost(brokenLink);
brokenLink.disconnect();
String deviceId = brokenLink.getDeviceId();
@@ -92,7 +94,7 @@ public class LanLinkProvider extends BaseLinkProvider {
}
};
private IoHandler udpHandler = new IoHandlerAdapter() {
private final IoHandler udpHandler = new IoHandlerAdapter() {
@Override
public void messageReceived(IoSession udpSession, Object message) throws Exception {
super.messageReceived(udpSession, message);
@@ -165,6 +167,10 @@ public class LanLinkProvider extends BaseLinkProvider {
String deviceId = identityPackage.getString("deviceId");
Log.i("LanLinkProvider","addLink to "+deviceId);
LanLink oldLink = visibleComputers.get(deviceId);
if (oldLink == link) {
Log.e("KDEConnect", "LanLinkProvider: oldLink == link. This should not happen!");
return;
}
visibleComputers.put(deviceId, link);
connectionAccepted(identityPackage, link);
if (oldLink != null) {
@@ -261,25 +267,21 @@ public class LanLinkProvider extends BaseLinkProvider {
@Override
public void onNetworkChange() {
onStop();
onStart();
}
@Override
public void onStop() {
udpAcceptor.unbind();
tcpAcceptor.unbind();
}
/*
@Override
public int getPriority() {
return 1000;
}
*/
@Override
public String getName() {
return "LanLinkProvider";

View File

@@ -7,7 +7,7 @@ import org.kde.kdeconnect.NetworkPackage;
public class LoopbackLinkProvider extends BaseLinkProvider {
private Context context;
private final Context context;
public LoopbackLinkProvider(Context context) {
this.context = context;
@@ -20,22 +20,19 @@ public class LoopbackLinkProvider extends BaseLinkProvider {
@Override
public void onStop() {
}
@Override
public void onNetworkChange() {
NetworkPackage np = NetworkPackage.createIdentityPackage(context);
connectionAccepted(np, new LoopbackLink(this));
}
/*
@Override
public int getPriority() {
return 0;
}
*/
@Override
public String getName() {
return "LoopbackLinkProvider";

View File

@@ -26,11 +26,11 @@ import java.util.concurrent.locks.ReentrantLock;
public class BackgroundService extends Service {
private ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>();
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>();
private HashMap<String, Device> devices = new HashMap<String, Device>();
private final HashMap<String, Device> devices = new HashMap<String, Device>();
private Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
@Override
public void incomingRequest() {
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged();
@@ -63,7 +63,7 @@ public class BackgroundService extends Service {
}
}
public void registerLinkProviders() {
private void registerLinkProviders() {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
@@ -81,7 +81,7 @@ public class BackgroundService extends Service {
return devices.get(id);
}
private BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
@Override
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseLink link) {
@@ -96,7 +96,6 @@ public class BackgroundService extends Service {
device.addLink(identityPackage, link);
} else {
Log.i("BackgroundService", "addLink,unknown device: " + deviceId);
String name = identityPackage.getString("deviceName");
device = new Device(BackgroundService.this, identityPackage, link);
devices.put(deviceId, device);
device.addPairingCallback(devicePairingCallback);
@@ -266,14 +265,13 @@ public class BackgroundService extends Service {
void onServiceStart(BackgroundService service);
}
private static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
private static final Lock mutex = new ReentrantLock(true);
private final static Lock mutex = new ReentrantLock(true);
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//This will be called for each intent launch, even if the service is already started and it is reused
Log.i("BackgroundService","onStartCommand");
mutex.lock();
for (InstanceCallback c : callbacks) {
c.onServiceStart(this);

View File

@@ -35,11 +35,11 @@ import java.util.TimerTask;
public class Device implements BaseLink.PackageReceiver {
private Context context;
private final Context context;
private String deviceId;
private String name;
private PublicKey publicKey;
private final String deviceId;
private final String name;
public PublicKey publicKey;
private int notificationId;
private int protocolVersion;
@@ -61,11 +61,11 @@ public class Device implements BaseLink.PackageReceiver {
private ArrayList<PairingCallback> pairingCallback = new ArrayList<PairingCallback>();
private Timer pairingTimer;
private ArrayList<BaseLink> links = new ArrayList<BaseLink>();
private HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
private HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
private final ArrayList<BaseLink> links = new ArrayList<BaseLink>();
private final HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
private final HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
SharedPreferences settings;
private final SharedPreferences settings;
//Remembered trusted device, we need to wait for a incoming devicelink to communicate
Device(Context context, String deviceId) {
@@ -209,8 +209,6 @@ public class Device implements BaseLink.PackageReceiver {
public void unpair() {
if (!isPaired()) return;
//Log.e("Device","Unpairing (unpair)");
pairStatus = PairStatus.NotPaired;
@@ -323,12 +321,14 @@ public class Device implements BaseLink.PackageReceiver {
Log.i("Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size());
/*
Collections.sort(links, new Comparator<BaseLink>() {
@Override
public int compare(BaseLink o, BaseLink o2) {
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
}
});
*/
link.addPackageReceiver(this);
@@ -408,7 +408,7 @@ public class Device implements BaseLink.PackageReceiver {
.setTicker(res.getString(R.string.pair_requested))
.setSmallIcon(android.R.drawable.ic_menu_help)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND)
.setDefaults(Notification.DEFAULT_ALL)
.build();
@@ -452,13 +452,19 @@ public class Device implements BaseLink.PackageReceiver {
}
} else if (!isPaired()) {
//TODO: Notify the other side that we don't trust them
unpair();
Log.e("onPackageReceived","Device not paired, ignoring package!");
} else {
for (Plugin plugin : plugins.values()) {
plugin.onPackageReceived(np);
try {
plugin.onPackageReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived()");
}
}
}
@@ -491,13 +497,19 @@ public class Device implements BaseLink.PackageReceiver {
ArrayList<BaseLink> mLinks = new ArrayList<BaseLink>(links);
boolean success = false;
for(BaseLink link : mLinks) {
if (useEncryption) {
success = link.sendPackageEncrypted(np, publicKey);
} else {
success = link.sendPackage(np);
try {
for (BaseLink link : mLinks) {
if (useEncryption) {
success = link.sendPackageEncrypted(np, publicKey);
} else {
success = link.sendPackage(np);
}
if (success) break;
}
if (success) break;
} catch(Exception e) {
e.printStackTrace();
Log.e("sendPackage","Error while sending package");
success = false;
}
if (success) {
@@ -547,24 +559,25 @@ public class Device implements BaseLink.PackageReceiver {
@Override
public void run() {
boolean success;
try {
boolean success = plugin.onCreate();
if (!success) {
Log.e("addPlugin", "plugin failed to load " + name);
failedPlugins.put(name, plugin);
return;
}
success = plugin.onCreate();
} catch (Exception e) {
failedPlugins.put(name, plugin);
success = false;
e.printStackTrace();
Log.e("addPlugin", "Exception loading plugin " + name);
return;
}
//Log.e("addPlugin","added " + name);
failedPlugins.remove(name);
plugins.put(name, plugin);
if (success) {
//Log.e("addPlugin","added " + name);
failedPlugins.remove(name);
plugins.put(name, plugin);
} else {
Log.e("addPlugin", "plugin failed to load " + name);
plugins.remove(name);
failedPlugins.put(name, plugin);
}
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(Device.this);
@@ -582,6 +595,7 @@ public class Device implements BaseLink.PackageReceiver {
if (plugin == null) {
if (failedPlugin == null) {
//Not found
return false;
}
plugin = failedPlugin;
@@ -589,25 +603,22 @@ public class Device implements BaseLink.PackageReceiver {
try {
plugin.onDestroy();
//Log.e("removePlugin","removed " + name);
} catch (Exception e) {
e.printStackTrace();
Log.e("removePlugin","Exception calling onDestroy for plugin "+name);
return false;
}
//Log.e("removePlugin","removed " + name);
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(this);
}
return true;
}
public void setPluginEnabled(String pluginName, boolean value) {
settings.edit().putBoolean(pluginName,value).commit();
if (value) addPlugin(pluginName);
if (value && isPaired() && isReachable()) addPlugin(pluginName);
else removePlugin(pluginName);
}
@@ -617,6 +628,9 @@ public class Device implements BaseLink.PackageReceiver {
return enabled;
}
public boolean hasPluginsLoaded() {
return !plugins.isEmpty() || !failedPlugins.isEmpty();
}
public void reloadPluginsFromSettings() {
@@ -636,9 +650,7 @@ public class Device implements BaseLink.PackageReceiver {
}
}
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(this);
}
//No need to call PluginsChangedListeners because addPlugin and removePlugin already do so
}
public HashMap<String,Plugin> getLoadedPlugins() {
@@ -653,7 +665,7 @@ public class Device implements BaseLink.PackageReceiver {
void onPluginsChanged(Device device);
}
private ArrayList<PluginsChangedListener> pluginsChangedListeners = new ArrayList<PluginsChangedListener>();
private final ArrayList<PluginsChangedListener> pluginsChangedListeners = new ArrayList<PluginsChangedListener>();
public void addPluginsChangedListener(PluginsChangedListener listener) {
pluginsChangedListeners.add(listener);

View File

@@ -13,7 +13,7 @@ public class AppsHelper {
try {
PackageManager pm = context.getPackageManager();
ApplicationInfo ai = pm.getApplicationInfo( packageName, 0);
ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
return pm.getApplicationLabel(ai).toString();

View File

@@ -3,9 +3,7 @@ package org.kde.kdeconnect.Helpers;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.util.Log;
public class ContactsHelper {
@@ -31,12 +29,12 @@ public class ContactsHelper {
// Take the first match only
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME);
int nameIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
if (nameIndex != -1) {
String name = cursor.getString(nameIndex);
//Log.e("PhoneNumberLookup", "success: " + name);
cursor.close();
return name;
return name + " (" + number + ")";
}
}

View File

@@ -11,7 +11,7 @@ public class DeviceHelper {
//from https://github.com/meetup/android-device-names
//Converted to java using:
//cat android_models.properties | awk -F'=' '{sub(/ *$/, "", $1)} sub(/^ */, "", $2) { if ($2 != "") print "humanReadableNames.put(\""$1"\",\"" $2 "\");"}'
private static HashMap<String,String> humanReadableNames = new HashMap<String,String>();
private final static HashMap<String,String> humanReadableNames = new HashMap<String,String>();
static {
humanReadableNames.put("5860E","Coolpad Quattro 4G");
humanReadableNames.put("ADR6300","HTC Droid Incredible");
@@ -24,6 +24,9 @@ public class DeviceHelper {
humanReadableNames.put("C5155","Kyocera Rise");
humanReadableNames.put("C5170","Kyocera Hydro");
humanReadableNames.put("C6603","Sony Xperia Z");
humanReadableNames.put("C6606","Sony Xperia Z");
humanReadableNames.put("C6903","Sony Xperia Z1");
humanReadableNames.put("D6503","Sony Xperia Z2");
humanReadableNames.put("Desire_HD","HTC Desire HD");
humanReadableNames.put("DROID2_GLOBAL","Motorola Droid 2 Global");
humanReadableNames.put("DROID2","Motorola Droid 2");
@@ -40,34 +43,45 @@ public class DeviceHelper {
humanReadableNames.put("Galaxy_Nexus","Samsung Galaxy Nexus");
humanReadableNames.put("google_sdk","Android Emulator");
humanReadableNames.put("GT-I8160","Samsung Galaxy Ace 2");
humanReadableNames.put("GT-I8190N","Samsung Galaxy S III Mini");
humanReadableNames.put("GT-I8190","Samsung Galaxy S III Mini");
humanReadableNames.put("GT-I9000","Samsung Galaxy S");
humanReadableNames.put("GT-I9001","Samsung Galaxy S Plus");
humanReadableNames.put("GT-I9070","Samsung Galaxy S Advance");
humanReadableNames.put("GT-I9082","Samsung Galaxy Grand");
humanReadableNames.put("GT-I9100M","Samsung Galaxy S II");
humanReadableNames.put("GT-I9100P","Samsung Galaxy S II");
humanReadableNames.put("GT-I9100","Samsung Galaxy S II");
humanReadableNames.put("GT-I9100T","Samsung Galaxy S II");
humanReadableNames.put("GT-I9195","Samsung Galaxy S4 Mini");
humanReadableNames.put("GT-I9300","Samsung Galaxy S III");
humanReadableNames.put("GT-I9300T","Samsung Galaxy S III");
humanReadableNames.put("GT-I9305","Samsung Galaxy S III");
humanReadableNames.put("GT-I9505","Samsung Galaxy S 4");
humanReadableNames.put("GT-I9305T","Samsung Galaxy S III");
humanReadableNames.put("GT-I9500","Samsung Galaxy S4");
humanReadableNames.put("GT-I9505","Samsung Galaxy S4");
humanReadableNames.put("GT-N5110","Samsung Galaxy Note 8.0");
humanReadableNames.put("GT-N7000","Samsung Galaxy Note");
humanReadableNames.put("GT-N7100","Samsung Galaxy Note II");
humanReadableNames.put("GT-N7105","Samsung Galaxy Note II");
humanReadableNames.put("GT-N8013","Samsung Galaxy Note 10.1");
humanReadableNames.put("GT-P3113","Samsung Galaxy Tab 2 7.0");
humanReadableNames.put("GT-P5113","Samsnung Galaxy Tab 2 10.1");
humanReadableNames.put("GT-P5210","Samsung Galaxy Tab 3 10.1");
humanReadableNames.put("GT-P7510","Samsung Galaxy Tab 10.1");
humanReadableNames.put("GT-S5360","Samsung Galaxy Y");
humanReadableNames.put("GT-S5570","Samsung Galaxy Mini");
humanReadableNames.put("GT-S5830i","Samsung Galaxy Ace");
humanReadableNames.put("GT-S5830","Samsung Galaxy Ace");
humanReadableNames.put("GT-S7562","Samsung Galaxy S Duos");
humanReadableNames.put("HTC6435LVW","HTC Droid DNA");
humanReadableNames.put("HTC6500LVW","HTC One");
humanReadableNames.put("HTC_Desire_HD_A9191","HTC Desire HD");
humanReadableNames.put("HTCEVODesign4G","HTC Evo Design 4G");
humanReadableNames.put("HTCEVOV4G","HTC Evo V 4G");
humanReadableNames.put("HTCONE","HTC One");
humanReadableNames.put("HTC_PH39100","HTC Vivid 4G");
humanReadableNames.put("HTC_PN071","HTC One");
humanReadableNames.put("HTC_Sensation_Z710e","HTC Sensation");
humanReadableNames.put("HTC_VLE_U","HTC One S");
humanReadableNames.put("KFJWA","Kindle Fire HD 8.9");
@@ -75,9 +89,17 @@ public class DeviceHelper {
humanReadableNames.put("KFOT","Kindle Fire");
humanReadableNames.put("KFTT","Kindle Fire HD 7");
humanReadableNames.put("LG-C800","LG myTouch Q");
humanReadableNames.put("LG-D800","LG G2");
humanReadableNames.put("LG-D801","LG G2");
humanReadableNames.put("LG-D802","LG G2");
humanReadableNames.put("LG-E739","LG MyTouch e739");
humanReadableNames.put("LG-E970","LG Optimus G");
humanReadableNames.put("LG-E980","LG Optimus G Pro");
humanReadableNames.put("LGL55C","LG LGL55C");
humanReadableNames.put("LG-LS840","LG Viper");
humanReadableNames.put("LG-LS970","LG Optimus G");
humanReadableNames.put("LG-LS980","LG G2");
humanReadableNames.put("LGMS769","LG Optimus L9");
humanReadableNames.put("LG-MS770","LG Motion 4G");
humanReadableNames.put("LG-MS910","LG Esteem");
humanReadableNames.put("LG-P509","LG Optimus T");
@@ -93,18 +115,20 @@ public class DeviceHelper {
humanReadableNames.put("MOTWX435KT","Motorola Triumph");
humanReadableNames.put("myTouch_4G_Slide","HTC myTouch 4G Slide");
humanReadableNames.put("N860","ZTE Warp N860");
humanReadableNames.put("Nexus_10","Google Nexus 10");
humanReadableNames.put("Nexus_4","Google Nexus 4");
humanReadableNames.put("Nexus_7","Asus Nexus 7");
humanReadableNames.put("Nexus_S_4G","Samsung Nexus S 4G");
humanReadableNames.put("Nexus_S","Samsung Nexus S");
humanReadableNames.put("Nexus_10","Nexus 10");
humanReadableNames.put("Nexus_4","Nexus 4");
humanReadableNames.put("Nexus_5","Nexus 5");
humanReadableNames.put("Nexus_7","Nexus 7");
humanReadableNames.put("Nexus_S_4G","Nexus S 4G");
humanReadableNames.put("Nexus_S","Nexus S");
humanReadableNames.put("PantechP9070","Pantech Burst");
humanReadableNames.put("PC36100","HTC Evo 4G");
humanReadableNames.put("PG06100","HTC EVO Shift 4G");
humanReadableNames.put("PG86100","HTC Evo 3D");
humanReadableNames.put("PH44100","HTC Evo Design 4G");
humanReadableNames.put("SAMSUNG-SGH-I317","Samsung Galaxy Note II");
humanReadableNames.put("SAMSUNG-SGH-I337","Samsung Galaxy S 4");
humanReadableNames.put("SAMSUNG-SGH-I337","Samsung Galaxy S4");
humanReadableNames.put("SAMSUNG-SGH-I537","Samsung Galaxy S4 Active");
humanReadableNames.put("SAMSUNG-SGH-I717","Samsung Galaxy Note");
humanReadableNames.put("SAMSUNG-SGH-I727","Samsung Skyrocket");
humanReadableNames.put("SAMSUNG-SGH-I747","Samsung Galaxy S III");
@@ -112,22 +136,27 @@ public class DeviceHelper {
humanReadableNames.put("SAMSUNG-SGH-I897","Samsung Captivate");
humanReadableNames.put("SAMSUNG-SGH-I927","Samsung Captivate Glide");
humanReadableNames.put("SAMSUNG-SGH-I997","Samsung Infuse 4G");
humanReadableNames.put("SAMSUNG-SM-N900A","Samsung Galaxy Note 3");
humanReadableNames.put("SCH-I200","Samsung Galaxy Stellar");
humanReadableNames.put("SCH-I405","Samsung Stratosphere");
humanReadableNames.put("SCH-I415","Samsung Galaxy Stratosphere II");
humanReadableNames.put("SCH-I500","Samsung Fascinate");
humanReadableNames.put("SCH-I510","Samsung Droid Charge");
humanReadableNames.put("SCH-I535","Samsung Galaxy S III");
humanReadableNames.put("SCH-I545","Samsung Galaxy S 4");
humanReadableNames.put("SCH-I545","Samsung Galaxy S4");
humanReadableNames.put("SCH-I605","Samsung Galaxy Note II");
humanReadableNames.put("SCH-I800","Samsung Galaxy Tab 7.0");
humanReadableNames.put("SCH-R530M","Samsung Galaxy S III");
humanReadableNames.put("SCH-R530U","Samsung Galaxy S III");
humanReadableNames.put("SCH-R720","Samsung Admire");
humanReadableNames.put("SCH-S720C","Samsung Proclaim");
humanReadableNames.put("SCH-S738C","Samsung Galaxy Centura");
humanReadableNames.put("SGH-I317M","Samsung Galaxy Note II");
humanReadableNames.put("SGH-I337M","Samsung Galaxy S4");
humanReadableNames.put("SGH-I727R","Samsung Galaxy S II");
humanReadableNames.put("SGH-I747M","Samsung Galaxy S III");
humanReadableNames.put("SGH-M919","Samsung Galaxy S 4");
humanReadableNames.put("SGH-M919","Samsung Galaxy S4");
humanReadableNames.put("SGH-T599N","Samsung Galaxy Exhibit");
humanReadableNames.put("SGH-T679","Samsung Exhibit II");
humanReadableNames.put("SGH-T769","Samsung Galaxy S Blaze");
humanReadableNames.put("SGH-T889","Samsung Galaxy Note II");
@@ -135,24 +164,44 @@ public class DeviceHelper {
humanReadableNames.put("SGH-T959V","Samsung Galaxy S 4G");
humanReadableNames.put("SGH-T989D","Samsung Galaxy S II");
humanReadableNames.put("SGH-T989","Samsung Galaxy S II");
humanReadableNames.put("SGH-T999L","Samsung Galaxy S III");
humanReadableNames.put("SGH-T999","Samsung Galaxy S III");
humanReadableNames.put("SGH-T999V","Samsung Galaxy S III");
humanReadableNames.put("SHV-E210S","Samsung Galaxy S III");
humanReadableNames.put("SM-N9005","Samsung Galaxy Note 3");
humanReadableNames.put("SM-N900P","Samsung Galaxy Note 3");
humanReadableNames.put("SM-N900T","Samsung Galaxy Note 3");
humanReadableNames.put("SM-N900V","Samsung Galaxy Note 3");
humanReadableNames.put("SM-N900W8","Samsung Galaxy Note 3");
humanReadableNames.put("SM-T210R","Samsung Galaxy Tab 3 7.0");
humanReadableNames.put("SM-T310","Samsung Galaxy Tab 3 8.0");
humanReadableNames.put("SPH-D600","Samsung Conquer 4G");
humanReadableNames.put("SPH-D700","Samsung Epic 4G");
humanReadableNames.put("SPH-D710BST","Samsung Galaxy S II");
humanReadableNames.put("SPH-D710","Samsung Epic");
humanReadableNames.put("SPH-D710VMUB","Samsung Galaxy S II");
humanReadableNames.put("SPH-L300","Samsung Galaxy Victory");
humanReadableNames.put("SPH-L710","Samsung Galaxy S III");
humanReadableNames.put("SPH-L720","Samsung Galaxy S 4");
humanReadableNames.put("SPH-L720","Samsung Galaxy S4");
humanReadableNames.put("SPH-L900","Samsung Galaxy Note II");
humanReadableNames.put("SPH-M820-BST","Samsung Galaxy Prevail");
humanReadableNames.put("SPH-M830","Samsung Galaxy Rush");
humanReadableNames.put("SPH-M930BST","Samsung Transform Ultra");
humanReadableNames.put("ST25i","Sony Xperia U");
humanReadableNames.put("Transformer_Prime_TF201","Asus Eee Pad Transformer Prime");
humanReadableNames.put("Transformer_TF101","Asus Eee Pad Transformer");
humanReadableNames.put("VM670","LG Optimus V");
humanReadableNames.put("VS840_4G","LG Lucid 4G");
humanReadableNames.put("VS910_4G","LG Revolution 4G");
humanReadableNames.put("VS920_4G","LG Spectrum 4G");
humanReadableNames.put("VS980_4G","LG G2");
humanReadableNames.put("Xoom","Motorola Xoom");
humanReadableNames.put("XT1030","Motorola Droid Mini");
humanReadableNames.put("XT1032","Motorola Moto G");
humanReadableNames.put("XT1058","Motorola Moto X");
humanReadableNames.put("XT1060","Motorola Moto X");
humanReadableNames.put("XT1080","Motorola Droid Ultra");
humanReadableNames.put("XT897","Motorola Photo Q");
humanReadableNames.put("XT907","Motorola Droid Razr M");
}

View File

@@ -0,0 +1,56 @@
package org.kde.kdeconnect.Helpers;
import android.webkit.MimeTypeMap;
public class FilesHelper {
public static String getFileExt(String fileName) {
//return MimeTypeMap.getFileExtensionFromUrl(fileName);
return fileName.substring((fileName.lastIndexOf(".") + 1), fileName.length());
}
public static String getMimeTypeFromFile(String file) {
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(getFileExt(file));
if (mime == null) mime = "*/*";
return mime;
}
//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.
* The result will only include ascii characters and numbers, and the "-","_", and "." characters.
*
* @param name
* @param dirSeparators
* @param maxFileLength
* @return
*/
public static String toFileSystemSafeName(String name, boolean dirSeparators, int maxFileLength) {
int size = name.length();
StringBuilder rc = new StringBuilder(size * 2);
for (int i = 0; i < size; i++) {
char c = name.charAt(i);
boolean valid = c >= 'a' && c <= 'z';
valid = valid || (c >= 'A' && c <= 'Z');
valid = valid || (c >= '0' && c <= '9');
valid = valid || (c == '_') || (c == '-') || (c == '.');
valid = valid || (dirSeparators && ( (c == '/') || (c == '\\')));
if (valid) {
rc.append(c);
}
}
String result = rc.toString();
if (result.length() > maxFileLength) {
result = result.substring(result.length()-maxFileLength,result.length());
}
return result;
}
public static String toFileSystemSafeName(String name, boolean dirSeparators) {
return toFileSystemSafeName(name, dirSeparators, 255);
}
public static String toFileSystemSafeName(String name) {
return toFileSystemSafeName(name, true, 255);
}
}

View File

@@ -0,0 +1,101 @@
package org.kde.kdeconnect.Helpers;
import android.os.Environment;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.StringTokenizer;
//Code from http://stackoverflow.com/questions/9340332/how-can-i-get-the-list-of-mounted-external-storage-of-android-device/19982338#19982338
public class StorageHelper {
private static final String TAG = "StorageHelper";
public static class StorageInfo {
public final String path;
public final boolean readonly;
public final boolean removable;
public final int number;
StorageInfo(String path, boolean readonly, boolean removable, int number) {
this.path = path;
this.readonly = readonly;
this.removable = removable;
this.number = number;
}
}
public static List<StorageInfo> getStorageList() {
List<StorageInfo> list = new ArrayList<StorageInfo>();
String def_path = Environment.getExternalStorageDirectory().getPath();
boolean def_path_removable = Environment.isExternalStorageRemovable();
String def_path_state = Environment.getExternalStorageState();
boolean def_path_available = def_path_state.equals(Environment.MEDIA_MOUNTED)
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
HashSet<String> paths = new HashSet<String>();
int cur_removable_number = 1;
if (def_path_available) {
paths.add(def_path);
list.add(0, new StorageInfo(def_path, def_path_readonly, def_path_removable, def_path_removable ? cur_removable_number++ : -1));
}
BufferedReader buf_reader = null;
try {
buf_reader = new BufferedReader(new FileReader("/proc/mounts"));
String line;
Log.d(TAG, "/proc/mounts");
while ((line = buf_reader.readLine()) != null) {
Log.d(TAG, line);
if (line.contains("vfat") || line.contains("/mnt")) {
StringTokenizer tokens = new StringTokenizer(line, " ");
String unused = tokens.nextToken(); //device
String mount_point = tokens.nextToken(); //mount point
if (paths.contains(mount_point)) {
continue;
}
unused = tokens.nextToken(); //file system
List<String> flags = Arrays.asList(tokens.nextToken().split(",")); //flags
boolean readonly = flags.contains("ro");
if (line.contains("/dev/block/vold")) {
if (!line.contains("/mnt/secure")
&& !line.contains("/mnt/asec")
&& !line.contains("/mnt/obb")
&& !line.contains("/dev/mapper")
&& !line.contains("tmpfs")) {
paths.add(mount_point);
list.add(new StorageInfo(mount_point, readonly, true, cur_removable_number++));
}
}
}
}
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (buf_reader != null) {
try {
buf_reader.close();
} catch (IOException ex) {}
}
}
return list;
}
}

View File

@@ -8,7 +8,6 @@ import android.util.Base64;
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.UserInterface.MainSettingsActivity;
@@ -33,9 +32,11 @@ public class NetworkPackage {
public final static String PACKAGE_TYPE_PING = "kdeconnect.ping";
public final static String PACKAGE_TYPE_TELEPHONY = "kdeconnect.telephony";
public final static String PACKAGE_TYPE_BATTERY = "kdeconnect.battery";
public final static String PACKAGE_TYPE_SFTP = "kdeconnect.sftp";
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
public final static String PACKAGE_TYPE_MOUSEPAD = "kdeconnect.mousepad";
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
private long mId;

View File

@@ -8,7 +8,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Drawable;
import android.os.BatteryManager;
import android.util.Log;
import android.widget.Button;
import org.kde.kdeconnect.NetworkPackage;
@@ -21,11 +20,7 @@ public class BatteryPlugin extends Plugin {
private static final int THRESHOLD_EVENT_NONE= 0;
private static final int THRESHOLD_EVENT_BATTERY_LOW = 1;
NetworkPackage lastInfo = null;
/*static {
PluginFactory.registerPlugin(BatteryPlugin.class);
}*/
private NetworkPackage lastInfo = null;
@Override
public String getPluginName() {
@@ -47,12 +42,17 @@ public class BatteryPlugin extends Plugin {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return false;
}
@Override
public boolean isEnabledByDefault() {
return true;
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent batteryIntent) {
@@ -114,7 +114,7 @@ public class BatteryPlugin extends Plugin {
}
@Override
public AlertDialog getErrorDialog(Context baseContext) {
public AlertDialog getErrorDialog(Activity deviceActivity) {
return null;
}

View File

@@ -10,12 +10,18 @@ import org.kde.kdeconnect.NetworkPackage;
public class ClipboardListener {
private final Context context;
private String currentContent;
private ClipboardManager cm = null;
ClipboardManager.OnPrimaryClipChangedListener listener;
private ClipboardManager.OnPrimaryClipChangedListener listener;
ClipboardListener(final Context ctx, final Device device) {
context = ctx;
if(android.os.Build.VERSION.SDK_INT < 11) {
return;
}
ClipboardListener(final Context context, final Device device) {
cm = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
listener = new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
@@ -41,12 +47,24 @@ 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;
cm.setText(text);
if(android.os.Build.VERSION.SDK_INT < 11) {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(text);
}
else
{
cm.setText(text);
}
}
}

View File

@@ -3,9 +3,7 @@ package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.widget.Button;
import org.kde.kdeconnect.NetworkPackage;
@@ -14,10 +12,6 @@ import org.kde.kdeconnect_tp.R;
public class ClipboardPlugin extends Plugin {
/*static {
PluginFactory.registerPlugin(ClipboardPlugin.class);
}*/
@Override
public String getPluginName() {
return "plugin_clipboard";
@@ -37,33 +31,28 @@ public class ClipboardPlugin extends Plugin {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return false;
}
@Override
public boolean isEnabledByDefault() {
return (Build.VERSION.SDK_INT >= 11);
//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);
}
private ClipboardListener listener;
@Override
public boolean onCreate() {
if (Build.VERSION.SDK_INT < 11) {
return false;
}
listener = new ClipboardListener(context, device);
return true;
}
@Override
public void onDestroy() {
if (Build.VERSION.SDK_INT < 11) return;
listener.stop();
}
@Override
@@ -76,22 +65,10 @@ public class ClipboardPlugin extends Plugin {
String content = np.getString("content");
listener.setText(content);
return true;
}
@Override
public AlertDialog getErrorDialog(Context baseContext) {
return new AlertDialog.Builder(baseContext)
.setTitle(R.string.pref_plugin_clipboard)
.setMessage(R.string.plugin_not_available)
.setPositiveButton("Ok",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
})
.create();
}
public AlertDialog getErrorDialog(Activity deviceActivity) { return null; }
@Override
public Button getInterfaceButton(Activity activity) {

View File

@@ -0,0 +1,103 @@
package org.kde.kdeconnect.Plugins.MousePadPlugin;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import java.nio.ByteBuffer;
import java.util.HashMap;
public class KeyListenerView extends View {
private String deviceId;
private static HashMap<Integer, Integer> SpecialKeysMap = new HashMap<Integer, Integer>();
static {
int i = 0;
SpecialKeysMap.put(KeyEvent.KEYCODE_DEL, ++i); // 1
SpecialKeysMap.put(KeyEvent.KEYCODE_TAB, ++i); // 2
SpecialKeysMap.put(KeyEvent.KEYCODE_ENTER, 12); ++i; // 3 is not used, return is 12 instead
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_LEFT, ++i); // 4
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_UP, ++i); // 5
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_RIGHT, ++i); // 6
SpecialKeysMap.put(KeyEvent.KEYCODE_DPAD_DOWN, ++i); // 7
SpecialKeysMap.put(KeyEvent.KEYCODE_PAGE_UP, ++i); // 8
SpecialKeysMap.put(KeyEvent.KEYCODE_PAGE_DOWN, ++i); // 9
if (Build.VERSION.SDK_INT >= 11) {
SpecialKeysMap.put(KeyEvent.KEYCODE_MOVE_HOME, ++i); // 10
SpecialKeysMap.put(KeyEvent.KEYCODE_MOVE_END, ++i); // 11
SpecialKeysMap.put(KeyEvent.KEYCODE_NUMPAD_ENTER, ++i); // 12
SpecialKeysMap.put(KeyEvent.KEYCODE_FORWARD_DEL, ++i); // 13
SpecialKeysMap.put(KeyEvent.KEYCODE_ESCAPE, ++i); // 14
}
}
public void setDeviceId(String id) {
deviceId = id;
}
public KeyListenerView(Context context, AttributeSet set) {
super(context, set);
setFocusable(true);
setFocusableInTouchMode(true);
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
return null;
}
@Override
public boolean onCheckIsTextEditor() {
return true;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
char utfChar = (char)event.getUnicodeChar();
if (utfChar == 9 || utfChar == 10) utfChar = 0; //Workaround to send enter and tab as special keys instead of characters
if (utfChar != 0) {
final String utfString = new String(new char[]{utfChar});
BackgroundService.RunCommand(getContext(), new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendKey(utfString);
}
});
} else {
if (!SpecialKeysMap.containsKey(keyCode)) {
return false;
}
final int specialKey = SpecialKeysMap.get(keyCode);
BackgroundService.RunCommand(getContext(), new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendSpecialKey(specialKey);
}
});
}
return true;
}
}

View File

@@ -0,0 +1,254 @@
package org.kde.kdeconnect.Plugins.MousePadPlugin;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.GestureDetector;
import android.view.inputmethod.InputMethodManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
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 float mPrevX;
private float mPrevY;
private float mCurrentX;
private float mCurrentY;
boolean isScrolling = false;
float accumulatedDistanceY = 0;
private GestureDetector mDetector;
private MousePadGestureDetector mMousePadGestureDetector;
KeyListenerView keyListenerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mousepad);
deviceId = getIntent().getStringExtra("deviceId");
mDetector = new GestureDetector(this, this);
mMousePadGestureDetector = new MousePadGestureDetector(this, this);
mDetector.setOnDoubleTapListener(this);
keyListenerView = (KeyListenerView)findViewById(R.id.keyListener);
keyListenerView.setDeviceId(deviceId);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_mousepad, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_right_click:
sendRightClick();
return true;
case R.id.menu_middle_click:
sendMiddleClick();
return true;
case R.id.menu_show_keyboard:
showKeyboard();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mMousePadGestureDetector.onTouchEvent(event)) {
return true;
}
if ( mDetector.onTouchEvent(event) ) {
return true;
}
int actionType = event.getAction();
if (isScrolling) {
if (actionType == MotionEvent.ACTION_UP) {
isScrolling = false;
} else {
return false;
}
}
switch (actionType) {
case MotionEvent.ACTION_DOWN:
mPrevX = event.getX();
mPrevY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
mCurrentX = event.getX();
mCurrentY = event.getY();
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendMouseDelta(mCurrentX - mPrevX, mCurrentY - mPrevY);
mPrevX = mCurrentX;
mPrevY = mCurrentY;
}
});
break;
}
return true;
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
//From GestureDetector, left empty
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@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
if (e2.getPointerCount() <= 1) {
return false;
}
isScrolling = true;
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 = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendScroll(0, scrollToSendY);
}
});
accumulatedDistanceY = 0;
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
//From GestureDetector, left empty
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendSingleClick();
}
});
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendDoubleClick();
}
});
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return false;
}
@Override
public boolean onTripleFingerTap(MotionEvent ev) {
sendMiddleClick();
return true;
}
@Override
public boolean onDoubleFingerTap(MotionEvent ev) {
sendRightClick();
return true;
}
private void sendMiddleClick() {
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendMiddleClick();
}
});
}
private void sendRightClick() {
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin)device.getPlugin("plugin_mousepad");
if (mousePadPlugin == null) return;
mousePadPlugin.sendRightClick();
}
});
}
private void showKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInputFromWindow(keyListenerView.getWindowToken(), 0, 0);
}
}

View File

@@ -0,0 +1,59 @@
package org.kde.kdeconnect.Plugins.MousePadPlugin;
import android.content.Context;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
public class MousePadGestureDetector {
private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout() + 100;
private OnGestureListener mGestureListener;
private Context mCtx;
private long mFirstDownTime = 0;
private boolean mIsGestureHandled;
public interface OnGestureListener {
boolean onTripleFingerTap(MotionEvent ev);
boolean onDoubleFingerTap(MotionEvent ev);
}
public MousePadGestureDetector(Context ctx, OnGestureListener gestureListener) {
if (gestureListener == null) {
throw new IllegalArgumentException("gestureListener cannot be null");
}
mGestureListener = gestureListener;
mCtx = ctx;
}
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mIsGestureHandled = false;
mFirstDownTime = event.getEventTime();
break;
case MotionEvent.ACTION_POINTER_UP:
int count = event.getPointerCount();
if (event.getEventTime() - mFirstDownTime <= TAP_TIMEOUT) {
if (count == 3) {
if (!mIsGestureHandled) {
mIsGestureHandled = mGestureListener.onTripleFingerTap(event);
}
} else if (count == 2) {
if (!mIsGestureHandled) {
mIsGestureHandled = mGestureListener.onDoubleFingerTap(event);
}
}
}
mFirstDownTime = 0;
break;
}
return mIsGestureHandled;
}
}

View File

@@ -0,0 +1,131 @@
package org.kde.kdeconnect.Plugins.MousePadPlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.Button;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
public class MousePadPlugin extends Plugin {
@Override
public String getPluginName() {
return "plugin_mousepad";
}
@Override
public String getDisplayName() {
return context.getString(R.string.pref_plugin_mousepad);
}
@Override
public String getDescription() {
return context.getString(R.string.pref_plugin_mousepad_desc);
}
@Override
public Drawable getIcon() {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@Override
public boolean hasSettings() {
return false;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public void onDestroy() {
}
@Override
public boolean onPackageReceived(NetworkPackage np) {
return false;
}
@Override
public AlertDialog getErrorDialog(Activity deviceActivity) { return null; }
@Override
public Button getInterfaceButton(final Activity activity) {
Button button = new Button(activity);
button.setText(R.string.open_mousepad);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(activity, MousePadActivity.class);
intent.putExtra("deviceId", device.getDeviceId());
activity.startActivity(intent);
}
});
return button;
}
public void sendMouseDelta(float dx, float dy) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("dx", dx);
np.set("dy", dy);
device.sendPackage(np);
}
public void sendSingleClick() {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("singleclick", true);
device.sendPackage(np);
}
public void sendDoubleClick() {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("doubleclick", true);
device.sendPackage(np);
}
public void sendMiddleClick() {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("middleclick", true);
device.sendPackage(np);
}
public void sendRightClick() {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("rightclick", true);
device.sendPackage(np);
}
public void sendScroll(float dx, float dy) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("scroll", true);
np.set("dx", dx);
np.set("dy", dy);
device.sendPackage(np);
}
public void sendKey(String utfChar) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("key", utfChar);
device.sendPackage(np);
}
public void sendSpecialKey(int specialKey) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("specialKey", specialKey);
device.sendPackage(np);
}
}

View File

@@ -1,10 +1,13 @@
package org.kde.kdeconnect.Plugins.MprisPlugin;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
@@ -24,9 +27,11 @@ import java.util.ArrayList;
public class MprisActivity extends Activity {
//TODO: Add a loading spinner at the begginning (to distinguish the loading state from a no-players state).
//TODO: Add a loading spinner at the beginning (to distinguish the loading state from a no-players state).
//TODO 2: Add a message when no players are detected after loading completes
private String deviceId;
protected void connectToPlugin() {
final String deviceId = getIntent().getStringExtra("deviceId");
@@ -67,8 +72,6 @@ public class MprisActivity extends Activity {
});
mpris.setPlayerListUpdatedHandler(new Handler() {
boolean firstLoad = true;
@Override
public void handleMessage(Message msg) {
final ArrayList<String> playerList = mpris.getPlayerList();
@@ -104,17 +107,18 @@ public class MprisActivity extends Activity {
@Override
public void onNothingSelected(AdapterView<?> arg0) {
mpris.setPlayer(null);
}
});
// restore the selected player
int position = adapter.getPosition(mpris.getPlayer());
if (position >= 0) {
spinner.setSelection(position);
}
}
});
if (firstLoad) {
firstLoad = false;
if (playerList.size() > 0) {
mpris.setPlayer(playerList.get(0));
}
}
}
});
@@ -123,7 +127,7 @@ public class MprisActivity extends Activity {
}
BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
private final BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
@Override
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link) {
connectToPlugin();
@@ -146,12 +150,78 @@ public class MprisActivity extends Activity {
});
}
/**
* Change current volume with provided step.
*
* @param mpris multimedia controller
* @param step step size volume change
*/
private void updateVolume(MprisPlugin mpris, int step) {
final int currentVolume = mpris.getVolume();
if(currentVolume < 100 || currentVolume > 0) {
int newVolume = currentVolume + step;
if(newVolume > 100) {
newVolume = 100;
} else if (newVolume <0 ) {
newVolume = 0;
}
mpris.setVolume(newVolume);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
if (mpris == null) return;
updateVolume(mpris, 5);
}
});
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
if (mpris == null) return;
updateVolume(mpris, -5);
}
});
return true;
default:
return super.onKeyDown(keyCode, event);
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
return true;
default:
return super.onKeyUp(keyCode, event);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mpris_control);
final String deviceId = getIntent().getStringExtra("deviceId");
deviceId = getIntent().getStringExtra("deviceId");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String interval_time_str = prefs.getString(getString(R.string.mpris_time_key),
getString(R.string.mpris_time_default));
final int interval_time = Integer.parseInt(interval_time_str);
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
@Override
@@ -200,7 +270,7 @@ public class MprisActivity extends Activity {
Device device = service.getDevice(deviceId);
MprisPlugin mpris = (MprisPlugin)device.getPlugin("plugin_mpris");
if (mpris == null) return;
mpris.Seek(-10000000); // -10 seconds. TODO: plugin settings UI?
mpris.Seek(interval_time * -1);
}
});
}
@@ -215,7 +285,7 @@ public class MprisActivity extends Activity {
Device device = service.getDevice(deviceId);
MprisPlugin mpris = (MprisPlugin)device.getPlugin("plugin_mpris");
if (mpris == null) return;
mpris.Seek(10000000); // 10 seconds. TODO: plugin settings UI?
mpris.Seek(interval_time);
}
});
}
@@ -238,10 +308,12 @@ public class MprisActivity extends Activity {
((SeekBar)findViewById(R.id.volume_seek)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) { }
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) { }
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(final SeekBar seekBar) {
@@ -253,12 +325,14 @@ public class MprisActivity extends Activity {
if (mpris == null) return;
mpris.setVolume(seekBar.getProgress());
}
});
});
}
});
}
}

View File

@@ -30,10 +30,6 @@ public class MprisPlugin extends Plugin {
private String player = "";
private boolean playing = false;
/*static {
PluginFactory.registerPlugin(MprisPlugin.class);
}*/
@Override
public String getPluginName() {
return "plugin_mpris";
@@ -54,6 +50,11 @@ public class MprisPlugin extends Plugin {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return true;
}
@Override
public boolean isEnabledByDefault() {
return true;
@@ -179,6 +180,10 @@ public class MprisPlugin extends Plugin {
requestPlayerStatus();
}
public String getPlayer() {
return player;
}
public int getVolume() {
return volume;
}
@@ -203,7 +208,7 @@ public class MprisPlugin extends Plugin {
}
@Override
public AlertDialog getErrorDialog(Context baseContext) {
public AlertDialog getErrorDialog(Activity deviceActivity) {
return null;
}

View File

@@ -5,7 +5,6 @@ import android.content.Context;
import android.content.Intent;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import java.util.ArrayList;
@@ -16,7 +15,7 @@ public class NotificationReceiver extends NotificationListenerService {
void onNotificationRemoved(StatusBarNotification statusBarNotification);
}
private ArrayList<NotificationListener> listeners = new ArrayList<NotificationListener>();
private final ArrayList<NotificationListener> listeners = new ArrayList<NotificationListener>();
public void addListener(NotificationListener listener) {
listeners.add(listener);
@@ -61,7 +60,7 @@ public class NotificationReceiver extends NotificationListenerService {
void onServiceStart(NotificationReceiver service);
}
private static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>();
public static void Start(Context c) {
RunCommand(c, null);

View File

@@ -6,31 +6,22 @@ import android.app.Notification;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Base64;
import android.util.Log;
import android.widget.Button;
import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.Helpers.ImagesHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.DeviceActivity;
import org.kde.kdeconnect_tp.R;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
/*static {
PluginFactory.registerPlugin(NotificationsPlugin.class);
}*/
@Override
public String getPluginName() {
return "plugin_notifications";
@@ -51,6 +42,11 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return false;
}
@Override
public boolean isEnabledByDefault() {
return true;
@@ -116,7 +112,9 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public boolean onCreate() {
if (Build.VERSION.SDK_INT < 18) return false;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
return false;
}
//Check for permissions
String notificationListenerList = Settings.Secure.getString(context.getContentResolver(), "enabled_notification_listeners");
@@ -128,7 +126,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
service.addListener(NotificationsPlugin.this);
StatusBarNotification[] notifications = service.getActiveNotifications();
for (StatusBarNotification notification : notifications) {
onNotificationPosted(notification);
sendNotification(notification, true);
}
} catch(Exception e) {
e.printStackTrace();
@@ -146,6 +144,11 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public void onDestroy() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
return;
}
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override
public void onServiceStart(NotificationReceiver service) {
@@ -176,6 +179,13 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
public void sendNotification(StatusBarNotification statusBarNotification, boolean requestAnswer) {
Notification notification = statusBarNotification.getNotification();
if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
|| (notification.flags & Notification.FLAG_ONGOING_EVENT) != 0 ) {
//This is not a notification!
return;
}
NotificationId id = NotificationId.fromNotification(statusBarNotification);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION);
@@ -202,7 +212,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
np.set("id", id.serialize());
np.set("appName", appName == null? packageName : appName);
np.set("isClearable", statusBarNotification.isClearable());
np.set("ticker", (notification != null && notification.tickerText != null)? notification.tickerText.toString() : "");
np.set("ticker", getTickerText(notification));
np.set("time", Long.toString(statusBarNotification.getPostTime()));
if (requestAnswer) np.set("requestAnswer", true);
@@ -210,7 +220,43 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
}
/**
* Returns the ticker text of the notification.
* If device android version is KitKat or newer, the title and text of the notification is used
* instead the ticker text.
*/
private String getTickerText(Notification notification) {
final String TITLE_KEY = "android.title";
final String TEXT_KEY = "android.text";
String ticker = "";
if(notification != null) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
try {
Bundle extras = notification.extras;
String extraTitle = extras.getString(TITLE_KEY);
String extraText = extras.getString(TEXT_KEY);
if (extraTitle != null && extraText != null) {
ticker = extraTitle + " " + extraText;
} else if (extraTitle != null) {
ticker = extraTitle;
} else if (extraText != null) {
ticker = extraText;
}
} catch(Exception e) {
Log.w("NotificationPlugin","problem parsing notification extras");
e.printStackTrace();
}
}
if (ticker.isEmpty()) {
ticker = (notification.tickerText != null)? notification.tickerText.toString() : "";
}
}
return ticker;
}
@Override
@@ -274,10 +320,10 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public AlertDialog getErrorDialog(final Context baseContext) {
public AlertDialog getErrorDialog(final Activity deviceActivity) {
if (Build.VERSION.SDK_INT < 18) {
return new AlertDialog.Builder(baseContext)
return new AlertDialog.Builder(deviceActivity)
.setTitle(R.string.pref_plugin_notifications)
.setMessage(R.string.plugin_not_available)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@@ -288,14 +334,14 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
})
.create();
} else {
return new AlertDialog.Builder(baseContext)
return new AlertDialog.Builder(deviceActivity)
.setTitle(R.string.pref_plugin_notifications)
.setMessage(R.string.no_permissions)
.setPositiveButton(R.string.open_settings, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
baseContext.startActivity(intent);
deviceActivity.startActivityForResult(intent, DeviceActivity.RESULT_NEEDS_RELOAD);
}
})
.setNegativeButton(R.string.cancel,new DialogInterface.OnClickListener() {

View File

@@ -4,23 +4,23 @@ 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.Intent;
import android.graphics.drawable.Drawable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.view.View;
import android.widget.Button;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MainActivity;
import org.kde.kdeconnect_tp.R;
public class PingPlugin extends Plugin {
/*static {
PluginFactory.registerPlugin(PingPlugin.class);
}*/
@Override
public String getPluginName() {
return "plugin_ping";
@@ -41,6 +41,11 @@ public class PingPlugin extends Plugin {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return false;
}
@Override
public boolean isEnabledByDefault() {
return true;
@@ -63,17 +68,36 @@ public class PingPlugin extends Plugin {
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_PING)) {
//Log.e("PingPackageReceiver", "was a ping!");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(new Intent(context, MainActivity.class));
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
int id;
String message;
if (np.has("message")) {
message = np.getString("message");
id = (int)System.currentTimeMillis();
} else {
message = "Ping!";
id = 42; //A unique id to create only one notification
}
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(device.getName())
.setContentText("Ping!")
.setTicker("Ping!")
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setTicker(message)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND)
.setDefaults(Notification.DEFAULT_ALL)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(42 /*a unique id to create only one notification*/, noti);
notificationManager.notify(id, noti);
return true;
}
@@ -81,7 +105,7 @@ public class PingPlugin extends Plugin {
}
@Override
public AlertDialog getErrorDialog(Context baseContext) {
public AlertDialog getErrorDialog(Activity deviceActivity) {
return null;
}

View File

@@ -51,6 +51,12 @@ public abstract class Plugin {
*/
public abstract boolean isEnabledByDefault();
/**
* Return true if this plugin needs an specific UI settings.
*/
public abstract boolean hasSettings();
/**
* Initialize the listeners and structures in your plugin.
* Should return true if initialization was successful.
@@ -73,7 +79,7 @@ public abstract class Plugin {
* If onCreate returns false, should create a dialog explaining
* the problem (and how to fix it, if possible) to the user.
*/
public abstract AlertDialog getErrorDialog(Context baseContext);
public abstract AlertDialog getErrorDialog(Activity deviceActivity);
/**
* Creates a button that will be displayed in the user interface

View File

@@ -7,10 +7,13 @@ import android.util.Log;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin;
import org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadPlugin;
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.SharePlugin.SharePlugin;
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
import java.util.Map;
@@ -21,12 +24,14 @@ public class PluginFactory {
public static class PluginInfo {
public PluginInfo(String pluginName, String displayName, String description, Drawable icon, boolean enabledByDefault) {
public PluginInfo(String pluginName, String displayName, String description, Drawable icon,
boolean enabledByDefault, boolean hasSettings) {
this.pluginName = pluginName;
this.displayName = displayName;
this.description = description;
this.icon = icon;
this.enabledByDefault = enabledByDefault;
this.hasSettings = hasSettings;
}
public String getPluginName() {
@@ -45,15 +50,18 @@ public class PluginFactory {
return icon;
}
public boolean hasSettings() { return hasSettings; }
public boolean isEnabledByDefault() {
return enabledByDefault;
}
private String pluginName;
private String displayName;
private String description;
private final String pluginName;
private final String displayName;
private final String description;
private final Drawable icon;
private boolean enabledByDefault;
private final boolean enabledByDefault;
private final boolean hasSettings;
}
@@ -61,13 +69,16 @@ public class PluginFactory {
private static final Map<String, PluginInfo> availablePluginsInfo = new TreeMap<String, PluginInfo>();
static {
//TODO: Avoid this factory having to know every plugin
//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);
PluginFactory.registerPlugin(ClipboardPlugin.class);
PluginFactory.registerPlugin(BatteryPlugin.class);
PluginFactory.registerPlugin(SftpPlugin.class);
PluginFactory.registerPlugin(NotificationsPlugin.class);
PluginFactory.registerPlugin(MousePadPlugin.class);
PluginFactory.registerPlugin(SharePlugin.class);
}
public static PluginInfo getPluginInfo(Context context, String pluginName) {
@@ -76,7 +87,8 @@ public class PluginFactory {
try {
Plugin p = ((Plugin)availablePlugins.get(pluginName).newInstance());
p.setContext(context, null);
info = new PluginInfo(pluginName, p.getDisplayName(), p.getDescription(), p.getIcon(), p.isEnabledByDefault());
info = new PluginInfo(pluginName, p.getDisplayName(), p.getDescription(), p.getIcon(),
p.isEnabledByDefault(), p.hasSettings());
availablePluginsInfo.put(pluginName, info); //Cache it
return info;
} catch(Exception e) {
@@ -109,10 +121,10 @@ public class PluginFactory {
}
public static void registerPlugin(Class pluginClass) {
public static void registerPlugin(Class<? extends Plugin> pluginClass) {
try {
//I hate this but I need to create an instance because abstract static functions can't be declared
String pluginName = ((Plugin)pluginClass.newInstance()).getPluginName();
String pluginName = (pluginClass.newInstance()).getPluginName();
availablePlugins.put(pluginName, pluginClass);
} catch(Exception e) {
Log.e("PluginFactory","addPlugin exception");

View File

@@ -0,0 +1,210 @@
package org.kde.kdeconnect.Plugins.SftpPlugin;
import android.content.Context;
import android.util.Log;
import org.apache.http.conn.util.InetAddressUtils;
import org.apache.sshd.SshServer;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.Session;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.FileSystemFactory;
import org.apache.sshd.server.FileSystemView;
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.filesystem.NativeFileSystemView;
import org.apache.sshd.server.filesystem.NativeSshFile;
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 java.io.File;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Arrays;
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 List<PublicKey> keys = new ArrayList<PublicKey>();
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";
public static int port = -1;
private static boolean started = false;
public final SimplePasswordAuthenticator passwordAuth = new SimplePasswordAuthenticator();
public final SimplePublicKeyAuthenticator keyAuth = new SimplePublicKeyAuthenticator();
private final SshServer sshd = SshServer.setUpDefaultServer();
public void init(Context ctx, Device device) {
passwordAuth.setUser(USER);
keyAuth.addKey(device.publicKey);
sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(ctx.getFilesDir() + "/sftpd.ser"));
//sshd.setFileSystemFactory(new NativeFileSystemFactory());
sshd.setFileSystemFactory(new SecureFileSystemFactory());
//sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", "-l" }));
sshd.setCommandFactory(new ScpCommandFactory());
sshd.setSubsystemFactories(Arrays.<NamedFactory<Command>>asList(new SftpSubsystem.Factory()));
sshd.setPasswordAuthenticator(passwordAuth);
sshd.setPublickeyAuthenticator(keyAuth);
}
public boolean start() {
if (!started) {
String password = Long.toHexString(Double.doubleToLongBits(Math.random()));
passwordAuth.setPassword(password);
port = STARTPORT;
while(!started) {
try {
sshd.setPort(port);
sshd.start();
started = true;
} catch(Exception e) {
port++;
if (port >= ENDPORT) {
port = -1;
Log.e("SftpServer", "No more ports available");
return false;
}
}
}
}
return true;
}
public void stop() {
try {
started = false;
sshd.stop();
} catch (InterruptedException e) {
}
}
public String getLocalIpAddress() {
String ip6 = null;
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()) {
String address = inetAddress.getHostAddress();
if (InetAddressUtils.isIPv4Address(address)) { //Prefer IPv4 over IPv6, because sshfs doesn't seem to like IPv6
return address;
} else {
ip6 = address;
}
}
}
}
} catch (SocketException ex) {
}
return ip6;
}
}
class SecureFileSystemFactory implements FileSystemFactory {
public SecureFileSystemFactory() {}
@Override
public FileSystemView createFileSystemView(final Session username) {
final String base = "/";
return new SecureFileSystemView(base, username.getUsername());
}
}
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;
private boolean caseInsensitive = false;
//
public SecureFileSystemView(final String rootDir, final String userName) {
super(userName);
this.rootDir = NativeSshFile.normalizeSeparateChar(rootDir);
this.userName = userName;
}
//
@Override
public SshFile getFile(final String file) {
return getFile(currDir, file);
}
@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
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(this, userFileName, fileObj, userName);
}
}
class SecureSshFile extends NativeSshFile {
//
public SecureSshFile(final SecureFileSystemView view, final String fileName, final File file, final String userName) {
super(fileName, file, userName);
}
}

View File

@@ -0,0 +1,128 @@
package org.kde.kdeconnect.Plugins.SftpPlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.widget.Button;
import org.kde.kdeconnect.Helpers.StorageHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class SftpPlugin extends Plugin {
private static final SimpleSftpServer server = new SimpleSftpServer();
@Override
public String getPluginName() {return "plugin_sftp";}
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_sftp);
}
@Override
public String getDescription() {
return context.getResources().getString(R.string.pref_plugin_sftp_desc);
}
@Override
public Drawable getIcon() {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return false;
}
@Override
public boolean isEnabledByDefault() { return true; }
@Override
public boolean onCreate() {
server.init(context, device);
return true;
}
@Override
public void onDestroy() {
server.stop();
}
@Override
public boolean onPackageReceived(NetworkPackage np) {
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_SFTP)) return false;
if (np.getBoolean("startBrowsing")) {
if (server.start()) {
NetworkPackage np2 = new NetworkPackage(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());
//Kept for compatibility, but new desktop clients will read "multiPaths" instead,
// that supports devices with more than one external storage
np2.set("path", Environment.getExternalStorageDirectory().getAbsolutePath());
List<StorageHelper.StorageInfo> storageList = StorageHelper.getStorageList();
ArrayList<String> paths = new ArrayList<String>();
ArrayList<String> pathNames = new ArrayList<String>();
for (StorageHelper.StorageInfo storage : storageList) {
paths.add(storage.path);
StringBuilder res = new StringBuilder();
if (storageList.size() > 1) {
if (!storage.removable) {
res.append(context.getString(R.string.sftp_internal_storage));
} else if (storage.number > 1) {
res.append(context.getString(R.string.sftp_sdcard_num, storage.number));
} else {
res.append(context.getString(R.string.sftp_sdcard));
}
} else {
res.append(context.getString(R.string.sftp_all_files));
}
if (storage.readonly) {
res.append(" ");
res.append(context.getString(R.string.sftp_readonly));
}
pathNames.add(res.toString());
}
//Shortcut for users that only want to browse camera pictures
String cameraDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera";
if (new File(cameraDir).exists()) {
paths.add(cameraDir);
pathNames.add(context.getString(R.string.sftp_camera));
}
np2.set("multiPaths", paths);
np2.set("pathNames", pathNames);
device.sendPackage(np2);
return true;
}
}
return false;
}
@Override
public AlertDialog getErrorDialog(Activity deviceActivity) { return null; }
@Override
public Button getInterfaceButton(Activity activity) { return null; }
}

View File

@@ -0,0 +1,255 @@
package org.kde.kdeconnect.Plugins.SharePlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;
import org.kde.kdeconnect.Helpers.FilesHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class SharePlugin extends Plugin {
@Override
public String getPluginName() {
return "plugin_share";
}
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_sharereceiver);
}
@Override
public String getDescription() {
return context.getResources().getString(R.string.pref_plugin_sharereceiver_desc);
}
@Override
public Drawable getIcon() {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return true;
}
@Override
public boolean isEnabledByDefault() {
return true;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public void onDestroy() {
}
@Override
public boolean onPackageReceived(NetworkPackage np) {
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_SHARE)) {
return false;
}
try {
if (np.hasPayload()) {
Log.e("SharePlugin", "hasPayload");
final InputStream input = np.getPayload();
final int fileLength = np.getPayloadSize();
final String filename = np.getString("filename", new Long(System.currentTimeMillis()).toString());
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();
//Create directories if needed
new File(destinationDir).mkdirs();
//Append filename to the destination path
final File destinationFullPath = new File(destinationDir, filename);
Log.e("SharePlugin", "destinationFullPath:" + destinationFullPath);
final int notificationId = (int)System.currentTimeMillis();
Resources res = context.getResources();
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(res.getString(R.string.incoming_file_title, device.getName()))
.setContentText(res.getString(R.string.incoming_file_text, filename))
.setTicker(res.getString(R.string.incoming_file_title, device.getName()))
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setAutoCancel(true)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationId, noti);
new Thread(new Runnable() {
@Override
public void run() {
try {
OutputStream output = new FileOutputStream(destinationFullPath.getPath());
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) >= 0) {
total += count;
output.write(data, 0, count);
if (fileLength > 0) {
if (total >= fileLength) break;
float progress = (total * 100 / fileLength);
}
//else Log.e("SharePlugin", "Infinite loop? :D");
}
output.flush();
output.close();
input.close();
Log.e("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);
//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();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentTitle(res.getString(R.string.received_file_title, device.getName()))
.setContentText(res.getString(R.string.received_file_text, filename))
.setContentIntent(resultPendingIntent)
.setTicker(res.getString(R.string.received_file_title, device.getName()))
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setAutoCancel(true);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("share_notification_preference", true)) {
builder.setDefaults(Notification.DEFAULT_ALL);
}
Notification noti = builder.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(notificationId, noti);
} catch (Exception e) {
Log.e("SharePlugin", "Receiver thread exception");
e.printStackTrace();
}
}
}).start();
} else if (np.has("text")) {
Log.e("SharePlugin", "hasText");
String text = np.getString("text");
if(android.os.Build.VERSION.SDK_INT >= 11) {
ClipboardManager cm = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(text);
} else {
android.text.ClipboardManager clipboard = (android.text.ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(text);
}
Toast.makeText(context, R.string.shareplugin_text_saved, Toast.LENGTH_LONG).show();
} else if (np.has("url")) {
String url = np.getString("url");
Log.e("SharePlugin", "hasUrl: "+url);
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Do not launch it directly, show a notification instead
//context.startActivity(browserIntent);
Resources res = context.getResources();
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addNextIntent(browserIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(res.getString(R.string.received_url_title, device.getName()))
.setContentText(res.getString(R.string.received_url_text, url))
.setContentIntent(resultPendingIntent)
.setTicker(res.getString(R.string.received_url_title, device.getName()))
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify((int)System.currentTimeMillis(), noti);
} else {
Log.e("SharePlugin", "Error: Nothing attached!");
}
} catch(Exception e) {
Log.e("SharePlugin","Exception");
e.printStackTrace();
}
return true;
}
@Override
public AlertDialog getErrorDialog(Activity deviceActivity) {
return null;
}
@Override
public Button getInterfaceButton(Activity activity) { return null; }
}

View File

@@ -10,7 +10,6 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Button;
import org.kde.kdeconnect.Helpers.ContactsHelper;
@@ -20,10 +19,6 @@ import org.kde.kdeconnect_tp.R;
public class TelephonyPlugin extends Plugin {
/*static {
PluginFactory.registerPlugin(TelephonyPlugin.class);
}*/
@Override
public String getPluginName() {
return "plugin_telephony";
@@ -49,7 +44,12 @@ public class TelephonyPlugin extends Plugin {
return true;
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public boolean hasSettings() {
return false;
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -183,7 +183,7 @@ public class TelephonyPlugin extends Plugin {
}
@Override
public AlertDialog getErrorDialog(Context baseContext) {
public AlertDialog getErrorDialog(Activity deviceActivity) {
return null;
}

View File

@@ -1,5 +1,6 @@
package org.kde.kdeconnect.UserInterface;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
@@ -8,8 +9,6 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
@@ -18,21 +17,27 @@ import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.List.ButtonItem;
import org.kde.kdeconnect.UserInterface.List.CustomItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.SectionItem;
import org.kde.kdeconnect.UserInterface.List.SmallEntryItem;
import org.kde.kdeconnect.UserInterface.List.TextItem;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
public class DeviceActivity extends ActionBarActivity {
static private String deviceId; //Static because if we get here by using the back button in the action bar, the extra deviceId will not be set.
private Device device;
private Device.PluginsChangedListener pluginsChangedListener = new Device.PluginsChangedListener() {
public static final int RESULT_NEEDS_RELOAD = Activity.RESULT_FIRST_USER;
TextView errorHeader;
private final Device.PluginsChangedListener pluginsChangedListener = new Device.PluginsChangedListener() {
@Override
public void onPluginsChanged(final Device device) {
@@ -40,44 +45,58 @@ public class DeviceActivity extends ActionBarActivity {
@Override
public void run() {
//Errors list
final HashMap<String, Plugin> failedPlugins = device.getFailedPlugins();
final String[] ids = failedPlugins.keySet().toArray(new String[failedPlugins.size()]);
String[] names = new String[failedPlugins.size()];
for(int i = 0; i < ids.length; i++) {
Plugin p = failedPlugins.get(ids[i]);
names[i] = p.getDisplayName();
}
ListView errorList = (ListView)findViewById(R.id.errors_list);
if (!failedPlugins.isEmpty() && errorList.getHeaderViewsCount() == 0) {
TextView header = new TextView(DeviceActivity.this);
header.setPadding(0,24,0,0);
header.setText(getResources().getString(R.string.plugins_failed_to_load));
errorList.addHeaderView(header);
}
errorList.setAdapter(new ArrayAdapter<String>(DeviceActivity.this, android.R.layout.simple_list_item_1, names));
errorList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Plugin p = failedPlugins.get(ids[position - 1]); //Header is position 0, so we have to subtract one
p.getErrorDialog(DeviceActivity.this).show();
}
});
try {
//Buttons list
ArrayList<ListAdapter.Item> items = new ArrayList<ListAdapter.Item>();
final Collection<Plugin> plugins = device.getLoadedPlugins().values();
for (Plugin p : plugins) {
Button b = p.getInterfaceButton(DeviceActivity.this);
if (b != null) {
items.add(new SectionItem(p.getDisplayName()));
items.add(new ButtonItem(b));
if (!device.isReachable()) {
//Not reachable, show unpair button
Button b = new Button(DeviceActivity.this);
b.setText(R.string.device_menu_unpair);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
device.unpair();
finish();
}
});
items.add(new TextItem(getString(R.string.device_not_reachable)));
items.add(new ButtonItem(b));
} else {
//Plugins button list
final Collection<Plugin> plugins = device.getLoadedPlugins().values();
for (Plugin p : plugins) {
Button b = p.getInterfaceButton(DeviceActivity.this);
if (b != null) {
items.add(new SectionItem(p.getDisplayName()));
items.add(new ButtonItem(b));
}
}
//Failed plugins List
final Collection<Plugin> failed = device.getFailedPlugins().values();
if (!failed.isEmpty()) {
if (errorHeader == null) {
errorHeader = new TextView(DeviceActivity.this);
errorHeader.setPadding(0, 48, 0, 0);
errorHeader.setOnClickListener(null);
errorHeader.setOnLongClickListener(null);
errorHeader.setText(getResources().getString(R.string.plugins_failed_to_load));
}
items.add(new CustomItem(errorHeader));
for (final Plugin p : failed) {
items.add(new SmallEntryItem(p.getDisplayName(), new View.OnClickListener() {
@Override
public void onClick(View v) {
p.getErrorDialog(DeviceActivity.this).show();
}
}));
}
}
}
ListView buttonsList = (ListView)findViewById(R.id.buttons_list);
buttonsList.setAdapter(new ListAdapter(DeviceActivity.this, items));
ListAdapter adapter = new ListAdapter(DeviceActivity.this, items);
buttonsList.setAdapter(adapter);
} catch(ConcurrentModificationException e) {
Log.e("DeviceActivity", "ConcurrentModificationException");
@@ -111,6 +130,9 @@ public class DeviceActivity extends ActionBarActivity {
setTitle(device.getName());
device.addPluginsChangedListener(pluginsChangedListener);
pluginsChangedListener.onPluginsChanged(device);
if (!device.hasPluginsLoaded()) {
device.reloadPluginsFromSettings();
}
}
});
@@ -122,6 +144,7 @@ public class DeviceActivity extends ActionBarActivity {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.removePluginsChangedListener(pluginsChangedListener);
}
});
@@ -132,7 +155,7 @@ public class DeviceActivity extends ActionBarActivity {
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.clear();
if (device.isPaired()) {
if (device != null && device.isPaired()) {
menu.add(R.string.device_menu_plugins).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
@@ -154,6 +177,25 @@ public class DeviceActivity extends ActionBarActivity {
} else {
return false;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode)
{
case RESULT_NEEDS_RELOAD:
BackgroundService.RunCommand(DeviceActivity.this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
device.reloadPluginsFromSettings();
}
});
break;
default:
super.onActivityResult(requestCode, resultCode, data);
}
}
}

View File

@@ -0,0 +1,20 @@
package org.kde.kdeconnect.UserInterface.List;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
public class CustomItem implements ListAdapter.Item {
private final View view;
public CustomItem(View v) {
this.view = v;
}
@Override
public View inflateView(LayoutInflater layoutInflater) {
return view;
}
}

View File

@@ -4,6 +4,7 @@ package org.kde.kdeconnect.UserInterface.List;
import android.app.Activity;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
@@ -24,7 +25,10 @@ public class DeviceItem implements ListAdapter.Item {
@Override
public View inflateView(LayoutInflater layoutInflater) {
View v = layoutInflater.inflate(R.layout.list_item_entry, null);
final View v = layoutInflater.inflate(R.layout.list_item_entry, null);
//Highlight when selected effect
v.setBackgroundDrawable(layoutInflater.getContext().getResources().getDrawable(R.drawable.kitkatcompatselector_list_selector_holo_dark));
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
if (titleView != null) titleView.setText(device.getName());

View File

@@ -1,15 +1,9 @@
package org.kde.kdeconnect.UserInterface.List;
import android.app.Activity;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.UserInterface.DeviceActivity;
import org.kde.kdeconnect.UserInterface.PairActivity;
import org.kde.kdeconnect_tp.R;
public class EntryItem implements ListAdapter.Item {

View File

@@ -14,8 +14,8 @@ public class ListAdapter extends ArrayAdapter<ListAdapter.Item> {
public View inflateView(LayoutInflater layoutInflater);
}
private ArrayList<Item> items;
private LayoutInflater layoutInflater;
private final ArrayList<Item> items;
private final LayoutInflater layoutInflater;
public ListAdapter(Context context, ArrayList<Item> items) {
super(context, 0, items);

View File

@@ -9,11 +9,11 @@ import org.kde.kdeconnect_tp.R;
public class SectionItem implements ListAdapter.Item {
private final String title;
public boolean isEmpty;
public boolean isSectionEmpty;
public SectionItem(String title) {
this.title = title;
this.isEmpty = false;
this.isSectionEmpty = false;
}
@Override
@@ -21,14 +21,14 @@ public class SectionItem implements ListAdapter.Item {
View v = layoutInflater.inflate(R.layout.list_item_category, null);
//Make it not selectable
v.setOnClickListener(null);
v.setOnLongClickListener(null);
v.setLongClickable(false);
TextView sectionView = (TextView) v.findViewById(R.id.list_item_category_text);
sectionView.setText(title);
if (isEmpty) {
if (isSectionEmpty) {
v.findViewById(R.id.list_item_category_empty_placeholder).setVisibility(View.VISIBLE);
}

View File

@@ -0,0 +1,40 @@
package org.kde.kdeconnect.UserInterface.List;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.kde.kdeconnect_tp.R;
public class SmallEntryItem implements ListAdapter.Item {
private final String title;
private final View.OnClickListener clickListener;
public SmallEntryItem(String title) {
this.title = title;
this.clickListener = null;
}
public SmallEntryItem(String title, View.OnClickListener clickListener) {
this.title = title;
this.clickListener = clickListener;
}
@Override
public View inflateView(LayoutInflater layoutInflater) {
View v = layoutInflater.inflate(android.R.layout.simple_list_item_1, null);
TextView titleView = (TextView)v.findViewById(android.R.id.text1);
if (titleView != null) titleView.setText(title);
if (clickListener != null) {
titleView.setOnClickListener(clickListener);
v.setBackgroundDrawable(layoutInflater.getContext().getResources().getDrawable(R.drawable.kitkatcompatselector_list_selector_holo_dark));
}
return v;
}
}

View File

@@ -0,0 +1,27 @@
package org.kde.kdeconnect.UserInterface.List;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.R;
public class TextItem implements ListAdapter.Item {
private final String title;
public TextItem(String title) {
this.title = title;
}
@Override
public View inflateView(LayoutInflater layoutInflater) {
TextView v = new TextView(layoutInflater.getContext());
v.setText(title);
v.setTextAppearance(layoutInflater.getContext(), R.style.TextAppearance_DeviceDefault_Medium);
return v;
}
}

View File

@@ -6,7 +6,6 @@ import android.content.res.Resources;
import android.os.Bundle;
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;
@@ -105,36 +104,36 @@ public class MainActivity extends ActionBarActivity {
Resources res = getResources();
section = new SectionItem(res.getString(R.string.category_connected_devices));
section.isEmpty = true;
section.isSectionEmpty = true;
items.add(section);
for(Device d : devices) {
if (d.isReachable() && d.isPaired()) {
items.add(new DeviceItem(MainActivity.this, d));
section.isEmpty = false;
section.isSectionEmpty = false;
}
}
section = new SectionItem(res.getString(R.string.category_not_paired_devices));
section.isEmpty = true;
section.isSectionEmpty = true;
items.add(section);
for(Device d : devices) {
if (d.isReachable() && !d.isPaired()) {
items.add(new DeviceItem(MainActivity.this, d));
section.isEmpty = false;
section.isSectionEmpty = false;
}
}
section = new SectionItem(res.getString(R.string.category_remembered_devices));
section.isEmpty = true;
section.isSectionEmpty = true;
items.add(section);
for(Device d : devices) {
if (!d.isReachable() && d.isPaired()) {
items.add(new DeviceItem(MainActivity.this, d));
section.isEmpty = false;
section.isSectionEmpty = false;
}
}
if (section.isEmpty) {
items.remove(items.size()-1); //Remove section
if (section.isSectionEmpty) {
items.remove(items.size()-1); //Remove remembered devices section if empty
}
runOnUiThread(new Runnable() {
@@ -145,12 +144,13 @@ public class MainActivity extends ActionBarActivity {
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
view.callOnClick();
view.performClick();
}
});
}
});
}
});
}

View File

@@ -13,10 +13,11 @@ import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect_tp.R;
public class MainSettingsActivity extends PreferenceActivity{
public class MainSettingsActivity extends PreferenceActivity {
public static final String KEY_DEVICE_NAME_PREFERENCE = "device_name_preference";
@@ -55,6 +56,14 @@ public class MainSettingsActivity extends PreferenceActivity{
deviceNamePref.setSummary(getString(
R.string.device_name_preference_summary,
newDeviceName.toString()));
//Broadcast the device information again since it has changed
BackgroundService.RunCommand(MainSettingsActivity.this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
service.onNetworkChange();
}
});
return true;
}
}
@@ -67,7 +76,7 @@ public class MainSettingsActivity extends PreferenceActivity{
/**
* Until now it sets only the default deviceName (if not already set).
* It's safe to call this multiple time because doesn't override any previous value.
* @param context
* @param context the application context
*/
public static void initializeDeviceName(Context context){
// I could have used getDefaultSharedPreferences(context).contains but we need to check

View File

@@ -0,0 +1,35 @@
package org.kde.kdeconnect.UserInterface;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import org.kde.kdeconnect_tp.R;
public class MaxWidthImageButton extends ImageButton {
int mMaxWidth = Integer.MAX_VALUE;
public MaxWidthImageButton(Context context) {
super(context);
}
public MaxWidthImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MaxWidthImageButton);
mMaxWidth = a.getDimensionPixelSize(R.styleable.MaxWidthImageButton_maxWidth, Integer.MAX_VALUE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(getMeasuredWidth() > mMaxWidth){
setMeasuredDimension(mMaxWidth, getMeasuredHeight());
}
}
}

View File

@@ -18,7 +18,7 @@ public class PairActivity extends ActionBarActivity {
private String deviceId;
private Device device = null;
private Device.PairingCallback pairingCallback = new Device.PairingCallback() {
private final Device.PairingCallback pairingCallback = new Device.PairingCallback() {
@Override
public void incomingRequest() {
@@ -76,6 +76,7 @@ public class PairActivity extends ActionBarActivity {
@Override
public void onServiceStart(BackgroundService service) {
device = service.getDevice(deviceId);
if (device == null) return;
setTitle(device.getName());
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(device.getNotificationId());
@@ -94,6 +95,7 @@ public class PairActivity extends ActionBarActivity {
@Override
public void onServiceStart(BackgroundService service) {
device = service.getDevice(deviceId);
if (device == null) return;
device.requestPairing();
}
});
@@ -134,6 +136,8 @@ public class PairActivity extends ActionBarActivity {
BackgroundService.RunCommand(PairActivity.this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
device = service.getDevice(deviceId);
if (device == null) return;
device.addPairingCallback(pairingCallback);
}
});

View File

@@ -0,0 +1,17 @@
package org.kde.kdeconnect.UserInterface;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
public class PluginSettingsActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String resource_name = getIntent().getStringExtra(Intent.EXTRA_INTENT);
int resource_file = getResources().getIdentifier(resource_name, "xml", getPackageName());
addPreferencesFromResource(resource_file);
}
}

View File

@@ -1,28 +0,0 @@
package org.kde.kdeconnect.UserInterface;
import android.content.Context;
import android.preference.Preference;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import java.util.ArrayList;
public class PreferenceListAdapter extends ArrayAdapter<Preference> {
private ArrayList<Preference> localList;
public PreferenceListAdapter(Context context, ArrayList<Preference> items) {
super(context,0, items);
localList = items;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Preference preference = localList.get(position);
return preference.getView(convertView, parent);
}
}

View File

@@ -1,27 +1,36 @@
package org.kde.kdeconnect.UserInterface;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.util.Log;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.view.View;
import android.widget.AdapterView;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.Set;
public class SettingsActivity extends ListActivity {
public class SettingsActivity extends PreferenceActivity {
static private String deviceId; //Static because if we get here by using the back button in the action bar, the extra deviceId will not be set.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(this);
setPreferenceScreen(preferenceScreen);
if (getIntent().hasExtra("deviceId")) {
deviceId = getIntent().getStringExtra("deviceId");
}
final String deviceId = getIntent().getStringExtra("deviceId");
BackgroundService.RunCommand(getApplicationContext(), new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
@@ -31,40 +40,49 @@ public class SettingsActivity extends ListActivity {
final ArrayList<Preference> preferences = new ArrayList<Preference>();
for (final String pluginName : plugins) {
CheckBoxPreference pref = new CheckBoxPreference(getBaseContext());
final CheckBoxPreference pref = new CheckBoxPreference(getBaseContext());
PluginFactory.PluginInfo info = PluginFactory.getPluginInfo(getBaseContext(), pluginName);
pref.setKey(pluginName);
pref.setTitle(info.getDisplayName());
pref.setSummary(info.getDescription());
pref.setChecked(device.isPluginEnabled(pluginName));
preferences.add(pref);
preferenceScreen.addPreference(pref);
if (info.hasSettings()) {
final Preference pluginPreference = new Preference(getBaseContext());
pluginPreference.setKey(pluginName + getString(R.string.plugin_settings_key));
pluginPreference.setTitle(info.getDisplayName());
pluginPreference.setSummary(R.string.plugin_settings);
preferences.add(pluginPreference);
preferenceScreen.addPreference(pluginPreference);
pluginPreference.setDependency(pref.getKey());
}
}
setListAdapter(new PreferenceListAdapter(SettingsActivity.this, preferences));
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
CheckBoxPreference pref = (CheckBoxPreference)preferences.get(i);
boolean enabled = device.isPluginEnabled(pref.getKey());
device.setPluginEnabled(pref.getKey(), !enabled);
pref.setChecked(!enabled);
getListAdapter().getView(i, view, null); //This will refresh the view (yes, this is the way to do it)
Preference pref = preferences.get(i);
if (pref.getDependency() == null) { //Is a plugin check
CheckBoxPreference check = (CheckBoxPreference)pref;
boolean enabled = device.isPluginEnabled(pref.getKey());
device.setPluginEnabled(pref.getKey(), !enabled);
check.setChecked(!enabled);
} else { //Is a plugin suboption
if (pref.isEnabled()) {
Intent intent = new Intent(SettingsActivity.this, PluginSettingsActivity.class);
intent.putExtra(Intent.EXTRA_INTENT, pref.getKey());
startActivity(intent);
}
}
}
});
getListView().setPadding(16,16,16,16);
}
});
}
}

View File

@@ -136,7 +136,8 @@ public class ShareToReceiver extends ActionBarActivity {
queuedSendUriList(device, uriList);
} catch (Exception e) {
Log.e(this.getClass().getName(), e.toString());
e.printStackTrace();
Log.e("ShareToReceiver", "Exception");
}
} else if (extras.containsKey(Intent.EXTRA_TEXT)) {
@@ -203,7 +204,7 @@ public class ShareToReceiver extends ActionBarActivity {
String path = cursor.getString(column_index);
np.set("filename", Uri.parse(path).getLastPathSegment());
np.set("size", (int)new File(path).length());
} catch(Exception _) {
} catch(Exception unused) {
Log.e("ShareToReceiver", "Could not resolve media to a file, trying to get info as media");

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

View File

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 750 B

View File

Before

Width:  |  Height:  |  Size: 365 B

After

Width:  |  Height:  |  Size: 365 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:drawable="@drawable/kitkatcompatselector_list_activated_holo" />
<item android:drawable="@android:color/transparent" />
</selector>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
<item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/kitkatcompatselector_list_selector_disabled_holo_dark" />
<item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/kitkatcompatselector_list_selector_disabled_holo_dark" />
<item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/kitkatcompatselector_list_selector_background_transition_holo_dark" />
<item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/kitkatcompatselector_list_selector_background_transition_holo_dark" />
<item android:state_focused="true" android:drawable="@drawable/kitkatcompatselector_list_focused_holo" />
<item android:drawable="@android:color/transparent" />
</selector>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/kitkatcompatselector_list_pressed_holo_dark" />
<item android:drawable="@drawable/kitkatcompatselector_list_longpressed_holo" />
</transition>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:drawable="@android:color/transparent" />
<!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
<item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/kitkatcompatselector_list_selector_disabled_holo_dark" />
<item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/kitkatcompatselector_list_selector_disabled_holo_dark" />
<item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/kitkatcompatselector_list_selector_background_transition_holo_dark" />
<item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/kitkatcompatselector_list_selector_background_transition_holo_dark" />
<item android:state_focused="true" android:drawable="@drawable/kitkatcompatselector_list_focused_holo" />
</selector>

View File

@@ -1,26 +1,13 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/buttons_list"
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"
android:fillViewport="true"
android:divider="@null"
tools:context=".MainActivity"
android:orientation="vertical">
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/buttons_list"
android:layout_weight="1"
/>
<ListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/errors_list"
android:layout_weight="1"
/>
</LinearLayout>
/>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mousepad_view">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/mousepad_info"
android:gravity="center"
style="@android:style/TextAppearance.Medium"
android:layout_centerInParent="true"
android:padding="12dip" />
<view
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="org.kde.kdeconnect.Plugins.MousePadPlugin.KeyListenerView"
android:id="@+id/keyListener"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="58dp" />
</RelativeLayout>

View File

@@ -1,43 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mpris_control_view"
android:gravity="center">
android:gravity="center"
android:paddingLeft="60dip"
android:paddingTop="5dip"
android:paddingRight="60dip"
android:paddingBottom="5dip">
<Spinner
android:layout_width="200dip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/player_spinner"
android:layout_gravity="center"
/>
<TextView
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="@+id/now_playing_textview"
android:singleLine="true"
android:gravity="center"
android:padding="5dip"
android:padding="8dip"
android:layout_gravity="center"
/>
<ImageButton
android:layout_width="200dip"
<org.kde.kdeconnect.UserInterface.MaxWidthImageButton
android:layout_width="fill_parent"
android:layout_height="75dip"
app:maxWidth="300dip"
android:id="@+id/play_button"
android:src="@android:drawable/ic_media_play"
android:contentDescription="@string/mpris_play"
android:layout_gravity="center"
/>
android:layout_weight="0"
android:clickable="false"
android:adjustViewBounds="false"
android:baselineAlignBottom="true" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="200dip"
android:layout_height="50dip"
android:layout_width="match_parent"
android:layout_height="60dip"
android:layout_gravity="center"
>
@@ -81,17 +90,18 @@
<LinearLayout
android:orientation="horizontal"
android:layout_width="200dip"
android:layout_width="match_parent"
android:layout_height="70dip"
android:id="@+id/volume_layout"
android:layout_gravity="center">
<ImageView
android:layout_width="50dip"
android:layout_height="50dip"
android:layout_width="30dip"
android:layout_height="30dip"
android:maxWidth="30dip"
android:layout_marginRight="10dip"
android:id="@+id/imageView"
android:layout_weight="1"
android:layout_weight="0"
android:layout_gravity="left|center_vertical"
android:contentDescription="@string/mpris_volume"
android:src="@drawable/ic_volume"
@@ -99,7 +109,7 @@
<SeekBar
android:layout_width="fill_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/volume_seek"
android:layout_weight="1"

View File

@@ -5,7 +5,7 @@
android:id="@+id/menu_refresh"
android:icon="@drawable/ic_action_refresh"
android:orderInCategory="200"
kdeconnect:showAsAction="always"
kdeconnect:showAsAction="ifRoom"
android:title="@string/reconnect"
/>
@@ -13,7 +13,7 @@
android:id="@+id/menu_progress"
android:orderInCategory="200"
android:visible="false"
kdeconnect:showAsAction="always"
kdeconnect:showAsAction="ifRoom"
kdeconnect:actionViewClass="android.widget.ProgressBar"
/>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:kdeconnect="http://schemas.android.com/apk/res-auto/android">
<item android:id="@+id/menu_show_keyboard"
android:title="@string/show_keyboard"
android:icon="@drawable/ic_action_keyboard"
kdeconnect:showAsAction="ifRoom" />
<item android:id="@+id/menu_right_click"
android:title="@string/right_click"
kdeconnect:showAsAction="never" />
<item android:id="@+id/menu_middle_click"
android:title="@string/middle_click"
kdeconnect:showAsAction="never" />
</menu>

View File

@@ -0,0 +1,80 @@
<?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_mpris">Отдалечен мултимедиен контрол</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>
<string name="pref_plugin_notifications_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="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="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="pair_requested">Заявено е сдвояване</string>
<string name="pairing_request_from">Заявено е сдвояване от %1s</string>
<string name="incoming_file_title">Заявено е сдвояване от %1s</string>
<string name="incoming_file_text">%s</string>
<string name="tap_to_answer">Докоснете за да отговорите</string>
<string name="reconnect">Ново свързване</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="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="device_name">Име на устройството</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Невалидно име на устройство</string>
<string name="share_notification_preference">Синхронизиране на уведомленията</string>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
<item>20 seconds</item>
<item>30 seconds</item>
<item>1 minute</item>
<item>2 minutes</item>
</string-array>
</resources>

View File

@@ -0,0 +1,16 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<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_ping">Ping</string>
<string name="pref_plugin_ping_desc">Šalji i primaj ping-ove</string>
<string name="pref_plugin_notifications">Sinhronizovano obavještenje</string>
<string name="share_notification_preference">Sinhronizovano obavještenje</string>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
<item>20 seconds</item>
<item>30 seconds</item>
<item>1 minute</item>
<item>2 minutes</item>
</string-array>
</resources>

View File

@@ -0,0 +1,101 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Notificador de 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_clipboard">Sincronitza el porta-retalls</string>
<string name="pref_plugin_clipboard_desc">Comparteix el contingut del porta-retalls</string>
<string name="pref_plugin_mousepad">Comandament a distància del ratolí</string>
<string name="pref_plugin_mousepad_desc">Controla el vostre ratolí de forma remota</string>
<string name="pref_plugin_mpris">Comandament a distància 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_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_sharereceiver">Receptor de compartits</string>
<string name="pref_plugin_sharereceiver_desc">Desa els fitxers entrants a l\'emmagatzematge del telèfon</string>
<string name="plugin_not_available">Aquesta característica no està disponible en la vostra versió d\'Android</string>
<string name="device_list_empty">No hi ha cap dispositiu</string>
<string name="ok">D\'acord</string>
<string name="cancel">Cancel·la</string>
<string name="open_settings">Obre l\'arranjament</string>
<string name="no_permissions">Us caldrà concedir permís per accedir a les notificacions</string>
<string name="send_ping">Envia un ping</string>
<string name="open_mpris_controls">Obre el comandament a distància</string>
<string name="open_mousepad">Obre el control pel plafó tàctil</string>
<string name="mousepad_info">Moveu un dit per la pantalla per a moure el cursor del ratolí</string>
<string name="category_connected_devices">Dispositius connectats</string>
<string name="category_not_paired_devices">Dispositius sense aparellar</string>
<string name="category_remembered_devices">Dispositius recordats</string>
<string name="plugins_failed_to_load">Els connectors han fallat en carregar-los (puntegeu per a més informació):</string>
<string name="device_menu_plugins">Selecció dels connectors</string>
<string name="device_menu_unpair">Desparella</string>
<string name="device_not_reachable">No s\'ha pogut accedir al dispositiu parell</string>
<string name="unknown_device">Dispositiu desconegut</string>
<string name="error_not_reachable">No es pot accedir al dispositiu</string>
<string name="error_already_requested">Ja s\'ha demanat aparellar</string>
<string name="error_already_paired">El dispositiu ja està aparellat</string>
<string name="error_could_not_send_package">No s\'ha pogut enviar el paquet</string>
<string name="error_timed_out">Ha excedit el temps</string>
<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="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>
<string name="received_url_text">Puntegeu per obrir «%1s»</string>
<string name="incoming_file_title">Fitxer entrant des de %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="received_file_title">Fitxer rebut des de %1s</string>
<string name="received_file_text">Puntegeu per obrir «%1s»</string>
<string name="tap_to_answer">Puntegeu per a respondre</string>
<string name="reconnect">Reconnecta</string>
<string name="right_click">Envia un clic del botó dret</string>
<string name="middle_click">Envia un clic del botó del mig</string>
<string name="show_keyboard">Mostra el teclat</string>
<string name="device_not_paired">El dispositiu no està aparellat</string>
<string name="request_pairing">Demana aparellar</string>
<string name="pairing_accept">Accepta</string>
<string name="pairing_reject">Rebutja</string>
<string name="device">Dispositiu</string>
<string name="pair_device">Dispositiu parell</string>
<string name="remote_control">Comandament a distància</string>
<string name="settings">Arranjament del KDE Connect</string>
<string name="mpris_play">Reprodueix</string>
<string name="mpris_previous">Anterior</string>
<string name="mpris_rew">Rebobina</string>
<string name="mpris_ff">Avanç ràpid</string>
<string name="mpris_next">Següent</string>
<string name="mpris_volume">Volum</string>
<string name="mpris_settings">Arranjament multimèdia</string>
<string name="mpris_time_settings_title">Interval de temps</string>
<string name="mpris_time_settings_summary">Ajusta el temps per avançar o rebobinar un fitxer multimèdia.</string>
<string name="share_to">Comparteix amb...</string>
<string name="protocol_version_older">Aquest dispositiu usa una versió antiga del protocol</string>
<string name="protocol_version_newer">Aquest dispositiu usa una versió nova del protocol</string>
<string name="general_settings">Arranjament general</string>
<string name="plugin_settings">Arranjament</string>
<string name="device_name">Nom del dispositiu</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">El nom del dispositiu no és vàlid</string>
<string name="shareplugin_text_saved">S\'ha rebut text i s\'ha desat al porta-retalls</string>
<string name="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="sftp_internal_storage">Emmagatzematge intern</string>
<string name="sftp_all_files">Tots els fitxers</string>
<string name="sftp_sdcard_num">Targeta SD %d</string>
<string name="sftp_sdcard">Targeta SD</string>
<string name="sftp_readonly">(només de lectura)</string>
<string name="sftp_camera">Imatges de la càmera</string>
<string-array name="mpris_time_entries">
<item>10 segons</item>
<item>20 segons</item>
<item>30 segons</item>
<item>1 minut</item>
<item>2 minuts</item>
</string-array>
</resources>

View File

@@ -1,30 +1,101 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Upozornění telefonie</string>
<string name="pref_plugin_telephony_desc">Posílat upozornění na SMS a hovory</string>
<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_clipboard">Synchronizace schránky</string>
<string name="pref_plugin_clipboard_desc">Sdílet obsah schránky</string>
<string name="pref_plugin_mousepad">Vzdálené ovládání myši</string>
<string name="pref_plugin_mousepad_desc">Ovládejte svou myš vzdáleně</string>
<string name="pref_plugin_mpris">Vzdálené 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_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_sharereceiver">Sdílet příjemce</string>
<string name="pref_plugin_sharereceiver_desc">Ukládat příchozí soubory do úložiště telefonu</string>
<string name="plugin_not_available">Tato vlastnost není pro vaši verzi Androidu platná</string>
<string name="device_list_empty">Žádná zařízení</string>
<string name="ok">OK</string>
<string name="cancel">Zrušit</string>
<string name="open_settings">Otevřít nastavení</string>
<string name="no_permissions">Pro zpřístupnění upozornění potřebujete oprávnění</string>
<string name="send_ping">Poslat ping</string>
<string name="open_mpris_controls">Otevřít vzdálené ovládání</string>
<string name="open_mousepad">Otevřít dotykové ovládání</string>
<string name="mousepad_info">Pohybujte prstem po monitoru pro pohybování kurzorem myši</string>
<string name="category_connected_devices">Připojená zařízení</string>
<string name="category_not_paired_devices">Nespárovaná zařízení</string>
<string name="category_remembered_devices">Zapamatovaná zařízení</string>
<string name="plugins_failed_to_load">Moduly nebyly načteny (ťukněte pro více informací):</string>
<string name="device_menu_plugins">Vyberte moduly</string>
<string name="device_menu_unpair">Zrušit párování</string>
<string name="device_not_reachable">Spárované zařízení je nedostupné</string>
<string name="unknown_device">Neznámé zařízení</string>
<string name="error_not_reachable">Zařízení je nedostupné</string>
<string name="error_already_requested">Párování již bylo vyžádáno</string>
<string name="error_already_paired">Zařízení je již spárované</string>
<string name="error_could_not_send_package">Balíček nelze poslat</string>
<string name="error_timed_out">Čas vypršel</string>
<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="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>
<string name="received_url_text">Ťukněte pro otevření \'%1s\'</string>
<string name="incoming_file_title">Příchozí soubor od %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="received_file_title">Přijat soubor od %1s</string>
<string name="received_file_text">Ťukněte pro otevření \'%1s\'</string>
<string name="tap_to_answer">Ťukněte pro odpovězení</string>
<string name="reconnect">Znovu připojit</string>
<string name="right_click">Poslat kliknutí pravým tlačítkem</string>
<string name="middle_click">Poslat kliknutí prostředním tlačítkem</string>
<string name="show_keyboard">Zobrazit klávesnici</string>
<string name="device_not_paired">Zařízení není spárované</string>
<string name="request_pairing">Vyžádat párování</string>
<string name="pairing_accept">Přijmout</string>
<string name="pairing_reject">Odmítnout</string>
<string name="device">Zařízení</string>
<string name="pair_device">Spárovat zařízení</string>
<string name="remote_control">Vzdálené ovládání</string>
<string name="settings">Nastavení KDE Connect</string>
<string name="mpris_play">Přehrát</string>
<string name="mpris_previous">Předchozí</string>
<string name="mpris_rew">Přetočit zpět</string>
<string name="mpris_ff">Rychle vpřed</string>
<string name="mpris_next">Následující</string>
<string name="mpris_volume">Hlasitost</string>
<string name="mpris_settings">Nastavení multimédií</string>
<string name="mpris_time_settings_title">Časový interval</string>
<string name="mpris_time_settings_summary">Upravit čas pro rychlý posun multimediálním souborem vpřed a zpět.</string>
<string name="share_to">Sdílet s...</string>
<string name="protocol_version_older">Toto zařízení používá starou verzi protokolu</string>
<string name="protocol_version_newer">Toto zařízení používá novější verzi protokolu</string>
<string name="general_settings">Obecná nastavení</string>
<string name="plugin_settings">Nastavení</string>
<string name="device_name">Název zařízení</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Neplatný název zařízení</string>
<string name="shareplugin_text_saved">Přijatý text byl uložen do schránky</string>
<string name="share_notification_preference">Hlasitá upozornění</string>
<string name="share_notification_preference_summary">Vibrovat a přehrát melodii při přijímání souboru</string>
<string name="sftp_internal_storage">Interní úložiště</string>
<string name="sftp_all_files">Všechny soubory</string>
<string name="sftp_sdcard_num">SD karta %d</string>
<string name="sftp_sdcard">SD karta</string>
<string name="sftp_readonly">(pouze ke čtení)</string>
<string name="sftp_camera">Obrázky z fotoaparátu</string>
<string-array name="mpris_time_entries">
<item>10 sekund</item>
<item>20 sekund</item>
<item>30 sekund</item>
<item>1 minuta</item>
<item>2 minuty</item>
</string-array>
</resources>

View File

@@ -0,0 +1,92 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Telefonibekendtgørelse</string>
<string name="pref_plugin_telephony_desc">Send bekendtgørelser om SMS\'er og opkald</string>
<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_clipboard">Synk. af udklipsholder</string>
<string name="pref_plugin_clipboard_desc">Del indholdet af udklipsholderen</string>
<string name="pref_plugin_mousepad">Fjernbetjening af mus</string>
<string name="pref_plugin_mousepad_desc">Styrer din mus på afstand</string>
<string name="pref_plugin_mpris">Fjernbetjening af multimedie</string>
<string name="pref_plugin_mpris_desc">Styr lyd og video fra din telefon</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_sharereceiver">Del modtager</string>
<string name="pref_plugin_sharereceiver_desc">Gem indkommende filer til telefonens lager</string>
<string name="plugin_not_available">Denne funktion er ikke tilgængelig i din Android-version</string>
<string name="device_list_empty">Ingen enheder</string>
<string name="ok">O.k.</string>
<string name="cancel">Annullér</string>
<string name="open_settings">Åbn indstillinger</string>
<string name="no_permissions">Du skal give tilladelse for at tilgå bekendtgørelser</string>
<string name="send_ping">Send ping</string>
<string name="open_mpris_controls">Åbn fjernbetjening</string>
<string name="open_mousepad">Åbn touchpad-kontrol</string>
<string name="mousepad_info">Bevæg en finger på skærmen for at flytte musemarkøren</string>
<string name="category_connected_devices">Forbundne enheder</string>
<string name="category_not_paired_devices">Ikke parrede enheder</string>
<string name="category_remembered_devices">Huskede enheder</string>
<string name="plugins_failed_to_load">Plugins kunne ikke indlæses (tap for mere info):</string>
<string name="device_menu_plugins">Vælg plugins</string>
<string name="device_menu_unpair">Fjern parring</string>
<string name="device_not_reachable">Den parrede enhed kan ikke tilgås</string>
<string name="unknown_device">Ukendt enhed</string>
<string name="error_not_reachable">Enheden kan ikke nås</string>
<string name="error_already_requested">Allerede anmodet om parring</string>
<string name="error_already_paired">Enhed allerede parret</string>
<string name="error_could_not_send_package">Kunne ikke sende pakke</string>
<string name="error_timed_out">Tidsudløb</string>
<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="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>
<string name="received_url_text">Tap for at åbne \"%1s\"</string>
<string name="incoming_file_title">Indkommende fil fra %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="received_file_title">Modtog fil fra %1s</string>
<string name="received_file_text">Tap for at åbne \"%1s\"</string>
<string name="tap_to_answer">Tap for at svare</string>
<string name="reconnect">Forbind igen</string>
<string name="right_click">Send højreklik</string>
<string name="middle_click">Send midterklik</string>
<string name="show_keyboard">Vis tastatur</string>
<string name="device_not_paired">Enhed ikke parret</string>
<string name="request_pairing">Anmod om parring</string>
<string name="pairing_accept">Acceptér</string>
<string name="pairing_reject">Afvis</string>
<string name="device">Enhed</string>
<string name="pair_device">Par med enhed</string>
<string name="remote_control">Fjernbetjening</string>
<string name="settings">Indstilling af KDE Connect</string>
<string name="mpris_play">Afspil</string>
<string name="mpris_previous">Forrige</string>
<string name="mpris_rew">Spol tilbage</string>
<string name="mpris_ff">Spol frem</string>
<string name="mpris_next">Næste</string>
<string name="mpris_volume">Lydstyrke</string>
<string name="mpris_settings">Fjernbetjening af multimedie</string>
<string name="share_to">Del på...</string>
<string name="protocol_version_older">Denne enhed bruger en gammel protokolversion</string>
<string name="protocol_version_newer">Denne enhed bruger en nyere protokolversion</string>
<string name="general_settings">Generelle indstillinger</string>
<string name="plugin_settings">Generelle indstillinger</string>
<string name="device_name">Enhedsnavn</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Ugyldigt enhedsnavn</string>
<string name="shareplugin_text_saved">Modtog tekst, gemt til udklipsholder</string>
<string name="share_notification_preference">Synk. af bekendtgørelser</string>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
<item>20 seconds</item>
<item>30 seconds</item>
<item>1 minute</item>
<item>2 minutes</item>
</string-array>
</resources>

View File

@@ -4,14 +4,18 @@
<string name="pref_plugin_telephony_desc">Benachrichtigungen über SMS und Anrufe senden</string>
<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_clipboard">Abgleich der Zwischenablage</string>
<string name="pref_plugin_clipboard_desc">Inhalt der Zwischenablage freigeben</string>
<string name="pref_plugin_mousepad">Maus-Fernbedienung</string>
<string name="pref_plugin_mpris">Multimedia-Fernbedienungen</string>
<string name="pref_plugin_mpris_desc">Audio und Video mit Ihrem Telefon steuern</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_sharereceiver_desc">Eingehende Dateien auf dem Telefonspeicher ablegen</string>
<string name="plugin_not_available">Diese Funktion ist in Ihrer Android-Version nicht verfügbar</string>
<string name="device_list_empty">Keine Geräte</string>
<string name="ok">OK</string>
@@ -20,11 +24,14 @@
<string name="no_permissions">Sie müssen die Erlaubnis zum Zugriff auf Benachrichtigungen erteilen</string>
<string name="send_ping">Ping senden</string>
<string name="open_mpris_controls">Fernbedienung öffnen</string>
<string name="open_mousepad">Touchpad-Steuerung öffnen</string>
<string name="category_connected_devices">Verbundene Geräte</string>
<string name="category_not_paired_devices">Keine angeschlossenen Geräte</string>
<string name="category_remembered_devices">Gemerkte Geräte</string>
<string name="plugins_failed_to_load">Laden der Module fehlgeschlagen, tippen Sie für weitere Details:</string>
<string name="device_menu_plugins">Module auswählen</string>
<string name="device_menu_unpair">Verbindung trennen</string>
<string name="device_not_reachable">Das angegeben Gerät ist nicht erreichbar</string>
<string name="unknown_device">Unbekanntes Gerät</string>
<string name="error_not_reachable">Das Gerät ist nicht erreichbar</string>
<string name="error_already_requested">Die Verbindung wurde bereits angefragt</string>
@@ -36,7 +43,15 @@
<string name="error_invalid_key">Ungültiger Schlüssel empfangen</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>
<string name="incoming_file_title">Eingehende Datei von %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="received_file_title">Datei von %1s erhalten</string>
<string name="tap_to_answer">Tippen zum Antworten</string>
<string name="reconnect">Erneut verbinden</string>
<string name="right_click">Rechtsklick senden</string>
<string name="middle_click">Mittelklick senden</string>
<string name="show_keyboard">Tastatur anzeigen</string>
<string name="device_not_paired">Das Gerät ist nicht verbunden</string>
<string name="request_pairing">Verbindung angefordert</string>
<string name="pairing_accept">Annehmen</string>
@@ -51,10 +66,29 @@
<string name="mpris_ff">Vorlauf</string>
<string name="mpris_next">Weiter</string>
<string name="mpris_volume">Lautstärke</string>
<string name="mpris_settings">Multimedia-Einstellungen</string>
<string name="share_to">Freigeben für ...</string>
<string name="protocol_version_older">Dieses Gerät verwendet ein alte Protokollversion</string>
<string name="protocol_version_newer">Dieses Gerät verwendet ein neuere Protokollversion</string>
<string name="general_settings">Allgemeine Einstellungen</string>
<string name="plugin_settings">Einstellungen</string>
<string name="device_name">Gerätename</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Ungültiger Gerätename</string>
<string name="shareplugin_text_saved">Text empfangen und in der Zwischenablage gespeichert</string>
<string name="share_notification_preference">Benachrichtigungs-Abgleich</string>
<string name="share_notification_preference_summary">Beim Empfang einer Datei vibrieren und einen Sound abspielen</string>
<string name="sftp_internal_storage">Interner Speicher</string>
<string name="sftp_all_files">Alle Dateien</string>
<string name="sftp_sdcard_num">SD-Karte %d</string>
<string name="sftp_sdcard">SD-Karte</string>
<string name="sftp_readonly">(Nur lesen)</string>
<string name="sftp_camera">Kamerabilder</string>
<string-array name="mpris_time_entries">
<item>10 Sekunden</item>
<item>20 Sekunden</item>
<item>30 Sekunden</item>
<item>1 Minute</item>
<item>2 Minuten</item>
</string-array>
</resources>

View File

@@ -4,15 +4,21 @@
<string name="pref_plugin_telephony_desc">Enviar notificaciones de SMS y llamadas</string>
<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">Revelar 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_clipboard">Sincronización del portapapeles</string>
<string name="pref_plugin_clipboard_desc">Compartir el contenido del portapapeles</string>
<string name="pref_plugin_mousepad">Control remoto del ratón</string>
<string name="pref_plugin_mousepad_desc">Controlar su ratón remotamente</string>
<string name="pref_plugin_mpris">Controles remotos multimedia</string>
<string name="pref_plugin_mpris_desc">Controlar audio y vídeo desde el teléfono</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="plugin_not_available">Esta función no está disponible en su versión de Android</string>
<string name="pref_plugin_sharereceiver">Compartir receptor</string>
<string name="pref_plugin_sharereceiver_desc">Guardar archivos entrantes en el almacenamiento del teléfono</string>
<string name="plugin_not_available">Esta funcionalidad no está disponible en su versión de Android</string>
<string name="device_list_empty">Ningún dispositivo</string>
<string name="ok">Aceptar</string>
<string name="cancel">Cancelar</string>
@@ -20,12 +26,15 @@
<string name="no_permissions">Debe otorgar permiso para acceder a las notificaciones</string>
<string name="send_ping">Enviar ping</string>
<string name="open_mpris_controls">Abrir control remoto</string>
<string name="open_mousepad">Abrir control del panel táctil</string>
<string name="mousepad_info">Mueva un dedo sobre la pantalla para mover el cursor del ratón</string>
<string name="category_connected_devices">Dispositivos conectados</string>
<string name="category_not_paired_devices">Dispositivos no vinculados</string>
<string name="category_remembered_devices">Dispositivos recordados</string>
<string name="plugins_failed_to_load">Complementos cuya carga ha fallado (toque para más información):</string>
<string name="device_menu_plugins">Seleccionar complementos</string>
<string name="device_menu_unpair">Desvincular</string>
<string name="device_not_reachable">No se encuentra el dispositivo aparejado</string>
<string name="unknown_device">Dispositivo desconocido</string>
<string name="error_not_reachable">No se encuentra el dispositivo</string>
<string name="error_already_requested">Ya ha solicitado vincularse</string>
@@ -37,8 +46,17 @@
<string name="error_invalid_key">Se ha recibido una clave no valida</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>
<string name="received_url_text">Pulse para abrir «%1s»</string>
<string name="incoming_file_title">Archivo entrante desde %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="received_file_title">Archivo recibido desde %1s</string>
<string name="received_file_text">Pulse para abrir «%1s»</string>
<string name="tap_to_answer">Toque para responder</string>
<string name="reconnect">Reconectar</string>
<string name="right_click">Enviar clic derecho</string>
<string name="middle_click">Enviar clic del botón central</string>
<string name="show_keyboard">Mostrar teclado</string>
<string name="device_not_paired">Dispositivo no vinculado</string>
<string name="request_pairing">Solicitar vinculación</string>
<string name="pairing_accept">Aceptar</string>
@@ -49,12 +67,29 @@
<string name="settings">Preferencias de KDE Connect</string>
<string name="mpris_play">Reproducir</string>
<string name="mpris_previous">Anterior</string>
<string name="mpris_rew">Rebobinar</string>
<string name="mpris_ff">Avance rápido</string>
<string name="mpris_next">Siguiente</string>
<string name="mpris_volume">Volumen</string>
<string name="mpris_settings">Preferencias multimedia</string>
<string name="mpris_time_settings_title">Intervalo de tiempo</string>
<string name="mpris_time_settings_summary">Ajustar el tiempo a avanzar o rebobinar un archivo multimedia.</string>
<string name="share_to">Compartir con...</string>
<string name="protocol_version_older">Este dispositivo usa una versión antigua del protocolo</string>
<string name="protocol_version_newer">Este dispositivo usa una versión más reciente del protocolo</string>
<string name="general_settings">Abrir preferencias</string>
<string name="device_name">Dispositivo no vinculado</string>
<string name="invalid_device_name">Se ha recibido una clave no valida</string>
<string name="general_settings">Preferencias generales</string>
<string name="plugin_settings">Preferencias</string>
<string name="device_name">Nombre de dispositivo</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Nombre de dispositivo no válido</string>
<string name="shareplugin_text_saved">Texto recibido y guardado en el portapapeles</string>
<string name="share_notification_preference">Notificaciones ruidosas</string>
<string name="share_notification_preference_summary">Vibrar y reproducir un sonido cuando se reciba un archivo</string>
<string-array name="mpris_time_entries">
<item>10 segundos</item>
<item>20 segundos</item>
<item>30 segundos</item>
<item>1 minuto</item>
<item>2 minutos</item>
</string-array>
</resources>

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