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

Compare commits

...

83 Commits
v1.0 ... v1.2

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

BUG: 360006

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

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.kde.kdeconnect_tp" package="org.kde.kdeconnect_tp"
android:versionCode="1000" android:versionCode="1200"
android:versionName="1.0"> android:versionName="1.2">
<uses-sdk android:minSdkVersion="9" <uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="22" /> android:targetSdkVersion="22" />
@@ -31,6 +31,7 @@
<application <application
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/icon" android:icon="@drawable/icon"
android:supportsRtl="true"
android:label="KDE Connect" android:label="KDE Connect"
android:theme="@style/KdeConnectTheme" android:theme="@style/KdeConnectTheme"
> >

View File

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

5
proguard-rules.pro vendored
View File

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

View File

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

View File

@@ -20,6 +20,11 @@
android:id="@+id/no_players" android:id="@+id/no_players"
android:layout_gravity="center_horizontal" /> android:layout_gravity="center_horizontal" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/artImageView" />
<Spinner <Spinner
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -151,7 +156,6 @@
android:layout_marginEnd="10dip" android:layout_marginEnd="10dip"
android:id="@+id/imageView" android:id="@+id/imageView"
android:layout_weight="0" android:layout_weight="0"
android:layout_gravity="left|center_vertical"
android:contentDescription="@string/mpris_volume" android:contentDescription="@string/mpris_volume"
android:src="@drawable/ic_volume" android:src="@drawable/ic_volume"
/> />

View File

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

View File

@@ -18,14 +18,13 @@
Preference is able to place a specific widget for its particular Preference is able to place a specific widget for its particular
type in the "widget_frame" layout. --> type in the "widget_frame" layout. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight" android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingEnd="?android:attr/scrollbarSize" android:paddingEnd="?android:attr/scrollbarSize"
android:paddingRight="?android:attr/scrollbarSize" android:paddingRight="?android:attr/scrollbarSize"
android:background="?attr/selectableItemBackground" > android:background="?attr/selectableItemBackground">
<!-- Preference should place its actual preference widget here. --> <!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@+android:id/widget_frame" <LinearLayout android:id="@+android:id/widget_frame"

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">Controles remotos multimedia</string> <string name="pref_plugin_mpris">Controles remotos multimedia</string>
<string name="pref_plugin_mpris_desc">Controla l\'audiu/videu dende\'l to teléfonu</string> <string name="pref_plugin_mpris_desc">Controla l\'audiu/videu dende\'l to teléfonu</string>
<string name="pref_plugin_runcommand">Executar comandu</string> <string name="pref_plugin_runcommand">Executar comandu</string>
<string name="pref_plugin_runcommand_desc">Executa un comandu nel to sistema</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Unvia y recibe pings</string> <string name="pref_plugin_ping_desc">Unvia y recibe pings</string>
<string name="pref_plugin_notifications">Sincronización d\'avisos</string> <string name="pref_plugin_notifications">Sincronización d\'avisos</string>
@@ -151,7 +150,6 @@
<string name="pref_plugin_telepathy">Unviar SMS</string> <string name="pref_plugin_telepathy">Unviar SMS</string>
<string name="pref_plugin_telepathy_desc">Unvia mensaxes de testu dende\'l to escritoriu</string> <string name="pref_plugin_telepathy_desc">Unvia mensaxes de testu dende\'l to escritoriu</string>
<string name="plugin_not_supported">Esti complementu nun ta sofitáu pol preséu</string> <string name="plugin_not_supported">Esti complementu nun ta sofitáu pol preséu</string>
<string name="findmyphone_title">Alcuéntra\'l mio teléfonu</string>
<string name="findmyphone_description">Fai sonar esti teléfonu pa que pueas alcontralu.</string> <string name="findmyphone_description">Fai sonar esti teléfonu pa que pueas alcontralu.</string>
<string name="findmyphone_found">Alcontrar</string> <string name="findmyphone_found">Alcontrar</string>
<string name="open">Abrir</string> <string name="open">Abrir</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Controls multimèdia</string> <string name="pref_plugin_mpris">Controls multimèdia</string>
<string name="pref_plugin_mpris_desc">Controla l\'àudio i el vídeo del vostre telèfon</string> <string name="pref_plugin_mpris_desc">Controla l\'àudio i el vídeo del vostre telèfon</string>
<string name="pref_plugin_runcommand">Executa una ordre</string> <string name="pref_plugin_runcommand">Executa una ordre</string>
<string name="pref_plugin_runcommand_desc">Executa una ordre al vostre sistema</string> <string name="pref_plugin_runcommand_desc">Activa les ordres remotes des del vostre telèfon</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envia i rep els pings</string> <string name="pref_plugin_ping_desc">Envia i rep els pings</string>
<string name="pref_plugin_notifications">Sincronitza les notificacions</string> <string name="pref_plugin_notifications">Sincronitza les notificacions</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Cancel·lat per l\'usuari</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_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="error_invalid_key">S\'ha rebut una clau no vàlida</string>
<string name="encryption_info_title">Informació d\'encriptatge</string>
<string name="encryption_info_msg_no_ssl">L\'altre dispositiu no usa una versió recent del KDE Connect, s\'utilitzarà el mètode d\'encriptatge antic.</string>
<string name="my_device_fingerprint">L\'empremta digital SHA1 del certificat del vostre dispositiu és:</string>
<string name="remote_device_fingerprint">L\'empremta digital SHA1 del certificat del dispositiu remot és:</string>
<string name="pair_requested">S\'ha demanat aparellar</string> <string name="pair_requested">S\'ha demanat aparellar</string>
<string name="pairing_request_from">S\'ha demanat aparellar des de %1s</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_title">S\'ha rebut un vincle des de %1s</string>
@@ -151,7 +155,7 @@
<string name="pref_plugin_telepathy">Envia un SMS</string> <string name="pref_plugin_telepathy">Envia un SMS</string>
<string name="pref_plugin_telepathy_desc">Envia missatges de text des de l\'escriptori</string> <string name="pref_plugin_telepathy_desc">Envia missatges de text des de l\'escriptori</string>
<string name="plugin_not_supported">Aquest connector no és admès pel dispositiu</string> <string name="plugin_not_supported">Aquest connector no és admès pel dispositiu</string>
<string name="findmyphone_title">Troba el meu telèfon</string> <string name="findmyphone_title">Cerca el meu telèfon</string>
<string name="findmyphone_description">Fa sonar aquest telèfon perquè el pugueu trobar.</string> <string name="findmyphone_description">Fa sonar aquest telèfon perquè el pugueu trobar.</string>
<string name="findmyphone_found">L\'he trobat</string> <string name="findmyphone_found">L\'he trobat</string>
<string name="open">Obre</string> <string name="open">Obre</string>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">Ovládání multimédií</string> <string name="pref_plugin_mpris">Ovládání multimédií</string>
<string name="pref_plugin_mpris_desc">Ovládejte audio/video z vašeho telefonu</string> <string name="pref_plugin_mpris_desc">Ovládejte audio/video z vašeho telefonu</string>
<string name="pref_plugin_runcommand">Spustit příkaz</string> <string name="pref_plugin_runcommand">Spustit příkaz</string>
<string name="pref_plugin_runcommand_desc">Spustí příkaz na vašem počítači</string>
<string name="pref_plugin_ping">Ping</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_ping_desc">Posílat a přijímat ping</string>
<string name="pref_plugin_notifications">Synchronizace upozornění</string> <string name="pref_plugin_notifications">Synchronizace upozornění</string>
@@ -66,6 +65,7 @@
<string name="error_canceled_by_user">Přerušeno uživatelem</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_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="error_invalid_key">Byl přijat neplatný klíč</string>
<string name="encryption_info_title">Informace o šifrování</string>
<string name="pair_requested">Bylo vyžádáno párování</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="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_title">Přijat odkaz od %1s</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Multimediekontroller</string> <string name="pref_plugin_mpris">Multimediekontroller</string>
<string name="pref_plugin_mpris_desc">Styr lyd og video fra din telefon</string> <string name="pref_plugin_mpris_desc">Styr lyd og video fra din telefon</string>
<string name="pref_plugin_runcommand">Kør kommando</string> <string name="pref_plugin_runcommand">Kør kommando</string>
<string name="pref_plugin_runcommand_desc">Kører en kommando på dit system</string> <string name="pref_plugin_runcommand_desc">Kør eksterne kommandoer fra din telefon</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send og modtag 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">Synk. af bekendtgørelser</string>
@@ -32,6 +32,8 @@
<string name="mousepad_info">Bevæg en finger på skærmen for at flytte musemarkøren. Tap for at klikke og brug to/tre-fingre for højre og midterste museknap. Brug et langt tryk til at trække og slippe.</string> <string name="mousepad_info">Bevæg en finger på skærmen for at flytte musemarkøren. Tap for at klikke og brug to/tre-fingre for højre og midterste museknap. Brug et langt tryk til at trække og slippe.</string>
<string name="mousepad_double_tap_settings_title">Angiv handling for tap med to fingre</string> <string name="mousepad_double_tap_settings_title">Angiv handling for tap med to fingre</string>
<string name="mousepad_triple_tap_settings_title">Angiv handling for tap med tre fingre</string> <string name="mousepad_triple_tap_settings_title">Angiv handling for tap med tre fingre</string>
<string name="mousepad_sensitivity_settings_title">Angiv følsomhed for touchpad</string>
<string name="mousepad_scroll_direction_title">Omvendt rulleretning</string>
<string-array name="mousepad_tap_entries"> <string-array name="mousepad_tap_entries">
<item>Højreklik</item> <item>Højreklik</item>
<item>Midterklik</item> <item>Midterklik</item>
@@ -39,12 +41,13 @@
</string-array> </string-array>
<string name="mousepad_double_default">højre</string> <string name="mousepad_double_default">højre</string>
<string name="mousepad_triple_default">midter</string> <string name="mousepad_triple_default">midter</string>
<string name="mousepad_sensitivity_default">standard</string>
<string-array name="mousepad_sensitivity_entries"> <string-array name="mousepad_sensitivity_entries">
<item>Slowest</item> <item>Mest langsom</item>
<item>Above Slowest</item> <item>Over mest langsom</item>
<item>Default</item> <item>Standard</item>
<item>Above Default</item> <item>Over standard</item>
<item>Fastest</item> <item>Hurtigst</item>
</string-array> </string-array>
<string name="category_connected_devices">Forbundne enheder</string> <string name="category_connected_devices">Forbundne enheder</string>
<string name="category_not_paired_devices">Tilgængelig enheder</string> <string name="category_not_paired_devices">Tilgængelig enheder</string>
@@ -63,6 +66,10 @@
<string name="error_canceled_by_user">Annulleret af brugeren</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_canceled_by_other_peer">Annulleret af modpart</string>
<string name="error_invalid_key">Ugyldige nøgle modtaget</string> <string name="error_invalid_key">Ugyldige nøgle modtaget</string>
<string name="encryption_info_title">Krypteringsinfo</string>
<string name="encryption_info_msg_no_ssl">Den anden enhed bruger ikke en nylig version af KDE Connect, og bruger dermed den forældede krypteringsmetode.</string>
<string name="my_device_fingerprint">SHA1-fingeraftrykket for dit enhedscertifikat er:</string>
<string name="remote_device_fingerprint">SHA1-fingeraftrykket for det eksterne enhedscertifikat er:</string>
<string name="pair_requested">Anmodet om parring</string> <string name="pair_requested">Anmodet om parring</string>
<string name="pairing_request_from">Parringsanmodning fra %1s</string> <string name="pairing_request_from">Parringsanmodning fra %1s</string>
<string name="received_url_title">Modtog link fra %1s</string> <string name="received_url_title">Modtog link fra %1s</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Multimedia-Bedienung</string> <string name="pref_plugin_mpris">Multimedia-Bedienung</string>
<string name="pref_plugin_mpris_desc">Audio und Video mit Ihrem Telefon steuern</string> <string name="pref_plugin_mpris_desc">Audio und Video mit Ihrem Telefon steuern</string>
<string name="pref_plugin_runcommand">Befehl ausführen</string> <string name="pref_plugin_runcommand">Befehl ausführen</string>
<string name="pref_plugin_runcommand_desc">Führt einen Befehl auf Ihrem System aus</string> <string name="pref_plugin_runcommand_desc">Von Ihrem Telefon Befehle auf anderen Geräten ausführen</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Senden und Empfangen von Pings</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">Benachrichtigungs-Abgleich</string>
@@ -29,6 +29,9 @@
<string name="send_ping">Ping senden</string> <string name="send_ping">Ping senden</string>
<string name="open_mpris_controls">Multimedia-Bedienung</string> <string name="open_mpris_controls">Multimedia-Bedienung</string>
<string name="open_mousepad">Ferneingabe</string> <string name="open_mousepad">Ferneingabe</string>
<string name="mousepad_double_tap_settings_title">Aktionsausführung bei Berührung mit zwei Fingern einstellen</string>
<string name="mousepad_triple_tap_settings_title">Aktionsausführung bei Berührung mit drei Fingern einstellen</string>
<string name="mousepad_sensitivity_settings_title">Empfindlichkeit des Touchpads einstellen</string>
<string name="mousepad_scroll_direction_title">Bildlaufrichtung umkehren</string> <string name="mousepad_scroll_direction_title">Bildlaufrichtung umkehren</string>
<string-array name="mousepad_tap_entries"> <string-array name="mousepad_tap_entries">
<item>Rechtsklick</item> <item>Rechtsklick</item>
@@ -62,6 +65,9 @@
<string name="error_canceled_by_user">Abbruch durch Benutzer</string> <string name="error_canceled_by_user">Abbruch durch Benutzer</string>
<string name="error_canceled_by_other_peer">Abbruch durch Gegenstelle</string> <string name="error_canceled_by_other_peer">Abbruch durch Gegenstelle</string>
<string name="error_invalid_key">Ungültiger Schlüssel empfangen</string> <string name="error_invalid_key">Ungültiger Schlüssel empfangen</string>
<string name="encryption_info_title">Verschlüsselungsinformationen</string>
<string name="my_device_fingerprint">Der SHA1-Fingerabdruck Ihres Gerätezertifikats lautet:</string>
<string name="remote_device_fingerprint">Der SHA1-Fingerabdruck des Gerätezertifikats der Gegenstelle lautet:</string>
<string name="pair_requested">Verbindung angefordert</string> <string name="pair_requested">Verbindung angefordert</string>
<string name="pairing_request_from">Verbindungsanfrage von %1s</string> <string name="pairing_request_from">Verbindungsanfrage von %1s</string>
<string name="received_url_title">Verknüpfung von %1s erhalten</string> <string name="received_url_title">Verknüpfung von %1s erhalten</string>
@@ -95,6 +101,7 @@
<string name="mpris_next">Weiter</string> <string name="mpris_next">Weiter</string>
<string name="mpris_volume">Lautstärke</string> <string name="mpris_volume">Lautstärke</string>
<string name="mpris_settings">Multimedia-Einstellungen</string> <string name="mpris_settings">Multimedia-Einstellungen</string>
<string name="mpris_time_settings_title">Knöpfe Vorwärts/Rückwärts</string>
<string-array name="mpris_time_entries"> <string-array name="mpris_time_entries">
<item>10 Sekunden</item> <item>10 Sekunden</item>
<item>20 Sekunden</item> <item>20 Sekunden</item>
@@ -141,7 +148,7 @@
<string name="pref_plugin_telepathy">SMS senden</string> <string name="pref_plugin_telepathy">SMS senden</string>
<string name="pref_plugin_telepathy_desc">Text-Nachrichten von Ihrer Arbeitsfläche senden</string> <string name="pref_plugin_telepathy_desc">Text-Nachrichten von Ihrer Arbeitsfläche senden</string>
<string name="plugin_not_supported">Dieses Modul wird durch das Gerät nicht unterstützt</string> <string name="plugin_not_supported">Dieses Modul wird durch das Gerät nicht unterstützt</string>
<string name="findmyphone_title">Telefon suchen</string> <string name="findmyphone_title">Mein Telefon suchen</string>
<string name="findmyphone_description">Ruft dieses Telefon an, um es zu suchen.</string> <string name="findmyphone_description">Ruft dieses Telefon an, um es zu suchen.</string>
<string name="findmyphone_found">Gefunden</string> <string name="findmyphone_found">Gefunden</string>
<string name="open">Öffnen</string> <string name="open">Öffnen</string>

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

