2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-09-02 07:05:09 +00:00

Compare commits

..

5 Commits
v1.1 ... v0.9g

Author SHA1 Message Date
Albert Vaca
3804ede5fc Bumped version number to release 2016-01-17 11:03:16 -08:00
Albert Vaca
92006f744f Android doesn't support languages with an @, hack to fix build 2016-01-17 10:40:03 -08:00
Albert Astals Cid
2a7107aecf Disable colored output
Messes up scripty output that ends up in a file
2016-01-17 10:39:41 -08:00
l10n daemon script
a3cc399424 SVN_SILENT made messages (after extraction) 2016-01-17 11:58:50 +00:00
l10n daemon script
4dd8a5d86c SVN_SILENT made messages (after extraction) 2016-01-16 11:50:30 +00:00
127 changed files with 1516 additions and 4066 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.kde.kdeconnect_tp"
android:versionCode="1102"
android:versionName="1.1">
android:versionCode="910"
android:versionName="0.9g">
<uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="22" />
@@ -14,16 +14,21 @@
android:smallScreens="true"
android:xlargeScreens="true" />
<uses-feature android:name="android.hardware.telephony" android:required="false" />
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission
android:name="android.permission.READ_PHONE_STATE"
android:required="false" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" android:required="false" />
<uses-permission android:name="android.permission.RECEIVE_SMS" android:required="false" />
<!-- <uses-permission android:name="android.permission.SEND_SMS" android:required="false" /> -->
<uses-permission
android:name="android.permission.RECEIVE_SMS"
android:required="false" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -31,7 +36,6 @@
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:supportsRtl="true"
android:label="KDE Connect"
android:theme="@style/KdeConnectTheme"
>
@@ -115,12 +119,6 @@
-->
</receiver>
<activity
android:name="org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhoneActivity"
android:label="@string/findmyphone_title"
android:launchMode="singleInstance">
</activity>
<!-- Plugin-related activities and services -->
<activity
@@ -132,15 +130,6 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MaterialActivity" />
</activity>
<activity
android:name="org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity"
android:label="@string/remote_control"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MaterialActivity"
>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MaterialActivity" />
</activity>
<activity
android:name="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity"
android:configChanges="orientation|keyboardHidden|screenSize"

View File