@@ -0,0 +1,163 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Ειδοποιήσεις τηλεφωνίας</string>
<string name="pref_plugin_telephony_desc">Αποστολή ειδοποιήσεων για SMS και κλήσεις</string>
<string name="pref_plugin_battery">Αναφορά μπαταρίας</string>
<string name="pref_plugin_battery_desc">Περιοδική αναφορά κατάστασης μπαταρίας</string>
<string name="pref_plugin_sftp">Αποκάλυψη συστήματος αρχείων</string>
<string name="pref_plugin_sftp_desc">Επιτρέπει την απομακρυσμένη περιήγηση του συστήματος αρχείων του κινητού</string>
<string name="pref_plugin_clipboard">Συγχρονισμός προχείρου</string>
<string name="pref_plugin_clipboard_desc">Διαμοιρασμός περιεχομένου προχείρου</string>
<string name="pref_plugin_mousepad">Απομακρυσμένη είσοδος στοιχείων</string>
<string name="pref_plugin_mousepad_desc">Χρήση του κινητού ως ποντίκι και πληκτρολόγιο</string>
<string name="pref_plugin_mpris">Κονσόλα πολυμέσων</string>
<string name="pref_plugin_mpris_desc">Έλεγχος μουσικής/βίντεο από το κινητό</string>
<string name="pref_plugin_runcommand">Εκτέλεση εντολής</string>
<string name="pref_plugin_runcommand_desc">Εκτέλεση απομακρυσμένων εντολών από το κινητό</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Αποστολή και λήψη pings</string>
<string name="pref_plugin_notifications">Συγχρονισμός ειδοποιήσεων</string>
<string name="pref_plugin_notifications_desc">Πρόσβαση σε ειδοποιήσεις από άλλες συσκευές</string>
<string name="pref_plugin_sharereceiver">Διαμοιρασμός και λήψη</string>
<string name="pref_plugin_sharereceiver_desc">Διαμοιρασμός αρχείων και URL μεταξύ συσκευών</string>
<string name="plugin_not_available">Αυτή η λειτουργία δεν είναι διαθέσιμη στην τρέχουσα έκδοση του Android</string>
<string name="device_list_empty">Χωρίς συσκευές</string>
<string name="ok">Εντάξει</string>
<string name="cancel">Ακύρωση</string>
<string name="open_settings">Ρυθμίσεις ανοίγματος</string>
<string name="no_permissions">Απαιτείται παραχώρηση δικαιωμάτων για την πρόσβαση σε ειδοποιήσεις</string>
<string name="send_ping">Αποστολή ping</string>
<string name="open_mpris_controls">Έλεγχος πολυμέσων</string>
<string name="open_mousepad">Απομακρυσμένη είσοδος στοιχείων</string>
<string name="mousepad_info">Μετακινείστε το δάκτυλο στην οθόνη για να μετακινηθεί ο δρομέας του ποντικιού. Χτυπήστε για κλικ και χρησιμοποιήστε δύο/τρία δάκτυλα για δεξί και μεσαίο κλικ. Πιέστε με διάρκεια για μετακίνηση και απόθεση.</string>
<string name="mousepad_double_tap_settings_title">Ρύθμιση δύο δακτύλων για την ενέργεια χτυπήματος</string>
<string name="mousepad_triple_tap_settings_title">Ρύθμιση τριών δακτύλων για την ενέργεια χτυπήματος</string>
<string name="mousepad_sensitivity_settings_title">Ρύθμιση ευαισθησίας της οθόνης αφής</string>
<string name="mousepad_scroll_direction_title">Κατεύθυνση ανάστροφης κύλησης</string>
<string-array name="mousepad_tap_entries">
<item>Δεξί κλικ</item>
<item>Μεσαίο κλικ</item>
<item>Τίποτα</item>
</string-array>
<string name="mousepad_double_default">δεξί</string>
<string name="mousepad_triple_default">μεσαίο</string>
<string name="mousepad_sensitivity_default">προκαθορισμένο</string>
<string-array name="mousepad_sensitivity_entries">
<item>Το πιο αργό</item>
<item>Πάνω από το πιο αργό</item>
<item>Προκαθορισμένο</item>
<item>Πάνω από το προκαθορισμένο</item>
<item>Το ταχύτερο</item>
</string-array>
<string name="category_connected_devices">Συνδεδεμένες συσκευές</string>
<string name="category_not_paired_devices">Διαθέσιμες συσκευές</string>
<string name="category_remembered_devices">Συσκευές στη μνήμη</string>
<string name="plugins_failed_to_load">Αποτυχία φόρτωσης προσθέτων (χτυπήστε για περισσότερες πληροφορίες):</string>
<string name="device_menu_plugins">Ρυθμίσεις προσθέτων</string>
<string name="device_menu_unpair">Διαχωρισμός</string>
<string name="device_not_reachable">Η συζευγμένη συσκευή δεν είναι προσβάσιμη</string>
<string name="pair_new_device">Σύζευξη νέας συσκευής</string>
<string name="unknown_device">Άγνωστη συσκευή</string>
<string name="error_not_reachable">Η συσκευή δεν είναι προσβάσιμη</string>
<string name="error_already_requested">Η σύζευξη ήδη ζητήθηκε</string>
<string name="error_already_paired">Η συσκευή ήδη συζεύχθηκε</string>
<string name="error_could_not_send_package">Αδυναμία αποστολής πακέτου</string>
<string name="error_timed_out">Τέλος χρονικού ορίου</string>
<string name="error_canceled_by_user">Ακυρώθηκε από τον χρήστη</string>
<string name="error_canceled_by_other_peer">Ακυρώθηκε από άλλον χρήστη</string>
<string name="error_invalid_key">Ελήφθη μη έγκυρο κλειδί</string>
<string name="encryption_info_title">Πληροφορίες κρυπτογράφησης</string>
<string name="encryption_info_msg_no_ssl">Η άλλη συσκευή δεν χρησιμοποιεί μια πρόσφατη έκδοση του KDE Connect, θα χρησιμοποιηθεί η παλαιά μέθοδος κρυπτογράφησης.</string>
<string name="my_device_fingerprint">Το ίχνος SHA1 του πιστοποιητικού της συσκευής σας είναι:</string>
<string name="remote_device_fingerprint">Το ίχνος SHA1 του πιστοποιητικού της απομακρυσμένης συσκευής είναι:</string>
<string name="pair_requested">Ζητήθηκε σύζευξη</string>
<string name="pairing_request_from">Αίτημα σύζευξης από %1s</string>
<string name="received_url_title">Ελήφθη σύνδεσμος από %1s</string>
<string name="received_url_text">Χτυπήστε για άνοιγμα \'%1s\'</string>
<string name="incoming_file_title">Εισερχόμενο αρχείο από %1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">Αποστολή αρχείου σε %1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">Ελήφθη αρχείο από %1s</string>
<string name="received_file_fail_title">Αποτυχία λήψης αρχείου από %1s</string>
<string name="received_file_text">Χτυπήστε για άνοιγμα \'%1s\'</string>
<string name="sent_file_title">Εστάλη αρχείο στο %1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">Αποτυχία αποστολής αρχείου %1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Χτυπήστε για να απαντήσετε</string>
<string name="reconnect">Επανασύνδεση</string>
<string name="right_click">Αποστολή δεξιού κλικ</string>
<string name="middle_click">Αποστολή μεσαίου κλικ</string>
<string name="show_keyboard">Εμφάνιση πληκτρολογίου</string>
<string name="device_not_paired">Η συσκευή δεν συζεύχθηκε</string>
<string name="request_pairing">Αίτημα σύζευξης</string>
<string name="pairing_accept">Αποδοχή</string>
<string name="pairing_reject">Απόρριψη</string>
<string name="device">Συσκευή</string>
<string name="pair_device">Σύζευξη συσκευής</string>
<string name="remote_control">Απομακρυσμένος έλεγχος</string>
<string name="settings">Ρυθμίσεις KDE Connect</string>
<string name="mpris_play">Αναπαραγωγή</string>
<string name="mpris_previous">Προηγούμενο</string>
<string name="mpris_rew">Ταχεία ώθηση όπισθεν</string>
<string name="mpris_ff">Ταχεία προώθηση</string>
<string name="mpris_next">Επόμενο</string>
<string name="mpris_volume">Τόμος</string>
<string name="mpris_settings">Ρυθμίσεις πολυμέσων</string>
<string name="mpris_time_settings_title">Κουμπιά ταχείας ώθησης</string>
<string name="mpris_time_settings_summary">Χρονική προσαρμογή ταχείας ώθησης ανάλογα με την πίεση.</string>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
<item>20 seconds</item>
<item>30 seconds</item>
<item>1 λεπτό</item>
<item>2 λεπτά</item>
</string-array>
<string name="share_to">Διαμοιρασμός με...</string>
<string name="protocol_version_older">Η συσκευή αυτή χρησιμοποιεί παλαιά έκδοση πρωτοκόλλου</string>
<string name="protocol_version_newer">Η συσκευή αυτή χρησιμοποιεί νεότερη έκδοση πρωτοκόλλου</string>
<string name="general_settings">Γενικές ρυθμίσεις</string>
<string name="plugin_settings">Ρυθμίσεις</string>
<string name="plugin_settings_with_name">%s ρυθμίσεις</string>
<string name="device_name">Όνομα συσκευής</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">Μη έγκυρο όνομα συσκευής</string>
<string name="shareplugin_text_saved">Ελήφθη κείμενο, αποθηκεύτηκε στο πρόχειρο</string>
<string name="custom_devices_settings">Προσαρμοσμένη λίστα συσκευών</string>
<string name="pair_device_action">Σύζευξη νέας συσκευής</string>
<string name="unpair_device_action">Διαχωρισμός %s</string>
<string name="custom_device_list">Προσθήκη συσκευών ανά IP</string>
<string name="share_notification_preference">Θορυβώδεις ειδοποιήσεις</string>
<string name="share_notification_preference_summary">Δόνηση και ηχητική ένδειξη με τη λήψη αρχείου</string>
<string name="title_activity_notification_filter">Φιλτράρισμα ειδοποιήσεων</string>
<string name="filter_apps_info">Οι ειδοποιήσεις θα συγχρονίζονται για επιλεγμένες εφαρμογές.</string>
<string name="sftp_internal_storage">Εσωτερικός αποθηκευτικός χώρος</string>
<string name="sftp_all_files">Όλα τα αρχεία</string>
<string name="sftp_sdcard_num">SD card %d</string>
<string name="sftp_sdcard">SD card</string>
<string name="sftp_readonly">(ανάγνωση μόνο)</string>
<string name="sftp_camera">Φωτογραφίες</string>
<string name="add_host">Προσθήκη υπολογιστή/IP</string>
<string name="add_host_hint">Όνομα υπολογιστή ή IP</string>
<string name="no_players_connected">Δεν βρέθηκαν συσκευές αναπαραγωγής</string>
<string name="custom_dev_list_help">Χρησιμοποιήστε την επιλογή αυτή μόνο αν η συσκευή σας δεν έχει εντοπιστεί αυτόματα. Δώστε την IP διεύθυνση ή το όνομα του υπολογιστή και αγγίξτε το κουμπί για να προστεθεί στη λίστα. Αγγίξτε ένα αντικείμενο της λίστας για να το αφαιρέσετε.</string>
<string name="mpris_player_on_device">%1$s σε %2$s</string>
<string name="send_files">Αποστολή αρχείων</string>
<string name="pairing_title">Συσκευές KDE Connect</string>
<string name="pairing_description">Άλλες συσκευές με KDE Connect στο ίδιο δίκτυο θα εμφανίζονται εδώ.</string>
<string name="device_paired">Η συσκευή συζεύχθηκε</string>
<string name="device_rename_title">Μετονομασία συσκευής</string>
<string name="device_rename_confirm">Μετονομασία</string>
<string name="refresh">Ανανέωση</string>
<string name="unreachable_description">Αυτή η συζευγμένη συσκευή δεν είναι προσβάσιμη. Βεβαιωθείτε ότι είναι συνδεδεμένη στο δίκτυό σας.</string>
<string name="no_file_browser">Δεν υπάρχουν εγκατεστημένοι περιηγητές αρχείων.</string>
<string name="pref_plugin_telepathy">Αποστολή SMS</string>
<string name="pref_plugin_telepathy_desc">Αποστολή μηνυμάτων κειμένου από τον υπολογιστή σας</string>
<string name="plugin_not_supported">Αυτό το πρόσθετο δεν υποστηρίζεται από τη συσκευή</string>
<string name="findmyphone_title">Αναζήτηση του κινητού μου</string>
<string name="findmyphone_description">Καλεί αυτό το κινητό ώστε να το εντοπίσετε.</string>
<string name="findmyphone_found">Βρέθηκε</string>
<string name="open">Άνοιγμα</string>
<string name="close">Κλείσιμο</string>
</resources>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">Multimedia controls</string> <string name="pref_plugin_mpris">Multimedia controls</string>
<string name="pref_plugin_mpris_desc">Control audio/video from your phone</string> <string name="pref_plugin_mpris_desc">Control audio/video from your phone</string>
<string name="pref_plugin_runcommand">Run Command</string> <string name="pref_plugin_runcommand">Run Command</string>
<string name="pref_plugin_runcommand_desc">Runs a command on your system</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send and receive pings</string> <string name="pref_plugin_ping_desc">Send and receive pings</string>
<string name="pref_plugin_notifications">Notification sync</string> <string name="pref_plugin_notifications">Notification sync</string>
@@ -151,7 +150,6 @@
<string name="pref_plugin_telepathy">Send SMS</string> <string name="pref_plugin_telepathy">Send SMS</string>
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string> <string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
<string name="plugin_not_supported">This plugin is not supported by the device</string> <string name="plugin_not_supported">This plugin is not supported by the device</string>
<string name="findmyphone_title">Find My Phone</string>
<string name="findmyphone_description">Rings this phone so you can find it.</string> <string name="findmyphone_description">Rings this phone so you can find it.</string>
<string name="findmyphone_found">Found</string> <string name="findmyphone_found">Found</string>
<string name="open">Open</string> <string name="open">Open</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Controles multimedia</string> <string name="pref_plugin_mpris">Controles multimedia</string>
<string name="pref_plugin_mpris_desc">Controlar audio y vídeo desde el teléfono</string> <string name="pref_plugin_mpris_desc">Controlar audio y vídeo desde el teléfono</string>
<string name="pref_plugin_runcommand">Ejecutar orden</string> <string name="pref_plugin_runcommand">Ejecutar orden</string>
<string name="pref_plugin_runcommand_desc">Ejecuta una orden en su sistema</string> <string name="pref_plugin_runcommand_desc">Desencadenar órdenes remotas desde su teléfono</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Enviar y recibir pings</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">Sincronizar notificaciones</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Cancelado por el usuario</string> <string name="error_canceled_by_user">Cancelado por el usuario</string>
<string name="error_canceled_by_other_peer">Cancelado por la otra parte</string> <string name="error_canceled_by_other_peer">Cancelado por la otra parte</string>
<string name="error_invalid_key">Se ha recibido una clave no valida</string> <string name="error_invalid_key">Se ha recibido una clave no valida</string>
<string name="encryption_info_title">Información de cifrado</string>
<string name="encryption_info_msg_no_ssl">El otro dispositivo no dispone de una versión reciente de KDE Connect, se usará un método de cifrado antiguo.</string>
<string name="my_device_fingerprint">La huella digital SHA1 del certificado de su dispositivo es:</string>
<string name="remote_device_fingerprint">La huella digital SHA1 del certificado del dispositivo remoto es:</string>
<string name="pair_requested">Vinculación solicitada</string> <string name="pair_requested">Vinculación solicitada</string>
<string name="pairing_request_from">Solicitud de vinculación de %1s</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_title">Enlace recibido desde %1s</string>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">Multimedian ohjaus</string> <string name="pref_plugin_mpris">Multimedian ohjaus</string>
<string name="pref_plugin_mpris_desc">Ohjaa ääntä ja videota puhelimestasi</string> <string name="pref_plugin_mpris_desc">Ohjaa ääntä ja videota puhelimestasi</string>
<string name="pref_plugin_runcommand">Suorita komento</string> <string name="pref_plugin_runcommand">Suorita komento</string>
<string name="pref_plugin_runcommand_desc">Suorita komento järjestelmässäsi</string>
<string name="pref_plugin_ping">Tiedustelupaketti</string> <string name="pref_plugin_ping">Tiedustelupaketti</string>
<string name="pref_plugin_ping_desc">Lähetä ja vastaanota tiedustelupaketteja</string> <string name="pref_plugin_ping_desc">Lähetä ja vastaanota tiedustelupaketteja</string>
<string name="pref_plugin_notifications">Ilmoitusten synkronointi</string> <string name="pref_plugin_notifications">Ilmoitusten synkronointi</string>
@@ -151,7 +150,6 @@
<string name="pref_plugin_telepathy">Lähetä tekstiviesti</string> <string name="pref_plugin_telepathy">Lähetä tekstiviesti</string>
<string name="pref_plugin_telepathy_desc">Lähetä tekstiviestejä työpöydältäsi</string> <string name="pref_plugin_telepathy_desc">Lähetä tekstiviestejä työpöydältäsi</string>
<string name="plugin_not_supported">Laite ei tue tätä liitännäistä</string> <string name="plugin_not_supported">Laite ei tue tätä liitännäistä</string>
<string name="findmyphone_title">Löydä puhelimeni</string>
<string name="findmyphone_description">Laittaa puhelimen soimaan, jotta voit löytää sen.</string> <string name="findmyphone_description">Laittaa puhelimen soimaan, jotta voit löytää sen.</string>
<string name="findmyphone_found">Löytyi</string> <string name="findmyphone_found">Löytyi</string>
<string name="open">Avaa</string> <string name="open">Avaa</string>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">Controis multimedia</string> <string name="pref_plugin_mpris">Controis multimedia</string>
<string name="pref_plugin_mpris_desc">Controle o son e o vídeo desde o teléfono.</string> <string name="pref_plugin_mpris_desc">Controle o son e o vídeo desde o teléfono.</string>
<string name="pref_plugin_runcommand">Executar unha orde</string> <string name="pref_plugin_runcommand">Executar unha orde</string>
<string name="pref_plugin_runcommand_desc">Executa unha orde no sistema.</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envíe e reciba pings.</string> <string name="pref_plugin_ping_desc">Envíe e reciba pings.</string>
<string name="pref_plugin_notifications">Sincronización de notificacións</string> <string name="pref_plugin_notifications">Sincronización de notificacións</string>
@@ -151,7 +150,6 @@
<string name="pref_plugin_telepathy">Enviar unha mensaxe de texto</string> <string name="pref_plugin_telepathy">Enviar unha mensaxe de texto</string>
<string name="pref_plugin_telepathy_desc">Enviar mensaxes de texto desde un computador de escritorio.</string> <string name="pref_plugin_telepathy_desc">Enviar mensaxes de texto desde un computador de escritorio.</string>
<string name="plugin_not_supported">O dispositivo non é compatíbel con este complemento.</string> <string name="plugin_not_supported">O dispositivo non é compatíbel con este complemento.</string>
<string name="findmyphone_title">Atopar o móbil</string>
<string name="findmyphone_description">Reproduce un son de chamada no móbil para que poida atopalo.</string> <string name="findmyphone_description">Reproduce un son de chamada no móbil para que poida atopalo.</string>
<string name="findmyphone_found">Atopado</string> <string name="findmyphone_found">Atopado</string>
<string name="open">Abrir</string> <string name="open">Abrir</string>

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