@@ -22,4 +22,4 @@ You can install this app from the [Play Store](https://play.google.com/store/app
## License
[GNU GPL v2](https://www.gnu.org/licenses/gpl-2.0.html) and [GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.html)
If you are reading this from Github, you should know that this is just a mirror of the [KDE Project repo](https://projects.kde.org/projects/extragear/network/kdeconnect-android/repository/).
If you are reading this from Github, you should know that this is just a mirror of the [KDE Project repo](https://projects.kde.org/projects/playground/base/kdeconnect-android/repository/).

View File

@@ -3,29 +3,18 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.android.tools.build:gradle:1.3.0'
}
}
apply plugin: 'com.android.application'
android {
buildToolsVersion '23.0.3'
compileSdkVersion 23
compileSdkVersion 22
buildToolsVersion '22.0.1'
defaultConfig {
minSdkVersion 9
targetSdkVersion 22 //Bumping to 23 means we have to support the new permissions model
//multiDexEnabled true
//testInstrumentationRunner "com.android.test.runner.MultiDexTestRunner"
}
dexOptions {
javaMaxHeapSize "2g"
}
compileOptions {
// Use Java 1.7, requires minSdk 8
//SSHD requires mina when running on JDK < 7
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
targetSdkVersion 22
}
sourceSets {
main {
@@ -43,45 +32,36 @@ android {
pickFirst "META-INF/DEPENDENCIES"
pickFirst "META-INF/LICENSE"
pickFirst "META-INF/NOTICE"
pickFirst "META-INF/BCKEY.SF"
pickFirst "META-INF/BCKEY.DSA"
pickFirst "META-INF/INDEX.LIST"
pickFirst "META-INF/io.netty.versions.properties"
}
lintOptions {
abortOnError false
checkReleaseBuilds false
}
buildTypes {
debug {
minifyEnabled false
useProguard false
}
release { //keep on 'releae', set to 'all' when testing to make sure proguard is not deleting important stuff
release {
minifyEnabled true
useProguard true
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
}
}
dependencies {
repositories {
mavenCentral()
}
compile 'com.android.support:support-v4:22.2.1'
compile 'com.android.support:appcompat-v7:22.2.1'
compile 'com.android.support:design:22.2.1'
compile 'com.android.support:support-v4:23.4.0'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'org.apache.mina:mina-core:2.0.9'
compile 'org.apache.sshd:sshd-core:0.8.0'
compile 'org.bouncycastle:bcprov-jdk16:1.46'
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 'com.madgag.spongycastle:pkix:1.54.0.0' //For SSL certificate generation
// Testing
androidTestCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'// Because mockito has some problems with dex environment
androidTestCompile 'org.skyscreamer:jsonassert:1.3.0'
// Because mockito has some problems with dex environment
androidTestCompile 'com.google.dexmaker:dexmaker:1.1'
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.1'
//compile fileTree(dir: 'libs', include: '*.jar')
}

15
proguard-rules.pro vendored
View File

@@ -16,24 +16,15 @@
# public *;
#}
-dontobfuscate
# Allow obfuscation of android.support.v7.internal.view.menu.**
# to avoid problem on Samsung 4.2.2 devices with appcompat v21
# see https://code.google.com/p/android/issues/detail?id=78377
-keepnames class !android.support.v7.internal.view.menu.**,android.support.v7.** {*;}
-keep class !android.support.v7.internal.view.menu.**,** {*;}
-dontwarn org.spongycastle.**
-dontwarn org.apache.sshd.**
-dontwarn org.apache.mina.**
-dontwarn org.bouncycastle.**
-dontwarn org.slf4j.**
-dontwarn io.netty.**
-keepattributes SourceFile,LineNumberTable,Signature,*Annotation*
-keepattributes SourceFile,LineNumberTable
-keep class org.spongycastle.** {*;}
# SSHd requires mina, and mina uses reflection so some classes would get deleted
-keep class org.apache.mina.** {*;}
-keep class org.kde.kdeconnect.** {*;}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 959 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 B

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 667 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 B

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -26,6 +26,7 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/device_not_paired"
android:id="@+id/pair_message"
android:layout_gravity="left|center_vertical"
/>
<Button
@@ -73,14 +74,10 @@
android:id="@+id/unpair_message"
android:visibility="gone"
android:layout_width="match_parent"
android:drawableStart="@drawable/ic_error_outline_black_48dp"
android:drawableLeft="@drawable/ic_error_outline_black_48dp"
android:drawablePadding="8dip"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:text="@string/unreachable_description"
android:textAppearance="?android:attr/textAppearanceMedium"
/>
android:gravity="center" />
<ListView
android:id="@+id/buttons_list"

View File

@@ -1,26 +0,0 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhoneActivity">
<Button
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@string/findmyphone_found"
android:textSize="50dp"
android:id="@+id/bFindMyPhone"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
</RelativeLayout>

View File

@@ -12,27 +12,22 @@
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:elevation="8dp"
android:background="?attr/colorPrimary" />
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
</LinearLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_drawer"
android:background="@drawable/state_list_drawer_background"
app:itemBackground="@drawable/state_list_drawer_background"
app:itemTextColor="@color/state_list_drawer_text"
app:itemIconTint="@color/state_list_drawer_text"

View File

@@ -29,8 +29,8 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
android:textColor="#CC2222"
android:visibility="gone"
android:textColor="@android:color/darker_gray"
android:text="" />

View File

@@ -7,9 +7,9 @@
android:layout_height="match_parent"
android:id="@+id/mpris_control_view"
android:gravity="center"
android:paddingLeft="30dip"
android:paddingLeft="60dip"
android:paddingTop="5dip"
android:paddingRight="30dip"
android:paddingRight="60dip"
android:paddingBottom="5dip">
<TextView
@@ -20,11 +20,6 @@
android:id="@+id/no_players"
android:layout_gravity="center_horizontal" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/artImageView" />
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -37,9 +32,6 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="@+id/now_playing_textview"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
android:singleLine="true"
android:gravity="center"
android:padding="8dip"
@@ -156,6 +148,7 @@
android:layout_marginEnd="10dip"
android:id="@+id/imageView"
android:layout_weight="0"
android:layout_gravity="left|center_vertical"
android:contentDescription="@string/mpris_volume"
android:src="@drawable/ic_volume"
/>

View File

@@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="160dp"
android:background="@drawable/drawer_header"
android:orientation="vertical"
android:gravity="bottom">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="KDE Connect"
android:textColor="#FFF"
android:textStyle="bold"
android:layout_above="@+id/device_name"
android:id="@+id/kdeconnect_label"
android:paddingLeft="16dp"
android:paddingStart="16dp"
@@ -27,6 +29,9 @@
android:id="@+id/device_name"
android:layout_marginBottom="0dp"
android:textColor="#fff"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:paddingBottom="16dp"
android:paddingTop="4dp"
android:paddingRight="48dp"
@@ -50,4 +55,4 @@
/>
-->
</LinearLayout>
</RelativeLayout>

View File

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

View File

@@ -27,13 +27,6 @@
<item/>
<item>Nothing</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">الأجهزة المقترن بها</string>
<string name="category_not_paired_devices">الأجهزة المتوفّرة</string>
<string name="category_remembered_devices">الأجهزة المتذكَّرة</string>

View File

@@ -4,20 +4,19 @@
<string name="pref_plugin_telephony_desc">Unvia avisos pa SMS y llamaes</string>
<string name="pref_plugin_battery">Informe de batería</string>
<string name="pref_plugin_battery_desc">Infoma davezu del estáu de la batería</string>
<string name="pref_plugin_sftp">Esposición del sistema de ficheros</string>
<string name="pref_plugin_sftp_desc">Permite restolar remotamente\'l sistema de ficheros del teléfonu</string>
<string name="pref_plugin_clipboard">Sincronización del cartafueyu</string>
<string name="pref_plugin_clipboard_desc">Comparte\'l conteníu del cartafueyu</string>
<string name="pref_plugin_mousepad">Entrada remota</string>
<string name="pref_plugin_mousepad_desc">Usa\'l to teléfonu como mur y tecláu</string>
<string name="pref_plugin_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_runcommand">Executar comandu</string>
<string name="pref_plugin_mpris_desc">Controla audiu/videu dende\'l to teléfonu</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_desc">Unvia y recibe pings</string>
<string name="pref_plugin_ping_desc">Unvia y recibi pings</string>
<string name="pref_plugin_notifications">Sincronización d\'avisos</string>
<string name="pref_plugin_notifications_desc">Accede a los tos avisos dende otros preseos</string>
<string name="pref_plugin_sharereceiver">Compartir y recibir</string>
<string name="pref_plugin_sharereceiver">Unvia y recibi pings</string>
<string name="pref_plugin_sharereceiver_desc">Comparte ficheros y URLs ente preseos</string>
<string name="plugin_not_available">Esta carauterística nun ta disponible na to versión d\'Android</string>
<string name="device_list_empty">Ensin preseos</string>
@@ -28,34 +27,16 @@
<string name="send_ping">Unviar ping</string>
<string name="open_mpris_controls">Controles multimedia</string>
<string name="open_mousepad">Entrada remota</string>
<string name="mousepad_info">Muevi un deu enriba la pantalla pa mover el mur. Calca pa un clic y usa dos/tres deos pa los botones de drecha y en mediu. Usa un primíu llargu p\'arrastrar y soltar.</string>
<string name="mousepad_double_tap_settings_title">Afitar aición de calcu con dos deos</string>
<string name="mousepad_triple_tap_settings_title">Afitar aición de calcu con tres deos</string>
<string name="mousepad_sensitivity_settings_title">Afitar sensibilidá del panel táutil</string>
<string name="mousepad_scroll_direction_title">Direición inversa de desplazamientu</string>
<string name="mousepad_info">Muevi un deu enriba la pantalla pa mover el mur. calca pa un clic y usa dos/tres deos pa los motones de drecha y mediu. Usa un primíu llargu pa arrastrar y soltar.</string>
<string-array name="mousepad_tap_entries">
<item>Clic drechu</item>
<item>Clic d\'en mediu</item>
<item>Nada</item>
</string-array>
<string name="mousepad_double_default">drecha</string>
<string name="mousepad_triple_default">d\'en mediu</string>
<string name="mousepad_sensitivity_default">por defeutu</string>
<string-array name="mousepad_sensitivity_entries">
<item>La más lenta</item>
<item>Above Slowest</item>
<item>Predeterminada</item>
<item>Penriba lo predeterminao</item>
<item>La más rápida</item>
</string-array>
<string name="category_connected_devices">Preseos coneutaos</string>
<string name="category_not_paired_devices">Preseos disponibles</string>
<string name="category_remembered_devices">Preseos recordaos</string>
<string name="plugins_failed_to_load">Los complementos fallaron al cargase (calca pa más información):</string>
<string name="device_menu_plugins">Axustes de complementos</string>
<string name="device_menu_unpair">Desempareyar</string>
<string name="device_not_reachable">El preséu empareyáu nun ye agamable</string>
<string name="pair_new_device">Empareyar preséu nuevu</string>
<string name="unknown_device">Preséu desconocíu</string>
<string name="error_not_reachable">Nun ye algamable\'l preséu</string>
<string name="error_already_requested">Empareyamientu yá solicitáu</string>
@@ -82,8 +63,6 @@
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Calca pa responder</string>
<string name="reconnect">Reconeutar</string>
<string name="right_click">Unviar clic drechu</string>
<string name="middle_click">Unviar clic d\'en mediu</string>
<string name="show_keyboard">Amosar tecláu</string>
<string name="device_not_paired">Preséu non empareyáu</string>
<string name="request_pairing">Solicitar empareyamientu</string>
@@ -109,7 +88,6 @@
<item>1 minutu</item>
<item>2 minutos</item>
</string-array>
<string name="share_to">Compartir en...</string>
<string name="protocol_version_older">Esti preséu usa una versión vieya del protocolu</string>
<string name="protocol_version_newer">Esti preséu usa una versión anovada del protocolu</string>
<string name="general_settings">Axustes xenerales</string>
@@ -140,7 +118,7 @@
<string name="mpris_player_on_device">%1$s en %2$s</string>
<string name="send_files">Unviar ficheros</string>
<string name="pairing_title">Preseos KDE Connect</string>
<string name="pairing_description">Equí deberíen apaecer otros preseos executando KDE Connect.</string>
<string name="pairing_description">Deberíen apaecer equí otros preseos executando KDE Connect.</string>
<string name="device_paired">Preséu empareyáu</string>
<string name="device_rename_title">Renomar preséu</string>
<string name="device_rename_confirm">Renomar</string>
@@ -150,8 +128,7 @@
<string name="pref_plugin_telepathy">Unviar SMS</string>
<string name="pref_plugin_telepathy_desc">Unvia mensaxes de testu dende\'l to escritoriu</string>
<string name="plugin_not_supported">Esti complementu nun ta sofitáu pol preséu</string>
<string name="findmyphone_title">Alcuéntra\'l mio teléfonu</string>
<string name="findmyphone_description">Fai sonar esti teléfonu pa que pueas alcontralu.</string>
<string name="findmyphone_found">Alcontrar</string>
<string name="open">Abrir</string>
<string name="close">Zarrar</string>
</resources>

View File

@@ -25,13 +25,6 @@
<item>Middle click</item>
<item>Nothing</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Свързани устройства</string>
<string name="category_remembered_devices">Запомнени устройства</string>
<string name="plugins_failed_to_load">Неуспешно зареждане на приставки (докоснете за подробности)</string>

View File

@@ -32,13 +32,6 @@
</string-array>
<string name="mousepad_double_default">desno</string>
<string name="mousepad_triple_default">Srednje</string>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Spojeni uređaji</string>
<string name="category_not_paired_devices">Dostupni uređaji</string>
<string name="category_remembered_devices">Zapamćeni uređaji</string>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Executa una ordre</string>
<string name="pref_plugin_runcommand_desc">Activa les ordres remotes des del vostre telèfon</string>
<string name="pref_plugin_runcommand_desc">Executa una ordre al vostre sistema</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envia i rep els pings</string>
<string name="pref_plugin_notifications">Sincronitza les notificacions</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Moveu un dit per la pantalla per a moure el cursor del ratolí. Toqueu per un clic, i empreu dos/tres dits pels botons dret i mig. Empreu un toc llarg per arrossegar i deixar anar.</string>
<string name="mousepad_double_tap_settings_title">Estableix l\'acció de tocar amb dos dits</string>
<string name="mousepad_triple_tap_settings_title">Estableix l\'acció de tocar amb tres dits</string>
<string name="mousepad_sensitivity_settings_title">Estableix la sensibilitat del ratolí tàctil</string>
<string name="mousepad_scroll_direction_title">Inverteix la direcció del desplaçament</string>
<string-array name="mousepad_tap_entries">
<item>Clic dret</item>
<item>Clic del mig</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">dret</string>
<string name="mousepad_triple_default">mig</string>
<string name="mousepad_sensitivity_default">Predeterminada</string>
<string-array name="mousepad_sensitivity_entries">
<item>La més lenta</item>
<item>Lenta</item>
<item>Predeterminada</item>
<item>Ràpida</item>
<item>La més ràpida</item>
</string-array>
<string name="category_connected_devices">Dispositius connectats</string>
<string name="category_not_paired_devices">Dispositius disponibles</string>
<string name="category_remembered_devices">Dispositius recordats</string>
@@ -66,10 +56,6 @@
<string name="error_canceled_by_user">Cancel·lat per l\'usuari</string>
<string name="error_canceled_by_other_peer">Cancel·lat per l\'altre parell</string>
<string name="error_invalid_key">S\'ha rebut una clau no vàlida</string>
<string name="encryption_info_title">Informació 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="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>
@@ -155,9 +141,7 @@
<string name="pref_plugin_telepathy">Envia un SMS</string>
<string name="pref_plugin_telepathy_desc">Envia missatges de text des de l\'escriptori</string>
<string name="plugin_not_supported">Aquest connector no és admès pel dispositiu</string>
<string name="findmyphone_title">Cerca el meu telèfon</string>
<string name="findmyphone_title">Troba el meu telèfon</string>
<string name="findmyphone_description">Fa sonar aquest telèfon perquè el pugueu trobar.</string>
<string name="findmyphone_found">L\'he trobat</string>
<string name="open">Obre</string>
<string name="close">Tanca</string>
</resources>

View File

@@ -13,6 +13,7 @@
<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_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_desc">Posílat a přijímat ping</string>
<string name="pref_plugin_notifications">Synchronizace upozornění</string>
@@ -31,8 +32,6 @@
<string name="mousepad_info">Pohybujte prstem po obrazovce pro pohybování kurzorem myši. Ťukněte pro kliknutí a použijte dva/tři prsty jako pravé a prostřední tlačítko. Pro přetažení dlouze podržte.</string>
<string name="mousepad_double_tap_settings_title">Nastavit činnost pro ťuknutí dvěma prsty</string>
<string name="mousepad_triple_tap_settings_title">Nastavit činnost pro ťuknutí třemi prsty</string>
<string name="mousepad_sensitivity_settings_title">Nastavit citlivost touchpadu</string>
<string name="mousepad_scroll_direction_title">Obrácený směr posunu</string>
<string-array name="mousepad_tap_entries">
<item>Kliknutí pravým tlačítkem myši</item>
<item>Kliknutí prostředním tlačítkem myši</item>
@@ -40,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">pravé</string>
<string name="mousepad_triple_default">prostřední</string>
<string name="mousepad_sensitivity_default">výchozí</string>
<string-array name="mousepad_sensitivity_entries">
<item>Nejpomalejší</item>
<item>Méně pomalý</item>
<item>Výchozí</item>
<item>Rychlejší</item>
<item>Nejrychlejší</item>
</string-array>
<string name="category_connected_devices">Připojená zařízení</string>
<string name="category_not_paired_devices">Dostupná zařízení</string>
<string name="category_remembered_devices">Zapamatovaná zařízení</string>
@@ -55,7 +46,6 @@
<string name="device_menu_plugins">Nastavení modulů</string>
<string name="device_menu_unpair">Zrušit párování</string>
<string name="device_not_reachable">Spárované zařízení je nedostupné</string>
<string name="pair_new_device">Spárovat nové zařízení</string>
<string name="unknown_device">Neznámé zařízení</string>
<string name="error_not_reachable">Zařízení je nedostupné</string>
<string name="error_already_requested">Párování již bylo vyžádáno</string>
@@ -65,7 +55,6 @@
<string name="error_canceled_by_user">Přerušeno uživatelem</string>
<string name="error_canceled_by_other_peer">Přerušeno druhým uživatelem</string>
<string name="error_invalid_key">Byl přijat neplatný klíč</string>
<string name="encryption_info_title">Informace o šifrování</string>
<string name="pair_requested">Bylo vyžádáno párování</string>
<string name="pairing_request_from">Požadavek o párování z %1s</string>
<string name="received_url_title">Přijat odkaz od %1s</string>
@@ -154,6 +143,4 @@
<string name="findmyphone_title">Najít můj telefon</string>
<string name="findmyphone_description">Prozvoní tento telefon, takže jej můžete najít.</string>
<string name="findmyphone_found">Nalezeno</string>
<string name="open">Otevřít</string>
<string name="close">Zavřít</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Kør kommando</string>
<string name="pref_plugin_runcommand_desc">Kør eksterne kommandoer fra din telefon</string>
<string name="pref_plugin_runcommand_desc">Kører en kommando på dit system</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Send og modtag ping</string>
<string name="pref_plugin_notifications">Synk. af bekendtgørelser</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Bevæg en finger på skærmen for at flytte musemarkøren. Tap for at klikke og brug to/tre-fingre for højre og midterste museknap. Brug et langt tryk til at trække og slippe.</string>
<string name="mousepad_double_tap_settings_title">Angiv handling for tap med to fingre</string>
<string name="mousepad_triple_tap_settings_title">Angiv handling for tap med tre fingre</string>
<string name="mousepad_sensitivity_settings_title">Angiv følsomhed for touchpad</string>
<string name="mousepad_scroll_direction_title">Omvendt rulleretning</string>
<string-array name="mousepad_tap_entries">
<item>Højreklik</item>
<item>Midterklik</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">højre</string>
<string name="mousepad_triple_default">midter</string>
<string name="mousepad_sensitivity_default">standard</string>
<string-array name="mousepad_sensitivity_entries">
<item>Mest langsom</item>
<item>Over mest langsom</item>
<item>Standard</item>
<item>Over standard</item>
<item>Hurtigst</item>
</string-array>
<string name="category_connected_devices">Forbundne enheder</string>
<string name="category_not_paired_devices">Tilgængelig enheder</string>
<string name="category_remembered_devices">Huskede enheder</string>
@@ -56,7 +46,6 @@
<string name="device_menu_plugins">Plugin-indstillinger</string>
<string name="device_menu_unpair">Fjern parring</string>
<string name="device_not_reachable">Den parrede enhed kan ikke tilgås</string>
<string name="pair_new_device">Par med ny enhed</string>
<string name="unknown_device">Ukendt enhed</string>
<string name="error_not_reachable">Enheden kan ikke nås</string>
<string name="error_already_requested">Allerede anmodet om parring</string>
@@ -66,10 +55,6 @@
<string name="error_canceled_by_user">Annulleret af brugeren</string>
<string name="error_canceled_by_other_peer">Annulleret af modpart</string>
<string name="error_invalid_key">Ugyldige nøgle modtaget</string>
<string name="encryption_info_title">Krypteringsinfo</string>
<string name="encryption_info_msg_no_ssl">Den anden enhed bruger ikke en nylig version af KDE Connect, og bruger dermed den forældede krypteringsmetode.</string>
<string name="my_device_fingerprint">SHA1-fingeraftrykket for dit enhedscertifikat er:</string>
<string name="remote_device_fingerprint">SHA1-fingeraftrykket for det eksterne enhedscertifikat er:</string>
<string name="pair_requested">Anmodet om parring</string>
<string name="pairing_request_from">Parringsanmodning fra %1s</string>
<string name="received_url_title">Modtog link fra %1s</string>
@@ -158,6 +143,4 @@
<string name="findmyphone_title">Find min telefon</string>
<string name="findmyphone_description">Ringer til denne telefon, så du kan finde den.</string>
<string name="findmyphone_found">Fundet</string>
<string name="open">Åbn</string>
<string name="close">Luk</string>
</resources>

View File

@@ -13,7 +13,7 @@
<string name="pref_plugin_mpris">Multimedia-Bedienung</string>
<string name="pref_plugin_mpris_desc">Audio und Video mit Ihrem Telefon steuern</string>
<string name="pref_plugin_runcommand">Befehl ausführen</string>
<string name="pref_plugin_runcommand_desc">Von Ihrem Telefon Befehle auf anderen Geräten ausführen</string>
<string name="pref_plugin_runcommand_desc">Führt einen Befehl auf Ihrem System aus</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Senden und Empfangen von Pings</string>
<string name="pref_plugin_notifications">Benachrichtigungs-Abgleich</string>
@@ -29,10 +29,6 @@
<string name="send_ping">Ping senden</string>
<string name="open_mpris_controls">Multimedia-Bedienung</string>
<string name="open_mousepad">Ferneingabe</string>
<string name="mousepad_double_tap_settings_title">Aktionsausführung bei Berührung mit zwei Fingern einstellen</string>
<string name="mousepad_triple_tap_settings_title">Aktionsausführung bei Berührung mit drei Fingern einstellen</string>
<string name="mousepad_sensitivity_settings_title">Empfindlichkeit des Touchpads einstellen</string>
<string name="mousepad_scroll_direction_title">Bildlaufrichtung umkehren</string>
<string-array name="mousepad_tap_entries">
<item>Rechtsklick</item>
<item>Mittelklick</item>
@@ -40,14 +36,6 @@
</string-array>
<string name="mousepad_double_default">Rechts</string>
<string name="mousepad_triple_default">Mitte</string>
<string name="mousepad_sensitivity_default">Standard</string>
<string-array name="mousepad_sensitivity_entries">
<item>Langsamste</item>
<item>Above Slowest</item>
<item>Standard</item>
<item>Above Default</item>
<item>Schnellste</item>
</string-array>
<string name="category_connected_devices">Verbundene Geräte</string>
<string name="category_not_paired_devices">Verfügbare Gerät</string>
<string name="category_remembered_devices">Gemerkte Geräte</string>
@@ -65,9 +53,6 @@
<string name="error_canceled_by_user">Abbruch durch Benutzer</string>
<string name="error_canceled_by_other_peer">Abbruch durch Gegenstelle</string>
<string name="error_invalid_key">Ungültiger Schlüssel empfangen</string>
<string name="encryption_info_title">Verschlüsselungsinformationen</string>
<string name="my_device_fingerprint">Der SHA1-Fingerabdruck Ihres Gerätezertifikats lautet:</string>
<string name="remote_device_fingerprint">Der SHA1-Fingerabdruck des Gerätezertifikats der Gegenstelle lautet:</string>
<string name="pair_requested">Verbindung angefordert</string>
<string name="pairing_request_from">Verbindungsanfrage von %1s</string>
<string name="received_url_title">Verknüpfung von %1s erhalten</string>
@@ -101,7 +86,6 @@
<string name="mpris_next">Weiter</string>
<string name="mpris_volume">Lautstärke</string>
<string name="mpris_settings">Multimedia-Einstellungen</string>
<string name="mpris_time_settings_title">Knöpfe Vorwärts/Rückwärts</string>
<string-array name="mpris_time_entries">
<item>10 Sekunden</item>
<item>20 Sekunden</item>
@@ -123,7 +107,6 @@
<string name="pair_device_action">Ein neues Gerät verbinden</string>
<string name="unpair_device_action">Verbindung %s trennen</string>
<string name="custom_device_list">Geräte nach IP hinzufügen</string>
<string name="share_notification_preference">Ausführliche Benachrichtigungen</string>
<string name="share_notification_preference_summary">Beim Empfang einer Datei vibrieren und einen Sound abspielen</string>
<string name="title_activity_notification_filter">Benachrichtigungs-Filter</string>
<string name="filter_apps_info">Benachrichtigungen werden zwischen den ausgewählten Anwendungen abgeglichen.</string>
@@ -139,18 +122,11 @@
<string name="mpris_player_on_device">%1$s auf %2$s</string>
<string name="send_files">Dateien senden</string>
<string name="pairing_title">KDE-Connect-Geräte</string>
<string name="pairing_description">Andere Geräte, auf denen KDE-Connect läuft im gleichen Netzwerk,sollte hier angezeigt werden.</string>
<string name="device_paired">Gerät verbunden</string>
<string name="device_rename_title">Geräte umbenennen</string>
<string name="device_rename_confirm">Umbenennen</string>
<string name="refresh">Aktualisieren</string>
<string name="no_file_browser">Es sind keine Dateiverwaltungsprogramme installiert.</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="plugin_not_supported">Dieses Modul wird durch das Gerät nicht unterstützt</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_found">Gefunden</string>
<string name="open">Öffnen</string>
<string name="close">Schließen</string>
</resources>

View File

@@ -1,163 +0,0 @@
<?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,6 +13,7 @@
<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_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_desc">Send and receive pings</string>
<string name="pref_plugin_notifications">Notification sync</string>
@@ -31,8 +32,6 @@
<string name="mousepad_info">Move a finger on the screen to move the mouse cursor. Tap for a click, and use two/three fingers for right and middle buttons. Use a long press to drag\'n drop.</string>
<string name="mousepad_double_tap_settings_title">Set two finger tap action</string>
<string name="mousepad_triple_tap_settings_title">Set three finger tap action</string>
<string name="mousepad_sensitivity_settings_title">Set touchpad sensitivity</string>
<string name="mousepad_scroll_direction_title">Reverse Scrolling Direction</string>
<string-array name="mousepad_tap_entries">
<item>Right click</item>
<item>Middle click</item>
@@ -40,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">right</string>
<string name="mousepad_triple_default">middle</string>
<string name="mousepad_sensitivity_default">default</string>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Connected devices</string>
<string name="category_not_paired_devices">Available devices</string>
<string name="category_remembered_devices">Remembered devices</string>
@@ -55,7 +46,6 @@
<string name="device_menu_plugins">Plugin settings</string>
<string name="device_menu_unpair">Unpair</string>
<string name="device_not_reachable">Paired device not reachable</string>
<string name="pair_new_device">Pair new device</string>
<string name="unknown_device">Unknown device</string>
<string name="error_not_reachable">Device not reachable</string>
<string name="error_already_requested">Pairing already requested</string>
@@ -150,8 +140,7 @@
<string name="pref_plugin_telepathy">Send SMS</string>
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
<string name="plugin_not_supported">This plugin is not supported by the device</string>
<string name="findmyphone_title">Find My Phone</string>
<string name="findmyphone_description">Rings this phone so you can find it.</string>
<string name="findmyphone_found">Found</string>
<string name="open">Open</string>
<string name="close">Close</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Ejecutar orden</string>
<string name="pref_plugin_runcommand_desc">Desencadenar órdenes remotas desde su teléfono</string>
<string name="pref_plugin_runcommand_desc">Ejecuta una orden en su sistema</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Enviar y recibir pings</string>
<string name="pref_plugin_notifications">Sincronizar notificaciones</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Mueva un dedo sobre la pantalla para mover el cursor del ratón. Pulse para ejecutar un clic y use dos/tres dedos para emular los botones derecho y central. Use una pulsación larga para arrastrar y soltar.</string>
<string name="mousepad_double_tap_settings_title">Establecer la acción al pulsar con dos dedos</string>
<string name="mousepad_triple_tap_settings_title">Establecer la acción al pulsar con tres dedos</string>
<string name="mousepad_sensitivity_settings_title">Establecer sensibilidad del panel táctil</string>
<string name="mousepad_scroll_direction_title">Invertir dirección de desplazamiento</string>
<string-array name="mousepad_tap_entries">
<item>Clic derecho</item>
<item>Clic del botón central</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">derecho</string>
<string name="mousepad_triple_default">medio</string>
<string name="mousepad_sensitivity_default">por defecto</string>
<string-array name="mousepad_sensitivity_entries">
<item>Muy poco sensible</item>
<item>Poco sensible</item>
<item>Predeterminada</item>
<item>Sensible</item>
<item>Muy sensible</item>
</string-array>
<string name="category_connected_devices">Dispositivos conectados</string>
<string name="category_not_paired_devices">Dispositivos disponibles</string>
<string name="category_remembered_devices">Dispositivos recordados</string>
@@ -56,7 +46,6 @@
<string name="device_menu_plugins">Preferencias del complemento</string>
<string name="device_menu_unpair">Desvincular</string>
<string name="device_not_reachable">No se encuentra el dispositivo aparejado</string>
<string name="pair_new_device">Vincular nuevo dispositivo</string>
<string name="unknown_device">Dispositivo desconocido</string>
<string name="error_not_reachable">No se encuentra el dispositivo</string>
<string name="error_already_requested">Ya ha solicitado vincularse</string>
@@ -66,10 +55,6 @@
<string name="error_canceled_by_user">Cancelado por el usuario</string>
<string name="error_canceled_by_other_peer">Cancelado por la otra parte</string>
<string name="error_invalid_key">Se ha recibido una clave no valida</string>
<string name="encryption_info_title">Información de cifrado</string>
<string name="encryption_info_msg_no_ssl">El otro dispositivo no dispone de una versión reciente de KDE Connect, se usará un método de cifrado antiguo.</string>
<string name="my_device_fingerprint">La huella digital SHA1 del certificado de su dispositivo es:</string>
<string name="remote_device_fingerprint">La huella digital SHA1 del certificado del dispositivo remoto es:</string>
<string name="pair_requested">Vinculación solicitada</string>
<string name="pairing_request_from">Solicitud de vinculación de %1s</string>
<string name="received_url_title">Enlace recibido desde %1s</string>
@@ -158,6 +143,4 @@
<string name="findmyphone_title">Encontrar mi teléfono</string>
<string name="findmyphone_description">Hace sonar este teléfono para que pueda encontrarlo.</string>
<string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string>
<string name="close">Cerrar</string>
</resources>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_mpris">Multimedian ohjaus</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_desc">Suorita komento järjestelmässäsi</string>
<string name="pref_plugin_ping">Tiedustelupaketti</string>
<string name="pref_plugin_ping_desc">Lähetä ja vastaanota tiedustelupaketteja</string>
<string name="pref_plugin_notifications">Ilmoitusten synkronointi</string>
@@ -31,8 +32,6 @@
<string name="mousepad_info">Liikuta hiiren osoitinta liikuttamalla sormeasi näytöllä. Napsauta napauttamalla yhdellä sormella, käytä oikeaa painiketta kahdella sormella ja keskipainiketta kolmella. Vedä ja pudota painamalla pitkään.</string>
<string name="mousepad_double_tap_settings_title">Aseta kahden sormen napautuksen toiminto</string>
<string name="mousepad_triple_tap_settings_title">Aseta kolmen sormen napautuksen toiminto</string>
<string name="mousepad_sensitivity_settings_title">Aseta kosketuslevyn herkkyys</string>
<string name="mousepad_scroll_direction_title">Käänteinen vierityssuunta</string>
<string-array name="mousepad_tap_entries">
<item>Oikea napsautus</item>
<item>Keskinapsautus</item>
@@ -40,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">Oikea painike</string>
<string name="mousepad_triple_default">Keskipainike</string>
<string name="mousepad_sensitivity_default">oletus</string>
<string-array name="mousepad_sensitivity_entries">
<item>Hitain</item>
<item>Hitainta suurempi</item>
<item>Oletus</item>
<item>Oletusta suurempi</item>
<item>Nopein</item>
</string-array>
<string name="category_connected_devices">Yhdistetyt laitteet</string>
<string name="category_not_paired_devices">Saatavilla olevat laitteet</string>
<string name="category_remembered_devices">Muistetut laitteet</string>
@@ -55,7 +46,6 @@
<string name="device_menu_plugins">Liitännäisten asetukset</string>
<string name="device_menu_unpair">Poista laitepari</string>
<string name="device_not_reachable">Laitepari tavoittamattomissa</string>
<string name="pair_new_device">Kytke uusi laite pariksi</string>
<string name="unknown_device">Tuntematon laite</string>
<string name="error_not_reachable">Laite tavoittamattomissa</string>
<string name="error_already_requested">Pariksi kytkemistä on jo pyydetty</string>
@@ -136,13 +126,13 @@
<string name="add_host">Lisää kone/IP</string>
<string name="add_host_hint">Konenimi tai IP-osoite</string>
<string name="no_players_connected">Soittimia ei löytynyt</string>
<string name="custom_dev_list_help">Käytä tätä vain, jos laitettasi ei tunnisteta automaattisesti. Kirjoita IP-osoite tai konenimi alle ja lisää se luetteloon koskettamalla painiketta. Voit poistaa olemassa olevan kohdan luettelosta koskettamalla sitä.</string>
<string name="custom_dev_list_help">Käytä tätä vain, jos laitettasi ei tunnisteta automaattisesti. Kirjoita IP-osoite tai konenimi alle ja kosketa painiketta lisätäksesi sen luetteloon. Kosketa olemassa olevaa kohtaa poistaaksesi sen luettelosta.</string>
<string name="mpris_player_on_device">%1$s laitteella %2$s</string>
<string name="send_files">Lähetä tiedostoja</string>
<string name="pairing_title">KDE Connect -laitteet</string>
<string name="pairing_description">Muiden samassa verkossa olevien KDE Connectia käyttävien laitteiden pitäisi ilmestyä tähän.</string>
<string name="device_paired">Laite kytketty pariksi</string>
<string name="device_rename_title">Muuta laitteen nimeä</string>
<string name="device_rename_title">Laitteen nimen muuttaminen</string>
<string name="device_rename_confirm">Muuta nimi</string>
<string name="refresh">Päivitä</string>
<string name="unreachable_description">Tämä pariksi kytketty laite ei ole tavoitettavissa. Tarkista, että se on yhteydessä samaan verkkoon.</string>
@@ -150,8 +140,7 @@
<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="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_found">Löytyi</string>
<string name="open">Avaa</string>
<string name="close">Sulje</string>
</resources>

View File

@@ -37,13 +37,6 @@
</string-array>
<string name="mousepad_double_default">Droite</string>
<string name="mousepad_triple_default">Milieu</string>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Périphériques connectés</string>
<string name="category_not_paired_devices">Périphériques disponibles</string>
<string name="category_remembered_devices">Périphériques mémorisés</string>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_mpris">Controis multimedia</string>
<string name="pref_plugin_mpris_desc">Controle o son e o vídeo desde o teléfono.</string>
<string name="pref_plugin_runcommand">Executar unha orde</string>
<string name="pref_plugin_runcommand_desc">Executa unha orde no sistema.</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envíe e reciba pings.</string>
<string name="pref_plugin_notifications">Sincronización de notificacións</string>
@@ -31,8 +32,6 @@
<string name="mousepad_info">Mova un dedo na pantalla para mover o cursor. Toque para facer clic, e use dous ou tres dedos para os botóns secundario e central. Prema durante un tempo para arrastrar e soltar.</string>
<string name="mousepad_double_tap_settings_title">Definir a acción de tocar con dous dedos</string>
<string name="mousepad_triple_tap_settings_title">Definir a acción de tocar con tres dedos</string>
<string name="mousepad_sensitivity_settings_title">Definir a sensibilidade do punteiro táctil</string>
<string name="mousepad_scroll_direction_title">Inverter a dirección de desprazamento</string>
<string-array name="mousepad_tap_entries">
<item>Clic dereito</item>
<item>Clic central</item>
@@ -40,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">dereita</string>
<string name="mousepad_triple_default">medio</string>
<string name="mousepad_sensitivity_default">predeterminado</string>
<string-array name="mousepad_sensitivity_entries">
<item>O máis lento</item>
<item>Lento</item>
<item>Predeterminado</item>
<item>Rápido</item>
<item>O mais rápido</item>
</string-array>
<string name="category_connected_devices">Dispositivos conectados</string>
<string name="category_not_paired_devices">Dispositivos dispoñíbeis</string>
<string name="category_remembered_devices">Dispositivos coñecidos</string>
@@ -55,7 +46,6 @@
<string name="device_menu_plugins">Configuración do engadido</string>
<string name="device_menu_unpair">Desemparellarse</string>
<string name="device_not_reachable">O dispositivo emparellado está fóra do alcance.</string>
<string name="pair_new_device">Emparellar cun novo dispositivo</string>
<string name="unknown_device">Dispositivo descoñecido</string>
<string name="error_not_reachable">Dispositivo fóra do alcance</string>
<string name="error_already_requested">Xa solicitou emparellarse.</string>
@@ -150,8 +140,7 @@
<string name="pref_plugin_telepathy">Enviar unha mensaxe de texto</string>
<string name="pref_plugin_telepathy_desc">Enviar mensaxes de texto desde un computador de escritorio.</string>
<string name="plugin_not_supported">O dispositivo non é compatíbel con este complemento.</string>
<string name="findmyphone_title">Atopar o móbil</string>
<string name="findmyphone_description">Reproduce un son de chamada no móbil para que poida atopalo.</string>
<string name="findmyphone_found">Atopado</string>
<string name="open">Abrir</string>
<string name="close">Pechar</string>
</resources>

View File

@@ -25,13 +25,6 @@
<item/>
<item>Nothing</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Csatlakoztatott eszközök</string>
<string name="category_remembered_devices">Megjegyzett eszközök</string>
<string name="plugins_failed_to_load">A bővítményeket nem sikerült betölteni (érintse meg a további információkért):</string>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Esegui comando</string>
<string name="pref_plugin_runcommand_desc">Esegui comandi remoti dal tuo telefono</string>
<string name="pref_plugin_runcommand_desc">Esegue un comando sul tuo sistema</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Invia e ricevi ping</string>
<string name="pref_plugin_notifications">Sincronizzazione notifiche</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Muovi un dito sullo schermo per spostare il puntatore del mouse. Tocca per un clic e usa due/tre dita per i pulsanti destro e centrale. Utilizza una pressione lunga per trascinare e rilasciare.</string>
<string name="mousepad_double_tap_settings_title">Imposta azione per il tocco a due dita</string>
<string name="mousepad_triple_tap_settings_title">Imposta azione per il tocco a tre dita</string>
<string name="mousepad_sensitivity_settings_title">Imposta la sensibilità del touchpad</string>
<string name="mousepad_scroll_direction_title">Inverti direzione di scorrimento</string>
<string-array name="mousepad_tap_entries">
<item>Clic destro</item>
<item>Clic centrale</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">destra</string>
<string name="mousepad_triple_default">centro</string>
<string name="mousepad_sensitivity_default">predefinita</string>
<string-array name="mousepad_sensitivity_entries">
<item>Minima</item>
<item>Più veloce</item>
<item>Predefinita</item>
<item>Superiore a predefinita</item>
<item>Massima</item>
</string-array>
<string name="category_connected_devices">Dispositivi connessi</string>
<string name="category_not_paired_devices">Dispositivi disponibili</string>
<string name="category_remembered_devices">Dispositivi memorizzati</string>
@@ -56,7 +46,6 @@
<string name="device_menu_plugins">Impostazioni estensioni</string>
<string name="device_menu_unpair">Disassocia</string>
<string name="device_not_reachable">Dispositivo associato non raggiungibile</string>
<string name="pair_new_device">Associa nuovo dispositivo</string>
<string name="unknown_device">Dispositivo sconosciuto</string>
<string name="error_not_reachable">Dispositivo fuori portata</string>
<string name="error_already_requested">Richiesta già inviata</string>
@@ -66,10 +55,6 @@
<string name="error_canceled_by_user">Annullata dall\'utente</string>
<string name="error_canceled_by_other_peer">Annullata dal dispositivo remoto</string>
<string name="error_invalid_key">Ricevuta chiave non valida</string>
<string name="encryption_info_title">Informazioni di cifratura</string>
<string name="encryption_info_msg_no_ssl">L\'altro dispositivo non utilizza una versione recente di KDE Connect, utilizzando il metodo di cifratura precedente.</string>
<string name="my_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo è:</string>
<string name="remote_device_fingerprint">L\'impronta digitale SHA1 del certificato di dispositivo remoto è:</string>
<string name="pair_requested">Richiesta di associazione</string>
<string name="pairing_request_from">Richiesta associazione da %1s</string>
<string name="received_url_title">Collegamento ricevuto da %1s</string>
@@ -158,6 +143,4 @@
<string name="findmyphone_title">Trova il mio telefono</string>
<string name="findmyphone_description">Fa squillare questo telefono per trovarlo.</string>
<string name="findmyphone_found">Trovato</string>
<string name="open">Apri</string>
<string name="close">Chiudi</string>
</resources>

View File

@@ -5,13 +5,6 @@
<item>Middle click</item>
<item>Nothing</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
<item>20 seconds</item>

View File

@@ -13,6 +13,7 @@
<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>
@@ -38,13 +39,6 @@
</string-array>
<string name="mousepad_double_default">오른쪽</string>
<string name="mousepad_triple_default">가운데</string>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">연결된 장치</string>
<string name="category_not_paired_devices">사용 가능한 장치</string>
<string name="category_remembered_devices">기억하는 장치</string>
@@ -146,8 +140,7 @@
<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

@@ -23,13 +23,6 @@
<item>Middle click</item>
<item>Nieko</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Prijungti įrenginiai</string>
<string name="category_not_paired_devices">Pasiekiami įrenginiai</string>
<string name="category_remembered_devices">Įsimintieji įrenginiai</string>
@@ -41,11 +34,14 @@
<string name="error_already_requested">Jau paprašyta suporuoti</string>
<string name="error_already_paired">Įrenginys jau suporuotas</string>
<string name="error_could_not_send_package">Nepavyksta išsiųsti paketo</string>
<string name="error_timed_out">Skirtasis laikas baigėsi</string>
<string name="error_canceled_by_user">Naudotojas atšaukė užduotį</string>
<string name="error_canceled_by_other_peer">Porininkas atšaukė užduotį</string>
<string name="error_invalid_key">Gautas netinkamas raktas</string>
<string name="pair_requested">Paprašyta suporuoti</string>
<string name="incoming_file_text">%1s</string>
<string name="outgoing_file_text">%1s</string>
<string name="sent_file_text">%1s</string>
<string name="sent_file_failed_text">%1s</string>
<string name="tap_to_answer">Norėdami atsakyti, palieskite</string>
<string name="reconnect">Prisijungti iš naujo</string>
<string name="show_keyboard">Rodyti klaviatūrą</string>
@@ -83,6 +79,7 @@
<string name="pair_device_action">Suporuoti naują įrenginį</string>
<string name="unpair_device_action">Atrišti %s</string>
<string name="custom_device_list">Pridėti įrenginį pagal IP</string>
<string name="sftp_internal_storage">Vidinė saugykla</string>
<string name="sftp_all_files">Visi failai</string>
<string name="sftp_sdcard_num">SD kortelė %d</string>
<string name="sftp_sdcard">SD kortelė</string>
@@ -96,13 +93,11 @@
<string name="pairing_description">Čia turėtų pasirodyti to kiti paties tinklo įrenginiai, kuriuose veikia „KDE Connect“</string>
<string name="device_paired">Įrenginys suporuotas</string>
<string name="device_rename_title">Pervadinti įrenginį</string>
<string name="device_rename_confirm">Pervadinti</string>
<string name="refresh">Atnaujinti</string>
<string name="unreachable_description">Šis suporuotas įrenginys nepasiekiamas. Patikrinkite, ar jis prisijungęs prie to paties tinklo.</string>
<string name="pref_plugin_telepathy">Siųsti SMS</string>
<string name="plugin_not_supported">Telefonas nepalaiko šio papildinio</string>
<string name="findmyphone_title">Rasti telefoną</string>
<string name="findmyphone_description">Telefonas skambės, tad galėsite jį rasti.</string>
<string name="findmyphone_found">Radau</string>
<string name="open">Atverti</string>
<string name="close">Užverti</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Commando uitvoeren</string>
<string name="pref_plugin_runcommand_desc">Commando\'s afvuren op afstand vanaf uw telefoon</string>
<string name="pref_plugin_runcommand_desc">Voert een commando uit op uw systeem</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Pings verzenden en ontvangen</string>
<string name="pref_plugin_notifications">Synchronisatie van meldingen</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Veeg met een vinger op het scherm om de muiscursor te verplaatsen. Tik om te klikken en gebruik twee/drie vingers voor rechter en middelste knop. Druk lang voor slepen en loslaten.</string>
<string name="mousepad_double_tap_settings_title">Tikactie met twee vingers instellen</string>
<string name="mousepad_triple_tap_settings_title">Tikactie met drie vingers instellen</string>
<string name="mousepad_sensitivity_settings_title">Gevoeligheid van touchpad instellen</string>
<string name="mousepad_scroll_direction_title">Schuifrichting omdraaien</string>
<string-array name="mousepad_tap_entries">
<item>Rechter muisklik</item>
<item>Middelste muisklik</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">rechts</string>
<string name="mousepad_triple_default">midden</string>
<string name="mousepad_sensitivity_default">standaard</string>
<string-array name="mousepad_sensitivity_entries">
<item>Langzaamst</item>
<item>Langzaam</item>
<item>Standaard</item>
<item>Boven standaard</item>
<item>Snelste</item>
</string-array>
<string name="category_connected_devices">Verbonden apparaten</string>
<string name="category_not_paired_devices">Beschikbare apparaten</string>
<string name="category_remembered_devices">Onthouden apparaten</string>
@@ -66,10 +56,6 @@
<string name="error_canceled_by_user">Geannuleerd door gebruiker</string>
<string name="error_canceled_by_other_peer">Geannuleerd door andere kant</string>
<string name="error_invalid_key">Ongeldige sleutel ontvangen</string>
<string name="encryption_info_title">Versleutelde informatie</string>
<string name="encryption_info_msg_no_ssl">Het andere apparaat gebruikt geen recente versie van KDE Connect, de verouderde versleutelingsmethode zal worden gebruikt.</string>
<string name="my_device_fingerprint">De SHA1 vingerafdruk van het certificaat van uw apparaat is:</string>
<string name="remote_device_fingerprint">De SHA1 vingerafdruk van het certificaat van het apparaat op afstand is:</string>
<string name="pair_requested">Paar gevraagd</string>
<string name="pairing_request_from">Verzoek om een paar te maken van %1s</string>
<string name="received_url_title">Ontvangen koppeling van %1s</string>
@@ -158,6 +144,4 @@
<string name="findmyphone_title">Zoek mijn telefoon</string>
<string name="findmyphone_description">Laat deze telefoon bellen zodat u het kunt vinden.</string>
<string name="findmyphone_found">Gevonden</string>
<string name="open">Openen</string>
<string name="close">Sluiten</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Wykonaj polecenie</string>
<string name="pref_plugin_runcommand_desc">Wyzwalaj zdalne polecenia z twojego telefonu</string>
<string name="pref_plugin_runcommand_desc">Wykonuje polecenie na twoim systmie</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Wysyłaj i otrzymuj pingi</string>
<string name="pref_plugin_notifications">Powiadomienia synchronizacji</string>
@@ -32,23 +32,13 @@
<string name="mousepad_info">Przesuń palcem po ekranie, aby przesunąć wskaźnik myszy. Stuknij, aby wywołać naciśniecie lewym przyciskiem myszy i użyj dwóch/trzech palców, aby wywołać naciśniecie prawym i środkowym przyciskiem myszy. Przyciśnij na dłużej, aby przeciągnąć i upuścić.</string>
<string name="mousepad_double_tap_settings_title">Ustaw działanie po dwukrotnym stuknięciu palcem</string>
<string name="mousepad_triple_tap_settings_title">Ustaw działanie po trzykrotnym stuknięciu palcem</string>
<string name="mousepad_sensitivity_settings_title">Ustaw czułość gładzika</string>
<string name="mousepad_scroll_direction_title">Odwróć stronę przewijania</string>
<string-array name="mousepad_tap_entries">
<item>Kliknięcie prawym</item>
<item>Kliknięcie środkowym</item>
<item>Naciśnięcie prawym</item>
<item>Naciśnięcie środkowym</item>
<item>Nic</item>
</string-array>
<string name="mousepad_double_default">prawo</string>
<string name="mousepad_triple_default">środek</string>
<string name="mousepad_sensitivity_default">domyślne</string>
<string-array name="mousepad_sensitivity_entries">
<item>Najmniejsza</item>
<item>Ponad najmniejszą</item>
<item>Domyślna</item>
<item>Powyżej domyślnej</item>
<item>Największa</item>
</string-array>
<string name="category_connected_devices">Podłączone urządzenia</string>
<string name="category_not_paired_devices">Dostępne urządzenia</string>
<string name="category_remembered_devices">Zapamiętane urządzenia</string>
@@ -56,7 +46,6 @@
<string name="device_menu_plugins">Ustawienia wtyczki</string>
<string name="device_menu_unpair">Odparuj</string>
<string name="device_not_reachable">Sparowane urządzenie nieosiągalne</string>
<string name="pair_new_device">Sparuj nowe urządzenie</string>
<string name="unknown_device">Nieznane urządzenie</string>
<string name="error_not_reachable">Urządzenie nieosiągalne</string>
<string name="error_already_requested">Już zażądano parowania</string>
@@ -66,10 +55,6 @@
<string name="error_canceled_by_user">Anulowane przez użytkownika</string>
<string name="error_canceled_by_other_peer">Anulowane przez innego partnera</string>
<string name="error_invalid_key">Otrzymano nieprawidłowy klucz</string>
<string name="encryption_info_title">Zaszyfrowane informacje</string>
<string name="encryption_info_msg_no_ssl">Drugie urządzenie nie używa ostatniej wersji KDE Connect, użyto przestarzałego szyfrowania.</string>
<string name="my_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego urządzenia to:</string>
<string name="remote_device_fingerprint">Odcisk palca SHA1 certyfikatu twojego zdalnego urządzenia to:</string>
<string name="pair_requested">Zażądano parowania</string>
<string name="pairing_request_from">Żądanie parowania z %1s</string>
<string name="received_url_title">Odebrano odsyłacz od %1s</string>
@@ -158,6 +143,4 @@
<string name="findmyphone_title">Znajdź mój telefon</string>
<string name="findmyphone_description">Dzwoni na ten telefon, tak abyś mógł go znaleźć.</string>
<string name="findmyphone_found">Znaleziony</string>
<string name="open">Otwórz</string>
<string name="close">Zamknij</string>
</resources>

View File

@@ -1,24 +1,25 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<string name="pref_plugin_telephony">Notificação de telefonia</string>
<string name="pref_plugin_telephony">Notificação telefônica</string>
<string name="pref_plugin_telephony_desc">Envia notificações de SMS e chamadas</string>
<string name="pref_plugin_battery">Relatório da bateria</string>
<string name="pref_plugin_battery_desc">Informação periódica do status da bateria</string>
<string name="pref_plugin_battery_desc">Informa o status da bateria periodicamente</string>
<string name="pref_plugin_sftp">Exposição do sistema de arquivos</string>
<string name="pref_plugin_sftp_desc">Navegação remota pelo sistema de arquivos do telefone</string>
<string name="pref_plugin_clipboard">Sincronização da área de transferência</string>
<string name="pref_plugin_sftp_desc">Permite navegar remotamente pelo sistema de arquivos telefone</string>
<string name="pref_plugin_clipboard">Sincronizar área de transferência</string>
<string name="pref_plugin_clipboard_desc">Compartilha o conteúdo da área de transferência</string>
<string name="pref_plugin_mousepad">Introdução de dados remota</string>
<string name="pref_plugin_mousepad_desc">Use seu telefone como mouse ou teclado</string>
<string name="pref_plugin_mousepad_desc">Usar seu telefone como mouse ou teclado</string>
<string name="pref_plugin_mpris">Controle multimídia</string>
<string name="pref_plugin_mpris_desc">Controla áudio e vídeo a partir do seu telefone</string>
<string name="pref_plugin_runcommand">Executar comando</string>
<string name="pref_plugin_runcommand_desc">Executa um comando no seu sistema</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Envia e recebe pings</string>
<string name="pref_plugin_notifications">Sincronização de notificações</string>
<string name="pref_plugin_notifications_desc">Acesse suas notificações a partir de outros dispositivos</string>
<string name="pref_plugin_sharereceiver">Compartilhar e receber</string>
<string name="pref_plugin_sharereceiver_desc">Compartilha arquivos e URLs entre os dispositivos</string>
<string name="pref_plugin_sharereceiver_desc">Compartilhar arquivos e URLs entre os dispositivos</string>
<string name="plugin_not_available">Esta funcionalidade não está disponível na sua versão do Android</string>
<string name="device_list_empty">Sem dispositivos</string>
<string name="ok">OK</string>
@@ -28,11 +29,9 @@
<string name="send_ping">Enviar ping</string>
<string name="open_mpris_controls">Controle multimídia</string>
<string name="open_mousepad">Introdução de dados remota</string>
<string name="mousepad_info">Mova um dedo pela tela para mover o ponteiro do mouse. Dê um toque para clicar e use dois/três dedos para os botões da direita e do meio. Use uma pressão longa para arrastar e soltar.</string>
<string name="mousepad_info">Mova um dedo pelo tela para mover o ponteiro do mouse. Dê um toque para clicar e use dois/três dedos para os botões da direita e do meio. Use uma pressão longa para arrastar e soltar.</string>
<string name="mousepad_double_tap_settings_title">Definir a ação do toque com dois dedos</string>
<string name="mousepad_triple_tap_settings_title">Definir a ação do toque com três dedos</string>
<string name="mousepad_sensitivity_settings_title">Definir a sensibilidade do touchpad</string>
<string name="mousepad_scroll_direction_title">Direção de rolagem inversa</string>
<string-array name="mousepad_tap_entries">
<item>Botão direito</item>
<item>Botão do meio</item>
@@ -40,22 +39,13 @@
</string-array>
<string name="mousepad_double_default">direita</string>
<string name="mousepad_triple_default">meio</string>
<string name="mousepad_sensitivity_default">padrão</string>
<string-array name="mousepad_sensitivity_entries">
<item>Mais lento</item>
<item>Ainda mais lento</item>
<item>Padrão</item>
<item>Acima do padrão</item>
<item>Mais rápido</item>
</string-array>
<string name="category_connected_devices">Dispositivos conectados</string>
<string name="category_not_paired_devices">Dispositivos disponíveis</string>
<string name="category_remembered_devices">Dispositivos lembrados</string>
<string name="plugins_failed_to_load">Plugins não carregados (toque para mais informações):</string>
<string name="device_menu_plugins">Configuração dos plugins</string>
<string name="device_menu_plugins">Configurações do plugin</string>
<string name="device_menu_unpair">Cancelar emparelhamento</string>
<string name="device_not_reachable">O dispositivo pareado está inacessível</string>
<string name="pair_new_device">Emparelhar novo dispositivo</string>
<string name="unknown_device">Dispositivo desconhecido</string>
<string name="error_not_reachable">Dispositivo inacessível</string>
<string name="error_already_requested">O emparelhamento já foi solicitado</string>
@@ -65,8 +55,8 @@
<string name="error_canceled_by_user">Cancelado pelo usuário</string>
<string name="error_canceled_by_other_peer">Cancelado pelo outro dispositivo</string>
<string name="error_invalid_key">Chave inválida recebida</string>
<string name="pair_requested">Solicitação de emparelhamento</string>
<string name="pairing_request_from">Emparelhamento solicitado por %1s</string>
<string name="pair_requested">Emparelhamento solicitado</string>
<string name="pairing_request_from">Emparelhando solicitação de %1s</string>
<string name="received_url_title">Link recebido de %1s</string>
<string name="received_url_text">Toque para abrir o \'%1s\'</string>
<string name="incoming_file_title">Arquivo recebido de %1s</string>
@@ -121,12 +111,12 @@
<string name="shareplugin_text_saved">Texto recebido e salvo na área de transferência</string>
<string name="custom_devices_settings">Lista de dispositivos personalizada</string>
<string name="pair_device_action">Emparelhar um novo dispositivo</string>
<string name="unpair_device_action">Desemparelhar %s</string>
<string name="unpair_device_action">Desemparelhar o %s</string>
<string name="custom_device_list">Adicionar dispositivos pelo IP</string>
<string name="share_notification_preference">Notificações sonoras</string>
<string name="share_notification_preference_summary">Vibra e reproduz um som ao receber um arquivo</string>
<string name="share_notification_preference">Notificações barulhentas</string>
<string name="share_notification_preference_summary">Vibrar e tocar um som ao receber um arquivo</string>
<string name="title_activity_notification_filter">Filtro de notificações</string>
<string name="filter_apps_info">As notificações dos aplicativos selecionados serão sincronizadas.</string>
<string name="filter_apps_info">As notificações serão sincronizadas para os aplicativos selecionados.</string>
<string name="sftp_internal_storage">Armazenamento interno</string>
<string name="sftp_all_files">Todos os arquivos</string>
<string name="sftp_sdcard_num">Cartão SD %d</string>
@@ -140,7 +130,7 @@
<string name="mpris_player_on_device">%1$s em %2$s</string>
<string name="send_files">Enviar arquivos</string>
<string name="pairing_title">Dispositivos do KDE Connect</string>
<string name="pairing_description">Outros dispositivos executando o KDE Connect na mesma rede devem aparecer aqui.</string>
<string name="pairing_description">Os outros dispositivos executando o KDE Connect na mesma rede devem aparecer aqui.</string>
<string name="device_paired">Dispositivo emparelhado</string>
<string name="device_rename_title">Renomear dispositivo</string>
<string name="device_rename_confirm">Renomear</string>
@@ -150,8 +140,7 @@
<string name="pref_plugin_telepathy">Enviar SMS</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="findmyphone_title">Encontrar meu telefone</string>
<string name="findmyphone_description">Faça este telefone tocar para encontrá-lo.</string>
<string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string>
<string name="close">Fechar</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Executar um Comando</string>
<string name="pref_plugin_runcommand_desc">Despoletar comandos remotos a partir do seu telefone</string>
<string name="pref_plugin_runcommand_desc">Executa um comando no seu sistema</string>
<string name="pref_plugin_ping">Contacto</string>
<string name="pref_plugin_ping_desc">Enviar e receber pedidos de contacto (\'ping\')</string>
<string name="pref_plugin_notifications">Sincronização da notificação</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Mova um dedo pelo ecrã para mover o cursor do rato. Dê um toque para carregar no botão esquerdo e use dois/três dedos para os botões direito e do meio. Use uma pressão longa para arrastar e largar.</string>
<string name="mousepad_double_tap_settings_title">Definir a acção do toque com dois dedos</string>
<string name="mousepad_triple_tap_settings_title">Definir a acção do toque com três dedos</string>
<string name="mousepad_sensitivity_settings_title">Definir a sensibilidade do rato por toque</string>
<string name="mousepad_scroll_direction_title">Direcção de Deslocamento Inversa</string>
<string-array name="mousepad_tap_entries">
<item>Botão direito</item>
<item>Botão do meio</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">direita</string>
<string name="mousepad_triple_default">meio</string>
<string name="mousepad_sensitivity_default">predefinição</string>
<string-array name="mousepad_sensitivity_entries">
<item>Mais Lento</item>
<item>Ainda Mais Lento</item>
<item>Predefinição</item>
<item>Acima da Predefinição</item>
<item>Mais Rápido</item>
</string-array>
<string name="category_connected_devices">Dispositivos ligados</string>
<string name="category_not_paired_devices">Dispositivos disponíveis</string>
<string name="category_remembered_devices">Dispositivos recordados</string>
@@ -56,7 +46,6 @@
<string name="device_menu_plugins">Configuração do \'plugin\'</string>
<string name="device_menu_unpair">Desemparelhar</string>
<string name="device_not_reachable">O dispositivo emparelhado está inacessível</string>
<string name="pair_new_device">Emparelhar um novo dispositivo</string>
<string name="unknown_device">Dispositivo desconhecido</string>
<string name="error_not_reachable">Dispositivo inacessível</string>
<string name="error_already_requested">O emparelhamento já foi pedido</string>
@@ -66,10 +55,6 @@
<string name="error_canceled_by_user">Cancelado pelo utilizador</string>
<string name="error_canceled_by_other_peer">Cancelado pela outra máquina</string>
<string name="error_invalid_key">Chave inválida recebida</string>
<string name="encryption_info_title">Dados de Encriptação</string>
<string name="encryption_info_msg_no_ssl">O outro dispositivo não usa uma versão recente do KDE Connect; será usado o método antigo de encriptação.</string>
<string name="my_device_fingerprint">A impressão digital SHA1 do certificado do seu dispositivo é:</string>
<string name="remote_device_fingerprint">A impressão digital SHA1 do certificado do dispositivo remoto é:</string>
<string name="pair_requested">Emparelhamento pedido</string>
<string name="pairing_request_from">Pedido de emparelhamento de %1s</string>
<string name="received_url_title">Ligação recebida de %1s</string>
@@ -155,9 +140,7 @@
<string name="pref_plugin_telepathy">Enviar um SMS</string>
<string name="pref_plugin_telepathy_desc">Enviar mensagens de texto a partir do seu ambiente de trabalho</string>
<string name="plugin_not_supported">Este \'plugin\' não é suportado pelo dispositivo</string>
<string name="findmyphone_title">Descobrir o meu telefone</string>
<string name="findmyphone_title">Descobrir o Meu Telefone</string>
<string name="findmyphone_description">Toca este telefone para que o possa encontrar.</string>
<string name="findmyphone_found">Encontrado</string>
<string name="open">Abrir</string>
<string name="close">Fechar</string>
</resources>

View File

@@ -25,13 +25,6 @@
<item>Middle click</item>
<item>Nothing</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Dispozitive conectate</string>
<string name="category_remembered_devices">Dispozitive memorizate</string>
<string name="plugins_failed_to_load">Încărcarea extensiilor a eșuat (atingeți pentru mai multe informații):</string>

View File

@@ -13,6 +13,7 @@
<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>
@@ -38,13 +39,6 @@
</string-array>
<string name="mousepad_double_default">Нажатие правой кнопки</string>
<string name="mousepad_triple_default">Нажатие средней кнопки</string>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">Подключённые устройства</string>
<string name="category_not_paired_devices">Доступные устройства</string>
<string name="category_remembered_devices">Известные устройства</string>
@@ -146,8 +140,7 @@
<string name="pref_plugin_telepathy">Отправка SMS</string>
<string name="pref_plugin_telepathy_desc">Отправка SMS-сообщений с вашего компьютера</string>
<string name="plugin_not_supported">Этот модуль не поддерживается устройством</string>
<string name="findmyphone_title">Поиск телефона</string>
<string name="findmyphone_description">Подача звукового сигнала на телефоне, чтобы вы могли его найти.</string>
<string name="findmyphone_found">Найден</string>
<string name="open">Открыть</string>
<string name="close">Закрыть</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Spustiť príkaz</string>
<string name="pref_plugin_runcommand_desc">Spustiť vzdialené príkazy z vášho telefónu</string>
<string name="pref_plugin_runcommand_desc">Spustí príkazy na vašom systéme</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Poslať a prijať pingy</string>
<string name="pref_plugin_notifications">Synchronizácia pripomienok</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Posúvajte prst na obrazovke na posun kurzora. Ťuknutie vyvolá klik a použite dva/tri prsty pre pravé a stredné tlačidlo. Použite dlhé stlačenie pre drag and drop.</string>
<string name="mousepad_double_tap_settings_title">Nastaviť akciu dvoch prstov</string>
<string name="mousepad_triple_tap_settings_title">Nastaviť akciu troch prstov</string>
<string name="mousepad_sensitivity_settings_title">Nastaviť citlivosť touchpadu</string>
<string name="mousepad_scroll_direction_title">Obrátený smer rolovania</string>
<string-array name="mousepad_tap_entries">
<item>Kliknutie pravým tlačidlom</item>
<item>Stredný klik</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">vpravo</string>
<string name="mousepad_triple_default">stred</string>
<string name="mousepad_sensitivity_default">predvolené</string>
<string-array name="mousepad_sensitivity_entries">
<item>Najpomalšie</item>
<item>Nad najpomalším</item>
<item>Predvolené</item>
<item>Nad priemerným</item>
<item>Najrýchlejšie</item>
</string-array>
<string name="category_connected_devices">Pripojené zariadenia</string>
<string name="category_not_paired_devices">Dostupné zariadenia</string>
<string name="category_remembered_devices">Zapamätané zariadenia</string>
@@ -56,7 +46,6 @@
<string name="device_menu_plugins">Nastavenia pluginu</string>
<string name="device_menu_unpair">Odpárovať</string>
<string name="device_not_reachable">Spárované zariadenie nedostupné</string>
<string name="pair_new_device">Spárovať nové zariadenie</string>
<string name="unknown_device">Neznáme zariadenie</string>
<string name="error_not_reachable">Zariadenie nedostupné</string>
<string name="error_already_requested">Spárovanie už vyžiadané</string>
@@ -66,10 +55,6 @@
<string name="error_canceled_by_user">Zrušené používateľom</string>
<string name="error_canceled_by_other_peer">Zrušené iným klientom</string>
<string name="error_invalid_key">Získaný nesprávny kľúč</string>
<string name="encryption_info_title">Informácia o šifrovaní</string>
<string name="encryption_info_msg_no_ssl">Ďalšie zariadenie nepoužíva aktuálnu verziu KDE Connect, používam klasickú metódu šifrovania.</string>
<string name="my_device_fingerprint">Odtlačok SHA1 vášho certifikátu zariadenia je:</string>
<string name="remote_device_fingerprint">Odtlačok SHA1 vzdialeného certifikátu zariadenia je:</string>
<string name="pair_requested">Spárovanie vyžiadané</string>
<string name="pairing_request_from">Požiadavka na spárovanie od %1s</string>
<string name="received_url_title">Prijatý odkaz od %1s</string>
@@ -158,6 +143,4 @@
<string name="findmyphone_title">Nájsť môj telefón</string>
<string name="findmyphone_description">Prezvoní vaše zariadenie, aby ste ho našli.</string>
<string name="findmyphone_found">Nájdené</string>
<string name="open">Otvoriť</string>
<string name="close">Zavrieť</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand">Kör kommando</string>
<string name="pref_plugin_runcommand_desc">Utlös fjärrkommandon från din telefon</string>
<string name="pref_plugin_runcommand_desc">Kör ett kommando på systemet</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">Skicka och ta emot ping</string>
<string name="pref_plugin_notifications">Synkronisering av underrättelser</string>
@@ -32,8 +32,6 @@
<string name="mousepad_info">Flytta fingret på skärmen för att röra muspekaren. Rör för att klicka, och använd två eller tre fingrar för höger- och mittenknapparna. Använd en längre beröring för drag och släpp.</string>
<string name="mousepad_double_tap_settings_title">Ställ in åtgärd vid två fingerberöringar</string>
<string name="mousepad_triple_tap_settings_title">Ställ in åtgärd vid tre fingerberöringar</string>
<string name="mousepad_sensitivity_settings_title">Ställ in tryckplattans känslighet</string>
<string name="mousepad_scroll_direction_title">Omvänd rullningsriktning</string>
<string-array name="mousepad_tap_entries">
<item>Högerklick</item>
<item>Mittenklick</item>
@@ -41,14 +39,6 @@
</string-array>
<string name="mousepad_double_default">höger</string>
<string name="mousepad_triple_default">mitten</string>
<string name="mousepad_sensitivity_default">normal</string>
<string-array name="mousepad_sensitivity_entries">
<item>Långsammaste</item>
<item>Ovanför långsammaste</item>
<item>Normal</item>
<item>Ovanför normal</item>
<item>Snabbaste</item>
</string-array>
<string name="category_connected_devices">Anslutna apparater</string>
<string name="category_not_paired_devices">Tillgängliga apparater</string>
<string name="category_remembered_devices">Ihågkomna apparater</string>
@@ -56,7 +46,6 @@
<string name="device_menu_plugins">Inställningar av insticksprogram</string>
<string name="device_menu_unpair">Ta bort ihopparning</string>
<string name="device_not_reachable">Ihopparad apparat kan inte nås</string>
<string name="pair_new_device">Para ihop ny apparat</string>
<string name="unknown_device">Okänd apparat</string>
<string name="error_not_reachable">Apparaten kan inte nås</string>
<string name="error_already_requested">Ihopparning redan begärd</string>
@@ -66,10 +55,6 @@
<string name="error_canceled_by_user">Avbruten av användaren</string>
<string name="error_canceled_by_other_peer">Avbruten av motparten</string>
<string name="error_invalid_key">Ogiltig nyckel mottagen</string>
<string name="encryption_info_title">Krypteringsinformation</string>
<string name="encryption_info_msg_no_ssl">Den andra apparaten använder inte en aktuell version av KDE-anslut. Använder den föråldrade krypteringsmetoden.</string>
<string name="my_device_fingerprint">SHA1-fingeravtryck för din apparats certifikat är:</string>
<string name="remote_device_fingerprint">SHA1-fingeravtryck för den andra apparatens certifikat är:</string>
<string name="pair_requested">Ihopparning begärd</string>
<string name="pairing_request_from">Begäran om ihopparning från %1s</string>
<string name="received_url_title">Tog emot länk från %1s</string>
@@ -158,6 +143,4 @@
<string name="findmyphone_title">Hitta min telefon</string>
<string name="findmyphone_description">Ringer till telefonen så att du kan hitta den.</string>
<string name="findmyphone_found">Hittade den</string>
<string name="open">Öppna</string>
<string name="close">Stäng</string>
</resources>

View File

@@ -13,7 +13,7 @@
<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_runcommand_desc">Виконує команду у вашій системі</string>
<string name="pref_plugin_ping">Підтримання зв’язку</string>
<string name="pref_plugin_ping_desc">Надсилання і отримання сигналів підтримання зв’язку</string>
<string name="pref_plugin_notifications">Синхронізація сповіщень</string>
@@ -32,8 +32,6 @@
<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>
@@ -41,14 +39,6 @@
</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>
@@ -66,10 +56,6 @@
<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>
@@ -158,6 +144,4 @@
<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

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="KdeConnectTheme.NoActionBar" parent="KdeConnectThemeBase.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
</style>
</resources>

View File

@@ -13,6 +13,7 @@
<string name="pref_plugin_mpris">多媒体控制</string>
<string name="pref_plugin_mpris_desc">从手机控制音频或视频</string>
<string name="pref_plugin_runcommand">执行命令</string>
<string name="pref_plugin_runcommand_desc">在您的系统上执行命令</string>
<string name="pref_plugin_ping">Ping</string>
<string name="pref_plugin_ping_desc">发送和接受ping</string>
<string name="pref_plugin_notifications">通知同步</string>
@@ -38,13 +39,6 @@
</string-array>
<string name="mousepad_double_default"></string>
<string name="mousepad_triple_default"></string>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string name="category_connected_devices">已连接设备</string>
<string name="category_not_paired_devices">可用设备</string>
<string name="category_remembered_devices">已记住设备</string>
@@ -52,7 +46,6 @@
<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>
@@ -147,8 +140,7 @@
<string name="pref_plugin_telepathy">发送短消息</string>
<string name="pref_plugin_telepathy_desc">从桌面发送短消息</string>
<string name="plugin_not_supported">设备不支持此插件</string>
<string name="findmyphone_title">找到我的手机</string>
<string name="findmyphone_description">让手机响铃从而找到它</string>
<string name="findmyphone_found">找到</string>
<string name="open">打开</string>
<string name="close">关闭</string>
</resources>

View File

@@ -14,7 +14,7 @@
<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_runcommand">Run Command</string>
<string name="pref_plugin_runcommand_desc">Trigger remote commands from your phone</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_desc">Send and receive pings</string>
<string name="pref_plugin_notifications">Notification sync</string>
@@ -33,12 +33,8 @@
<string name="mousepad_info">Move a finger on the screen to move the mouse cursor. Tap for a click, and use two/three fingers for right and middle buttons. Use a long press to drag\'n drop.</string>
<string name="mousepad_double_tap_settings_title">Set two finger tap action</string>
<string name="mousepad_triple_tap_settings_title">Set three finger tap action</string>
<string name="mousepad_sensitivity_settings_title">Set touchpad sensitivity</string>
<string name="mousepad_double_tap_key" translatable="false">mousepad_double_tap_key</string>
<string name="mousepad_triple_tap_key" translatable="false">mousepad_triple_tap_key</string>
<string name="mousepad_sensitivity_key" translatable="false">mousepad_sensitivity_key</string>
<string name="mousepad_scroll_direction_title">Reverse Scrolling Direction</string>
<string name="mousepad_scroll_direction" translatable="false">mousepad_scroll_direction</string>
<string-array name="mousepad_tap_entries">
<item>Right click</item>
<item>Middle click</item>
@@ -46,26 +42,11 @@
</string-array>
<string name="mousepad_double_default">right</string>
<string name="mousepad_triple_default">middle</string>
<string name="mousepad_sensitivity_default">default</string>
<string-array name="mousepad_tap_values" translatable="false">
<item>right</item>
<item>middle</item>
<item>none</item>
</string-array>
<string-array name="mousepad_sensitivity_entries">
<item>Slowest</item>
<item>Above Slowest</item>
<item>Default</item>
<item>Above Default</item>
<item>Fastest</item>
</string-array>
<string-array name="mousepad_sensitivity_values" translatable="false">
<item>slowest</item>
<item>aboveSlowest</item>
<item>default</item>
<item>aboveDefault</item>
<item>fastest</item>
</string-array>
<string name="category_connected_devices">Connected devices</string>
<string name="category_not_paired_devices">Available devices</string>
<string name="category_remembered_devices">Remembered devices</string>
@@ -83,10 +64,6 @@
<string name="error_canceled_by_user">Canceled by user</string>
<string name="error_canceled_by_other_peer">Canceled by other peer</string>
<string name="error_invalid_key">Invalid key received</string>
<string name="encryption_info_title">Encryption Info</string>
<string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string>
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is:</string>
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is:</string>
<string name="pair_requested">Pair requested</string>
<string name="pairing_request_from">Pairing request from %1s</string>
<string name="received_url_title">Received link from %1s</string>
@@ -182,10 +159,7 @@
<string name="pref_plugin_telepathy">Send SMS</string>
<string name="pref_plugin_telepathy_desc">Send text messages from your desktop</string>
<string name="plugin_not_supported">This plugin is not supported by the device</string>
<string name="findmyphone_title">Find my phone</string>
<string name="findmyphone_title">Find My Phone</string>
<string name="findmyphone_description">Rings this phone so you can find it.</string>
<string name="findmyphone_found">Found</string>
<string name="open">Open</string>
<string name="close">Close</string>
</resources>

View File

@@ -4,7 +4,7 @@
<color name="accent">#4ebffa</color>
<!-- NoActionBar because we use a Toolbar widget as ActionBar -->
<style name="KdeConnectThemeBase" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="KdeConnectTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- The three colors used by system widgets, according to https://chris.banes.me/2014/10/17/appcompat-v21/ -->
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primaryDark</item>
@@ -13,15 +13,9 @@
<item name="android:textColor">@android:color/black</item>
</style>
<style name="KdeConnectThemeBase.NoActionBar" parent="KdeConnectThemeBase">
<style name="KdeConnectTheme.NoActionBar" parent="KdeConnectTheme">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="KdeConnectTheme" parent="KdeConnectThemeBase">
</style>
<style name="KdeConnectTheme.NoActionBar" parent="KdeConnectThemeBase.NoActionBar">
</style>
</resources>

View File

@@ -21,18 +21,4 @@
android:entryValues="@array/mousepad_tap_values"
android:defaultValue="@string/mousepad_triple_default" />
<ListPreference
android:id="@+id/mousepad_sensitivity_preference"
android:key="@string/mousepad_sensitivity_key"
android:title="@string/mousepad_sensitivity_settings_title"
android:summary="%s"
android:entries="@array/mousepad_sensitivity_entries"
android:entryValues="@array/mousepad_sensitivity_values"
android:defaultValue="@string/mousepad_sensitivity_default" />
<CheckBoxPreference
android:id="@+id/mousepad_scroll_preference"
android:key="@string/mousepad_scroll_direction"
android:title="@string/mousepad_scroll_direction_title"
android:defaultValue="false" />
</PreferenceScreen>

View File

@@ -20,8 +20,6 @@
package org.kde.kdeconnect.Backends;
import android.content.Context;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
@@ -32,7 +30,9 @@ import java.util.ArrayList;
public abstract class BaseLink {
protected final Context context;
public enum ConnectionStarted {
Locally, Remotely;
};
public interface PackageReceiver {
void onPackageReceived(NetworkPackage np);
@@ -43,16 +43,17 @@ public abstract class BaseLink {
private final ArrayList<PackageReceiver> receivers = new ArrayList<>();
protected PrivateKey privateKey;
protected BaseLink(Context context, String deviceId, BaseLinkProvider linkProvider) {
this.context = context;
protected ConnectionStarted connectionSource; // If the other device sent me a broadcast,
// I should not close the connection with it
// because it's probably trying to find me and
// potentially ask for pairing.
protected BaseLink(String deviceId, BaseLinkProvider linkProvider, ConnectionStarted connectionSource) {
this.linkProvider = linkProvider;
this.deviceId = deviceId;
this.connectionSource = connectionSource;
}
/* To be implemented by each link for pairing handlers */
public abstract String getName();
public abstract BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback);
public String getDeviceId() {
return deviceId;
}
@@ -65,9 +66,8 @@ public abstract class BaseLink {
return linkProvider;
}
//The daemon will periodically destroy unpaired links if this returns false
public boolean linkShouldBeKeptAlive() {
return false;
public ConnectionStarted getConnectionSource() {
return connectionSource;
}
public void addPackageReceiver(PackageReceiver pr) {
@@ -90,6 +90,5 @@ public abstract class BaseLink {
//TO OVERRIDE, should be sync
public abstract void sendPackage(NetworkPackage np,Device.SendPackageStatusCallback callback);
@Deprecated
public abstract void sendPackageEncrypted(NetworkPackage np,Device.SendPackageStatusCallback callback, PublicKey key);
}

View File

@@ -22,6 +22,7 @@ package org.kde.kdeconnect.Backends;
import org.kde.kdeconnect.NetworkPackage;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class BaseLinkProvider {

View File

@@ -1,92 +0,0 @@
/*
* Copyright 2015 Vineet Garg <grg.vineet@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.Backends;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
import java.util.Timer;
/**
* This class separates the pairing interface for each type of link.
* Since different links can pair via different methods, like for LanLink certificate and public key should be shared,
* for Bluetooth link they should be paired via bluetooth etc.
* Each "Device" instance maintains a hash map for these pairing handlers so that there can be single pairing handler per
* per link type per device.
* Pairing handler keeps information about device, latest link, and pair status of the link
* During first pairing process, the pairing process is nearly same as old process.
* After that if any one of the link is paired, then we can say that device is paired, so new link will pair automatically
*/
public abstract class BasePairingHandler {
protected enum PairStatus{
NotPaired,
Requested,
RequestedByPeer,
Paired
}
public interface PairingHandlerCallback {
void incomingRequest();
void pairingDone();
void pairingFailed(String error);
void unpaired();
}
protected Device mDevice;
protected BaseLink mBaseLink;
protected PairStatus mPairStatus;
protected PairingHandlerCallback mCallback;
protected Timer mPairingTimer;
public BasePairingHandler(Device device, PairingHandlerCallback callback) {
this.mDevice = device;
this.mCallback = callback;
}
public void setLink(BaseLink baseLink) {
this.mBaseLink = baseLink;
}
public boolean isPaired() {
return mPairStatus == PairStatus.Paired;
}
public boolean isPairRequested() {
return mPairStatus == PairStatus.Requested;
}
public boolean isPairRequestedByPeer() {
return mPairStatus == PairStatus.RequestedByPeer;
}
/* To be implemented by respective pairing handler */
public abstract NetworkPackage createPairPackage();
public abstract void packageReceived(NetworkPackage np) throws Exception;
public abstract void requestPairing();
public abstract void acceptPairing();
public abstract void rejectPairing();
public abstract void pairingDone();
public abstract void unpair();
}

View File

@@ -20,130 +20,51 @@
package org.kde.kdeconnect.Backends.LanBackend;
import android.content.Context;
import android.util.Log;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.session.IoSession;
import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.StringsHelper;
import org.kde.kdeconnect.NetworkPackage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.channels.NotYetConnectedException;
import java.security.PublicKey;
import javax.net.ssl.SSLSocket;
public class LanLink extends BaseLink {
public interface LinkDisconnectedCallback {
void linkDisconnected(LanLink brokenLink);
}
public enum ConnectionStarted {
Locally, Remotely;
};
private ConnectionStarted connectionSource; // If the other device sent me a broadcast,
// I should not close the connection with it
// because it's probably trying to find me and
// potentially ask for pairing.
private Socket socket = null;
private LinkDisconnectedCallback callback;
private IoSession session = null;
@Override
public void disconnect() {
Log.i("LanLink/Disconnect","socket:"+ socket.hashCode());
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
closeSocket();
super.disconnect();
}
public void closeSocket() {
if (session == null) {
Log.e("KDE/LanLink", "Not yet connected");
return;
}
session.close(true);
}
//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);
callback = linkProvider;
reset(socket, connectionSource);
}
@Override
public String getName() {
return "LanLink";
}
@Override
public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) {
return new LanPairingHandler(device, callback);
public LanLink(IoSession session, String deviceId, BaseLinkProvider linkProvider, ConnectionStarted connectionSource) {
super(deviceId, linkProvider, connectionSource);
this.session = session;
}
//Blocking, do not call from main thread
private void sendPackageInternal(NetworkPackage np, final Device.SendPackageStatusCallback callback, PublicKey key) {
if (socket == null) {
if (session == null) {
Log.e("KDE/sendPackage", "Not yet connected");
callback.sendFailure(new NotYetConnectedException());
return;
@@ -154,7 +75,7 @@ public class LanLink extends BaseLink {
//Prepare socket for the payload
final ServerSocket server;
if (np.hasPayload()) {
server = LanLinkProvider.openServerSocketOnFreePort(LanLinkProvider.PAYLOAD_TRANSFER_MIN_PORT);
server = openTcpSocketOnFreePort();
JSONObject payloadTransferInfo = new JSONObject();
payloadTransferInfo.put("port", server.getLocalPort());
np.setPayloadTransferInfo(payloadTransferInfo);
@@ -164,67 +85,52 @@ public class LanLink extends BaseLink {
//Encrypt if key provided
if (key != null) {
np = RsaHelper.encrypt(np, key);
np = np.encrypt(key);
}
//Log.e("LanLink/sendPackage", np.getType());
//Send body of the network package
try {
OutputStream writter = socket.getOutputStream();
writter.write(np.serialize().getBytes(StringsHelper.UTF8));
writter.flush();
} catch (Exception e) {
callback.sendFailure(e);
e.printStackTrace();
disconnect();
WriteFuture future = session.write(np.serialize());
future.awaitUninterruptibly();
if (!future.isWritten()) {
//Log.e("KDE/sendPackage", "!future.isWritten()");
callback.sendFailure(future.getException());
return;
}
//Send payload
if (server != null) {
Socket payloadSocket = null;
OutputStream outputStream = null;
InputStream inputStream = null;
OutputStream socket = null;
try {
//Wait a maximum of 10 seconds for the other end to establish a connection with our socket, close it afterwards
server.setSoTimeout(10*1000);
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();
socket = server.accept().getOutputStream();
Log.i("KDE/LanLink", "Beginning to send payload");
byte[] buffer = new byte[4096];
int bytesRead;
long progress = 0;
while ((bytesRead = inputStream.read(buffer)) != -1) {
InputStream stream = np.getPayload();
while ((bytesRead = stream.read(buffer)) != -1) {
//Log.e("ok",""+bytesRead);
progress += bytesRead;
outputStream.write(buffer, 0, bytesRead);
socket.write(buffer, 0, bytesRead);
if (np.getPayloadSize() > 0) {
callback.sendProgress((int)(progress / np.getPayloadSize()));
}
}
outputStream.flush();
outputStream.close();
socket.flush();
stream.close();
Log.i("KDE/LanLink", "Finished sending payload ("+progress+" bytes written)");
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/sendPackage", "Exception: "+e);
callback.sendFailure(e);
return;
} finally {
if (socket != null) {
try { socket.close(); } catch (Exception e) { }
}
try { server.close(); } catch (Exception e) { }
try { payloadSocket.close(); } catch (Exception e) { }
try { inputStream.close(); } catch (Exception e) { }
try { outputStream.close(); } catch (Exception e) { }
}
}
@@ -255,31 +161,30 @@ public class LanLink extends BaseLink {
sendPackageInternal(np, callback, key);
}
private void receivedNetworkPackage(NetworkPackage np) {
public void injectNetworkPackage(NetworkPackage np) {
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
try {
np = RsaHelper.decrypt(np, privateKey);
np = np.decrypt(privateKey);
} catch(Exception e) {
e.printStackTrace();
Log.e("KDE/onPackageReceived","Exception decrypting the package");
Log.e("KDE/onPackageReceived","Exception reading the key needed to decrypt the package");
}
}
if (np.hasPayloadTransferInfo()) {
Socket payloadSocket = new Socket();
Socket socket = null;
try {
// Use ssl if existing link is on ssl
if (socket instanceof SSLSocket) {
payloadSocket = SslHelper.convertToSslSocket(context, payloadSocket, getDeviceId(), true, true);
}
socket = new Socket();
int tcpPort = np.getPayloadTransferInfo().getInt("port");
InetSocketAddress address = (InetSocketAddress) socket.getRemoteSocketAddress();
payloadSocket.connect(new InetSocketAddress(address.getAddress(), tcpPort));
np.setPayload(payloadSocket.getInputStream(), np.getPayloadSize());
InetSocketAddress address = (InetSocketAddress)session.getRemoteAddress();
socket.connect(new InetSocketAddress(address.getAddress(), tcpPort));
np.setPayload(socket.getInputStream(), np.getPayloadSize());
} catch (Exception e) {
try { payloadSocket.close(); } catch(Exception ignored) { }
try { socket.close(); } catch(Exception ignored) { }
e.printStackTrace();
Log.e("KDE/LanLink", "Exception connecting to payload remote socket");
}
@@ -289,19 +194,27 @@ public class LanLink extends BaseLink {
packageReceived(np);
}
@Override
public boolean linkShouldBeKeptAlive() {
//We keep the remotely initiated connections, since the remotes require them if they want to request
//pairing to us, or connections that are already paired. TODO: Keep connections in the process of pairing
if (connectionSource == ConnectionStarted.Remotely) {
//Log.e("LinkShouldBeKeptAlive", "because the other end started the connection");
return true;
static ServerSocket openTcpSocketOnFreePort() 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;
}
}
}
//Log.e("LinkShouldBeKeptAlive", "false");
return false;
return candidateServer;
}
}

View File

@@ -21,332 +21,303 @@
package org.kde.kdeconnect.Backends.LanBackend;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.support.v4.util.LongSparseArray;
import android.util.Log;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Helpers.StringsHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.security.cert.Certificate;
import java.net.SocketAddress;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Set;
import javax.net.SocketFactory;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLSocket;
public class LanLinkProvider extends BaseLinkProvider {
public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDisconnectedCallback {
public static final int MIN_VERSION_WITH_SSL_SUPPORT = 6;
public static final int MIN_VERSION_WITH_NEW_PORT_SUPPORT = 7;
final static int MIN_PORT_LEGACY = 1714;
final static int MIN_PORT = 1716;
final static int MAX_PORT = 1764;
final static int PAYLOAD_TRANSFER_MIN_PORT = 1739;
public static final String KEY_CUSTOM_DEVLIST_PREFERENCE = "device_list_preference";
private final static int port = 1714;
private final Context context;
private final HashMap<String, LanLink> visibleComputers = new HashMap<>();
private final LongSparseArray<LanLink> nioSessions = new LongSparseArray<>();
private final LongSparseArray<NioSocketConnector> nioConnectors = new LongSparseArray<>();
private final HashMap<String, LanLink> visibleComputers = new HashMap<>(); //Links by device id
private NioSocketAcceptor tcpAcceptor = null;
private NioDatagramAcceptor udpAcceptor = null;
private ServerSocket tcpServer;
private DatagramSocket udpServer;
private DatagramSocket udpServerOldPort;
private final IoHandler tcpHandler = new IoHandlerAdapter() {
@Override
public void sessionClosed(IoSession session) throws Exception {
try {
long id = session.getId();
final LanLink brokenLink = nioSessions.get(id);
NioSocketConnector connector = nioConnectors.get(id);
if (connector != null) {
connector.dispose();
nioConnectors.remove(id);
}
if (brokenLink != null) {
nioSessions.remove(id);
//Log.i("KDE/LanLinkProvider", "nioSessions.size(): " + nioSessions.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 emiting connectionLost, in case the same device re-appears
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
connectionLost(brokenLink);
private boolean listening = false;
}
}).start();
// 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<>();
@Override // SocketClosedCallback
public void linkDisconnected(LanLink brokenLink) {
String deviceId = brokenLink.getDeviceId();
visibleComputers.remove(deviceId);
connectionLost(brokenLink);
}
//They received my UDP broadcast and are connecting to me. The first thing they sned should be their identity.
public void tcpPackageReceived(Socket socket) throws Exception {
NetworkPackage networkPackage;
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String message = reader.readLine();
networkPackage = NetworkPackage.unserialize(message);
//Log.e("TcpListener","Received TCP package: "+networkPackage.serialize());
} catch (Exception e) {
e.printStackTrace();
return;
}
} catch (Exception e) { //If we don't catch it here, Mina will swallow it :/
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "sessionClosed exception");
}
}
if (!networkPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
Log.e("KDE/LanLinkProvider", "Expecting an identity package instead of " + networkPackage.getType());
return;
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
super.messageReceived(session, message);
Log.i("KDE/LanLinkProvider", "Identity package received from a TCP connection from " + networkPackage.getString("deviceName"));
identityPackageReceived(networkPackage, socket, LanLink.ConnectionStarted.Locally);
}
//Log.e("LanLinkProvider","Incoming package, address: "+session.getRemoteAddress()).toString());
//Log.e("LanLinkProvider","Received:"+message);
//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");
String theMessage = (String) message;
if (theMessage.isEmpty()) {
Log.w("KDE/LanLinkProvider","Empty package received");
return;
} else {
}
NetworkPackage np = NetworkPackage.unserialize(theMessage);
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
String myId = DeviceHelper.getDeviceId(context);
if (deviceId.equals(myId)) {
//Ignore my own broadcast
if (np.getString("deviceId").equals(myId)) {
return;
}
//Log.i("KDE/LanLinkProvider", "Identity package received from " + np.getString("deviceName"));
LanLink link = new LanLink(session, np.getString("deviceId"), LanLinkProvider.this, BaseLink.ConnectionStarted.Locally);
nioSessions.put(session.getId(),link);
//Log.e("KDE/LanLinkProvider","nioSessions.size(): " + nioSessions.size());
addLink(np, link);
} else {
LanLink prevLink = nioSessions.get(session.getId());
if (prevLink == null) {
Log.e("KDE/LanLinkProvider","Expecting an identity package (A)");
} else {
prevLink.injectNetworkPackage(np);
}
}
if (identityPackage.getInt("protocolVersion") >= MIN_VERSION_WITH_NEW_PORT_SUPPORT && identityPackage.getInt("tcpPort") < MIN_PORT) {
Log.w("KDE/LanLinkProvider", "Ignoring a udp broadcast from legacy port because it comes from a device which knows about the new port.");
return;
}
}
};
Log.i("KDE/LanLinkProvider", "Broadcast identity package received from " + identityPackage.getString("deviceName"));
private final IoHandler udpHandler = new IoHandlerAdapter() {
@Override
public void messageReceived(IoSession udpSession, Object message) throws Exception {
super.messageReceived(udpSession, message);
int tcpPort = identityPackage.getInt("tcpPort", MIN_PORT);
//Log.e("LanLinkProvider", "Udp message received (" + message.getClass() + ") " + message.toString());
SocketFactory socketFactory = SocketFactory.getDefault();
Socket socket = socketFactory.createSocket(address, tcpPort);
configureSocket(socket);
try {
//We should receive a string thanks to the TextLineCodecFactory filter
String theMessage = (String) message;
final NetworkPackage identityPackage = NetworkPackage.unserialize(theMessage);
OutputStream out = socket.getOutputStream();
NetworkPackage myIdentity = NetworkPackage.createIdentityPackage(context);
out.write(myIdentity.serialize().getBytes());
out.flush();
identityPackageReceived(identityPackage, socket, LanLink.ConnectionStarted.Remotely);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider", "Cannot connect to " + address);
e.printStackTrace();
if (!reverseConnectionBlackList.contains(address)) {
Log.w("KDE/LanLinkProvider","Blacklisting "+address);
reverseConnectionBlackList.add(address);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
reverseConnectionBlackList.remove(address);
if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
Log.e("KDE/LanLinkProvider", "Expecting an identity package (B)");
return;
} else {
String myId = DeviceHelper.getDeviceId(context);
if (identityPackage.getString("deviceId").equals(myId)) {
return;
}
}, 5*1000);
}
// Try to cause a reverse connection
onNetworkChange();
}
}
}
//Log.i("KDE/LanLinkProvider", "Identity package received, creating link");
private void configureSocket(Socket socket) {
try {
socket.setKeepAlive(true);
} catch (SocketException e) {
e.printStackTrace();
}
}
final InetSocketAddress address = (InetSocketAddress) udpSession.getRemoteAddress();
private void identityPackageReceived(final NetworkPackage identityPackage, final Socket socket, final LanLink.ConnectionStarted connectionStarted) {
final NioSocketConnector connector = new NioSocketConnector();
connector.setHandler(tcpHandler);
connector.getSessionConfig().setKeepAlive(true);
//TextLineCodecFactory will buffer incoming data and emit a message very time it finds a \n
TextLineCodecFactory textLineFactory = new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.UNIX, LineDelimiter.UNIX);
textLineFactory.setDecoderMaxLineLength(512*1024); //Allow to receive up to 512kb of data
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(textLineFactory));
String myId = DeviceHelper.getDeviceId(context);
final String deviceId = identityPackage.getString("deviceId");
if (deviceId.equals(myId)) {
Log.e("KDE/LanLinkProvider", "Somehow I'm connected to myself, ignoring. This should not happen.");
return;
}
int tcpPort = identityPackage.getInt("tcpPort", port);
final ConnectFuture future = connector.connect(new InetSocketAddress(address.getAddress(), tcpPort));
future.addListener(new IoFutureListener<IoFuture>() {
// If I'm the TCP server I will be the SSL client and viceversa.
final boolean clientMode = (connectionStarted == LanLink.ConnectionStarted.Locally);
// Add ssl handler if device uses new protocol
try {
if (identityPackage.getInt("protocolVersion") >= MIN_VERSION_WITH_SSL_SUPPORT) {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
boolean isDeviceTrusted = preferences.getBoolean(deviceId, false);
Log.i("KDE/LanLinkProvider","Starting SSL handshake with " + identityPackage.getString("deviceName") + " trusted:"+isDeviceTrusted);
final SSLSocket sslsocket = SslHelper.convertToSslSocket(context, socket, deviceId, isDeviceTrusted, clientMode);
sslsocket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
@Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
String mode = clientMode? "client" : "server";
public void operationComplete(IoFuture ioFuture) {
try {
Certificate certificate = event.getPeerCertificates()[0];
identityPackage.set("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
Log.i("KDE/LanLinkProvider","Handshake as " + mode + " successful with " + identityPackage.getString("deviceName") + " secured with " + event.getCipherSuite());
addLink(identityPackage, sslsocket, connectionStarted);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider","Handshake as " + mode + " failed with " + identityPackage.getString("deviceName"));
e.printStackTrace();
BackgroundService.RunCommand(context, new BackgroundService.InstanceCallback() {
future.removeListener(this);
final IoSession session = ioFuture.getSession();
Log.i("KDE/LanLinkProvider", "Connection successful: " + session.isConnected());
final LanLink link = new LanLink(session, identityPackage.getString("deviceId"), LanLinkProvider.this, BaseLink.ConnectionStarted.Remotely);
new Thread(new Runnable() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
if (device == null) return;
device.unpair();
public void run() {
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
link.sendPackage(np2,new Device.SendPackageStatusCallback() {
@Override
protected void onSuccess() {
nioSessions.put(session.getId(), link);
nioConnectors.put(session.getId(), connector);
//Log.e("KDE/LanLinkProvider","nioSessions.size(): " + nioSessions.size());
addLink(identityPackage, link);
}
@Override
protected void onFailure(Throwable e) {
Log.e("KDE/LanLinkProvider", "Connection failed: could not send identity package back");
}
});
}
});
}).start();
} catch (Exception e) { //If we don't catch it here, Mina will swallow it :/
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "sessionClosed exception");
}
}
});
//Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection
new Thread(new Runnable() {
@Override
public void run() {
try {
sslsocket.startHandshake();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} else {
addLink(identityPackage, socket, connectionStarted);
} catch (Exception e) {
Log.e("KDE/LanLinkProvider","Exception receiving udp package!!");
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
};
}
private void addLink(final NetworkPackage identityPackage, Socket socket, LanLink.ConnectionStarted connectionOrigin) throws IOException {
private void addLink(NetworkPackage identityPackage, LanLink link) {
String deviceId = identityPackage.getString("deviceId");
LanLink currentLink = visibleComputers.get(deviceId);
if (currentLink != null) {
//Update old link
Log.i("KDE/LanLinkProvider", "Reusing same link for device " + deviceId);
final Socket oldSocket = currentLink.reset(socket, connectionOrigin);
//Log.e("KDE/LanLinkProvider", "Replacing socket. old: "+ oldSocket.hashCode() + " - new: "+ socket.hashCode());
} else {
Log.i("KDE/LanLinkProvider", "Creating a new link for device " + deviceId);
//Let's create the link
LanLink link = new LanLink(context, deviceId, this, socket, connectionOrigin);
visibleComputers.put(deviceId, link);
connectionAccepted(identityPackage, link);
Log.i("KDE/LanLinkProvider","addLink to "+deviceId);
LanLink oldLink = visibleComputers.get(deviceId);
if (oldLink == link) {
Log.e("KDE/LanLinkProvider", "oldLink == link. This should not happen!");
return;
}
visibleComputers.put(deviceId, link);
connectionAccepted(identityPackage, link);
if (oldLink != null) {
Log.i("KDE/LanLinkProvider","Removing old connection to same device");
oldLink.closeSocket();
connectionLost(oldLink);
}
}
public LanLinkProvider(Context context) {
this.context = context;
//This handles the case when I'm the new device in the network and somebody answers my introduction package
tcpAcceptor = new NioSocketAcceptor();
tcpAcceptor.setHandler(tcpHandler);
tcpAcceptor.getSessionConfig().setKeepAlive(true);
tcpAcceptor.getSessionConfig().setReuseAddress(true);
//TextLineCodecFactory will buffer incoming data and emit a message very time it finds a \n
TextLineCodecFactory textLineFactory = new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.UNIX, LineDelimiter.UNIX);
textLineFactory.setDecoderMaxLineLength(512*1024); //Allow to receive up to 512kb of data
tcpAcceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(textLineFactory));
udpAcceptor = new NioDatagramAcceptor();
udpAcceptor.getSessionConfig().setReuseAddress(true); //Share port if existing
//TextLineCodecFactory will buffer incoming data and emit a message very time it finds a \n
//This one will have the default MaxLineLength of 1KB
udpAcceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(
new TextLineCodecFactory(Charset.defaultCharset(), LineDelimiter.UNIX, LineDelimiter.UNIX)
)
);
}
private DatagramSocket setupUdpListener(int udpPort) {
final DatagramSocket server;
try {
server = new DatagramSocket(udpPort);
server.setReuseAddress(true);
server.setBroadcast(true);
} catch (SocketException e) {
Log.e("LanLinkProvider", "Error creating udp server");
e.printStackTrace();
return null;
@Override
public void onStart() {
//This handles the case when I'm the existing device in the network and receive a "hello" UDP package
Set<SocketAddress> addresses = udpAcceptor.getLocalAddresses();
for (SocketAddress address : addresses) {
Log.i("KDE/LanLinkProvider", "UDP unbind old address");
udpAcceptor.unbind(address);
}
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;
}
private void setupTcpListener() {
//Log.i("KDE/LanLinkProvider", "UDP Bind.");
udpAcceptor.setHandler(udpHandler);
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) {
udpAcceptor.bind(new InetSocketAddress(port));
} catch(Exception e) {
Log.e("KDE/LanLinkProvider", "Error: Could not bind udp socket");
e.printStackTrace();
}
}
static ServerSocket openServerSocketOnFreePort(int minPort) throws IOException {
int tcpPort = minPort;
while(tcpPort < MAX_PORT) {
boolean success = false;
int tcpPort = port;
while(!success) {
try {
ServerSocket candidateServer = new ServerSocket();
candidateServer.bind(new InetSocketAddress(tcpPort));
Log.i("KDE/LanLink", "Using port "+tcpPort);
return candidateServer;
} catch(IOException e) {
tcpAcceptor.bind(new InetSocketAddress(tcpPort));
success = true;
} catch(Exception e) {
tcpPort++;
}
}
Log.e("KDE/LanLink", "No ports available");
throw new IOException("No ports available");
}
void broadcastUdpPackage() {
Log.i("KDE/LanLinkProvider","Using tcpPort "+tcpPort);
//I'm on a new network, let's be polite and introduce myself
final int finalTcpPort = tcpPort;
new Thread(new Runnable() {
@Override
public void run() {
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(CustomDevicesActivity.KEY_CUSTOM_DEVLIST_PREFERENCE, "");
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(
KEY_CUSTOM_DEVLIST_PREFERENCE, "");
ArrayList<String> iplist = new ArrayList<>();
if (!deviceListPrefs.isEmpty()) {
iplist = CustomDevicesActivity.deserializeIpList(deviceListPrefs);
@@ -354,14 +325,14 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
iplist.add("255.255.255.255"); //Default: broadcast.
NetworkPackage identity = NetworkPackage.createIdentityPackage(context);
identity.set("tcpPort", MIN_PORT);
identity.set("tcpPort", finalTcpPort);
DatagramSocket socket = null;
byte[] bytes = null;
try {
socket = new DatagramSocket();
socket.setReuseAddress(true);
socket.setBroadcast(true);
bytes = identity.serialize().getBytes(StringsHelper.UTF8);
bytes = identity.serialize().getBytes("UTF-8");
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider","Failed to create DatagramSocket");
@@ -372,9 +343,9 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
for (String ipstr : iplist) {
try {
InetAddress client = InetAddress.getByName(ipstr);
socket.send(new DatagramPacket(bytes, bytes.length, client, MIN_PORT));
socket.send(new DatagramPacket(bytes, bytes.length, client, MIN_PORT_LEGACY));
//Log.i("KDE/LanLinkProvider","Udp identity package sent to address "+client);
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, client, port);
socket.send(packet);
//Log.i("KDE/LanLinkProvider","Udp identity package sent to address "+packet.getAddress());
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Sending udp identity package failed. Invalid address? (" + ipstr + ")");
@@ -382,62 +353,34 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
}
}
if (socket != null) {
socket.close();
}
socket.close();
}
}).start();
}
@Override
public void onStart() {
//Log.i("KDE/LanLinkProvider", "onStart");
if (!listening) {
listening = true;
udpServer = setupUdpListener(MIN_PORT);
udpServerOldPort = setupUdpListener(MIN_PORT_LEGACY);
// Due to certificate request from SSL server to client, the certificate request message from device with latest android version to device with
// old android version causes a FATAL ALERT message stating that incorrect certificate request
// Server is disabled on these devices and using a reverse connection strategy. This works well for connection of these devices with kde
// and newer android versions. Although devices with android version less than ICS cannot connect to other devices who also have android version less
// than ICS because server is disabled on both
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Log.w("KDE/LanLinkProvider","Not starting a TCP server because it's not supported on Android < 14. Operating only as client.");
} else {
setupTcpListener();
}
broadcastUdpPackage();
}
}
@Override
public void onNetworkChange() {
broadcastUdpPackage();
//Log.e("KDE/LanLinkProvider","onNetworkChange");
//FilesHelper.LogOpenFileCount();
//Keep existing connections open while unbinding the socket
tcpAcceptor.setCloseOnDeactivation(false);
onStop();
tcpAcceptor.setCloseOnDeactivation(true);
//FilesHelper.LogOpenFileCount();
onStart();
//FilesHelper.LogOpenFileCount();
}
@Override
public void onStop() {
//Log.i("KDE/LanLinkProvider", "onStop");
listening = false;
try {
tcpServer.close();
} catch (Exception e){
e.printStackTrace();
}
try {
udpServer.close();
} catch (Exception e){
e.printStackTrace();
}
try {
udpServerOldPort.close();
} catch (Exception e){
e.printStackTrace();
}
udpAcceptor.unbind();
tcpAcceptor.unbind();
}
@Override
@@ -445,4 +388,6 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
return "LanLinkProvider";
}
}

View File

@@ -1,239 +0,0 @@
/*
* Copyright 2015 Vineet Garg <grg.vineet@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.Backends.LanBackend;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect_tp.R;
import java.security.KeyFactory;
import java.security.cert.CertificateEncodingException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Timer;
import java.util.TimerTask;
public class LanPairingHandler extends BasePairingHandler {
public LanPairingHandler(Device device, final PairingHandlerCallback callback) {
super(device, callback);
if (device.isPaired()) {
mPairStatus = PairStatus.Paired;
} else {
mPairStatus = PairStatus.NotPaired;
}
}
@Override
public NetworkPackage createPairPackage() {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
np.set("pair", true);
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(mDevice.getContext());
String publicKey = "-----BEGIN PUBLIC KEY-----\n" + globalSettings.getString("publicKey", "").trim()+ "\n-----END PUBLIC KEY-----\n";
np.set("publicKey", publicKey);
return np;
}
@Override
public void packageReceived(NetworkPackage np) throws Exception{
boolean wantsPair = np.getBoolean("pair");
if (wantsPair == isPaired()) {
if (mPairStatus == PairStatus.Requested) {
//Log.e("Device","Unpairing (pair rejected)");
mPairStatus = PairStatus.NotPaired;
hidePairingNotification();
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
}
return;
}
if (wantsPair) {
//Retrieve their public key
try {
String publicKeyContent = np.getString("publicKey").replace("-----BEGIN PUBLIC KEY-----\n","").replace("-----END PUBLIC KEY-----\n", "");
byte[] publicKeyBytes = Base64.decode(publicKeyContent, 0);
mDevice.publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch (Exception e) {
//IGNORE
}
if (mPairStatus == PairStatus.Requested) { //We started pairing
hidePairingNotification();
pairingDone();
} else {
// If device is already paired, accept pairing silently
if (mDevice.isPaired()) {
acceptPairing();
return;
}
// Pairing notifications are still managed by device as there is no other way to
// know about notificationId to cancel notification when PairActivity is started
// Even putting notificationId in intent does not work because PairActivity can be
// started from MainActivity too, so then notificationId cannot be set
hidePairingNotification();
mDevice.displayPairingNotification();
mPairingTimer = new Timer();
mPairingTimer.schedule(new TimerTask() {
@Override
public void run() {
Log.w("KDE/Device","Unpairing (timeout B)");
mPairStatus = PairStatus.NotPaired;
hidePairingNotification();
}
}, 25*1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
mPairStatus = PairStatus.RequestedByPeer;
mCallback.incomingRequest();
}
} else {
Log.i("KDE/Pairing", "Unpair request");
if (mPairStatus == PairStatus.Requested) {
hidePairingNotification();
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
} else if (mPairStatus == PairStatus.Paired) {
mCallback.unpaired();
}
mPairStatus = PairStatus.NotPaired;
}
}
@Override
public void requestPairing() {
Device.SendPackageStatusCallback statusCallback = new Device.SendPackageStatusCallback() {
@Override
protected void onSuccess() {
hidePairingNotification(); //Will stop the pairingTimer if it was running
mPairingTimer = new Timer();
mPairingTimer.schedule(new TimerTask() {
@Override
public void run() {
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_timed_out));
Log.w("KDE/Device","Unpairing (timeout A)");
mPairStatus = PairStatus.NotPaired;
}
}, 30*1000); //Time to wait for the other to accept
mPairStatus = PairStatus.Requested;
}
@Override
protected void onFailure(Throwable e) {
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_could_not_send_package));
}
};
mDevice.sendPackage(createPairPackage(), statusCallback);
}
public void hidePairingNotification() {
mDevice.hidePairingNotification();
if (mPairingTimer != null) {
mPairingTimer .cancel();
}
}
@Override
public void acceptPairing() {
hidePairingNotification();
Device.SendPackageStatusCallback statusCallback = new Device.SendPackageStatusCallback() {
@Override
protected void onSuccess() {
pairingDone();
}
@Override
protected void onFailure(Throwable e) {
mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_not_reachable));
}
};
mDevice.sendPackage(createPairPackage(), statusCallback);
}
@Override
public void rejectPairing() {
hidePairingNotification();
mPairStatus = PairStatus.NotPaired;
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
np.set("pair", false);
mDevice.sendPackage(np);
}
@Override
public void pairingDone() {
// Store device information needed to create a Device object in a future
//Log.e("KDE/PairingDone", "Pairing Done");
SharedPreferences.Editor editor = mDevice.getContext().getSharedPreferences(mDevice.getDeviceId(), Context.MODE_PRIVATE).edit();
if (mDevice.publicKey != null) {
try {
String encodedPublicKey = Base64.encodeToString(mDevice.publicKey.getEncoded(), 0);
editor.putString("publicKey", encodedPublicKey);
} catch (Exception e) {
Log.e("KDE/PairingDone", "Error encoding public key");
}
}
try {
String encodedCertificate = Base64.encodeToString(mDevice.certificate.getEncoded(), 0);
editor.putString("certificate", encodedCertificate);
} catch (NullPointerException n) {
Log.w("KDE/PairingDone", "Certificate is null, remote device does not support ssl");
} catch (CertificateEncodingException c) {
Log.e("KDE/PairingDOne", "Error encoding certificate");
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Pairng", "Exception");
}
editor.apply();
mPairStatus = PairStatus.Paired;
mCallback.pairingDone();
}
@Override
public void unpair() {
mPairStatus = PairStatus.NotPaired;
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
np.set("pair", false);
mDevice.sendPackage(np);
}
}

View File

@@ -20,11 +20,8 @@
package org.kde.kdeconnect.Backends.LoopbackBackend;
import android.content.Context;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
@@ -32,33 +29,35 @@ import java.security.PublicKey;
public class LoopbackLink extends BaseLink {
public LoopbackLink(Context context, BaseLinkProvider linkProvider) {
super(context, "loopback", linkProvider);
}
@Override
public String getName() {
return "LoopbackLink";
}
@Override
public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) {
return new LoopbackPairingHandler(device, callback);
public LoopbackLink(BaseLinkProvider linkProvider) {
super("loopback", linkProvider, ConnectionStarted.Remotely);
}
@Override
public void sendPackage(NetworkPackage in, Device.SendPackageStatusCallback callback) {
packageReceived(in);
if (in.hasPayload()) {
callback.sendProgress(0);
in.setPayload(in.getPayload(), in.getPayloadSize());
callback.sendProgress(100);
}
callback.sendSuccess();
sendPackageEncrypted(in, callback, null);
}
@Override
public void sendPackageEncrypted(NetworkPackage np, Device.SendPackageStatusCallback callback, PublicKey key) {
sendPackage(np, callback);
public void sendPackageEncrypted(NetworkPackage in, Device.SendPackageStatusCallback callback, PublicKey key) {
try {
if (key != null) {
in = in.encrypt(key);
}
String s = in.serialize();
NetworkPackage out= NetworkPackage.unserialize(s);
if (key != null) {
out = out.decrypt(privateKey);
}
packageReceived(out);
if (in.hasPayload()) {
callback.sendProgress(0);
out.setPayload(in.getPayload(), in.getPayloadSize());
callback.sendProgress(100);
}
callback.sendSuccess();
} catch(Exception e) {
callback.sendFailure(e);
}
}
}

View File

@@ -45,7 +45,7 @@ public class LoopbackLinkProvider extends BaseLinkProvider {
@Override
public void onNetworkChange() {
NetworkPackage np = NetworkPackage.createIdentityPackage(context);
connectionAccepted(np, new LoopbackLink(context, this));
connectionAccepted(np, new LoopbackLink(this));
}
/*
@Override

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2015 Vineet Garg <grg.vineet@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.Backends.LoopbackBackend;
import org.kde.kdeconnect.Backends.LanBackend.LanPairingHandler;
import org.kde.kdeconnect.Device;
public class LoopbackPairingHandler extends LanPairingHandler{
public LoopbackPairingHandler(Device device, PairingHandlerCallback callback) {
super(device, callback);
}
// Extending from LanPairingHandler, as it is similar to it
}

View File

@@ -20,6 +20,7 @@
package org.kde.kdeconnect;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -28,15 +29,18 @@ import android.content.SharedPreferences;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.Backends.LoopbackBackend.LoopbackLinkProvider;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -63,13 +67,11 @@ public class BackgroundService extends Service {
if (wasEmpty) {
onNetworkChange();
}
//Log.e("acquireDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
return wasEmpty;
}
public void releaseDiscoveryMode(Object key) {
boolean removed = discoveryModeAcquisitions.remove(key);
//Log.e("releaseDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
if (removed && discoveryModeAcquisitions.isEmpty()) {
cleanDevices();
}
@@ -154,25 +156,16 @@ public class BackgroundService extends Service {
}
public ArrayList<BaseLinkProvider> getLinkProviders() {
return linkProviders;
}
public Device getDevice(String id) {
return devices.get(id);
}
private void cleanDevices() {
new Thread(new Runnable() {
@Override
public void run() {
for(Device d : devices.values()) {
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
d.disconnect();
}
}
for(Device d : devices.values()) {
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByOtherEnd() && d.getConnectionSource() == BaseLink.ConnectionStarted.Remotely) {
d.disconnect();
}
}).start();
}
}
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
@@ -189,8 +182,8 @@ public class BackgroundService extends Service {
} else {
Log.i("KDE/BackgroundService", "addLink,unknown device: " + deviceId);
device = new Device(BackgroundService.this, identityPackage, link);
if (device.isPaired() || device.isPairRequested() || device.isPairRequestedByPeer()
|| link.linkShouldBeKeptAlive()
if (device.isPaired() || device.isPairRequested() || device.isPairRequestedByOtherEnd()
|| link.getConnectionSource() == BaseLink.ConnectionStarted.Locally
||!discoveryModeAcquisitions.isEmpty() )
{
devices.put(deviceId, device);
@@ -261,7 +254,7 @@ public class BackgroundService extends Service {
Log.i("KDE/BackgroundService", "Service not started yet, initializing...");
initializeSecurityParameters();
initializeRsaKeys();
loadRememberedDevicesFromSettings();
registerLinkProviders();
@@ -271,11 +264,62 @@ public class BackgroundService extends Service {
for (BaseLinkProvider a : linkProviders) {
a.onStart();
}
}
void initializeSecurityParameters() {
RsaHelper.initialiseRsaKeys(this);
SslHelper.initialiseCertificate(this);
private void initializeRsaKeys() {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
if (!settings.contains("publicKey") || !settings.contains("privateKey")) {
KeyPair keyPair;
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
keyPair = keyGen.genKeyPair();
} catch(Exception e) {
e.printStackTrace();
Log.e("KDE/initializeRsaKeys","Exception");
return;
}
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
SharedPreferences.Editor edit = settings.edit();
edit.putString("publicKey",Base64.encodeToString(publicKey, 0).trim()+"\n");
edit.putString("privateKey",Base64.encodeToString(privateKey, 0));
edit.apply();
}
/*
// Encryption and decryption test
//================================
try {
NetworkPackage np = NetworkPackage.createIdentityPackage(this);
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(this);
byte[] publicKeyBytes = Base64.decode(globalSettings.getString("publicKey",""), 0);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
np.encrypt(publicKey);
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey",""), 0);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
NetworkPackage decrypted = np.decrypt(privateKey);
Log.e("ENCRYPTION AND DECRYPTION TEST", decrypted.serialize());
} catch (Exception e) {
e.printStackTrace();
Log.e("ENCRYPTION AND DECRYPTION TEST","Exception: "+e);
}
*/
}
@Override
@@ -306,14 +350,11 @@ public class BackgroundService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
//This will be called for each intent launch, even if the service is already started and it is reused
mutex.lock();
try {
for (InstanceCallback c : callbacks) {
c.onServiceStart(this);
}
callbacks.clear();
} finally {
mutex.unlock();
for (InstanceCallback c : callbacks) {
c.onServiceStart(this);
}
callbacks.clear();
mutex.unlock();
return Service.START_STICKY;
}
@@ -327,11 +368,8 @@ public class BackgroundService extends Service {
public void run() {
if (callback != null) {
mutex.lock();
try {
callbacks.add(callback);
} finally {
mutex.unlock();
}
callbacks.add(callback);
mutex.unlock();
}
Intent serviceIntent = new Intent(c, BackgroundService.class);
c.startService(serviceIntent);

View File

@@ -28,6 +28,8 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
@@ -35,28 +37,22 @@ import android.util.Base64;
import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Helpers.ObjectsHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect_tp.R;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -67,39 +63,21 @@ public class Device implements BaseLink.PackageReceiver {
private final String deviceId;
private String name;
public PublicKey publicKey;
public Certificate certificate;
private int notificationId;
private int protocolVersion;
private static final int MIN_VERSION_WITH_CAPPABILITIES_SUPPORT = 6;
private DeviceType deviceType;
private PairStatus pairStatus;
private final CopyOnWriteArrayList<PairingCallback> pairingCallback = new CopyOnWriteArrayList<>();
private Map<String, BasePairingHandler> pairingHandlers = new HashMap<>();
private Timer pairingTimer;
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
private ArrayList<String> incomingCapabilities = new ArrayList<>();
private ArrayList<String> outgoingCapabilities = new ArrayList<>();
private final ConcurrentHashMap<String, Plugin> plugins = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Plugin> failedPlugins = new ConcurrentHashMap<>();
private ArrayList<String> unsupportedPlugins = new ArrayList<>();
private HashSet<String> supportedIncomingInterfaces = new HashSet<>();
private HashSet<String> supportedOutgoingInterfaces = new HashSet<>();
private HashMap<String, ArrayList<String>> pluginsByIncomingInterface;
private HashMap<String, ArrayList<String>> pluginsByOutgoingInterface;
private final SharedPreferences settings;
public ArrayList<String> getUnsupportedPlugins() {
return unsupportedPlugins;
}
private final CopyOnWriteArrayList<PluginsChangedListener> pluginsChangedListeners = new CopyOnWriteArrayList<>();
public interface PluginsChangedListener {
@@ -108,6 +86,8 @@ public class Device implements BaseLink.PackageReceiver {
public enum PairStatus {
NotPaired,
Requested,
RequestedByPeer,
Paired
}
@@ -151,14 +131,12 @@ public class Device implements BaseLink.PackageReceiver {
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
try {
String publicKeyStr = settings.getString("publicKey", null);
if (publicKeyStr != null) {
byte[] publicKeyBytes = Base64.decode(publicKeyStr, 0);
publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
}
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device","Exception deserializing stored public key for device");
unpair();
Log.e("KDE/Device","Exception");
}
reloadPluginsFromSettings();
@@ -205,10 +183,6 @@ public class Device implements BaseLink.PackageReceiver {
return deviceId;
}
public Context getContext() {
return context;
}
//Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer
public int compareProtocolVersion() {
return protocolVersion - NetworkPackage.ProtocolVersion;
@@ -217,6 +191,7 @@ public class Device implements BaseLink.PackageReceiver {
//
// Pairing-related functions
//
@@ -225,28 +200,17 @@ public class Device implements BaseLink.PackageReceiver {
return pairStatus == PairStatus.Paired;
}
/* Asks all pairing handlers that, is pair requested? */
public boolean isPairRequested() {
boolean pairRequested = false;
for (BasePairingHandler ph: pairingHandlers.values()) {
pairRequested = pairRequested || ph.isPairRequested();
}
return pairRequested;
return pairStatus == PairStatus.Requested;
}
/* Asks all pairing handlers that, is pair requested by peer? */
public boolean isPairRequestedByPeer() {
boolean pairRequestedByPeer = false;
for (BasePairingHandler ph : pairingHandlers.values()) {
pairRequestedByPeer = pairRequestedByPeer || ph.isPairRequestedByPeer();
}
return pairRequestedByPeer;
public boolean isPairRequestedByOtherEnd() {
return pairStatus == PairStatus.RequestedByPeer;
}
public void addPairingCallback(PairingCallback callback) {
pairingCallback.add(callback);
}
public void removePairingCallback(PairingCallback callback) {
pairingCallback.remove(callback);
}
@@ -261,6 +225,15 @@ public class Device implements BaseLink.PackageReceiver {
cb.pairingFailed(res.getString(R.string.error_already_paired));
}
return;
case Requested:
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(res.getString(R.string.error_already_requested));
}
return;
case RequestedByPeer:
Log.d("requestPairing", "Pairing already started by the other end, accepting their request.");
acceptPairing();
return;
case NotPaired:
;
}
@@ -272,35 +245,59 @@ public class Device implements BaseLink.PackageReceiver {
return;
}
for (BasePairingHandler ph : pairingHandlers.values()) {
ph.requestPairing();
}
//Send our own public key
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
sendPackage(np, new SendPackageStatusCallback() {
@Override
public void onSuccess() {
hidePairingNotification(); //Will stop the pairingTimer if it was running
pairingTimer = new Timer();
pairingTimer.schedule(new TimerTask() {
@Override
public void run() {
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(context.getString(R.string.error_timed_out));
}
Log.e("KDE/Device", "Unpairing (timeout A)");
pairStatus = PairStatus.NotPaired;
}
}, 30 * 1000); //Time to wait for the other to accept
pairStatus = PairStatus.Requested;
}
@Override
public void onFailure(Throwable e) {
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(context.getString(R.string.error_could_not_send_package));
}
Log.e("KDE/Device", "Unpairing (sendFailed A)");
pairStatus = PairStatus.NotPaired;
}
});
}
public void hidePairingNotification() {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
if (pairingTimer != null) {
pairingTimer.cancel();
}
BackgroundService.removeGuiInUseCounter(context);
}
public void unpair() {
for (BasePairingHandler ph : pairingHandlers.values()) {
ph.unpair();
}
unpairInternal(); // Even if there are no pairing handlers, unpair
}
/**
* This method does not send an unpair package, instead it unpairs internally by deleting trusted device info. . Likely to be called after sending package from
* pairing handler
*/
private void unpairInternal() {
//Log.e("Device","Unpairing (unpairInternal)");
//Log.e("Device","Unpairing (unpair)");
pairStatus = PairStatus.NotPaired;
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
preferences.edit().remove(deviceId).apply();
// FIXME : We delete all device info here, but the xml file still persists
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
devicePreferences.edit().clear().apply();
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
np.set("pair", false);
sendPackage(np);
for (PairingCallback cb : pairingCallback) cb.unpaired();
@@ -308,7 +305,6 @@ public class Device implements BaseLink.PackageReceiver {
}
/* This method should be called after pairing is done from pairing handler. Calling this method again should not create any problem as most of the things will get over writter*/
private void pairingDone() {
//Log.e("Device", "Storing as trusted, deviceId: "+deviceId);
@@ -321,9 +317,12 @@ public class Device implements BaseLink.PackageReceiver {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
preferences.edit().putBoolean(deviceId,true).apply();
SharedPreferences.Editor editor = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE).edit();
editor.putString("deviceName", name);
//Store device information needed to create a Device object in a future
SharedPreferences.Editor editor = settings.edit();
editor.putString("deviceName", getName());
editor.putString("deviceType", deviceType.toString());
String encodedPublicKey = Base64.encodeToString(publicKey.getEncoded(), 0);
editor.putString("publicKey", encodedPublicKey);
editor.apply();
reloadPluginsFromSettings();
@@ -334,28 +333,39 @@ public class Device implements BaseLink.PackageReceiver {
}
/* This method is called after accepting pair request form GUI */
public void acceptPairing() {
Log.i("KDE/Device", "Accepted pair request started by the other device");
Log.i("KDE/Device","Accepted pair request started by the other device");
for (BasePairingHandler ph : pairingHandlers.values()) {
ph.acceptPairing();
}
//Send our own public key
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
sendPackage(np, new SendPackageStatusCallback() {
@Override
protected void onSuccess() {
pairingDone();
}
@Override
protected void onFailure(Throwable e) {
Log.e("Device","Unpairing (sendFailed B)");
pairStatus = PairStatus.NotPaired;
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(context.getString(R.string.error_not_reachable));
}
}
});
}
/* This method is called after rejecting pairing from GUI */
public void rejectPairing() {
Log.i("KDE/Device", "Rejected pair request started by the other device");
Log.i("KDE/Device","Rejected pair request started by the other device");
//Log.e("Device","Unpairing (rejectPairing)");
pairStatus = PairStatus.NotPaired;
for (BasePairingHandler ph : pairingHandlers.values()) {
ph.rejectPairing();
}
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
np.set("pair", false);
sendPackage(np);
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(context.getString(R.string.error_canceled_by_user));
@@ -363,52 +373,8 @@ public class Device implements BaseLink.PackageReceiver {
}
//
// Notification related methods used during pairing
//
public int getNotificationId() {
return notificationId;
}
public void displayPairingNotification() {
hidePairingNotification();
notificationId = (int)System.currentTimeMillis();
Intent intent = new Intent(getContext(), MaterialActivity.class);
intent.putExtra("deviceId", getDeviceId());
intent.putExtra("notificationId", notificationId);
PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
Resources res = getContext().getResources();
Notification noti = new NotificationCompat.Builder(getContext())
.setContentTitle(res.getString(R.string.pairing_request_from, getName()))
.setContentText(res.getString(R.string.tap_to_answer))
.setContentIntent(pendingIntent)
.setTicker(res.getString(R.string.pair_requested))
.setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.build();
final NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
try {
BackgroundService.addGuiInUseCounter(context);
notificationManager.notify(notificationId, noti);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
}
public void hidePairingNotification() {
final NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationId);
BackgroundService.removeGuiInUseCounter(context);
}
//
// ComputerLink-related functions
@@ -434,20 +400,6 @@ public class Device implements BaseLink.PackageReceiver {
this.deviceType = DeviceType.FromString(identityPackage.getString("deviceType", "desktop"));
}
if (identityPackage.has("certificate")) {
String certificateString = identityPackage.getString("certificate");
try {
byte[] certificateBytes = Base64.decode(certificateString, 0);
certificate = SslHelper.parseCertificate(certificateBytes);
Log.i("KDE/Device", "Got certificate ");
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Error getting certificate");
}
}
links.add(link);
@@ -463,36 +415,6 @@ public class Device implements BaseLink.PackageReceiver {
Log.i("KDE/Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size());
if (!pairingHandlers.containsKey(link.getName())) {
BasePairingHandler.PairingHandlerCallback callback = new BasePairingHandler.PairingHandlerCallback() {
@Override
public void incomingRequest() {
for (PairingCallback cb : pairingCallback) {
cb.incomingRequest();
}
}
@Override
public void pairingDone() {
Device.this.pairingDone();
}
@Override
public void pairingFailed(String error) {
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(error);
}
}
@Override
public void unpaired() {
unpairInternal();
}
};
pairingHandlers.put(link.getName(), link.getPairingHandler(this, callback));
}
pairingHandlers.get(link.getName()).setLink(link);
/*
Collections.sort(links, new Comparator<BaseLink>() {
@Override
@@ -512,21 +434,9 @@ public class Device implements BaseLink.PackageReceiver {
public void removeLink(BaseLink link) {
//FilesHelper.LogOpenFileCount();
/* Remove pairing handler corresponding to that link too if it was the only link*/
boolean linkPresent = false;
for (BaseLink bl : links) {
if (bl.getName().equals(link.getName())) {
linkPresent = true;
break;
}
}
if (!linkPresent) {
pairingHandlers.remove(link.getName());
}
link.removePackageReceiver(this);
links.remove(link);
Log.i("KDE/Device", "removeLink: " + link.getLinkProvider().getName() + " -> " + getName() + " active links: " + links.size());
Log.i("KDE/Device","removeLink: "+link.getLinkProvider().getName() + " -> "+getName() + " active links: "+ links.size());
if (links.isEmpty()) {
reloadPluginsFromSettings();
}
@@ -535,72 +445,139 @@ public class Device implements BaseLink.PackageReceiver {
@Override
public void onPackageReceived(NetworkPackage np) {
hackToMakeRetrocompatiblePacketTypes(np);
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR)) {
if (NetworkPackage.PACKAGE_TYPE_PAIR.equals(np.getType())) {
Log.i("KDE/Device","Pair package");
Log.i("KDE/Device", "Pair package");
boolean wantsPair = np.getBoolean("pair");
for (BasePairingHandler ph: pairingHandlers.values()) {
try {
ph.packageReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("PairingPackageReceived","Exception");
}
}
} else if (NetworkPackage.PACKAGE_TYPE_CAPABILITIES.equals(np.getType())) {
ArrayList<String> newIncomingCapabilities = np.getStringList("IncomingCapabilities");
ArrayList<String> newOutgoingCapabilities = np.getStringList("OutgoingCapabilities");
if (!ObjectsHelper.equals(newIncomingCapabilities, incomingCapabilities) ||
!ObjectsHelper.equals(newOutgoingCapabilities, outgoingCapabilities)) {
incomingCapabilities = newIncomingCapabilities;
outgoingCapabilities = newOutgoingCapabilities;
reloadPluginsFromSettings();
}
} else if (isPaired()) {
//If capabilities are not supported, iterate all plugins
Collection<String> targetPlugins = pluginsByIncomingInterface.get(np.getType());
if (targetPlugins != null && !targetPlugins.isEmpty()) {
for (String pluginKey : targetPlugins) {
Plugin plugin = plugins.get(pluginKey);
try {
plugin.onPackageReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception in " + plugin.getPluginKey() + "'s onPackageReceived()");
if (wantsPair == isPaired()) {
if (pairStatus == PairStatus.Requested) {
//Log.e("Device","Unpairing (pair rejected)");
pairStatus = PairStatus.NotPaired;
hidePairingNotification();
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
}
}
} else {
Log.w("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it");
return;
}
if (wantsPair) {
//Retrieve their public key
try {
String publicKeyContent = np.getString("publicKey").replace("-----BEGIN PUBLIC KEY-----\n","").replace("-----END PUBLIC KEY-----\n","");
byte[] publicKeyBytes = Base64.decode(publicKeyContent, 0);
publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
} catch(Exception e) {
e.printStackTrace();
Log.e("KDE/Device","Pairing exception: Received incorrect key");
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(context.getString(R.string.error_invalid_key));
}
return;
}
if (pairStatus == PairStatus.Requested) { //We started pairing
hidePairingNotification();
pairingDone();
} else {
Intent intent = new Intent(context, MaterialActivity.class);
intent.putExtra("deviceId", deviceId);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
Resources res = context.getResources();
hidePairingNotification();
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(res.getString(R.string.pairing_request_from, getName()))
.setContentText(res.getString(R.string.tap_to_answer))
.setContentIntent(pendingIntent)
.setTicker(res.getString(R.string.pair_requested))
.setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.build();
final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationId = (int)System.currentTimeMillis();
try {
BackgroundService.addGuiInUseCounter(context);
notificationManager.notify(notificationId, noti);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
pairingTimer = new Timer();
pairingTimer.schedule(new TimerTask() {
@Override
public void run() {
Log.e("KDE/Device","Unpairing (timeout B)");
hidePairingNotification();
pairStatus = PairStatus.NotPaired;
}
}, 25*1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
pairStatus = PairStatus.RequestedByPeer;
for (PairingCallback cb : pairingCallback) cb.incomingRequest();
}
} else {
Log.i("KDE/Pairing","Unpair request");
if (pairStatus == PairStatus.Requested) {
hidePairingNotification();
for (PairingCallback cb : pairingCallback) {
cb.pairingFailed(context.getString(R.string.error_canceled_by_other_peer));
}
} else if (pairStatus == PairStatus.Paired) {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
preferences.edit().remove(deviceId).apply();
}
pairStatus = PairStatus.NotPaired;
reloadPluginsFromSettings();
for (PairingCallback cb : pairingCallback) cb.unpaired();
}
} else if (isPaired()) {
for (Plugin plugin : plugins.values()) {
try {
plugin.onPackageReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived()");
}
}
} else {
//Log.e("KDE/onPackageReceived","Device not paired, will pass package to unpairedPackageListeners");
// If it is pair package, it should be captured by "if" at start
// If not and device is paired, it should be captured by isPaired
// Else unpair, this handles the situation when one device unpairs, but other dont know like unpairing when wi-fi is off
unpair();
//If capabilities are not supported, iterate all plugins
Collection<String> targetPlugins = pluginsByIncomingInterface.get(np.getType());
if (targetPlugins != null && !targetPlugins.isEmpty()) {
for (String pluginKey : targetPlugins) {
Plugin plugin = plugins.get(pluginKey);
try {
plugin.onUnpairedDevicePackageReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception in " + plugin.getDisplayName() + "'s onPackageReceived() in unPairedPackageListeners");
}
}
} else {
Log.e("Device", "Ignoring packet with type " + np.getType() + " because no plugin can handle it");
if (pairStatus != PairStatus.Requested) {
unpair();
}
for (Plugin plugin : plugins.values()) {
try {
plugin.onUnpairedDevicePackageReceived(np);
} catch (Exception e) {
e.printStackTrace();
Log.e("KDE/Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived() in unPairedPackageListeners");
}
}
}
}
@@ -640,13 +617,6 @@ public class Device implements BaseLink.PackageReceiver {
//Async
public void sendPackage(final NetworkPackage np, final SendPackageStatusCallback callback) {
hackToMakeRetrocompatiblePacketTypes(np);
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.w("Device/sendPackage", "Supported package types: " + Arrays.toString(supportedOutgoingInterfaces.toArray()));
}
//Log.e("sendPackage", "Sending package...");
//Log.e("sendPackage", np.serialize());
@@ -655,7 +625,7 @@ public class Device implements BaseLink.PackageReceiver {
@Override
public void run() {
boolean useEncryption = (protocolVersion < LanLinkProvider.MIN_VERSION_WITH_SSL_SUPPORT && (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired()));
boolean useEncryption = (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired());
//Make a copy to avoid concurrent modification exception if the original list changes
for (final BaseLink link : links) {
@@ -702,40 +672,46 @@ public class Device implements BaseLink.PackageReceiver {
return plugin;
}
private synchronized boolean addPlugin(final String pluginKey) {
private synchronized void addPlugin(final String pluginKey) {
Plugin existing = plugins.get(pluginKey);
if (existing != null) {
//Log.w("KDE/addPlugin","plugin already present:" + pluginKey);
return true;
Log.w("KDE/addPlugin","plugin already present:" + pluginKey);
return;
}
final Plugin plugin = PluginFactory.instantiatePluginForDevice(context, pluginKey, this);
if (plugin == null) {
Log.e("KDE/addPlugin","could not instantiate plugin: "+pluginKey);
failedPlugins.put(pluginKey, null);
return false;
return;
}
boolean success;
try {
success = plugin.onCreate();
} catch (Exception e) {
success = false;
e.printStackTrace();
Log.e("KDE/addPlugin", "Exception loading plugin " + pluginKey);
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (success) {
//Log.e("addPlugin","added " + pluginKey);
failedPlugins.remove(pluginKey);
plugins.put(pluginKey, plugin);
} else {
Log.e("KDE/addPlugin", "plugin failed to load " + pluginKey);
plugins.remove(pluginKey);
failedPlugins.put(pluginKey, plugin);
}
boolean success;
try {
success = plugin.onCreate();
} catch (Exception e) {
success = false;
e.printStackTrace();
Log.e("KDE/addPlugin", "Exception loading plugin " + pluginKey);
}
if (success) {
//Log.e("addPlugin","added " + pluginKey);
failedPlugins.remove(pluginKey);
plugins.put(pluginKey, plugin);
} else {
Log.e("KDE/addPlugin", "plugin failed to load " + pluginKey);
plugins.remove(pluginKey);
failedPlugins.put(pluginKey, plugin);
}
}
});
return success;
}
private synchronized boolean removePlugin(String pluginKey) {
@@ -759,12 +735,22 @@ public class Device implements BaseLink.PackageReceiver {
Log.e("KDE/removePlugin","Exception calling onDestroy for plugin "+pluginKey);
}
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(this);
}
return true;
}
public void setPluginEnabled(String pluginKey, boolean value) {
settings.edit().putBoolean(pluginKey,value).apply();
reloadPluginsFromSettings();
if (value && isPaired() && isReachable()) addPlugin(pluginKey);
else removePlugin(pluginKey);
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(Device.this);
}
}
public boolean isPluginEnabled(String pluginKey) {
@@ -783,104 +769,20 @@ public class Device implements BaseLink.PackageReceiver {
Set<String> availablePlugins = PluginFactory.getAvailablePlugins();
ArrayList<String> newUnsupportedPlugins = new ArrayList<>();
HashSet<String> newSupportedIncomingInterfaces = new HashSet<>();
HashSet<String> newSupportedOutgoingInterfaces = new HashSet<>();
HashMap<String, ArrayList<String>> newPluginsByIncomingInterface = new HashMap<>();
HashMap<String, ArrayList<String>> newPluginsByOutgoingInterface = new HashMap<>();
final boolean supportsCapabilities = (protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT);
for (String pluginKey : availablePlugins) {
PluginFactory.PluginInfo pluginInfo = PluginFactory.getPluginInfo(context, pluginKey);
Set<String> incomingInterfaces = pluginInfo.getSupportedPackageTypes();
Set<String> outgoingInterfaces = pluginInfo.getOutgoingPackageTypes();
boolean pluginEnabled = false;
boolean listenToUnpaired = pluginInfo.listenToUnpaired();
for(String pluginKey : availablePlugins) {
boolean enabled = false;
boolean listenToUnpaired = PluginFactory.getPluginInfo(context, pluginKey).listenToUnpaired();
if ((isPaired() || listenToUnpaired) && isReachable()) {
pluginEnabled = isPluginEnabled(pluginKey);
enabled = isPluginEnabled(pluginKey);
}
//TODO: Check for plugins that will fail to load before checking the capabilities
if (supportsCapabilities && (!incomingInterfaces.isEmpty() || !outgoingInterfaces.isEmpty())) {
HashSet<String> supportedOut = new HashSet<>(outgoingInterfaces);
supportedOut.retainAll(incomingCapabilities); //Intersection
HashSet<String> supportedIn = new HashSet<>(incomingInterfaces);
supportedIn.retainAll(outgoingCapabilities);
if (supportedOut.isEmpty() && supportedIn.isEmpty()) {
newUnsupportedPlugins.add(pluginKey);
if (pluginEnabled) {
//We still want to announce this capability, to prevent a deadlock
newSupportedOutgoingInterfaces.addAll(outgoingInterfaces);
newSupportedIncomingInterfaces.addAll(incomingInterfaces);
pluginEnabled = false;
}
}
}
if (pluginEnabled) {
boolean success = addPlugin(pluginKey);
if (success) {
newSupportedIncomingInterfaces.addAll(incomingInterfaces);
newSupportedOutgoingInterfaces.addAll(outgoingInterfaces);
for (String packageType : incomingInterfaces) {
packageType = hackToMakeRetrocompatiblePacketTypes(packageType);
ArrayList<String> plugins = newPluginsByIncomingInterface.get(packageType);
if (plugins == null) plugins = new ArrayList<>();
plugins.add(pluginKey);
newPluginsByIncomingInterface.put(packageType, plugins);
}
for (String packageType : outgoingInterfaces) {
packageType = hackToMakeRetrocompatiblePacketTypes(packageType);
ArrayList<String> plugins = newPluginsByOutgoingInterface.get(packageType);
if (plugins == null) plugins = new ArrayList<>();
plugins.add(pluginKey);
newPluginsByOutgoingInterface.put(packageType, plugins);
}
}
if (enabled) {
addPlugin(pluginKey);
} else {
removePlugin(pluginKey);
}
}
boolean capabilitiesChanged = false;
if (!newSupportedIncomingInterfaces.equals(supportedIncomingInterfaces) ||
!newSupportedOutgoingInterfaces.equals(pluginsByOutgoingInterface)) {
capabilitiesChanged = true;
}
pluginsByOutgoingInterface = newPluginsByOutgoingInterface;
pluginsByIncomingInterface = newPluginsByIncomingInterface;
supportedIncomingInterfaces = newSupportedIncomingInterfaces;
supportedOutgoingInterfaces = newSupportedOutgoingInterfaces;
unsupportedPlugins = newUnsupportedPlugins;
if (!unsupportedPlugins.isEmpty()) {
Log.i("ReloadPlugins", "not loading " + Arrays.toString(unsupportedPlugins.toArray()) + " because of unmatched capabilities");
}
onPluginsChanged();
//Only send capabilities to devices using protocol version 6 or later
if (capabilitiesChanged && isReachable() && isPaired() && protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_CAPABILITIES);
np.set("IncomingCapabilities", new ArrayList<>(newSupportedIncomingInterfaces));
np.set("OutgoingCapabilities", new ArrayList<>(newSupportedOutgoingInterfaces));
sendPackage(np);
}
}
public void onPluginsChanged() {
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(Device.this);
}
@@ -908,30 +810,13 @@ public class Device implements BaseLink.PackageReceiver {
}
}
public boolean deviceShouldBeKeptAlive() {
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
if (preferences.contains(getDeviceId())) {
//Log.e("DeviceShouldBeKeptAlive", "because it's a paired device");
return true; //Already paired
}
public BaseLink.ConnectionStarted getConnectionSource() {
for(BaseLink l : links) {
if (l.linkShouldBeKeptAlive()) {
return true;
if (l.getConnectionSource() == BaseLink.ConnectionStarted.Locally) {
return BaseLink.ConnectionStarted.Locally;
}
}
return false;
}
public void hackToMakeRetrocompatiblePacketTypes(NetworkPackage np) {
if (protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT) return;
np.mType = np.getType().replace(".request","");
}
public String hackToMakeRetrocompatiblePacketTypes(String type) {
if (protocolVersion >= MIN_VERSION_WITH_CAPPABILITIES_SUPPORT) return type;
return type.replace(".request","");
return BaseLink.ConnectionStarted.Remotely;
}
}

View File

@@ -20,95 +20,47 @@
package org.kde.kdeconnect.Helpers;
import android.annotation.TargetApi;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.util.Base64;
import android.util.Base64OutputStream;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
public class ContactsHelper {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static Map<String, String> phoneNumberLookup(Context context, String number) {
public static String phoneNumberLookup(Context context, String number) {
//Log.e("PhoneNumberLookup", number);
Map<String, String> contactInfo = new HashMap<>();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(
uri,
new String[] {
PhoneLookup.DISPLAY_NAME,
ContactsContract.PhoneLookup.PHOTO_URI
PhoneLookup.DISPLAY_NAME
/*, PhoneLookup.TYPE
, PhoneLookup.LABEL
, PhoneLookup.ID */
},
null, null, null);
} catch (IllegalArgumentException e) {
return contactInfo;
return number;
}
// Take the first match only
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
if (nameIndex != -1) {
contactInfo.put("name", cursor.getString(nameIndex));
}
nameIndex = cursor.getColumnIndex(PhoneLookup.PHOTO_URI);
if (nameIndex != -1) {
contactInfo.put("photoID", cursor.getString(nameIndex));
}
if (!contactInfo.isEmpty()) {
String name = cursor.getString(nameIndex);
//Log.e("PhoneNumberLookup", "success: " + name);
cursor.close();
return contactInfo;
return name + " (" + number + ")";
}
}
return contactInfo;
}
if (cursor != null) cursor.close();
public static String photoId64Encoded(Context context, String photoId) {
if (photoId == null) {
return "";
}
Uri photoUri = Uri.parse(photoId);
return number;
InputStream input = null;
Base64OutputStream output= null;
try {
ByteArrayOutputStream encodedPhoto = new ByteArrayOutputStream();
output = new Base64OutputStream(encodedPhoto, Base64.DEFAULT);
input = context.getContentResolver().openInputStream(photoUri);
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) != -1) {
output.write(buffer, 0, len);
}
return encodedPhoto.toString();
} catch (Exception ex) {
Log.e("ContactsHelper", ex.toString());
return "";
} finally {
try { input.close(); } catch(Exception ignored) { };
try { output.close(); } catch(Exception ignored) { };
}
}
}

View File

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

View File

@@ -1,22 +0,0 @@
package org.kde.kdeconnect.Helpers;
import java.security.SecureRandom;
public class RandomHelper {
public static SecureRandom secureRandom = new SecureRandom();
private static final char[] symbols = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"+
"abcdefghijklmnopqrstuvwxyz"+
"1234567890").toCharArray();
public static String randomString(int length) {
char[] buffer= new char[length];
for (int idx = 0; idx < length; ++idx) {
buffer[idx] = symbols[secureRandom.nextInt(symbols.length)];
}
return new String(buffer);
}
}

View File

@@ -1,161 +0,0 @@
/*
* Copyright 2015 Albert Vaca Cintora <albertvaka@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.Helpers.SecurityHelpers;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.kde.kdeconnect.NetworkPackage;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
public class RsaHelper {
public static void initialiseRsaKeys(Context context) {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
if (!settings.contains("publicKey") || !settings.contains("privateKey")) {
KeyPair keyPair;
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
keyPair = keyGen.genKeyPair();
} catch(Exception e) {
e.printStackTrace();
Log.e("KDE/initializeRsaKeys", "Exception");
return;
}
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
SharedPreferences.Editor edit = settings.edit();
edit.putString("publicKey", Base64.encodeToString(publicKey, 0).trim()+"\n");
edit.putString("privateKey",Base64.encodeToString(privateKey, 0));
edit.apply();
}
}
public static PublicKey getPublicKey (Context context) throws Exception{
try {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
return publicKey;
}catch (Exception e){
throw e;
}
}
public static PublicKey getPublicKey(Context context, String deviceId) throws Exception{
try {
SharedPreferences settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
return publicKey;
} catch (Exception e) {
throw e;
}
}
public static PrivateKey getPrivateKey(Context context) throws Exception{
try {
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
return privateKey;
} catch (Exception e) {
throw e;
}
}
public static NetworkPackage encrypt(NetworkPackage np, PublicKey publicKey) throws GeneralSecurityException, JSONException {
String serialized = np.serialize();
int chunkSize = 128;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
JSONArray chunks = new JSONArray();
while (serialized.length() > 0) {
if (serialized.length() < chunkSize) {
chunkSize = serialized.length();
}
String chunk = serialized.substring(0, chunkSize);
serialized = serialized.substring(chunkSize);
byte[] chunkBytes = chunk.getBytes(Charset.defaultCharset());
byte[] encryptedChunk;
encryptedChunk = cipher.doFinal(chunkBytes);
chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP));
}
//Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks");
NetworkPackage encrypted = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_ENCRYPTED);
encrypted.set("data", chunks);
encrypted.setPayload(np.getPayload(), np.getPayloadSize());
return encrypted;
}
public static NetworkPackage decrypt(NetworkPackage np, PrivateKey privateKey) throws GeneralSecurityException, JSONException {
JSONArray chunks = np.getJSONArray("data");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
String decryptedJson = "";
for (int i = 0; i < chunks.length(); i++) {
byte[] encryptedChunk = Base64.decode(chunks.getString(i), Base64.NO_WRAP);
String decryptedChunk = new String(cipher.doFinal(encryptedChunk));
decryptedJson += decryptedChunk;
}
NetworkPackage decrypted = NetworkPackage.unserialize(decryptedJson);
decrypted.setPayload(np.getPayload(), np.getPayloadSize());
return decrypted;
}
}

View File

@@ -1,256 +0,0 @@
/*
* Copyright 2015 Vineet Garg <grg.vineet@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kde.kdeconnect.Helpers.SecurityHelpers;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Base64;
import android.util.Log;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.RandomHelper;
import org.spongycastle.asn1.x500.X500NameBuilder;
import org.spongycastle.asn1.x500.style.BCStyle;
import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.X509v3CertificateBuilder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import java.io.IOException;
import java.math.BigInteger;
import java.net.Socket;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Formatter;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class SslHelper {
public enum SslMode{
Client,
Server
}
public static X509Certificate certificate; //my device's certificate
public static final BouncyCastleProvider BC = new BouncyCastleProvider();
public static void initialiseCertificate(Context context){
PrivateKey privateKey;
PublicKey publicKey;
try {
privateKey = RsaHelper.getPrivateKey(context);
publicKey = RsaHelper.getPublicKey(context);
}catch (Exception e){
Log.e("SslHelper", "Error getting keys, can't create certificate");
return;
}
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
if (!settings.contains("certificate")) {
try {
X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE);
nameBuilder.addRDN(BCStyle.CN, DeviceHelper.getDeviceId(context));
nameBuilder.addRDN(BCStyle.OU, "KDE Connect");
nameBuilder.addRDN(BCStyle.O, "KDE");
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, -1);
Date notBefore = calendar.getTime();
calendar.add(Calendar.YEAR, 10);
Date notAfter = calendar.getTime();
X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
nameBuilder.build(),
BigInteger.ONE,
notBefore,
notAfter,
nameBuilder.build(),
publicKey
);
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(privateKey);
certificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateBuilder.build(contentSigner));
SharedPreferences.Editor edit = settings.edit();
edit.putString("certificate", Base64.encodeToString(certificate.getEncoded(), 0));
edit.apply();
} catch(Exception e) {
e.printStackTrace();
Log.e("KDE/initialiseCert", "Exception");
return;
}
} else {
try {
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
byte[] certificateBytes = Base64.decode(globalSettings.getString("certificate", ""), 0);
X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes);
certificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
} catch (Exception e) {
Log.e("KDE/SslHelper", "Exception reading own certificate");
e.printStackTrace();
}
}
}
public static SSLContext getSslContext(Context context, String deviceId, boolean isDeviceTrusted) {
//TODO: Cache
try {
// Get device private key
PrivateKey privateKey = RsaHelper.getPrivateKey(context);
// Get remote device certificate if trusted
X509Certificate remoteDeviceCertificate = null;
if (isDeviceTrusted){
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0);
X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes);
remoteDeviceCertificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
}
// Setup keystore
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setKeyEntry("key", privateKey, "".toCharArray(), new Certificate[]{certificate});
// Set certificate if device trusted
if (remoteDeviceCertificate != null){
keyStore.setCertificateEntry(deviceId, remoteDeviceCertificate);
}
// Setup key manager factory
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "".toCharArray());
// Setup default trust manager
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
// Setup custom trust manager if device not trusted
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext tlsContext = SSLContext.getInstance("TLSv1"); //Newer TLS versions are only supported on API 16+
if (isDeviceTrusted) {
tlsContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), RandomHelper.secureRandom);
}else {
tlsContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, RandomHelper.secureRandom);
}
return tlsContext;
} catch (Exception e) {
Log.e("KDE/SslHelper", "Error creating tls context");
e.printStackTrace();
}
return null;
}
public static void configureSslSocket(SSLSocket socket, boolean isDeviceTrusted, boolean isClient) {
socket.setEnabledProtocols(new String[]{ "TLSv1" }); //Newer TLS versions are only supported on API 16+
// These cipher suites are most common of them that are accepted by kde and android during handshake
ArrayList<String> supportedCiphers = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384");
supportedCiphers.add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256");
supportedCiphers.add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
} else {
// Following ciphers are for and due to old devices
supportedCiphers.add("SSL_RSA_WITH_RC4_128_SHA");
supportedCiphers.add("SSL_RSA_WITH_RC4_128_MD5");
}
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) {
try {
byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded());
Formatter formatter = new Formatter();
int i;
for (i = 0; i < hash.length-1; i++) {
formatter.format("%02x:", hash[i]);
}
formatter.format("%02x", hash[i]);
return formatter.toString();
} catch (Exception e) {
return null;
}
}
public static Certificate parseCertificate(byte[] certificateBytes) throws IOException, CertificateException {
X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes);
return new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
}
}

View File

@@ -1,9 +0,0 @@
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,6 +21,10 @@
package org.kde.kdeconnect;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
import org.json.JSONArray;
@@ -30,28 +34,34 @@ import org.kde.kdeconnect.Helpers.DeviceHelper;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import javax.crypto.Cipher;
public class NetworkPackage {
public final static int ProtocolVersion = 7;
public final static int ProtocolVersion = 5;
//TODO: Move these to their respective plugins
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted";
public 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);
}};
public final static String PACKAGE_TYPE_PING = "kdeconnect.ping";
public final static String PACKAGE_TYPE_TELEPHONY = "kdeconnect.telephony";
public final static String PACKAGE_TYPE_BATTERY = "kdeconnect.battery";
public final static String PACKAGE_TYPE_SFTP = "kdeconnect.sftp";
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
public final static String PACKAGE_TYPE_MOUSEPAD = "kdeconnect.mousepad";
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
private long mId;
String mType;
private String mType;
private JSONObject mBody;
private InputStream mPayload;
private JSONObject mPayloadTransferInfo;
@@ -130,37 +140,109 @@ public class NetworkPackage {
public boolean isEncrypted() { return mType.equals(PACKAGE_TYPE_ENCRYPTED); }
public String serialize() throws JSONException {
public String serialize() {
JSONObject jo = new JSONObject();
jo.put("id", mId);
jo.put("type", mType);
jo.put("body", mBody);
if (hasPayload()) {
jo.put("payloadSize", mPayloadSize);
jo.put("payloadTransferInfo", mPayloadTransferInfo);
try {
jo.put("id", mId);
jo.put("type", mType);
jo.put("body", mBody);
if (hasPayload()) {
jo.put("payloadSize", mPayloadSize);
jo.put("payloadTransferInfo", mPayloadTransferInfo);
}
} catch(Exception e) {
e.printStackTrace();
Log.e("NetworkPackage", "Serialization exception");
}
//QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
String json = jo.toString().replace("\\/","/")+"\n";
if (!isEncrypted()) {
//Log.e("NetworkPackage.serialize", json);
}
return json;
}
static public NetworkPackage unserialize(String s) throws JSONException {
static public NetworkPackage unserialize(String s) {
NetworkPackage np = new NetworkPackage();
JSONObject jo = new JSONObject(s);
np.mId = jo.getLong("id");
np.mType = jo.getString("type");
np.mBody = jo.getJSONObject("body");
if (jo.has("payloadSize")) {
np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo");
np.mPayloadSize = jo.getLong("payloadSize");
} else {
np.mPayloadTransferInfo = new JSONObject();
np.mPayloadSize = 0;
try {
JSONObject jo = new JSONObject(s);
np.mId = jo.getLong("id");
np.mType = jo.getString("type");
np.mBody = jo.getJSONObject("body");
if (jo.has("payloadSize")) {
np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo");
np.mPayloadSize = jo.getLong("payloadSize");
} else {
np.mPayloadTransferInfo = new JSONObject();
np.mPayloadSize = 0;
}
} catch (Exception e) {
e.printStackTrace();
Log.e("NetworkPackage", "Unserialization exception unserializing "+s);
return null;
}
if (!np.isEncrypted()) {
//Log.e("NetworkPackage.unserialize", s);
}
return np;
}
public NetworkPackage encrypt(PublicKey publicKey) throws GeneralSecurityException {
String serialized = serialize();
int chunkSize = 128;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
JSONArray chunks = new JSONArray();
while (serialized.length() > 0) {
if (serialized.length() < chunkSize) {
chunkSize = serialized.length();
}
String chunk = serialized.substring(0, chunkSize);
serialized = serialized.substring(chunkSize);
byte[] chunkBytes = chunk.getBytes(Charset.defaultCharset());
byte[] encryptedChunk;
encryptedChunk = cipher.doFinal(chunkBytes);
chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP));
}
//Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks");
NetworkPackage encrypted = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_ENCRYPTED);
encrypted.set("data", chunks);
encrypted.setPayload(mPayload, mPayloadSize);
return encrypted;
}
public NetworkPackage decrypt(PrivateKey privateKey) throws GeneralSecurityException, JSONException {
JSONArray chunks = mBody.getJSONArray("data");
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
String decryptedJson = "";
for (int i = 0; i < chunks.length(); i++) {
byte[] encryptedChunk = Base64.decode(chunks.getString(i), Base64.NO_WRAP);
String decryptedChunk = new String(cipher.doFinal(encryptedChunk));
decryptedJson += decryptedChunk;
}
NetworkPackage decrypted = unserialize(decryptedJson);
decrypted.setPayload(mPayload, mPayloadSize);
return decrypted;
}
static public NetworkPackage createIdentityPackage(Context context) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
@@ -180,6 +262,21 @@ public class NetworkPackage {
}
static public NetworkPackage createPublicKeyPackage(Context context) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
np.set("pair", true);
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
String publicKey = "-----BEGIN PUBLIC KEY-----\n" + globalSettings.getString("publicKey", "").trim()+ "\n-----END PUBLIC KEY-----\n";
np.set("publicKey", publicKey);
return np;
}
public void setPayload(byte[] data) {
setPayload(new ByteArrayInputStream(data), data.length);
}

View File

@@ -33,9 +33,6 @@ import org.kde.kdeconnect_tp.R;
public class BatteryPlugin extends Plugin {
public final static String PACKAGE_TYPE_BATTERY = "kdeconnect.battery";
public final static String PACKAGE_TYPE_BATTERY_REQUEST = "kdeconnect.battery.request";
// keep these fields in sync with kdeconnect-kded:BatteryPlugin.h:ThresholdBatteryEvent
private static final int THRESHOLD_EVENT_NONE= 0;
private static final int THRESHOLD_EVENT_BATTERY_LOW = 1;
@@ -75,7 +72,7 @@ public class BatteryPlugin extends Plugin {
} else {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_BATTERY);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_BATTERY);
np.set("currentCharge", currentCharge);
np.set("isCharging", isCharging);
np.set("thresholdEvent", thresholdEvent);
@@ -102,6 +99,7 @@ public class BatteryPlugin extends Plugin {
@Override
public boolean onPackageReceived(NetworkPackage np) {
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_BATTERY)) return false;
if (np.getBoolean("request")) {
if (lastInfo != null) {
@@ -112,16 +110,4 @@ public class BatteryPlugin extends Plugin {
return true;
}
@Override
public String[] getSupportedPackageTypes() {
String[] packetTypes = {PACKAGE_TYPE_BATTERY_REQUEST};
return packetTypes;
}
@Override
public String[] getOutgoingPackageTypes() {
String[] packetTypes = {PACKAGE_TYPE_BATTERY};
return packetTypes;
}
}

View File

@@ -20,18 +20,13 @@
package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.content.ClipboardManager;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ClipboardListener {
@@ -47,33 +42,28 @@ public class ClipboardListener {
return;
}
new Handler(Looper.getMainLooper()).post(new Runnable() {
cm = (ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
listener = new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void run() {
cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
listener = new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
try {
public void onPrimaryClipChanged() {
try {
ClipData.Item item = cm.getPrimaryClip().getItemAt(0);
String content = item.coerceToText(context).toString();
ClipData.Item item = cm.getPrimaryClip().getItemAt(0);
String content = item.coerceToText(context).toString();
if (!content.equals(currentContent)) {
NetworkPackage np = new NetworkPackage(ClipboardPlugin.PACKAGE_TYPE_CLIPBOARD);
np.set("content", content);
device.sendPackage(np);
currentContent = content;
}
} catch (Exception e) {
//Probably clipboard was not text
}
if (!content.equals(currentContent)) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_CLIPBOARD);
np.set("content", content);
device.sendPackage(np);
currentContent = content;
}
};
cm.addPrimaryClipChangedListener(listener);
} catch(Exception e) {
//Probably clipboard was not text
}
}
});
};
cm.addPrimaryClipChangedListener(listener);
}
public void stop() {

View File

@@ -20,14 +20,18 @@
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.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
public class ClipboardPlugin extends Plugin {
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_clipboard);
@@ -59,23 +63,13 @@ public class ClipboardPlugin extends Plugin {
@Override
public boolean onPackageReceived(NetworkPackage np) {
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_CLIPBOARD)) {
return false;
}
String content = np.getString("content");
listener.setText(content);
return true;
}
@Override
public String[] getSupportedPackageTypes() {
String[] packetTypes = {PACKAGE_TYPE_CLIPBOARD};
return packetTypes;
}
@Override
public String[] getOutgoingPackageTypes() {
String[] packetTypes = {PACKAGE_TYPE_CLIPBOARD};
return packetTypes;
}
}

View File

@@ -1,55 +0,0 @@
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
import android.app.Activity;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import org.kde.kdeconnect_tp.R;
public class FindMyPhoneActivity extends Activity {
Ringtone ringtone;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_my_phone);
Window window = this.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
ringtone = RingtoneManager.getRingtone(getApplicationContext(), ringtoneUri);
if (android.os.Build.VERSION.SDK_INT >= 21) {
AudioAttributes.Builder b = new AudioAttributes.Builder();
b.setUsage(AudioAttributes.USAGE_ALARM);
ringtone.setAudioAttributes(b.build());
} else {
ringtone.setStreamType(AudioManager.STREAM_ALARM);
}
ringtone.play();
findViewById(R.id.bFindMyPhone).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
@Override
public void finish() {
ringtone.stop();
super.finish();
}
}

View File

@@ -1,48 +0,0 @@
package org.kde.kdeconnect.Plugins.FindMyPhonePlugin;
import android.content.Intent;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
/**
* Created by vineet on 1/11/14.
* and David Edmundson 2015
*/
public class FindMyPhonePlugin extends Plugin {
public final static String PACKAGE_TYPE_FINDMYPHONE = "kdeconnect.findmyphone";
public final static String PACKAGE_TYPE_FINDMYPHONE_REQUEST = "kdeconnect.findmyphone.request";
@Override
public String getDisplayName() {
return context.getString(R.string.findmyphone_title);
}
@Override
public String getDescription() {
return context.getString(R.string.findmyphone_description);
}
@Override
public boolean onPackageReceived(NetworkPackage np) {
Intent intent = new Intent(context,FindMyPhoneActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
}
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_FINDMYPHONE_REQUEST};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[0];
}
}

View File

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

View File

@@ -101,7 +101,7 @@ public class KeyListenerView extends View {
}
public void sendChars(CharSequence chars) {
final NetworkPackage np = new NetworkPackage(MousePadPlugin.PACKAGE_TYPE_MOUSEPAD_REQUEST);
final NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("key", chars.toString());
sendKeyPressPackage(np);
}
@@ -135,7 +135,7 @@ public class KeyListenerView extends View {
//Log.e("KeyDown", "utfChar:" + (char)event.getUnicodeChar());
//Log.e("KeyDown", "intUtfChar:" + event.getUnicodeChar());
final NetworkPackage np = new NetworkPackage(MousePadPlugin.PACKAGE_TYPE_MOUSEPAD_REQUEST);
final NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
boolean modifier = false;
if (event.isAltPressed()) {

View File

@@ -28,29 +28,27 @@ import android.preference.PreferenceManager;
import android.support.v7.app.ActionBarActivity;
import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect_tp.R;
public class MousePadActivity extends ActionBarActivity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, MousePadGestureDetector.OnGestureListener {
String deviceId;
private final static float MinDistanceToSendScroll = 2.5f; // touch gesture scroll
private final static float MinDistanceToSendGenericScroll = 0.1f; // real mouse scroll wheel event
private final static float MinDistanceToSendScroll = 2.5f;
private float mPrevX;
private float mPrevY;
private float mCurrentX;
private float mCurrentY;
private float mCurrentSensitivity;
private int scrollDirection = 1;
boolean isScrolling = false;
float accumulatedDistanceY = 0;
@@ -91,42 +89,14 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
keyListenerView.setDeviceId(deviceId);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean(getString(R.string.mousepad_scroll_direction),false)) {
scrollDirection = -1;
} else {
scrollDirection = 1;
}
String doubleTapSetting = prefs.getString(getString(R.string.mousepad_double_tap_key),
getString(R.string.mousepad_double_default));
String tripleTapSetting = prefs.getString(getString(R.string.mousepad_triple_tap_key),
getString(R.string.mousepad_triple_default));
String sensitivitySetting = prefs.getString(getString(R.string.mousepad_sensitivity_key),
getString(R.string.mousepad_sensitivity_default));
doubleTapAction = ClickType.fromString(doubleTapSetting);
tripleTapAction = ClickType.fromString(tripleTapSetting);
switch (sensitivitySetting){
case "slowest":
mCurrentSensitivity = 0.2f;
break;
case "aboveSlowest":
mCurrentSensitivity = 0.5f;
break;
case "default":
mCurrentSensitivity = 1.0f;
break;
case "aboveDefault":
mCurrentSensitivity = 1.5f;
break;
case "fastest":
mCurrentSensitivity = 2.0f;
break;
default:
mCurrentSensitivity = 1.0f;
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
final View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@@ -214,7 +184,7 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = device.getPlugin(MousePadPlugin.class);
if (mousePadPlugin == null) return;
mousePadPlugin.sendMouseDelta(mCurrentX - mPrevX, mCurrentY - mPrevY, mCurrentSensitivity);
mousePadPlugin.sendMouseDelta(mCurrentX - mPrevX, mCurrentY - mPrevY);
mPrevX = mCurrentX;
mPrevY = mCurrentY;
}
@@ -239,25 +209,6 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
return false;
}
@Override
public boolean onGenericMotionEvent(MotionEvent e)
{
if (android.os.Build.VERSION.SDK_INT >= 12) { // MotionEvent.getAxisValue is >= 12
if (e.getAction() == MotionEvent.ACTION_SCROLL) {
final float distanceY = e.getAxisValue(MotionEvent.AXIS_VSCROLL);
accumulatedDistanceY += distanceY;
if (accumulatedDistanceY > MinDistanceToSendGenericScroll || accumulatedDistanceY < -MinDistanceToSendGenericScroll) {
sendScroll(accumulatedDistanceY);
accumulatedDistanceY = 0;
}
}
}
return super.onGenericMotionEvent(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, final float distanceX, final float distanceY) {
// If only one thumb is used then cancel the scroll gesture
@@ -270,7 +221,17 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
accumulatedDistanceY += distanceY;
if (accumulatedDistanceY > MinDistanceToSendScroll || accumulatedDistanceY < -MinDistanceToSendScroll)
{
sendScroll(scrollDirection * accumulatedDistanceY);
final float scrollToSendY = accumulatedDistanceY;
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = device.getPlugin(MousePadPlugin.class);
if (mousePadPlugin == null) return;
mousePadPlugin.sendScroll(0, scrollToSendY);
}
});
accumulatedDistanceY = 0;
}
@@ -397,18 +358,6 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
});
}
private void sendScroll(final float y) {
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = device.getPlugin(MousePadPlugin.class);
if (mousePadPlugin == null) return;
mousePadPlugin.sendScroll(0, y);
}
});
}
private void showKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInputFromWindow(keyListenerView.getWindowToken(), 0, 0);

View File

@@ -31,9 +31,6 @@ import org.kde.kdeconnect_tp.R;
public class MousePadPlugin extends Plugin {
//public final static String PACKAGE_TYPE_MOUSEPAD = "kdeconnect.mousepad";
public final static String PACKAGE_TYPE_MOUSEPAD_REQUEST = "kdeconnect.mousepad.request";
@Override
public String getDisplayName() {
return context.getString(R.string.pref_plugin_mousepad);
@@ -66,66 +63,50 @@ public class MousePadPlugin extends Plugin {
parentActivity.startActivity(intent);
}
@Override
public String[] getSupportedPackageTypes() {
return new String[0];
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{PACKAGE_TYPE_MOUSEPAD_REQUEST};
}
@Override
public String getActionName() {
return context.getString(R.string.open_mousepad);
}
public void sendMouseDelta(float dx, float dy, float sensitivity) {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
if (sensitivity <= 0.0001f) {
sensitivity = 1.0f;
}
np.set("dx", dx*sensitivity);
np.set("dy", dy*sensitivity);
public void sendMouseDelta(float dx, float dy) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("dx", dx);
np.set("dy", dy);
device.sendPackage(np);
}
public void sendSingleClick() {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("singleclick", true);
device.sendPackage(np);
}
public void sendDoubleClick() {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("doubleclick", true);
device.sendPackage(np);
}
public void sendMiddleClick() {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("middleclick", true);
device.sendPackage(np);
}
public void sendRightClick() {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("rightclick", true);
device.sendPackage(np);
}
public void sendSingleHold(){
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("singlehold", true);
device.sendPackage(np);
}
public void sendScroll(float dx, float dy) {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MOUSEPAD_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("scroll", true);
np.set("dx", dx);
np.set("dy", dy);

View File

@@ -21,7 +21,6 @@
package org.kde.kdeconnect.Plugins.MprisPlugin;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -34,15 +33,14 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect_tp.R;
@@ -91,18 +89,7 @@ public class MprisActivity extends ActionBarActivity {
@Override
public void run() {
String song = mpris.getCurrentSong();
TextView nowPlaying = (TextView) findViewById(R.id.now_playing_textview);
if (!nowPlaying.getText().toString().equals(song)) {
nowPlaying.setText(song);
Bitmap currentArt = mpris.getCurrentArt();
ImageView artView = (ImageView) findViewById(R.id.artImageView);
if (currentArt != null) {
artView.setImageBitmap(currentArt);
}
}
((TextView) findViewById(R.id.now_playing_textview)).setText(song);
if (mpris.getLength() > -1 && mpris.getPosition() > -1 && !"spotify".equals(mpris.getPlayer().toLowerCase())) {
((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(mpris.getLength()));
@@ -423,13 +410,10 @@ public class MprisActivity extends ActionBarActivity {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
if (device != null) {
MprisPlugin mpris = device.getPlugin(MprisPlugin.class);
if (mpris != null) {
positionSeek.setProgress((int) (mpris.getPosition()));
}
MprisPlugin mpris = device.getPlugin(MprisPlugin.class);
if (mpris != null) {
positionSeek.setProgress((int) (mpris.getPosition()));
}
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable);
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 1000);
}
});
@@ -466,7 +450,6 @@ public class MprisActivity extends ActionBarActivity {
});
findViewById(R.id.now_playing_textview).setSelected(true);
}
@Override

View File

@@ -22,13 +22,10 @@ package org.kde.kdeconnect.Plugins.MprisPlugin;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.util.Base64;
import android.util.Log;
import org.kde.kdeconnect.NetworkPackage;
@@ -40,13 +37,9 @@ import java.util.HashMap;
public class MprisPlugin extends Plugin {
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
public final static String PACKAGE_TYPE_MPRIS_REQUEST = "kdeconnect.mpris.request";
private String player = "";
private boolean playing = false;
private String currentSong = "";
private Bitmap currentArt;
private int volume = 50;
private long length = -1;
private long lastPosition;
@@ -89,7 +82,7 @@ public class MprisPlugin extends Plugin {
}
public void sendAction(String player, String action) {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player", player);
np.set("action", action);
device.sendPackage(np);
@@ -99,14 +92,14 @@ public class MprisPlugin extends Plugin {
}
public void setVolume(int volume) {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player", player);
np.set("setVolume",volume);
device.sendPackage(np);
}
public void setPosition(int position) {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player", player);
np.set("SetPosition", position);
device.sendPackage(np);
@@ -115,7 +108,7 @@ public class MprisPlugin extends Plugin {
}
public void Seek(int offset) {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player", player);
np.set("Seek", offset);
device.sendPackage(np);
@@ -123,9 +116,9 @@ public class MprisPlugin extends Plugin {
@Override
public boolean onPackageReceived(NetworkPackage np) {
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_MPRIS)) return false;
if (np.has("nowPlaying") || np.has("volume") || np.has("isPlaying") || np.has("length") ||
np.has("pos") || np.has("artImage")) {
if (np.has("nowPlaying") || np.has("volume") || np.has("isPlaying") || np.has("length") || np.has("pos")) {
if (np.getString("player").equals(player)) {
currentSong = np.getString("nowPlaying", currentSong);
volume = np.getInt("volume", volume);
@@ -134,11 +127,6 @@ public class MprisPlugin extends Plugin {
lastPosition = np.getLong("pos", lastPosition);
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);
for (String key : playerStatusUpdated.keySet()) {
try {
@@ -182,16 +170,6 @@ public class MprisPlugin extends Plugin {
return true;
}
@Override
public String[] getSupportedPackageTypes() {
return new String[] {PACKAGE_TYPE_MPRIS};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[] {PACKAGE_TYPE_MPRIS_REQUEST};
}
public void setPlayerStatusUpdatedHandler(String id, Handler h) {
playerStatusUpdated.put(id, h);
@@ -218,7 +196,6 @@ public class MprisPlugin extends Plugin {
if (player == null || player.equals(this.player)) return;
this.player = player;
currentSong = "";
currentArt = null;
volume = 50;
playing = false;
for (String key : playerStatusUpdated.keySet()) {
@@ -241,8 +218,6 @@ public class MprisPlugin extends Plugin {
return currentSong;
}
public Bitmap getCurrentArt() { return currentArt; }
public String getPlayer() {
return player;
}
@@ -266,13 +241,13 @@ public class MprisPlugin extends Plugin {
}
private void requestPlayerList() {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("requestPlayerList",true);
device.sendPackage(np);
}
private void requestPlayerStatus() {
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player",player);
np.set("requestNowPlaying",true);
np.set("requestVolume",true);

View File

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

View File

@@ -80,14 +80,11 @@ public class NotificationReceiver extends NotificationListenerService {
public int onStartCommand(Intent intent, int flags, int startId) {
//Log.e("NotificationReceiver", "onStartCommand");
mutex.lock();
try {
for (InstanceCallback c : callbacks) {
c.onServiceStart(this);
}
callbacks.clear();
} finally {
mutex.unlock();
for (InstanceCallback c : callbacks) {
c.onServiceStart(this);
}
callbacks.clear();
mutex.unlock();
return Service.START_STICKY;
}
@@ -99,11 +96,8 @@ public class NotificationReceiver extends NotificationListenerService {
public static void RunCommand(Context c, final InstanceCallback callback) {
if (callback != null) {
mutex.lock();
try {
callbacks.add(callback);
} finally {
mutex.unlock();
}
callbacks.add(callback);
mutex.unlock();
}
Intent serviceIntent = new Intent(c, NotificationReceiver.class);
c.startService(serviceIntent);

View File

@@ -24,36 +24,23 @@ import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.SettingsActivity;
import org.kde.kdeconnect_tp.R;
import java.io.InputStream;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
public final static String PACKAGE_TYPE_NOTIFICATION_REQUEST = "kdeconnect.notification.request";
/*
private boolean sendIcons = false;
*/
@@ -90,52 +77,54 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public boolean onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if (hasPermission()) {
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override
public void onServiceStart(NotificationReceiver service) {
try {
service.addListener(NotificationsPlugin.this);
StatusBarNotification[] notifications = service.getActiveNotifications();
for (StatusBarNotification notification : notifications) {
sendNotification(notification, true);
}
} catch (Exception e) {
Log.e("NotificationsPlugin", "Exception");
e.printStackTrace();
}
}
});
} else {
return false;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
return false;
}
if (hasPermission()) {
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override
public void onServiceStart(NotificationReceiver service) {
try {
service.addListener(NotificationsPlugin.this);
StatusBarNotification[] notifications = service.getActiveNotifications();
for (StatusBarNotification notification : notifications) {
sendNotification(notification, true);
}
} catch (Exception e) {
Log.e("NotificationsPlugin", "Exception");
e.printStackTrace();
}
}
});
return true;
} else {
return false;
}
// request all existing notifications
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION_REQUEST);
np.set("request", true);
device.sendPackage(np);
return true;
}
@Override
public void onDestroy() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override
public void onServiceStart(NotificationReceiver service) {
service.removeListener(NotificationsPlugin.this);
}
});
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
return;
}
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override
public void onServiceStart(NotificationReceiver service) {
service.removeListener(NotificationsPlugin.this);
}
});
}
@Override
public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
String id = getNotificationKeyCompat(statusBarNotification);
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION_REQUEST);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION);
np.set("id", id);
np.set("isCancel", true);
device.sendPackage(np);
@@ -177,19 +166,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
return;
}
if ("com.android.systemui".equals(packageName) &&
"low_battery".equals(statusBarNotification.getTag()))
{
//HACK: Android low battery notification are posted again every few seconds. Ignore them, as we already have a battery indicator.
return;
}
if (packageName.equals("com.google.android.googlequicksearchbox")) {
//HACK: Hide Google Now notifications that keep constantly popping up (and without text because we don't know how to read them properly)
return;
}
NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_NOTIFICATION);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION);
if (packageName.equals("org.kde.kdeconnect_tp"))
{
@@ -272,6 +254,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override
public boolean onPackageReceived(final NetworkPackage np) {
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_NOTIFICATION)) return false;
/*
if (np.getBoolean("sendIcons")) {
sendIcons = true;
@@ -279,103 +262,53 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
*/
if (np.getBoolean("request")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
private void sendCurrentNotifications(NotificationReceiver service) {
StatusBarNotification[] notifications = service.getActiveNotifications();
for (StatusBarNotification notification : notifications) {
sendNotification(notification, true);
}
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
private void sendCurrentNotifications(NotificationReceiver service) {
StatusBarNotification[] notifications = service.getActiveNotifications();
for (StatusBarNotification notification : notifications) {
sendNotification(notification, true);
}
}
@Override
public void onServiceStart(final NotificationReceiver service) {
try {
//If service just started, this call will throw an exception because the answer is not ready yet
sendCurrentNotifications(service);
} catch(Exception e) {
Log.e("onPackageReceived","Error when answering 'request': Service failed to start. Retrying in 100ms...");
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
Log.e("onPackageReceived","Error when answering 'request': Service failed to start. Retrying...");
sendCurrentNotifications(service);
} catch (Exception e) {
Log.e("onPackageReceived","Error when answering 'request': Service failed to start twice!");
e.printStackTrace();
}
@Override
public void onServiceStart(final NotificationReceiver service) {
try {
//If service just started, this call will throw an exception because the answer is not ready yet
sendCurrentNotifications(service);
} catch(Exception e) {
Log.e("onPackageReceived","Error when answering 'request': Service failed to start. Retrying in 100ms...");
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
Log.e("onPackageReceived","Error when answering 'request': Service failed to start. Retrying...");
sendCurrentNotifications(service);
} catch (Exception e) {
Log.e("onPackageReceived","Error when answering 'request': Service failed to start twice!");
e.printStackTrace();
}
}).start();
}
}
}).start();
}
});
}
});
} else if (np.has("cancel")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override
public void onServiceStart(NotificationReceiver service) {
String dismissedId = np.getString("cancel");
cancelNotificationCompat(service, dismissedId);
}
});
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override
public void onServiceStart(NotificationReceiver service) {
String dismissedId = np.getString("cancel");
cancelNotificationCompat(service, dismissedId);
}
});
} 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();
Log.w("NotificationsPlugin", "Nothing to do");
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;
@@ -417,26 +350,12 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
}
}
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION, PACKAGE_TYPE_NOTIFICATION_REQUEST};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{PACKAGE_TYPE_NOTIFICATION, PACKAGE_TYPE_NOTIFICATION_REQUEST};
}
//For compat with API<21, because lollipop changed the way to cancel notifications
//For compat with API<21, because lollipop changed they way to cancel notifications
public static void cancelNotificationCompat(NotificationReceiver service, String compatKey) {
if (Build.VERSION.SDK_INT >= 21) {
service.cancelNotification(compatKey);
} else {
int first = compatKey.indexOf(':');
if (first == -1) {
Log.e("cancelNotificationCompa","Not formated like a notification key: "+ compatKey);
return;
}
int last = compatKey.lastIndexOf(':');
String packageName = compatKey.substring(0, first);
String tag = compatKey.substring(first + 1, last);
@@ -453,20 +372,16 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
}
public static String getNotificationKeyCompat(StatusBarNotification statusBarNotification) {
String result;
// first check if it's one of our remoteIds
String tag = statusBarNotification.getTag();
if (tag != null && tag.startsWith("kdeconnectId:"))
result = Integer.toString(statusBarNotification.getId());
else if (Build.VERSION.SDK_INT >= 21) {
result = statusBarNotification.getKey();
if (Build.VERSION.SDK_INT >= 21) {
return statusBarNotification.getKey();
} else {
String packageName = statusBarNotification.getPackageName();
String tag = statusBarNotification.getTag();
int id = statusBarNotification.getId();
String safePackageName = (packageName == null) ? "" : packageName;
String safeTag = (tag == null) ? "" : tag;
result = safePackageName + ":" + safeTag + ":" + id;
return safePackageName + ":" + safeTag + ":" + id;
}
return result;
}
}

View File

@@ -28,18 +28,15 @@ import android.content.Context;
import android.content.Intent;
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.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
public class PingPlugin extends Plugin {
public final static String PACKAGE_TYPE_PING = "kdeconnect.ping";
@Override
public String getDisplayName() {
return context.getResources().getString(R.string.pref_plugin_ping);
@@ -53,51 +50,50 @@ public class PingPlugin extends Plugin {
@Override
public boolean onPackageReceived(NetworkPackage np) {
if (!np.getType().equals(PACKAGE_TYPE_PING)) {
Log.e("PingPlugin", "Ping plugin should not receive packets other than pings!");
return false;
//Log.e("PingPackageReceiver", "onPackageReceived");
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_PING)) {
//Log.e("PingPackageReceiver", "was a ping!");
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
);
int id;
String message;
if (np.has("message")) {
message = np.getString("message");
id = (int)System.currentTimeMillis();
} else {
message = "Ping!";
id = 42; //A unique id to create only one notification
}
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(device.getName())
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setTicker(message)
.setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
try {
notificationManager.notify(id, noti);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
return true;
}
//Log.e("PingPackageReceiver", "was a ping!");
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
);
int id;
String message;
if (np.has("message")) {
message = np.getString("message");
id = (int)System.currentTimeMillis();
} else {
message = "Ping!";
id = 42; //A unique id to create only one notification
}
Notification noti = new NotificationCompat.Builder(context)
.setContentTitle(device.getName())
.setContentText(message)
.setContentIntent(resultPendingIntent)
.setTicker(message)
.setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
try {
notificationManager.notify(id, noti);
} catch(Exception e) {
//4.1 will throw an exception about not having the VIBRATE permission, ignore it.
//https://android.googlesource.com/platform/frameworks/base/+/android-4.2.1_r1.2%5E%5E!/
}
return true;
return false;
}
@Override
@@ -108,7 +104,7 @@ public class PingPlugin extends Plugin {
@Override
public void startMainActivity(Activity activity) {
if (device != null) {
device.sendPackage(new NetworkPackage(PACKAGE_TYPE_PING));
device.sendPackage(new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PING));
}
}
@@ -121,15 +117,4 @@ public class PingPlugin extends Plugin {
public boolean displayInContextMenu() {
return true;
}
@Override
public String[] getSupportedPackageTypes() {
return new String[]{PACKAGE_TYPE_PING};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{PACKAGE_TYPE_PING};
}
}

View File

@@ -174,16 +174,6 @@ public abstract class Plugin {
return null;
}
/**
* Should return the list of NetworkPackage types that this plugin can handle
*/
public abstract String[] getSupportedPackageTypes();
/**
* Should return the list of NetworkPackage types that this plugin can send
*/
public abstract String[] getOutgoingPackageTypes();
/**
* Creates a button that will be displayed in the user interface
* It can open an activity or perform any other action that the

View File

@@ -26,42 +26,32 @@ import android.util.Log;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardPlugin;
import org.kde.kdeconnect.Plugins.FindMyPhonePlugin.FindMyPhonePlugin;
import org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadPlugin;
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardPlugin;
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin;
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationsPlugin;
import org.kde.kdeconnect.Plugins.PingPlugin.PingPlugin;
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
import org.kde.kdeconnect.Plugins.SftpPlugin.SftpPlugin;
import org.kde.kdeconnect.Plugins.SharePlugin.SharePlugin;
import org.kde.kdeconnect.Plugins.TelephonyPlugin.TelephonyPlugin;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.lang.reflect.Method;
public class PluginFactory {
public static class PluginInfo {
public PluginInfo(String displayName, String description, Drawable icon,
boolean enabledByDefault, boolean hasSettings, boolean listenToUnpaired,
String[] supportedPackageTypes, String[] outgoingPackageTypes) {
boolean enabledByDefault, boolean hasSettings, boolean listenToUnpaired) {
this.displayName = displayName;
this.description = description;
this.icon = icon;
this.enabledByDefault = enabledByDefault;
this.hasSettings = hasSettings;
this.listenToUnpaired = listenToUnpaired;
HashSet<String> incoming = new HashSet<>();
if (supportedPackageTypes != null) Collections.addAll(incoming, supportedPackageTypes);
this.supportedPackageTypes = Collections.unmodifiableSet(incoming);
HashSet<String> outgoing = new HashSet<>();
if (outgoingPackageTypes != null) Collections.addAll(outgoing, outgoingPackageTypes);
this.outgoingPackageTypes = Collections.unmodifiableSet(outgoing);
}
public String getDisplayName() {
@@ -86,23 +76,12 @@ public class PluginFactory {
return listenToUnpaired;
}
public Set<String> getOutgoingPackageTypes() {
return outgoingPackageTypes;
}
public Set<String> getSupportedPackageTypes() {
return supportedPackageTypes;
}
private final String displayName;
private final String description;
private final Drawable icon;
private final boolean enabledByDefault;
private final boolean hasSettings;
private final boolean listenToUnpaired;
private final Set<String> supportedPackageTypes;
private final Set<String> outgoingPackageTypes;
}
private static final Map<String, Class> availablePlugins = new TreeMap<>();
@@ -119,24 +98,16 @@ public class PluginFactory {
PluginFactory.registerPlugin(NotificationsPlugin.class);
PluginFactory.registerPlugin(MousePadPlugin.class);
PluginFactory.registerPlugin(SharePlugin.class);
//PluginFactory.registerPlugin(TelepathyPlugin.class);
PluginFactory.registerPlugin(FindMyPhonePlugin.class);
PluginFactory.registerPlugin(RunCommandPlugin.class);
}
public static PluginInfo getPluginInfo(Context context, String pluginKey) {
PluginInfo info = availablePluginsInfo.get(pluginKey); //Is it cached?
if (info != null) {
return info;
}
if (info != null) return info;
try {
Plugin p = ((Plugin)availablePlugins.get(pluginKey).newInstance());
p.setContext(context, null);
info = new PluginInfo(p.getDisplayName(), p.getDescription(), p.getIcon(),
p.isEnabledByDefault(), p.hasSettings(), p.listensToUnpairedDevices(),
p.getSupportedPackageTypes(), p.getOutgoingPackageTypes());
p.isEnabledByDefault(), p.hasSettings(), p.listensToUnpairedDevices());
availablePluginsInfo.put(pluginKey, info); //Cache it
return info;
} catch(Exception e) {
@@ -144,7 +115,6 @@ public class PluginFactory {
e.printStackTrace();
return null;
}
}
public static Set<String> getAvailablePlugins() {

View File

@@ -1,146 +0,0 @@
/*
* Copyright 2015 Aleix Pol Gonzalez <aleixpol@kde.org>
* Copyright 2015 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.RunCommandPlugin;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import org.json.JSONException;
import org.json.JSONObject;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.UserInterface.List.EntryItem;
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
public class RunCommandActivity extends ActionBarActivity {
private String deviceId;
private void updateView() {
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(final BackgroundService service) {
final Device device = service.getDevice(deviceId);
final RunCommandPlugin plugin = device.getPlugin(RunCommandPlugin.class);
if (plugin == null) {
Log.e("RunCommandActivity", "device has no runcommand plugin!");
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
ListView view = (ListView) findViewById(R.id.listView1);
final ArrayList<JSONObject> commands = plugin.getCommandList();
ArrayList<ListAdapter.Item> commandItems = new ArrayList<>();
for (JSONObject obj : commands) {
try {
commandItems.add(new EntryItem(obj.getString("name"), obj.getString("command")));
} catch (JSONException e) {
e.printStackTrace();
}
}
ListAdapter adapter = new ListAdapter(RunCommandActivity.this, commandItems);
view.setAdapter(adapter);
view.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
try {
plugin.runCommand(commands.get(i).getString("key"));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
});
}
});
}
private final RunCommandPlugin.CommandsChangedCallback theCallback = new RunCommandPlugin.CommandsChangedCallback() {
@Override
public void update() {
updateView();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
deviceId = getIntent().getStringExtra("deviceId");
updateView();
}
@Override
protected void onResume() {
super.onResume();
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(final BackgroundService service) {
final Device device = service.getDevice(deviceId);
final RunCommandPlugin plugin = device.getPlugin(RunCommandPlugin.class);
if (plugin == null) {
Log.e("RunCommandActivity", "device has no runcommand plugin!");
return;
}
plugin.addCommandsUpdatedCallback(theCallback);
}
});
}
@Override
protected void onPause() {
super.onPause();
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(final BackgroundService service) {
final Device device = service.getDevice(deviceId);
final RunCommandPlugin plugin = device.getPlugin(RunCommandPlugin.class);
if (plugin == null) {
Log.e("RunCommandActivity", "device has no runcommand plugin!");
return;
}
plugin.removeCommandsUpdatedCallback(theCallback);
}
});
}
}

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