@@ -0,0 +1,163 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">התראות טלפוניות</string>
<string name="pref_plugin_telephony_desc">שלח התראה על הודעות ושיחות</string>
<string name="pref_plugin_battery">דיווח סוללה</string>
<string name="pref_plugin_battery_desc">מדווח על אחוז הסוללה למחשב</string>
<string name="pref_plugin_sftp">גישה לקבצים</string>
<string name="pref_plugin_sftp_desc">אפשר להתקן המרוחק לדפדף בקבצים שבפלאפון</string>
<string name="pref_plugin_clipboard">סנכנרון לוח העתקה</string>
<string name="pref_plugin_clipboard_desc">שתף בין המחשבים את מה שמועתק</string>
<string name="pref_plugin_mousepad">שליטה מרחוק</string>
<string name="pref_plugin_mousepad_desc">השתמש בפלאפון כדי לשלוט בעכבר ובמקדלת של ההתקן המרוחק</string>
<string name="pref_plugin_mpris">שליטה במדיה</string>
<string name="pref_plugin_mpris_desc">שלוט בניגון שמע או וידאו מהפלאפון</string>
<string name="pref_plugin_runcommand">הרץ פקודה</string>
<string name="pref_plugin_runcommand_desc">הרץ פקודה במחשב מהפלאפון</string>
<string name="pref_plugin_ping">פינג</string>
<string name="pref_plugin_ping_desc">שלח וקבל פינגים</string>
<string name="pref_plugin_notifications">סנכרון התראות</string>
<string name="pref_plugin_notifications_desc">הראה את ההתראות מהפלאפון בהתקן אחר</string>
<string name="pref_plugin_sharereceiver">שתף וקבל קבצים וכתובות</string>
<string name="pref_plugin_sharereceiver_desc">שתף וקבל קבצים וכתובת אינטרנט בין התקנים</string>
<string name="plugin_not_available">אפשרות זו אינה זמינה בגרסת האנדרואיד שלך</string>
<string name="device_list_empty">אין התקנים</string>
<string name="ok">בסדר</string>
<string name="cancel">בטל</string>
<string name="open_settings">פתח הגדרות</string>
<string name="no_permissions">אתה צריך לתת הרשאות לגישה להתראות</string>
<string name="send_ping">שלח פינג</string>
<string name="open_mpris_controls">שליטה על המדיה</string>
<string name="open_mousepad">שלוט על המחשב</string>
<string name="mousepad_info">הזז את האצבע על המסך כדי להזיז את סמן העכבר במחשב. לחץ כדי ללחוץ במחשב, השתמש בשנים או שלוש אצבעות כדי ללחוץ על המקש הימני או האמצעי. השתמש בליחצה ארוכה לגרירה ושחרור.</string>
<string name="mousepad_double_tap_settings_title">הגדר פעולה ללחיצת שתי אצבעות</string>
<string name="mousepad_triple_tap_settings_title">הגדר פעולה ללחיצת שלוש אצבעות</string>
<string name="mousepad_sensitivity_settings_title">הגדר רגישות משטח המגע</string>
<string name="mousepad_scroll_direction_title">הפוך את כיוון הגלילה</string>
<string-array name="mousepad_tap_entries">
<item>"לחיצה ימנית "</item>
<item>לחיצה אצמעית (גלגלת)</item>
<item>שום דבר</item>
</string-array>
<string name="mousepad_double_default">ימין</string>
<string name="mousepad_triple_default">אמצע</string>
<string name="mousepad_sensitivity_default">ברירת מחדל</string>
<string-array name="mousepad_sensitivity_entries">
<item>הכי איטי</item>
<item>יותר מההכי איטי</item>
<item>ברירת החדל</item>
<item>יותר מהברירת מחדל</item>
<item>הכי מהיר</item>
</string-array>
<string name="category_connected_devices">התקנים מחוברים</string>
<string name="category_not_paired_devices">התקנים זמינים</string>
<string name="category_remembered_devices">התקנים זכורים</string>
<string name="plugins_failed_to_load">טעינת התוסף נכשלה (לחץ לעוד מידע):</string>
<string name="device_menu_plugins">הגדרות תוספים</string>
<string name="device_menu_unpair">בטל התאמה</string>
<string name="device_not_reachable">ההתקן המותאם לא זמין</string>
<string name="pair_new_device">התאם התקן חדש</string>
<string name="unknown_device">התקן לא ידוע</string>
<string name="error_not_reachable">ההתקן לא זמין</string>
<string name="error_already_requested">כבר בוקשה התאמה</string>
<string name="error_already_paired">ההתקן כבר מותאם</string>
<string name="error_could_not_send_package">לא יכול לשלוח חבילה</string>
<string name="error_timed_out">נגמר הזמן</string>
<string name="error_canceled_by_user">בוטל ע\"י המשתמש</string>
<string name="error_canceled_by_other_peer">בוטל ע\"י מישהו אחר</string>
<string name="error_invalid_key">התקבל מפתח לא חוקי</string>
<string name="encryption_info_title">פרטי הצפנה</string>
<string name="encryption_info_msg_no_ssl">ההתקן השני אינו משתמש בגרסה האחרונה של KDE Connect, משתמש בשיטת ההצפנה הישנה.</string>
<string name="my_device_fingerprint">טביעת האצבע SHA1 של ההתקן היא:</string>
<string name="remote_device_fingerprint">"טביעת האצבע SHA1 של ההתקן המרוחק היא: "</string>
<string name="pair_requested">בקשת התאמה</string>
<string name="pairing_request_from">בוקשה התאמה מ־%1s</string>
<string name="received_url_title">התקבל קישור מ־%1s</string>
<string name="received_url_text">לחץ כדי לפתוח את \'%1s\'</string>
<string name="incoming_file_title">התקבל קובץ ־%1s</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_title">שולח קובץ ל־%1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="received_file_title">התקבל קובץ מ־%1s</string>
<string name="received_file_fail_title">נכשל בקבלת קובץ מ־%1s</string>
<string name="received_file_text">לחץ כדי לפתוח את %1s</string>
<string name="sent_file_title">הקובץ נשלח ל־%1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_title">נכשל בשליחת הקובץ ל־%1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">לחץ כדי לענות</string>
<string name="reconnect">התחבר מחדש</string>
<string name="right_click">שלח לחיצה ימנית</string>
<string name="middle_click">שלח לחיצה אמצעית (גלגלת)</string>
<string name="show_keyboard">הראה מקלדת</string>
<string name="device_not_paired">ההתקן לא מותאם</string>
<string name="request_pairing">בקש התאמה</string>
<string name="pairing_accept">אשר</string>
<string name="pairing_reject">דחה</string>
<string name="device">התקן</string>
<string name="pair_device">התאם התקן</string>
<string name="remote_control">שלוט מרחוק</string>
<string name="settings">הגדרות KDE Connect</string>
<string name="mpris_play">נגן</string>
<string name="mpris_previous">הקודם</string>
<string name="mpris_rew">דילוג אחורה</string>
<string name="mpris_ff">דילוג קדימה</string>
<string name="mpris_next">הבא</string>
<string name="mpris_volume">עוצמה</string>
<string name="mpris_settings">הגדרות מדיה</string>
<string name="mpris_time_settings_title">כפתור דילוג קדימה או אחורה</string>
<string name="mpris_time_settings_summary">בחר כמה זמן ידולג שלוחצים על דילוג קדימה או אחורה</string>
<string-array name="mpris_time_entries">
<item>10 שניות</item>
<item>20 שניות</item>
<item>חצי דקה</item>
<item>דקה</item>
<item>שתי דקות</item>
</string-array>
<string name="share_to">שתף ל...</string>
<string name="protocol_version_older">ההתקן משתמש בגרסה ישנה יותר</string>
<string name="protocol_version_newer">ההתקן משתמש בגרסה חדשה יותר</string>
<string name="general_settings">הגדרות כלליות</string>
<string name="plugin_settings">הגדרות</string>
<string name="plugin_settings_with_name">הגדרות %s</string>
<string name="device_name">שם המכשיר</string>
<string name="device_name_preference_summary">%s</string>
<string name="invalid_device_name">שם המכשיר לא תקין</string>
<string name="shareplugin_text_saved">התקבל טקסט, נשמר ללוח העתקה</string>
<string name="custom_devices_settings">רשימת התקנים מותאמת אישית</string>
<string name="pair_device_action">התאם התקן חדש</string>
<string name="unpair_device_action">בטל את ההתאמה עם %s</string>
<string name="custom_device_list">הוסף התקן ע\"י כתובת IP</string>
<string name="share_notification_preference">התראות רועשות</string>
<string name="share_notification_preference_summary">רטוט ונגן צליל בעת קבלת קובץ</string>
<string name="title_activity_notification_filter">סנן התראות</string>
<string name="filter_apps_info">התראות יסונכרנו רק לאפליקציות נבחרות</string>
<string name="sftp_internal_storage">זיכרון פנימי</string>
<string name="sftp_all_files">כל הקבצים</string>
<string name="sftp_sdcard_num">כרטיס זיכרון %d</string>
<string name="sftp_sdcard">כרטיס זיכרון</string>
<string name="sftp_readonly">(לקריאה בלבד)</string>
<string name="sftp_camera">תמונות מצלמה</string>
<string name="add_host">הוסף כתובת שרת או IP</string>
<string name="add_host_hint">כתבות שרת או IP</string>
<string name="no_players_connected">לא נמצא נגן</string>
<string name="custom_dev_list_help">השתמש באפשרות זו רק אם המכשיר שלך לא מזוהה באופן אוטומטי. הקלד את כתובת הIP או את כינוי ההתקן למטה ולחץ על הכפתור כדי להוסיף לרשימה. לחץ על פריט קיים כדי להסיר אותו מהרשימה.</string>
<string name="mpris_player_on_device">%1$s ב־%2$s</string>
<string name="send_files">שלח קובץ</string>
<string name="pairing_title">מכשירי KDE Connect</string>
<string name="pairing_description">התקנים אחרים המריצים KDE Connect ברשת הנוכחית צריכים להופיע פה.</string>
<string name="device_paired">התקן מתואם</string>
<string name="device_rename_title">שנה שם התקן</string>
<string name="device_rename_confirm">שנה שם</string>
<string name="refresh">רענן</string>
<string name="unreachable_description">ההתקן המתואם לא זמין, וודא שהוא מחובר לאותה רשת אליה התקן זה מחובר.</string>
<string name="no_file_browser">לא נמצאו מנהלי קבצים מותקנים במכשיר זה.</string>
<string name="pref_plugin_telepathy">שלח SMS</string>
<string name="pref_plugin_telepathy_desc">שלח הודעות מהמחשב שלך</string>
<string name="plugin_not_supported">התוסף הזה לא נתמך ע\"י המכשיר שלך</string>
<string name="findmyphone_title">מצא את המכשיר שלי</string>
<string name="findmyphone_description">הפעל רעש במכשיר כדי שתוכל למצוא אותו.</string>
<string name="findmyphone_found">נמצא</string>
<string name="open">פתח</string>
<string name="close">סגור</string>
</resources>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Controlli multimediali</string> <string name="pref_plugin_mpris">Controlli multimediali</string>
<string name="pref_plugin_mpris_desc">Controlla la riproduzione audio/video dal telefono</string> <string name="pref_plugin_mpris_desc">Controlla la riproduzione audio/video dal telefono</string>
<string name="pref_plugin_runcommand">Esegui comando</string> <string name="pref_plugin_runcommand">Esegui comando</string>
<string name="pref_plugin_runcommand_desc">Esegue un comando sul tuo sistema</string> <string name="pref_plugin_runcommand_desc">Esegui comandi remoti dal tuo telefono</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Invia e ricevi ping</string> <string name="pref_plugin_ping_desc">Invia e ricevi ping</string>
<string name="pref_plugin_notifications">Sincronizzazione notifiche</string> <string name="pref_plugin_notifications">Sincronizzazione notifiche</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Annullata dall\'utente</string> <string name="error_canceled_by_user">Annullata dall\'utente</string>
<string name="error_canceled_by_other_peer">Annullata dal dispositivo remoto</string> <string name="error_canceled_by_other_peer">Annullata dal dispositivo remoto</string>
<string name="error_invalid_key">Ricevuta chiave non valida</string> <string name="error_invalid_key">Ricevuta chiave non valida</string>
<string name="encryption_info_title">Informazioni di cifratura</string>
<string name="encryption_info_msg_no_ssl">L\'altro dispositivo non utilizza una versione recente di KDE Connect, utilizzando il metodo di cifratura precedente.</string>
<string name="my_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo è:</string>
<string name="remote_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo remoto è:</string>
<string name="pair_requested">Richiesta di associazione</string> <string name="pair_requested">Richiesta di associazione</string>
<string name="pairing_request_from">Richiesta associazione da %1s</string> <string name="pairing_request_from">Richiesta associazione da %1s</string>
<string name="received_url_title">Collegamento ricevuto da %1s</string> <string name="received_url_title">Collegamento ricevuto da %1s</string>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">멀티미디어 제어</string> <string name="pref_plugin_mpris">멀티미디어 제어</string>
<string name="pref_plugin_mpris_desc">휴대폰에서 오디오/비디오 제어</string> <string name="pref_plugin_mpris_desc">휴대폰에서 오디오/비디오 제어</string>
<string name="pref_plugin_runcommand">명령 실행</string> <string name="pref_plugin_runcommand">명령 실행</string>
<string name="pref_plugin_runcommand_desc">시스템에서 명령 실행</string>
<string name="pref_plugin_ping"></string> <string name="pref_plugin_ping"></string>
<string name="pref_plugin_ping_desc">핑 보내고 받기</string> <string name="pref_plugin_ping_desc">핑 보내고 받기</string>
<string name="pref_plugin_notifications">알림 동기화</string> <string name="pref_plugin_notifications">알림 동기화</string>
@@ -147,7 +146,6 @@
<string name="pref_plugin_telepathy">SMS 보내기</string> <string name="pref_plugin_telepathy">SMS 보내기</string>
<string name="pref_plugin_telepathy_desc">데스크톱에서 문자 메시지 보내기</string> <string name="pref_plugin_telepathy_desc">데스크톱에서 문자 메시지 보내기</string>
<string name="plugin_not_supported">장치에서 이 플러그인을 지원하지 않습니다</string> <string name="plugin_not_supported">장치에서 이 플러그인을 지원하지 않습니다</string>
<string name="findmyphone_title">내 휴대폰 찾기</string>
<string name="findmyphone_description">소리를 울려서 휴대폰을 찾는 데 도움을 줍니다.</string> <string name="findmyphone_description">소리를 울려서 휴대폰을 찾는 데 도움을 줍니다.</string>
<string name="findmyphone_found">찾았음</string> <string name="findmyphone_found">찾았음</string>
<string name="open">열기</string> <string name="open">열기</string>

View File

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

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Bediening van multimedia</string> <string name="pref_plugin_mpris">Bediening van multimedia</string>
<string name="pref_plugin_mpris_desc">Bedien de audio/video vanaf uw telefoon</string> <string name="pref_plugin_mpris_desc">Bedien de audio/video vanaf uw telefoon</string>
<string name="pref_plugin_runcommand">Commando uitvoeren</string> <string name="pref_plugin_runcommand">Commando uitvoeren</string>
<string name="pref_plugin_runcommand_desc">Voert een commando uit op uw systeem</string> <string name="pref_plugin_runcommand_desc">Commando\'s afvuren op afstand vanaf uw telefoon</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Pings verzenden en ontvangen</string> <string name="pref_plugin_ping_desc">Pings verzenden en ontvangen</string>
<string name="pref_plugin_notifications">Synchronisatie van meldingen</string> <string name="pref_plugin_notifications">Synchronisatie van meldingen</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Geannuleerd door gebruiker</string> <string name="error_canceled_by_user">Geannuleerd door gebruiker</string>
<string name="error_canceled_by_other_peer">Geannuleerd door andere kant</string> <string name="error_canceled_by_other_peer">Geannuleerd door andere kant</string>
<string name="error_invalid_key">Ongeldige sleutel ontvangen</string> <string name="error_invalid_key">Ongeldige sleutel ontvangen</string>
<string name="encryption_info_title">Versleutelde informatie</string>
<string name="encryption_info_msg_no_ssl">Het andere apparaat gebruikt geen recente versie van KDE Connect, de verouderde versleutelingsmethode zal worden gebruikt.</string>
<string name="my_device_fingerprint">De SHA1 vingerafdruk van het certificaat van uw apparaat is:</string>
<string name="remote_device_fingerprint">De SHA1 vingerafdruk van het certificaat van het apparaat op afstand is:</string>
<string name="pair_requested">Paar gevraagd</string> <string name="pair_requested">Paar gevraagd</string>
<string name="pairing_request_from">Verzoek om een paar te maken van %1s</string> <string name="pairing_request_from">Verzoek om een paar te maken van %1s</string>
<string name="received_url_title">Ontvangen koppeling van %1s</string> <string name="received_url_title">Ontvangen koppeling van %1s</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Sterowanie multimediami</string> <string name="pref_plugin_mpris">Sterowanie multimediami</string>
<string name="pref_plugin_mpris_desc">Steruj dźwiękiem/obrazem ze swojego telefonu</string> <string name="pref_plugin_mpris_desc">Steruj dźwiękiem/obrazem ze swojego telefonu</string>
<string name="pref_plugin_runcommand">Wykonaj polecenie</string> <string name="pref_plugin_runcommand">Wykonaj polecenie</string>
<string name="pref_plugin_runcommand_desc">Wykonuje polecenie na twoim systmie</string> <string name="pref_plugin_runcommand_desc">Wyzwalaj zdalne polecenia z twojego telefonu</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Wysyłaj i otrzymuj pingi</string> <string name="pref_plugin_ping_desc">Wysyłaj i otrzymuj pingi</string>
<string name="pref_plugin_notifications">Powiadomienia synchronizacji</string> <string name="pref_plugin_notifications">Powiadomienia synchronizacji</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Anulowane przez użytkownika</string> <string name="error_canceled_by_user">Anulowane przez użytkownika</string>
<string name="error_canceled_by_other_peer">Anulowane przez innego partnera</string> <string name="error_canceled_by_other_peer">Anulowane przez innego partnera</string>
<string name="error_invalid_key">Otrzymano nieprawidłowy klucz</string> <string name="error_invalid_key">Otrzymano nieprawidłowy klucz</string>
<string name="encryption_info_title">Zaszyfrowane informacje</string>
<string name="encryption_info_msg_no_ssl">Drugie urządzenie nie używa ostatniej wersji KDE Connect, użyto przestarzałego szyfrowania.</string>
<string name="my_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego urządzenia to:</string>
<string name="remote_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego zdalnego urządzenia to:</string>
<string name="pair_requested">Zażądano parowania</string> <string name="pair_requested">Zażądano parowania</string>
<string name="pairing_request_from">Żądanie parowania z %1s</string> <string name="pairing_request_from">Żądanie parowania z %1s</string>
<string name="received_url_title">Odebrano odsyłacz od %1s</string> <string name="received_url_title">Odebrano odsyłacz od %1s</string>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">Controle multimídia</string> <string name="pref_plugin_mpris">Controle multimídia</string>
<string name="pref_plugin_mpris_desc">Controla áudio e vídeo a partir do seu telefone</string> <string name="pref_plugin_mpris_desc">Controla áudio e vídeo a partir do seu telefone</string>
<string name="pref_plugin_runcommand">Executar comando</string> <string name="pref_plugin_runcommand">Executar comando</string>
<string name="pref_plugin_runcommand_desc">Executa um comando no seu sistema</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envia e recebe pings</string> <string name="pref_plugin_ping_desc">Envia e recebe pings</string>
<string name="pref_plugin_notifications">Sincronização de notificações</string> <string name="pref_plugin_notifications">Sincronização de notificações</string>
@@ -151,7 +150,6 @@
<string name="pref_plugin_telepathy">Enviar SMS</string> <string name="pref_plugin_telepathy">Enviar SMS</string>
<string name="pref_plugin_telepathy_desc">Enviar mensagens de texto do seu Desktop</string> <string name="pref_plugin_telepathy_desc">Enviar mensagens de texto do seu Desktop</string>
<string name="plugin_not_supported">Este plugin não é suportado pelo dispositivo</string> <string name="plugin_not_supported">Este plugin não é suportado pelo dispositivo</string>
<string name="findmyphone_title">Encontrar meu telefone</string>
<string name="findmyphone_description">Faça este telefone tocar para encontrá-lo.</string> <string name="findmyphone_description">Faça este telefone tocar para encontrá-lo.</string>
<string name="findmyphone_found">Encontrado</string> <string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string> <string name="open">Abrir</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Comandos multimédia</string> <string name="pref_plugin_mpris">Comandos multimédia</string>
<string name="pref_plugin_mpris_desc">Controlar o áudio/vídeo a partir do seu telefone</string> <string name="pref_plugin_mpris_desc">Controlar o áudio/vídeo a partir do seu telefone</string>
<string name="pref_plugin_runcommand">Executar um Comando</string> <string name="pref_plugin_runcommand">Executar um Comando</string>
<string name="pref_plugin_runcommand_desc">Executa um comando no seu sistema</string> <string name="pref_plugin_runcommand_desc">Despoletar comandos remotos a partir do seu telefone</string>
<string name="pref_plugin_ping">Contacto</string> <string name="pref_plugin_ping">Contacto</string>
<string name="pref_plugin_ping_desc">Enviar e receber pedidos de contacto (\'ping\')</string> <string name="pref_plugin_ping_desc">Enviar e receber pedidos de contacto (\'ping\')</string>
<string name="pref_plugin_notifications">Sincronização da notificação</string> <string name="pref_plugin_notifications">Sincronização da notificação</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Cancelado pelo utilizador</string> <string name="error_canceled_by_user">Cancelado pelo utilizador</string>
<string name="error_canceled_by_other_peer">Cancelado pela outra máquina</string> <string name="error_canceled_by_other_peer">Cancelado pela outra máquina</string>
<string name="error_invalid_key">Chave inválida recebida</string> <string name="error_invalid_key">Chave inválida recebida</string>
<string name="encryption_info_title">Dados de Encriptação</string>
<string name="encryption_info_msg_no_ssl">O outro dispositivo não usa uma versão recente do KDE Connect; será usado o método antigo de encriptação.</string>
<string name="my_device_fingerprint">A impressão digital SHA1 do certificado do seu dispositivo é:</string>
<string name="remote_device_fingerprint">A impressão digital SHA1 do certificado do dispositivo remoto é:</string>
<string name="pair_requested">Emparelhamento pedido</string> <string name="pair_requested">Emparelhamento pedido</string>
<string name="pairing_request_from">Pedido de emparelhamento de %1s</string> <string name="pairing_request_from">Pedido de emparelhamento de %1s</string>
<string name="received_url_title">Ligação recebida de %1s</string> <string name="received_url_title">Ligação recebida de %1s</string>
@@ -151,7 +155,7 @@
<string name="pref_plugin_telepathy">Enviar um SMS</string> <string name="pref_plugin_telepathy">Enviar um SMS</string>
<string name="pref_plugin_telepathy_desc">Enviar mensagens de texto a partir do seu ambiente de trabalho</string> <string name="pref_plugin_telepathy_desc">Enviar mensagens de texto a partir do seu ambiente de trabalho</string>
<string name="plugin_not_supported">Este \'plugin\' não é suportado pelo dispositivo</string> <string name="plugin_not_supported">Este \'plugin\' não é suportado pelo dispositivo</string>
<string name="findmyphone_title">Descobrir o Meu Telefone</string> <string name="findmyphone_title">Descobrir o meu telefone</string>
<string name="findmyphone_description">Toca este telefone para que o possa encontrar.</string> <string name="findmyphone_description">Toca este telefone para que o possa encontrar.</string>
<string name="findmyphone_found">Encontrado</string> <string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string> <string name="open">Abrir</string>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">Управление воспроизведением</string> <string name="pref_plugin_mpris">Управление воспроизведением</string>
<string name="pref_plugin_mpris_desc">Управление медиапроигрывателем с телефона</string> <string name="pref_plugin_mpris_desc">Управление медиапроигрывателем с телефона</string>
<string name="pref_plugin_runcommand">Запуск команд</string> <string name="pref_plugin_runcommand">Запуск команд</string>
<string name="pref_plugin_runcommand_desc">Выполнение команд на вашем компьютере</string>
<string name="pref_plugin_ping">Пинг</string> <string name="pref_plugin_ping">Пинг</string>
<string name="pref_plugin_ping_desc">Отправка и получение тестовых сигналов</string> <string name="pref_plugin_ping_desc">Отправка и получение тестовых сигналов</string>
<string name="pref_plugin_notifications">Синхронизация уведомлений</string> <string name="pref_plugin_notifications">Синхронизация уведомлений</string>
@@ -147,7 +146,6 @@
<string name="pref_plugin_telepathy">Отправка SMS</string> <string name="pref_plugin_telepathy">Отправка SMS</string>
<string name="pref_plugin_telepathy_desc">Отправка SMS-сообщений с вашего компьютера</string> <string name="pref_plugin_telepathy_desc">Отправка SMS-сообщений с вашего компьютера</string>
<string name="plugin_not_supported">Этот модуль не поддерживается устройством</string> <string name="plugin_not_supported">Этот модуль не поддерживается устройством</string>
<string name="findmyphone_title">Поиск телефона</string>
<string name="findmyphone_description">Подача звукового сигнала на телефоне, чтобы вы могли его найти.</string> <string name="findmyphone_description">Подача звукового сигнала на телефоне, чтобы вы могли его найти.</string>
<string name="findmyphone_found">Найден</string> <string name="findmyphone_found">Найден</string>
<string name="open">Открыть</string> <string name="open">Открыть</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Multimediálne ovládače</string> <string name="pref_plugin_mpris">Multimediálne ovládače</string>
<string name="pref_plugin_mpris_desc">Ovládať audio/video z vášho telefónu</string> <string name="pref_plugin_mpris_desc">Ovládať audio/video z vášho telefónu</string>
<string name="pref_plugin_runcommand">Spustiť príkaz</string> <string name="pref_plugin_runcommand">Spustiť príkaz</string>
<string name="pref_plugin_runcommand_desc">Spustí príkazy na vašom systéme</string> <string name="pref_plugin_runcommand_desc">Spustiť vzdialené príkazy z vášho telefónu</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Poslať a prijať pingy</string> <string name="pref_plugin_ping_desc">Poslať a prijať pingy</string>
<string name="pref_plugin_notifications">Synchronizácia pripomienok</string> <string name="pref_plugin_notifications">Synchronizácia pripomienok</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Zrušené používateľom</string> <string name="error_canceled_by_user">Zrušené používateľom</string>
<string name="error_canceled_by_other_peer">Zrušené iným klientom</string> <string name="error_canceled_by_other_peer">Zrušené iným klientom</string>
<string name="error_invalid_key">Získaný nesprávny kľúč</string> <string name="error_invalid_key">Získaný nesprávny kľúč</string>
<string name="encryption_info_title">Informácia o šifrovaní</string>
<string name="encryption_info_msg_no_ssl">Ďalšie zariadenie nepoužíva aktuálnu verziu KDE Connect, používam klasickú metódu šifrovania.</string>
<string name="my_device_fingerprint">Odtlačok SHA1 vášho certifikátu zariadenia je:</string>
<string name="remote_device_fingerprint">Odtlačok SHA1 vzdialeného certifikátu zariadenia je:</string>
<string name="pair_requested">Spárovanie vyžiadané</string> <string name="pair_requested">Spárovanie vyžiadané</string>
<string name="pairing_request_from">Požiadavka na spárovanie od %1s</string> <string name="pairing_request_from">Požiadavka na spárovanie od %1s</string>
<string name="received_url_title">Prijatý odkaz od %1s</string> <string name="received_url_title">Prijatý odkaz od %1s</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Multimediakontroller</string> <string name="pref_plugin_mpris">Multimediakontroller</string>
<string name="pref_plugin_mpris_desc">Styr ljud och video från telefonen</string> <string name="pref_plugin_mpris_desc">Styr ljud och video från telefonen</string>
<string name="pref_plugin_runcommand">Kör kommando</string> <string name="pref_plugin_runcommand">Kör kommando</string>
<string name="pref_plugin_runcommand_desc">Kör ett kommando på systemet</string> <string name="pref_plugin_runcommand_desc">Utlös fjärrkommandon från din telefon</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Skicka och ta emot ping</string> <string name="pref_plugin_ping_desc">Skicka och ta emot ping</string>
<string name="pref_plugin_notifications">Synkronisering av underrättelser</string> <string name="pref_plugin_notifications">Synkronisering av underrättelser</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Avbruten av användaren</string> <string name="error_canceled_by_user">Avbruten av användaren</string>
<string name="error_canceled_by_other_peer">Avbruten av motparten</string> <string name="error_canceled_by_other_peer">Avbruten av motparten</string>
<string name="error_invalid_key">Ogiltig nyckel mottagen</string> <string name="error_invalid_key">Ogiltig nyckel mottagen</string>
<string name="encryption_info_title">Krypteringsinformation</string>
<string name="encryption_info_msg_no_ssl">Den andra apparaten använder inte en aktuell version av KDE-anslut. Använder den föråldrade krypteringsmetoden.</string>
<string name="my_device_fingerprint">SHA1-fingeravtryck för din apparats certifikat är:</string>
<string name="remote_device_fingerprint">SHA1-fingeravtryck för den andra apparatens certifikat är:</string>
<string name="pair_requested">Ihopparning begärd</string> <string name="pair_requested">Ihopparning begärd</string>
<string name="pairing_request_from">Begäran om ihopparning från %1s</string> <string name="pairing_request_from">Begäran om ihopparning från %1s</string>
<string name="received_url_title">Tog emot länk från %1s</string> <string name="received_url_title">Tog emot länk från %1s</string>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Керування відтворенням</string> <string name="pref_plugin_mpris">Керування відтворенням</string>
<string name="pref_plugin_mpris_desc">Керування відтворенням звуку та відео з вашого телефону</string> <string name="pref_plugin_mpris_desc">Керування відтворенням звуку та відео з вашого телефону</string>
<string name="pref_plugin_runcommand">Виконати команду</string> <string name="pref_plugin_runcommand">Виконати команду</string>
<string name="pref_plugin_runcommand_desc">Виконує команду у вашій системі</string> <string name="pref_plugin_runcommand_desc">Віддалені команди з вашого телефону</string>
<string name="pref_plugin_ping">Підтримання зв’язку</string> <string name="pref_plugin_ping">Підтримання зв’язку</string>
<string name="pref_plugin_ping_desc">Надсилання і отримання сигналів підтримання зв’язку</string> <string name="pref_plugin_ping_desc">Надсилання і отримання сигналів підтримання зв’язку</string>
<string name="pref_plugin_notifications">Синхронізація сповіщень</string> <string name="pref_plugin_notifications">Синхронізація сповіщень</string>
@@ -66,6 +66,10 @@
<string name="error_canceled_by_user">Скасовано користувачем</string> <string name="error_canceled_by_user">Скасовано користувачем</string>
<string name="error_canceled_by_other_peer">Скасовано з іншого вузла пов’язування</string> <string name="error_canceled_by_other_peer">Скасовано з іншого вузла пов’язування</string>
<string name="error_invalid_key">Отримано некоректний ключ</string> <string name="error_invalid_key">Отримано некоректний ключ</string>
<string name="encryption_info_title">Дані щодо шифрування</string>
<string name="encryption_info_msg_no_ssl">На сторонньому пристрої не використовується нова версія KDE Connect, у якій використовується застарілий спосіб шифрування.</string>
<string name="my_device_fingerprint">Відбиток SHA1 сертифіката пристрою:</string>
<string name="remote_device_fingerprint">Відбиток SHA1 сертифіката віддаленого пристрою:</string>
<string name="pair_requested">Запит щодо пов’язування</string> <string name="pair_requested">Запит щодо пов’язування</string>
<string name="pairing_request_from">Запит щодо пов’язування від %1s</string> <string name="pairing_request_from">Запит щодо пов’язування від %1s</string>
<string name="received_url_title">Отримано посилання від %1s</string> <string name="received_url_title">Отримано посилання від %1s</string>

View File

@@ -13,7 +13,6 @@
<string name="pref_plugin_mpris">多媒体控制</string> <string name="pref_plugin_mpris">多媒体控制</string>
<string name="pref_plugin_mpris_desc">从手机控制音频或视频</string> <string name="pref_plugin_mpris_desc">从手机控制音频或视频</string>
<string name="pref_plugin_runcommand">执行命令</string> <string name="pref_plugin_runcommand">执行命令</string>
<string name="pref_plugin_runcommand_desc">在您的系统上执行命令</string>
<string name="pref_plugin_ping">Ping</string> <string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">发送和接受ping</string> <string name="pref_plugin_ping_desc">发送和接受ping</string>
<string name="pref_plugin_notifications">通知同步</string> <string name="pref_plugin_notifications">通知同步</string>
@@ -148,7 +147,6 @@
<string name="pref_plugin_telepathy">发送短消息</string> <string name="pref_plugin_telepathy">发送短消息</string>
<string name="pref_plugin_telepathy_desc">从桌面发送短消息</string> <string name="pref_plugin_telepathy_desc">从桌面发送短消息</string>
<string name="plugin_not_supported">设备不支持此插件</string> <string name="plugin_not_supported">设备不支持此插件</string>
<string name="findmyphone_title">找到我的手机</string>
<string name="findmyphone_description">让手机响铃从而找到它</string> <string name="findmyphone_description">让手机响铃从而找到它</string>
<string name="findmyphone_found">找到</string> <string name="findmyphone_found">找到</string>
<string name="open">打开</string> <string name="open">打开</string>

View File

@@ -19,6 +19,8 @@
<string name="pref_plugin_ping_desc">Send and receive pings</string> <string name="pref_plugin_ping_desc">Send and receive pings</string>
<string name="pref_plugin_notifications">Notification sync</string> <string name="pref_plugin_notifications">Notification sync</string>
<string name="pref_plugin_notifications_desc">Access your notifications from other devices</string> <string name="pref_plugin_notifications_desc">Access your notifications from other devices</string>
<string name="pref_plugin_receive_notifications">Receive notifications</string>
<string name="pref_plugin_receive_notifications_desc">Receive notifications from other devices</string>
<string name="pref_plugin_sharereceiver">Share and receive</string> <string name="pref_plugin_sharereceiver">Share and receive</string>
<string name="pref_plugin_sharereceiver_desc">Share files and URLs between devices</string> <string name="pref_plugin_sharereceiver_desc">Share files and URLs between devices</string>
<string name="plugin_not_available">This feature is not available in your Android version</string> <string name="plugin_not_available">This feature is not available in your Android version</string>
@@ -85,8 +87,8 @@
<string name="error_invalid_key">Invalid key received</string> <string name="error_invalid_key">Invalid key received</string>
<string name="encryption_info_title">Encryption Info</string> <string name="encryption_info_title">Encryption Info</string>
<string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string> <string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string>
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is : </string> <string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is:</string>
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is : </string> <string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is:</string>
<string name="pair_requested">Pair requested</string> <string name="pair_requested">Pair requested</string>
<string name="pairing_request_from">Pairing request from %1s</string> <string name="pairing_request_from">Pairing request from %1s</string>
<string name="received_url_title">Received link from %1s</string> <string name="received_url_title">Received link from %1s</string>

View File

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

View File

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

View File

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

View File

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

View File

@@ -202,11 +202,13 @@ public class LanPairingHandler extends BasePairingHandler {
//Log.e("KDE/PairingDone", "Pairing Done"); //Log.e("KDE/PairingDone", "Pairing Done");
SharedPreferences.Editor editor = mDevice.getContext().getSharedPreferences(mDevice.getDeviceId(), Context.MODE_PRIVATE).edit(); SharedPreferences.Editor editor = mDevice.getContext().getSharedPreferences(mDevice.getDeviceId(), Context.MODE_PRIVATE).edit();
try { if (mDevice.publicKey != null) {
String encodedPublicKey = Base64.encodeToString(mDevice.publicKey.getEncoded(), 0); try {
editor.putString("publicKey", encodedPublicKey); String encodedPublicKey = Base64.encodeToString(mDevice.publicKey.getEncoded(), 0);
} catch (Exception e) { editor.putString("publicKey", encodedPublicKey);
Log.e("KDE/PairingDone", "Error encoding public key"); } catch (Exception e) {
Log.e("KDE/PairingDone", "Error encoding public key");
}
} }
try { try {

View File

@@ -26,11 +26,9 @@ import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider; import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.ArrayList;
public class LoopbackLink extends BaseLink { public class LoopbackLink extends BaseLink {

View File

@@ -20,7 +20,6 @@
package org.kde.kdeconnect; package org.kde.kdeconnect;
import android.app.Activity;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -64,11 +63,13 @@ public class BackgroundService extends Service {
if (wasEmpty) { if (wasEmpty) {
onNetworkChange(); onNetworkChange();
} }
//Log.e("acquireDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
return wasEmpty; return wasEmpty;
} }
public void releaseDiscoveryMode(Object key) { public void releaseDiscoveryMode(Object key) {
boolean removed = discoveryModeAcquisitions.remove(key); boolean removed = discoveryModeAcquisitions.remove(key);
//Log.e("releaseDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
if (removed && discoveryModeAcquisitions.isEmpty()) { if (removed && discoveryModeAcquisitions.isEmpty()) {
cleanDevices(); cleanDevices();
} }
@@ -162,11 +163,16 @@ public class BackgroundService extends Service {
} }
private void cleanDevices() { private void cleanDevices() {
for(Device d : devices.values()) { new Thread(new Runnable() {
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) { @Override
d.disconnect(); public void run() {
for(Device d : devices.values()) {
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
d.disconnect();
}
}
} }
} }).start();
} }
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() { private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {

View File

@@ -28,17 +28,15 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Helpers.ObjectsHelper; import org.kde.kdeconnect.Helpers.ObjectsHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
@@ -58,7 +56,6 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@@ -74,11 +71,13 @@ public class Device implements BaseLink.PackageReceiver {
private int notificationId; private int notificationId;
private int protocolVersion; private int protocolVersion;
private static final int MIN_VERSION_WITH_CAPPABILITIES_SUPPORT = 6;
private DeviceType deviceType; private DeviceType deviceType;
private PairStatus pairStatus; private PairStatus pairStatus;
private final CopyOnWriteArrayList<PairingCallback> pairingCallback = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<PairingCallback> pairingCallback = new CopyOnWriteArrayList<>();
private Map<String, BasePairingHandler> pairingHandlers = new HashMap<String, BasePairingHandler>(); private Map<String, BasePairingHandler> pairingHandlers = new HashMap<>();
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>(); private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
@@ -572,10 +571,11 @@ public class Device implements BaseLink.PackageReceiver {
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
Log.e("KDE/Device", "Exception in " + plugin.getPluginKey() + "'s onPackageReceived()"); Log.e("KDE/Device", "Exception in " + plugin.getPluginKey() + "'s onPackageReceived()");
//try { Log.e("KDE/Device", "NetworkPackage:" + np.serialize()); } catch (Exception _) { }
} }
} }
} else { } else {
Log.e("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it"); Log.w("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it");
} }
} else { } else {
@@ -643,9 +643,9 @@ public class Device implements BaseLink.PackageReceiver {
hackToMakeRetrocompatiblePacketTypes(np); hackToMakeRetrocompatiblePacketTypes(np);
if (protocolVersion >= 6 && !supportedOutgoingInterfaces.contains(np.getType())) { if (protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT && !supportedOutgoingInterfaces.contains(np.getType()) && !NetworkPackage.protocolPackageTypes.contains(np.getType())) {
Log.e("Device/sendPackage", "Plugin tried to send an unsupported package: " + np.getType()); Log.e("Device/sendPackage", "Plugin tried to send an unsupported package: " + np.getType());
Log.e("Device/sendPackage", "Supported package types: " + Arrays.toString(supportedOutgoingInterfaces.toArray())); Log.w("Device/sendPackage", "Supported package types: " + Arrays.toString(supportedOutgoingInterfaces.toArray()));
} }
//Log.e("sendPackage", "Sending package..."); //Log.e("sendPackage", "Sending package...");
@@ -656,7 +656,7 @@ public class Device implements BaseLink.PackageReceiver {
@Override @Override
public void run() { public void run() {
boolean useEncryption = (protocolVersion < 6 && (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired())); boolean useEncryption = (protocolVersion < LanLinkProvider.MIN_VERSION_WITH_SSL_SUPPORT && (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired()));
//Make a copy to avoid concurrent modification exception if the original list changes //Make a copy to avoid concurrent modification exception if the original list changes
for (final BaseLink link : links) { for (final BaseLink link : links) {
@@ -790,7 +790,7 @@ public class Device implements BaseLink.PackageReceiver {
HashMap<String, ArrayList<String>> newPluginsByIncomingInterface = new HashMap<>(); HashMap<String, ArrayList<String>> newPluginsByIncomingInterface = new HashMap<>();
HashMap<String, ArrayList<String>> newPluginsByOutgoingInterface = new HashMap<>(); HashMap<String, ArrayList<String>> newPluginsByOutgoingInterface = new HashMap<>();
final boolean supportsCapabilities = (protocolVersion >= 6); final boolean supportsCapabilities = (protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT);
for (String pluginKey : availablePlugins) { for (String pluginKey : availablePlugins) {
@@ -865,12 +865,14 @@ public class Device implements BaseLink.PackageReceiver {
supportedOutgoingInterfaces = newSupportedOutgoingInterfaces; supportedOutgoingInterfaces = newSupportedOutgoingInterfaces;
unsupportedPlugins = newUnsupportedPlugins; unsupportedPlugins = newUnsupportedPlugins;
Log.i("ReloadPlugins", "not loading " + Arrays.toString(unsupportedPlugins.toArray()) + " because of unmatched capabilities"); if (!unsupportedPlugins.isEmpty()) {
Log.i("ReloadPlugins", "not loading " + Arrays.toString(unsupportedPlugins.toArray()) + " because of unmatched capabilities");
}
onPluginsChanged(); onPluginsChanged();
//Only send capabilities to devices using protocol version 6 or later //Only send capabilities to devices using protocol version 6 or later
if (capabilitiesChanged && isReachable() && isPaired() && protocolVersion >= 6) { if (capabilitiesChanged && isReachable() && isPaired() && protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_CAPABILITIES); NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_CAPABILITIES);
np.set("IncomingCapabilities", new ArrayList<>(newSupportedIncomingInterfaces)); np.set("IncomingCapabilities", new ArrayList<>(newSupportedIncomingInterfaces));
np.set("OutgoingCapabilities", new ArrayList<>(newSupportedOutgoingInterfaces)); np.set("OutgoingCapabilities", new ArrayList<>(newSupportedOutgoingInterfaces));
@@ -908,6 +910,13 @@ public class Device implements BaseLink.PackageReceiver {
} }
public boolean deviceShouldBeKeptAlive() { public boolean deviceShouldBeKeptAlive() {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
if (preferences.contains(getDeviceId())) {
//Log.e("DeviceShouldBeKeptAlive", "because it's a paired device");
return true; //Already paired
}
for(BaseLink l : links) { for(BaseLink l : links) {
if (l.linkShouldBeKeptAlive()) { if (l.linkShouldBeKeptAlive()) {
return true; return true;
@@ -917,12 +926,12 @@ public class Device implements BaseLink.PackageReceiver {
} }
public void hackToMakeRetrocompatiblePacketTypes(NetworkPackage np) { public void hackToMakeRetrocompatiblePacketTypes(NetworkPackage np) {
if (protocolVersion >= 6) return; if (protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT) return;
np.mType = np.getType().replace(".request",""); np.mType = np.getType().replace(".request","");
} }
public String hackToMakeRetrocompatiblePacketTypes(String type) { public String hackToMakeRetrocompatiblePacketTypes(String type) {
if (protocolVersion >= 6) return type; if (protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT) return type;
return type.replace(".request",""); return type.replace(".request","");
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
package org.kde.kdeconnect.Helpers;
import java.nio.charset.Charset;
public class StringsHelper {
public static final Charset UTF8 = Charset.forName("UTF-8");
}

View File

@@ -21,8 +21,6 @@
package org.kde.kdeconnect; package org.kde.kdeconnect;
import android.content.Context; import android.content.Context;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import org.json.JSONArray; import org.json.JSONArray;
@@ -33,15 +31,24 @@ import org.kde.kdeconnect.Helpers.DeviceHelper;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class NetworkPackage { public class NetworkPackage {
public final static int ProtocolVersion = 6; public final static int ProtocolVersion = 7;
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity"; public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair"; public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted"; public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted";
public static final String PACKAGE_TYPE_CAPABILITIES = "kdeconnect.capabilities"; public final static String PACKAGE_TYPE_CAPABILITIES = "kdeconnect.capabilities";
public static Set<String> protocolPackageTypes = new HashSet<String>() {{
add(PACKAGE_TYPE_IDENTITY);
add(PACKAGE_TYPE_PAIR);
add(PACKAGE_TYPE_ENCRYPTED);
add(PACKAGE_TYPE_CAPABILITIES);
}};
private long mId; private long mId;
String mType; String mType;
@@ -121,8 +128,6 @@ public class NetworkPackage {
} }
public boolean has(String key) { return mBody.has(key); } public boolean has(String key) { return mBody.has(key); }
public boolean isEncrypted() { return mType.equals(PACKAGE_TYPE_ENCRYPTED); }
public String serialize() throws JSONException { public String serialize() throws JSONException {
JSONObject jo = new JSONObject(); JSONObject jo = new JSONObject();
jo.put("id", mId); jo.put("id", mId);

View File

@@ -22,8 +22,8 @@ package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.ClipData; import android.content.ClipData;
import android.content.Context;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;

View File

@@ -20,12 +20,6 @@
package org.kde.kdeconnect.Plugins.ClibpoardPlugin; package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.widget.Button;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;

View File

@@ -1,11 +1,6 @@
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin; package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.Button;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;

View File

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

View File

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

View File

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

View File

@@ -21,6 +21,7 @@
package org.kde.kdeconnect.Plugins.MprisPlugin; package org.kde.kdeconnect.Plugins.MprisPlugin;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
@@ -33,14 +34,15 @@ import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar; import android.widget.SeekBar;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
@@ -93,6 +95,13 @@ public class MprisActivity extends ActionBarActivity {
TextView nowPlaying = (TextView) findViewById(R.id.now_playing_textview); TextView nowPlaying = (TextView) findViewById(R.id.now_playing_textview);
if (!nowPlaying.getText().toString().equals(song)) { if (!nowPlaying.getText().toString().equals(song)) {
nowPlaying.setText(song); nowPlaying.setText(song);
Bitmap currentArt = mpris.getCurrentArt();
ImageView artView = (ImageView) findViewById(R.id.artImageView);
if (currentArt != null) {
artView.setImageBitmap(currentArt);
}
} }
if (mpris.getLength() > -1 && mpris.getPosition() > -1 && !"spotify".equals(mpris.getPlayer().toLowerCase())) { if (mpris.getLength() > -1 && mpris.getPosition() > -1 && !"spotify".equals(mpris.getPlayer().toLowerCase())) {

View File

@@ -22,10 +22,13 @@ package org.kde.kdeconnect.Plugins.MprisPlugin;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.util.Base64;
import android.util.Log; import android.util.Log;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
@@ -43,6 +46,7 @@ public class MprisPlugin extends Plugin {
private String player = ""; private String player = "";
private boolean playing = false; private boolean playing = false;
private String currentSong = ""; private String currentSong = "";
private Bitmap currentArt;
private int volume = 50; private int volume = 50;
private long length = -1; private long length = -1;
private long lastPosition; private long lastPosition;
@@ -120,7 +124,8 @@ public class MprisPlugin extends Plugin {
@Override @Override
public boolean onPackageReceived(NetworkPackage np) { public boolean onPackageReceived(NetworkPackage np) {
if (np.has("nowPlaying") || np.has("volume") || np.has("isPlaying") || np.has("length") || np.has("pos")) { if (np.has("nowPlaying") || np.has("volume") || np.has("isPlaying") || np.has("length") ||
np.has("pos") || np.has("artImage")) {
if (np.getString("player").equals(player)) { if (np.getString("player").equals(player)) {
currentSong = np.getString("nowPlaying", currentSong); currentSong = np.getString("nowPlaying", currentSong);
volume = np.getInt("volume", volume); volume = np.getInt("volume", volume);
@@ -129,6 +134,11 @@ public class MprisPlugin extends Plugin {
lastPosition = np.getLong("pos", lastPosition); lastPosition = np.getLong("pos", lastPosition);
lastPositionTime = System.currentTimeMillis(); lastPositionTime = System.currentTimeMillis();
} }
if (np.has("artImage")) {
String base64Image = np.getString("artImage");
byte[] decodedBytes = Base64.decode(base64Image, 0);
currentArt = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
}
playing = np.getBoolean("isPlaying", playing); playing = np.getBoolean("isPlaying", playing);
for (String key : playerStatusUpdated.keySet()) { for (String key : playerStatusUpdated.keySet()) {
try { try {
@@ -142,9 +152,8 @@ public class MprisPlugin extends Plugin {
} }
} }
if (np.has("playerList")) { ArrayList<String> newPlayerList = np.getStringList("playerList");
if (newPlayerList != null) {
ArrayList<String> newPlayerList = np.getStringList("playerList");
boolean equals = false; boolean equals = false;
if (newPlayerList.size() == playerList.size()) { if (newPlayerList.size() == playerList.size()) {
equals = true; equals = true;
@@ -208,6 +217,7 @@ public class MprisPlugin extends Plugin {
if (player == null || player.equals(this.player)) return; if (player == null || player.equals(this.player)) return;
this.player = player; this.player = player;
currentSong = ""; currentSong = "";
currentArt = null;
volume = 50; volume = 50;
playing = false; playing = false;
for (String key : playerStatusUpdated.keySet()) { for (String key : playerStatusUpdated.keySet()) {
@@ -230,6 +240,8 @@ public class MprisPlugin extends Plugin {
return currentSong; return currentSong;
} }
public Bitmap getCurrentArt() { return currentArt; }
public String getPlayer() { public String getPlayer() {
return player; return player;
} }

View File

@@ -23,8 +23,8 @@ package org.kde.kdeconnect.Plugins.NotificationsPlugin;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View; import android.view.View;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;

View File

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

View File

@@ -31,8 +31,8 @@ import android.support.v4.app.TaskStackBuilder;
import android.util.Log; import android.util.Log;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;

View File

@@ -26,25 +26,23 @@ import android.util.Log;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin; import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardPlugin;
import org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhonePlugin; import org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhonePlugin;
import org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadPlugin; import org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadPlugin;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardPlugin;
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin; import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin;
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin; import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin;
import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin; import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin;
import org.kde.kdeconnect.Plugins.ReceiveNotificationsPlugin.ReceiveNotificationsPlugin;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
import org.kde.kdeconnect.Plugins.SharePlugin.SharePlugin; import org.kde.kdeconnect.Plugins.SharePlugin.SharePlugin;
import org.kde.kdeconnect.Plugins.TelepathyPlugin.TelepathyPlugin;
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin; import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import java.lang.reflect.Method;
public class PluginFactory { public class PluginFactory {
@@ -120,6 +118,7 @@ public class PluginFactory {
PluginFactory.registerPlugin(BatteryPlugin.class); PluginFactory.registerPlugin(BatteryPlugin.class);
PluginFactory.registerPlugin(SftpPlugin.class); PluginFactory.registerPlugin(SftpPlugin.class);
PluginFactory.registerPlugin(NotificationsPlugin.class); PluginFactory.registerPlugin(NotificationsPlugin.class);
PluginFactory.registerPlugin(ReceiveNotificationsPlugin.class);
PluginFactory.registerPlugin(MousePadPlugin.class); PluginFactory.registerPlugin(MousePadPlugin.class);
PluginFactory.registerPlugin(SharePlugin.class); PluginFactory.registerPlugin(SharePlugin.class);
//PluginFactory.registerPlugin(TelepathyPlugin.class); //PluginFactory.registerPlugin(TelepathyPlugin.class);

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2014 Albert Vaca Cintora <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.Plugins.ReceiveNotificationsPlugin;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect_tp.R;
import java.io.InputStream;
public class ReceiveNotificationsPlugin extends Plugin {
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
public final static String PACKAGE_TYPE_NOTIFICATION_REQUEST = "kdeconnect.notification.request";
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_receive_notifications);
}
@Override
public String getDescription() {
return context.getResources().getString(R.string.pref_plugin_receive_notifications_desc);
}
@Override
public boolean isEnabledByDefault() {
return false;
}
@Override
public boolean onCreate() {
// request all existing notifications
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION_REQUEST);
np.set("request", true);
device.sendPackage(np);
return true;
}
@Override
public boolean onPackageReceived(final NetworkPackage np) {
if (!np.has("ticker") || !np.has("appName") || !np.has("id")) {
Log.e("NotificationsPlugin", "Received notification package lacks properties");
} else {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MaterialActivity.class);
stackBuilder.addNextIntent(new Intent(context, MaterialActivity.class));
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
Bitmap largeIcon = null;
if (np.hasPayload()) {
int width = 64; // default icon dimensions
int height = 64;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
width = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_width);
height = context.getResources().getDimensionPixelSize(android.R.dimen.notification_large_icon_height);
}
final InputStream input = np.getPayload();
largeIcon = BitmapFactory.decodeStream(np.getPayload());
try { input.close(); } catch (Exception e) { }
if (largeIcon != null) {
//Log.i("NotificationsPlugin", "hasPayload: size=" + largeIcon.getWidth() + "/" + largeIcon.getHeight() + " opti=" + width + "/" + height);
if (largeIcon.getWidth() > width || largeIcon.getHeight() > height) {
// older API levels don't scale notification icons automatically, therefore:
largeIcon = Bitmap.createScaledBitmap(largeIcon, width, height, false);
}
}
}
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(np.getString("appName"))
.setContentText(np.getString("ticker"))
.setContentIntent(resultPendingIntent)
.setTicker(np.getString("ticker"))
.setSmallIcon(R.drawable.ic_notification)
.setLargeIcon(largeIcon)
.setAutoCancel(true)
.setLocalOnly(true) // to avoid bouncing the notification back to other kdeconnect nodes
.setDefaults(Notification.DEFAULT_ALL)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
try {
// tag all incoming notifications
notificationManager.notify("kdeconnectId:" + np.getString("id", "0"), np.getInt("id", 0), noti);
} catch (Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
return true;
}
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION_REQUEST};
}
}

View File

@@ -24,7 +24,6 @@ import android.content.Context;
import android.util.Log; import android.util.Log;
import org.apache.sshd.SshServer; import org.apache.sshd.SshServer;
import org.apache.sshd.common.KeyExchange;
import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.Session; import org.apache.sshd.common.Session;
import org.apache.sshd.common.util.SecurityUtils; import org.apache.sshd.common.util.SecurityUtils;
@@ -35,10 +34,10 @@ import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator; import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.SshFile; import org.apache.sshd.server.SshFile;
import org.apache.sshd.server.command.ScpCommandFactory; import org.apache.sshd.server.command.ScpCommandFactory;
import org.apache.sshd.server.kex.DHG1;
import org.apache.sshd.server.kex.DHG14;
import org.apache.sshd.server.filesystem.NativeFileSystemView; import org.apache.sshd.server.filesystem.NativeFileSystemView;
import org.apache.sshd.server.filesystem.NativeSshFile; import org.apache.sshd.server.filesystem.NativeSshFile;
import org.apache.sshd.server.kex.DHG1;
import org.apache.sshd.server.kex.DHG14;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession; import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.sftp.SftpSubsystem; import org.apache.sshd.server.sftp.SftpSubsystem;
@@ -59,43 +58,6 @@ import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
class SimplePasswordAuthenticator implements PasswordAuthenticator {
public void setUser(String user) {this.user = user;}
public String getUser() {return this.user;}
public void setPassword(String password) {this.password = password;}
public String getPassword() {return this.password;}
@Override
public boolean authenticate(String user, String password, ServerSession session) {
return user.equals(this.user) && password.equals(this.password);
}
private String user;
private String password;
}
class SimplePublicKeyAuthenticator implements PublickeyAuthenticator {
private final List<PublicKey> keys = new ArrayList<>();
public void addKey(PublicKey key) {
keys.add(key);
}
@Override
public boolean authenticate(String user, PublicKey key, ServerSession session) {
for (PublicKey k : keys) {
if (key.equals(k)) {
return true;
}
}
return false;
}
}
class SimpleSftpServer { class SimpleSftpServer {
private static final int STARTPORT = 1739; private static final int STARTPORT = 1739;
private static final int ENDPORT = 1764; private static final int ENDPORT = 1764;
@@ -109,7 +71,7 @@ class SimpleSftpServer {
public final SimplePublicKeyAuthenticator keyAuth = new SimplePublicKeyAuthenticator(); public final SimplePublicKeyAuthenticator keyAuth = new SimplePublicKeyAuthenticator();
static { static {
Security.insertProviderAt( SslHelper.BC, 1); Security.insertProviderAt(SslHelper.BC, 1);
SecurityUtils.setRegisterBouncyCastle(false); SecurityUtils.setRegisterBouncyCastle(false);
} }
private final SshServer sshd = SshServer.setUpDefaultServer(); private final SshServer sshd = SshServer.setUpDefaultServer();
@@ -129,9 +91,8 @@ class SimpleSftpServer {
if (device.publicKey != null) { if (device.publicKey != null) {
keyAuth.addKey(device.publicKey); keyAuth.addKey(device.publicKey);
sshd.setPublickeyAuthenticator(keyAuth); sshd.setPublickeyAuthenticator(keyAuth);
} else {
sshd.setPasswordAuthenticator(passwordAuth);
} }
sshd.setPasswordAuthenticator(passwordAuth);
} }
public boolean start() { public boolean start() {
@@ -192,20 +153,18 @@ class SimpleSftpServer {
return ip6; return ip6;
} }
} static class SecureFileSystemFactory implements FileSystemFactory {
class SecureFileSystemFactory implements FileSystemFactory {
public SecureFileSystemFactory() {} public SecureFileSystemFactory() {}
@Override @Override
public FileSystemView createFileSystemView(final Session username) { public FileSystemView createFileSystemView(final Session username) {
final String base = "/"; final String base = "/";
return new SecureFileSystemView(base, username.getUsername()); return new SecureFileSystemView(base, username.getUsername());
} }
} }
class SecureFileSystemView extends NativeFileSystemView { static class SecureFileSystemView extends NativeFileSystemView {
// the first and the last character will always be '/' // the first and the last character will always be '/'
// It is always with respect to the root directory. // It is always with respect to the root directory.
private String currDir = "/"; private String currDir = "/";
@@ -241,8 +200,47 @@ class SimpleSftpServer {
} }
} }
class SecureSshFile extends NativeSshFile { static class SecureSshFile extends NativeSshFile {
public SecureSshFile(final String fileName, final File file, final String userName) { public SecureSshFile(final String fileName, final File file, final String userName) {
super(fileName, file, userName); super(fileName, file, userName);
} }
} }
static 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;
}
static class SimplePublicKeyAuthenticator implements PublickeyAuthenticator {
private final List<PublicKey> keys = new ArrayList<>();
public void addKey(PublicKey key) {
keys.add(key);
}
@Override
public boolean authenticate(String user, PublicKey key, ServerSession session) {
for (PublicKey k : keys) {
if (key.equals(k)) {
return true;
}
}
return false;
}
}
}

View File

@@ -21,45 +21,18 @@
package org.kde.kdeconnect.Plugins.SharePlugin; package org.kde.kdeconnect.Plugins.SharePlugin;
import android.app.Activity; import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarActivity;
import android.util.Log; import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast; import android.widget.Toast;
import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.List.EntryItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.SectionItem;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
public class SendFileActivity extends ActionBarActivity { public class SendFileActivity extends ActionBarActivity {

View File

@@ -170,7 +170,7 @@ public class ShareActivity extends ActionBarActivity {
} catch (Exception e) { } catch (Exception e) {
isUrl = false; isUrl = false;
} }
NetworkPackage np = new NetworkPackage(SharePlugin.PACKAGE_TYPE_SHARE); NetworkPackage np = new NetworkPackage(SharePlugin.PACKAGE_TYPE_SHARE_REQUEST);
if (isUrl) { if (isUrl) {
np.set("url", text); np.set("url", text);
} else { } else {

View File

@@ -58,7 +58,7 @@ import java.util.ArrayList;
public class SharePlugin extends Plugin { public class SharePlugin extends Plugin {
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share"; //public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
public final static String PACKAGE_TYPE_SHARE_REQUEST = "kdeconnect.share.request"; public final static String PACKAGE_TYPE_SHARE_REQUEST = "kdeconnect.share.request";
final static boolean openUrlsDirectly = true; final static boolean openUrlsDirectly = true;

View File

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

View File

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

View File

@@ -22,7 +22,6 @@ package org.kde.kdeconnect.UserInterface;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@@ -42,17 +41,16 @@ import android.widget.TextView;
import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.UserInterface.List.PluginItem;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.List.CustomItem; import org.kde.kdeconnect.UserInterface.List.CustomItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter; import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.PluginItem;
import org.kde.kdeconnect.UserInterface.List.SmallEntryItem; import org.kde.kdeconnect.UserInterface.List.SmallEntryItem;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@@ -259,8 +257,8 @@ public class DeviceFragment extends Fragment {
if (device.certificate == null) { if (device.certificate == null) {
builder.setMessage(R.string.encryption_info_msg_no_ssl); builder.setMessage(R.string.encryption_info_msg_no_ssl);
} else { } else {
builder.setMessage(context.getResources().getString(R.string.my_device_fingerprint) + "\n " + SslHelper.getCertificateHash(SslHelper.certificate) + "\n\n" builder.setMessage(context.getResources().getString(R.string.my_device_fingerprint) + "\n" + SslHelper.getCertificateHash(SslHelper.certificate) + "\n\n"
+ context.getResources().getString(R.string.remote_device_fingerprint) + "\n " + SslHelper.getCertificateHash(device.certificate)); + context.getResources().getString(R.string.remote_device_fingerprint) + "\n" + SslHelper.getCertificateHash(device.certificate));
} }
builder.create().show(); builder.create().show();
return true; return true;

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -37,8 +36,8 @@ import android.widget.TextView;
import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter; import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem;
import org.kde.kdeconnect.UserInterface.List.SectionItem; import org.kde.kdeconnect.UserInterface.List.SectionItem;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;

View File

@@ -8,7 +8,6 @@ import android.widget.TextView;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.SettingsActivity;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
public class PluginPreference extends CheckBoxPreference { public class PluginPreference extends CheckBoxPreference {

View File

@@ -22,7 +22,6 @@ package org.kde.kdeconnect.UserInterface;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.MenuItem; import android.view.MenuItem;
import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.BackgroundService;

View File

@@ -26,17 +26,19 @@ import android.test.AndroidTestCase;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Backends.LanBackend.LanLink; import org.kde.kdeconnect.Backends.LanBackend.LanLink;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Backends.LanBackend.LanPairingHandler;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.bouncycastle.asn1.x500.X500NameBuilder; import org.spongycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle; import org.spongycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509v3CertificateBuilder; import org.spongycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner; import org.spongycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigInteger; import java.math.BigInteger;
@@ -133,6 +135,7 @@ public class DeviceTest extends AndroidTestCase {
Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider");
LanLink link = Mockito.mock(LanLink.class); LanLink link = Mockito.mock(LanLink.class);
Mockito.when(link.getLinkProvider()).thenReturn(linkProvider); Mockito.when(link.getLinkProvider()).thenReturn(linkProvider);
Mockito.when(link.getPairingHandler(Mockito.any(Device.class), Mockito.any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class));
Device device = new Device(getContext(), fakeNetworkPackage, link); Device device = new Device(getContext(), fakeNetworkPackage, link);
KeyPair keyPair; KeyPair keyPair;
@@ -213,7 +216,7 @@ public class DeviceTest extends AndroidTestCase {
} catch(Exception e) { } catch(Exception e) {
e.printStackTrace(); e.printStackTrace();
Log.e("KDE/initialiseCertificate", "Exception"); Log.e("KDE/initialiseCert", "Exception");
} }
NetworkPackage fakeNetworkPackage = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY); NetworkPackage fakeNetworkPackage = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
@@ -226,6 +229,7 @@ public class DeviceTest extends AndroidTestCase {
LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class);
Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider");
LanLink link = Mockito.mock(LanLink.class); LanLink link = Mockito.mock(LanLink.class);
Mockito.when(link.getPairingHandler(Mockito.any(Device.class), Mockito.any(BasePairingHandler.PairingHandlerCallback.class))).thenReturn(Mockito.mock(LanPairingHandler.class));
Mockito.when(link.getLinkProvider()).thenReturn(linkProvider); Mockito.when(link.getLinkProvider()).thenReturn(linkProvider);
Device device = new Device(getContext(), fakeNetworkPackage, link); Device device = new Device(getContext(), fakeNetworkPackage, link);
device.publicKey = keyPair.getPublic(); device.publicKey = keyPair.getPublic();

View File

@@ -20,27 +20,19 @@
package org.kde.kdeconnect; package org.kde.kdeconnect;
import android.support.v4.util.LongSparseArray;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.kde.kdeconnect.Backends.LanBackend.LanLink; import org.kde.kdeconnect.Backends.LanBackend.LanLink;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.mockito.Mockito; import org.mockito.Mockito;
import java.io.InputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.InetSocketAddress; import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.util.HashMap; import java.util.HashMap;
public class LanLinkProviderTest extends AndroidTestCase { public class LanLinkProviderTest extends AndroidTestCase {
private NioSocketAcceptor tcpAcceptor = null;
private NioDatagramAcceptor udpAcceptor = null;
private LanLinkProvider linkProvider; private LanLinkProvider linkProvider;
@Override @Override
@@ -50,178 +42,35 @@ public class LanLinkProviderTest extends AndroidTestCase {
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath()); System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath());
linkProvider = new LanLinkProvider(getContext()); linkProvider = new LanLinkProvider(getContext());
try {
Field field = LanLinkProvider.class.getDeclaredField("tcpAcceptor");
field.setAccessible(true);
tcpAcceptor = (NioSocketAcceptor)field.get(linkProvider);
assertNotNull(tcpAcceptor);
}catch (Exception e){
fail("Error getting tcpAcceptor from LanLinkProvider");
}
try{
Field field = LanLinkProvider.class.getDeclaredField("udpAcceptor");
field.setAccessible(true);
udpAcceptor = (NioDatagramAcceptor)field.get(linkProvider);
assertNotNull(udpAcceptor);
}catch (Exception e){
fail("Error getting udp acceptor from LanLinkProvider");
}
} }
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
super.tearDown(); super.tearDown();
tcpAcceptor.dispose();
udpAcceptor.dispose();
} }
public void testTcpAcceptor(){ public void testIdentityPackageReceived() throws Exception{
assertNotNull(tcpAcceptor.getHandler());
assertEquals(tcpAcceptor.getSessionConfig().isKeepAlive(), true);
assertEquals(tcpAcceptor.getSessionConfig().isReuseAddress(), true);
assertNotNull(tcpAcceptor.getFilterChain().get("codec"));
}
public void testUdpAcceptor(){
assertNull(udpAcceptor.getHandler());
assertEquals(udpAcceptor.getSessionConfig().isReuseAddress(), true);
assertNotNull(udpAcceptor.getFilterChain().get("codec"));
}
public void testOnStart() throws Exception{
IoSession session = Mockito.mock(IoSession.class);
Mockito.when(session.getId()).thenReturn(12345l);
Mockito.when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(5000));
linkProvider.onStart();
assertNotNull(udpAcceptor.getHandler());
assertEquals(udpAcceptor.getLocalAddress().getPort(), 1714);
}
public void testUdpPackageReceived() throws Exception {
final int port = 5000;
NetworkPackage networkPackage = Mockito.mock(NetworkPackage.class); NetworkPackage networkPackage = Mockito.mock(NetworkPackage.class);
Mockito.when(networkPackage.getType()).thenReturn("kdeconnect.identity"); Mockito.when(networkPackage.getType()).thenReturn("kdeconnect.identity");
Mockito.when(networkPackage.getString("deviceId")).thenReturn("testDevice"); Mockito.when(networkPackage.getString("deviceId")).thenReturn("testDevice");
Mockito.when(networkPackage.getString("deviceName")).thenReturn("Test Device"); Mockito.when(networkPackage.getString("deviceName")).thenReturn("Test Device");
Mockito.when(networkPackage.getInt("protocolVersion")).thenReturn(NetworkPackage.ProtocolVersion); Mockito.when(networkPackage.getInt("protocolVersion")).thenReturn(5);
Mockito.when(networkPackage.getString("deviceType")).thenReturn("phone");
Mockito.when(networkPackage.getInt("tcpPort")).thenReturn(port);
final String serialized = "{\"type\":\"kdeconnect.identity\",\"id\":12345,\"body\":{\"deviceName\":\"Test Device\",\"deviceType\":\"phone\",\"deviceId\":\"testDevice\",\"protocolVersion\":5,\"tcpPort\": "+ port +"}}";
Mockito.when(networkPackage.serialize()).thenReturn(serialized);
// Mocking udp session
IoSession session = Mockito.mock(IoSession.class);
Mockito.when(session.getId()).thenReturn(12345l);
Mockito.when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(port));
// Making a server socket, so that original LanLinkProvider can connect to it when it receives our fake package
final ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
final Thread thread = new Thread (new Runnable() {
@Override
public void run() {
try {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
while (true) {
if (inputStream.available() != 0) {
// Data received from socket should be an identity package
byte[] inputData = new byte[inputStream.available()];
inputStream.read(inputData);
NetworkPackage receivedPackage = NetworkPackage.unserialize(new String(inputData));
NetworkPackage identityPackage = NetworkPackage.createIdentityPackage(getContext());
// If any assertion fails, its output will be in logcat, not on test case thread anymore
assertEquals(receivedPackage.getType(), identityPackage.getType());
assertEquals(receivedPackage.getString("deviceName"), identityPackage.getString("deviceName"));
assertEquals(receivedPackage.getString("deviceId"), identityPackage.getString("deviceId"));
assertEquals(receivedPackage.getInt("protocolVersion"), identityPackage.getInt("protocolVersion"));
serverSocket.close();
// Socket not closed to ensure visibleComputers contains entry for testDevice
break;
}
}
}catch (Exception e){
assertEquals("Exception in thread",1,5);
}
}
});
try {
thread.start();
linkProvider.onStart();
udpAcceptor.getHandler().messageReceived(session, networkPackage.serialize());
}catch (Exception e){
throw e;
}
// Wait 1 secs for our server, and then end test
thread.join(1 * 1000);
// visibleComputers should contain an entry for testDevice
HashMap<String, LanLink> visibleComputers;
try {
Field field = LanLinkProvider.class.getDeclaredField("visibleComputers");
field.setAccessible(true);
visibleComputers = (HashMap<String, LanLink>)field.get(linkProvider);
}catch (Exception e){
throw e;
}
assertNotNull(visibleComputers.get("testDevice"));
}
public void testTcpIdentityPackageReceived() throws Exception{
IoSession session = Mockito.mock(IoSession.class);
Mockito.when(session.getId()).thenReturn(12345l);
NetworkPackage networkPackage = Mockito.mock(NetworkPackage.class);
Mockito.when(networkPackage.getType()).thenReturn("kdeconnect.identity");
Mockito.when(networkPackage.getString("deviceId")).thenReturn("testDevice");
Mockito.when(networkPackage.getString("deviceName")).thenReturn("Test Device");
Mockito.when(networkPackage.getInt("protocolVersion")).thenReturn(NetworkPackage.ProtocolVersion);
Mockito.when(networkPackage.getString("deviceType")).thenReturn("phone"); Mockito.when(networkPackage.getString("deviceType")).thenReturn("phone");
String serialized = "{\"type\":\"kdeconnect.identity\",\"id\":12345,\"body\":{\"deviceName\":\"Test Device\",\"deviceType\":\"phone\",\"deviceId\":\"testDevice\",\"protocolVersion\":5}}"; String serialized = "{\"type\":\"kdeconnect.identity\",\"id\":12345,\"body\":{\"deviceName\":\"Test Device\",\"deviceType\":\"phone\",\"deviceId\":\"testDevice\",\"protocolVersion\":5}}";
Mockito.when(networkPackage.serialize()).thenReturn(serialized); Mockito.when(networkPackage.serialize()).thenReturn(serialized);
Socket channel = Mockito.mock(Socket.class);
try { try {
tcpAcceptor.getHandler().messageReceived(session, networkPackage.serialize()); Method method = LanLinkProvider.class.getDeclaredMethod("identityPackageReceived", NetworkPackage.class, Socket.class, LanLink.ConnectionStarted.class);
method.setAccessible(true);
method.invoke(linkProvider, networkPackage, channel, LanLink.ConnectionStarted.Locally);
}catch (Exception e){ }catch (Exception e){
throw e; throw e;
} }
LongSparseArray<LanLink> nioSessions;
try {
Field field = LanLinkProvider.class.getDeclaredField("nioSessions");
field.setAccessible(true);
nioSessions = (LongSparseArray<LanLink>)field.get(linkProvider);
}catch (Exception e){
throw e;
}
assertNotNull(nioSessions.get(12345l));
HashMap<String, LanLink> visibleComputers; HashMap<String, LanLink> visibleComputers;
try { try {
Field field = LanLinkProvider.class.getDeclaredField("visibleComputers"); Field field = LanLinkProvider.class.getDeclaredField("visibleComputers");
@@ -232,14 +81,5 @@ public class LanLinkProviderTest extends AndroidTestCase {
} }
assertNotNull(visibleComputers.get("testDevice")); assertNotNull(visibleComputers.get("testDevice"));
// Testing session closed
try {
tcpAcceptor.getHandler().sessionClosed(session);
}catch (Exception e){
throw e;
}
assertNull(nioSessions.get(12345l));
assertNull(visibleComputers.get("testDevice"));
} }
} }

View File

@@ -23,8 +23,8 @@ package org.kde.kdeconnect;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.util.Log; import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.LanBackend.LanLink; import org.kde.kdeconnect.Backends.LanBackend.LanLink;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.mockito.Mockito; import org.mockito.Mockito;
@@ -35,19 +35,19 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
public class LanLinkTest extends AndroidTestCase { public class LanLinkTest extends AndroidTestCase {
LanLink lanLink; LanLink badLanLink;
Channel channel; LanLink goodLanLink;
Device.SendPackageStatusCallback callback;
ChannelFuture channelFutureSuccess, channelFutureFailure; OutputStream badOutputStream;
OutputStream goodOutputStream;
Device.SendPackageStatusCallback callback;
@Override @Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
@@ -58,35 +58,23 @@ public class LanLinkTest extends AndroidTestCase {
LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class);
Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider");
channel = Mockito.mock(Channel.class);
// Mockito.when(channel.hashCode()).thenReturn(12345);
Mockito.when(channel.remoteAddress()).thenReturn(new InetSocketAddress(5000));
// Mockito.doReturn(new InetSocketAddress(5000)).when(channel).remoteAddress();
callback = Mockito.mock(Device.SendPackageStatusCallback.class); callback = Mockito.mock(Device.SendPackageStatusCallback.class);
Mockito.doNothing().when(callback).sendSuccess();
Mockito.doNothing().when(callback).sendProgress(Mockito.any(Integer.class));
Mockito.doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
throw (Throwable) invocationOnMock.getArguments()[0];
}
}).when(callback).sendFailure(Mockito.any(Throwable.class));
channelFutureSuccess = Mockito.mock(ChannelFuture.class); goodOutputStream = Mockito.mock(OutputStream.class);
Mockito.when(channelFutureSuccess.isDone()).thenReturn(true); badOutputStream = Mockito.mock(OutputStream.class);
Mockito.when(channelFutureSuccess.isSuccess()).thenReturn(true); Mockito.doThrow(new IOException("AAA")).when(badOutputStream).write(Mockito.any(byte[].class));
Mockito.when(channelFutureSuccess.channel()).thenReturn(channel);
Mockito.when(channelFutureSuccess.sync()).thenReturn(channelFutureSuccess);
channelFutureFailure = Mockito.mock(ChannelFuture.class);
Mockito.when(channelFutureFailure.isDone()).thenReturn(true);
Mockito.when(channelFutureFailure.isSuccess()).thenReturn(false);
Mockito.when(channelFutureFailure.cause()).thenReturn(new IOException("Cannot send package"));
Mockito.when(channelFutureFailure.channel()).thenReturn(channel);
Mockito.when(channelFutureFailure.sync()).thenReturn(channelFutureFailure);
lanLink = new LanLink(getContext(), channel, "testDevice", linkProvider, BaseLink.ConnectionStarted.Remotely); Socket socketMock = Mockito.mock(Socket.class);
Mockito.when(socketMock.getRemoteSocketAddress()).thenReturn(new InetSocketAddress(5000));
Mockito.when(socketMock.getOutputStream()).thenReturn(goodOutputStream);
Socket socketBadMock = Mockito.mock(Socket.class);
Mockito.when(socketBadMock.getRemoteSocketAddress()).thenReturn(new InetSocketAddress(5000));
Mockito.when(socketBadMock.getOutputStream()).thenReturn(badOutputStream);
goodLanLink = new LanLink(getContext(), "testDevice", linkProvider, socketMock, LanLink.ConnectionStarted.Remotely);
badLanLink = new LanLink(getContext(), "testDevice", linkProvider, socketBadMock, LanLink.ConnectionStarted.Remotely);
} }
@Override @Override
@@ -94,7 +82,7 @@ public class LanLinkTest extends AndroidTestCase {
super.tearDown(); super.tearDown();
} }
public void testSendPackageSuccess(){ public void testSendPackageSuccess() throws JSONException {
NetworkPackage testPackage = Mockito.mock(NetworkPackage.class); NetworkPackage testPackage = Mockito.mock(NetworkPackage.class);
Mockito.when(testPackage.getType()).thenReturn("kdeconnect.test"); Mockito.when(testPackage.getType()).thenReturn("kdeconnect.test");
@@ -102,18 +90,12 @@ public class LanLinkTest extends AndroidTestCase {
Mockito.when(testPackage.getString("testName")).thenReturn("testSendPackageSuccess"); Mockito.when(testPackage.getString("testName")).thenReturn("testSendPackageSuccess");
Mockito.when(testPackage.serialize()).thenReturn("{\"id\":123,\"type\":\"kdeconnect.test\",\"body\":{\"isTesting\":true,\"testName\":\"testSendPackageSuccess\"}}"); Mockito.when(testPackage.serialize()).thenReturn("{\"id\":123,\"type\":\"kdeconnect.test\",\"body\":{\"isTesting\":true,\"testName\":\"testSendPackageSuccess\"}}");
try { goodLanLink.sendPackage(testPackage, callback);
Mockito.when(channel.writeAndFlush(testPackage.serialize())).thenReturn(channelFutureSuccess);
lanLink.sendPackage(testPackage, callback);
} catch (Exception e) {
e.printStackTrace();
}
assertEquals(channelFutureSuccess.isDone(), true); Mockito.verify(callback).sendSuccess();
assertEquals(channelFutureSuccess.isSuccess(), true);
} }
public void testSendPackageFail(){ public void testSendPackageFail() throws JSONException {
NetworkPackage testPackage = Mockito.mock(NetworkPackage.class); NetworkPackage testPackage = Mockito.mock(NetworkPackage.class);
Mockito.when(testPackage.getType()).thenReturn("kdeconnect.test"); Mockito.when(testPackage.getType()).thenReturn("kdeconnect.test");
@@ -121,16 +103,10 @@ public class LanLinkTest extends AndroidTestCase {
Mockito.when(testPackage.getString("testName")).thenReturn("testSendPackageFail"); Mockito.when(testPackage.getString("testName")).thenReturn("testSendPackageFail");
Mockito.when(testPackage.serialize()).thenReturn("{\"id\":123,\"type\":\"kdeconnect.test\",\"body\":{\"isTesting\":true,\"testName\":\"testSendPackageFail\"}}"); Mockito.when(testPackage.serialize()).thenReturn("{\"id\":123,\"type\":\"kdeconnect.test\",\"body\":{\"isTesting\":true,\"testName\":\"testSendPackageFail\"}}");
try { badLanLink.sendPackage(testPackage, callback);
Mockito.when(channel.writeAndFlush(testPackage.serialize())).thenReturn(channelFutureFailure);
lanLink.sendPackage(testPackage, callback); Mockito.verify(callback).sendFailure(Mockito.any(RuntimeException.class));
} catch (Exception e) {
e.printStackTrace();
}
assertEquals(channelFutureFailure.isDone(), true);
assertEquals(channelFutureFailure.isSuccess(), false);
assertEquals(channelFutureFailure.cause() instanceof IOException, true );
} }
@@ -157,7 +133,7 @@ public class LanLinkTest extends AndroidTestCase {
try { try {
socket = new Socket(); socket = new Socket();
int tcpPort = np.getPayloadTransferInfo().getInt("port"); int tcpPort = np.getPayloadTransferInfo().getInt("port");
InetSocketAddress address = (InetSocketAddress)channel.remoteAddress(); InetSocketAddress address = new InetSocketAddress(5000);
socket.connect(new InetSocketAddress(address.getAddress(), tcpPort)); socket.connect(new InetSocketAddress(address.getAddress(), tcpPort));
np.setPayload(socket.getInputStream(), np.getPayloadSize()); np.setPayload(socket.getInputStream(), np.getPayloadSize());
} catch (Exception e) { } catch (Exception e) {
@@ -252,17 +228,18 @@ public class LanLinkTest extends AndroidTestCase {
@Override @Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable { public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
String stringNetworkPackage = (String)invocationOnMock.getArguments()[0]; Log.e("LanLinkTest","Write to stream");
String stringNetworkPackage = new String((byte[])invocationOnMock.getArguments()[0]);
final NetworkPackage np = NetworkPackage.unserialize(stringNetworkPackage); final NetworkPackage np = NetworkPackage.unserialize(stringNetworkPackage);
downloader.setNetworkPackage(np); downloader.setNetworkPackage(np);
downloader.start(); downloader.start();
return channelFutureSuccess; return stringNetworkPackage.length();
} }
}).when(channel).writeAndFlush(Mockito.anyString()); }).when(goodOutputStream).write(Mockito.any(byte[].class));
lanLink.sendPackage(sharePackage, callback); goodLanLink.sendPackage(sharePackage, callback);
try { try {
// Wait 1 secs for downloader to finish (if some error, it will continue and assert will fail) // Wait 1 secs for downloader to finish (if some error, it will continue and assert will fail)
@@ -273,5 +250,7 @@ public class LanLinkTest extends AndroidTestCase {
} }
assertEquals(new String(data), new String(downloader.getOutputStream().toByteArray())); assertEquals(new String(data), new String(downloader.getOutputStream().toByteArray()));
Mockito.verify(callback).sendSuccess();
} }
} }

View File

@@ -25,6 +25,7 @@ import android.util.Log;
import org.json.JSONException; import org.json.JSONException;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.skyscreamer.jsonassert.JSONAssert;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
@@ -124,15 +125,13 @@ public class NetworkPackageTest extends AndroidTestCase{
assertEquals(decrypted.getType(), copy.getType()); assertEquals(decrypted.getType(), copy.getType());
assertEquals(decrypted.getJSONArray("body"), copy.getJSONArray("body")); assertEquals(decrypted.getJSONArray("body"), copy.getJSONArray("body"));
String json = "{\"body\":{\"nowPlaying\":\"A really long song name - A really long artist name\",\"player\":\"A really long player name\",\"the_meaning_of_life_the_universe_and_everything\":\"42\"},\"id\":\"A really long package id\",\"payloadSize\":0,\"payloadTransferInfo\":{},\"type\":\"kdeconnect.a_really_really_long_package_type\"}\n"; String json = "{\"body\":{\"nowPlaying\":\"A really long song name - A really long artist name\",\"player\":\"A really long player name\",\"the_meaning_of_life_the_universe_and_everything\":\"42\"},\"id\":945945945,\"type\":\"kdeconnect.a_really_really_long_package_type\"}\n";
NetworkPackage longJsonNp = NetworkPackage.unserialize(json); NetworkPackage longJsonNp = NetworkPackage.unserialize(json);
try { try {
NetworkPackage encrypted = RsaHelper.encrypt(longJsonNp, publicKey); NetworkPackage encrypted = RsaHelper.encrypt(longJsonNp, publicKey);
decrypted = RsaHelper.decrypt(encrypted, privateKey); decrypted = RsaHelper.decrypt(encrypted, privateKey);
String decryptedJson = decrypted.serialize(); String decryptedJson = decrypted.serialize();
assertEquals(json, decryptedJson); JSONAssert.assertEquals(json, decryptedJson, true);
}catch (Exception e){ }catch (Exception e){
e.printStackTrace(); e.printStackTrace();
} }