2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-29 13:17:43 +00:00

Merge branch 'master' into ssl

# Conflicts:
#	build.gradle
#	src/org/kde/kdeconnect/Backends/BaseLink.java
#	src/org/kde/kdeconnect/Backends/BaseLinkProvider.java
#	src/org/kde/kdeconnect/Backends/LanBackend/LanLink.java
#	src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java
#	src/org/kde/kdeconnect/Backends/LoopbackBackend/LoopbackLink.java
#	src/org/kde/kdeconnect/BackgroundService.java
#	src/org/kde/kdeconnect/Device.java
#	src/org/kde/kdeconnect/Helpers/DeviceHelper.java
#	src/org/kde/kdeconnect/UserInterface/DeviceActivity.java
#	src/org/kde/kdeconnect/UserInterface/PairActivity.java
#	tests/org/kde/kdeconnect/LanLinkProviderTest.java
#	tests/org/kde/kdeconnect/LanLinkTest.java
This commit is contained in:
Albert Vaca 2015-09-11 09:24:35 -07:00
commit f5725b7c8d
127 changed files with 3507 additions and 1995 deletions

View File

@ -1,19 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.kde.kdeconnect_tp" package="org.kde.kdeconnect_tp"
android:versionCode="808" android:versionCode="904"
android:versionName="0.8h"> android:versionName="0.9">
<uses-sdk android:minSdkVersion="9" <uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="22" /> android:targetSdkVersion="22" />
<supports-screens <supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"
android:anyDensity="true" android:anyDensity="true"
/> android:largeScreens="true"
android:normalScreens="true"
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" />
@ -21,12 +20,13 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <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.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.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.RECEIVE_SMS" android:required="false" />
<uses-permission android:name="android.permission.SEND_SMS" android:required="false" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application <application
android:allowBackup="true" android:allowBackup="true"
@ -36,80 +36,61 @@
> >
<service <service
android:enabled="true" android:name="org.kde.kdeconnect.BackgroundService"
android:name="org.kde.kdeconnect.BackgroundService"> android:enabled="true" >
</service> </service>
<activity <activity
android:name="org.kde.kdeconnect.UserInterface.MainActivity" android:name="org.kde.kdeconnect.UserInterface.MaterialActivity"
android:label="KDE Connect" > android:label="KDE Connect"
android:theme="@style/KdeConnectTheme.NoActionBar">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="org.kde.kdeconnect.UserInterface.MainSettingsActivity"
android:label="@string/settings"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
>
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
</activity>
<activity
android:name="org.kde.kdeconnect.UserInterface.DeviceActivity"
android:label="@string/device"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
>
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
</activity>
<activity
android:name="org.kde.kdeconnect.UserInterface.PairActivity"
android:label="@string/pair_device"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MainActivity"
>
<meta-data android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MainActivity" />
</activity>
<activity <activity
android:name="org.kde.kdeconnect.UserInterface.SettingsActivity" android:name="org.kde.kdeconnect.UserInterface.SettingsActivity"
android:label="@string/device_menu_plugins" android:label="@string/device_menu_plugins"
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity" android:parentActivityName="org.kde.kdeconnect.UserInterface.MaterialActivity" >
> <meta-data
<meta-data android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" /> android:value="org.kde.kdeconnect.UserInterface.MaterialActivity" />
</activity> </activity>
<activity <activity
android:name="org.kde.kdeconnect.UserInterface.CustomDevicesActivity" android:name="org.kde.kdeconnect.UserInterface.CustomDevicesActivity"
android:label="@string/custom_devices_settings" android:label="@string/custom_devices_settings"
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity" android:parentActivityName="org.kde.kdeconnect.UserInterface.MaterialActivity" >
> <meta-data
<meta-data android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" /> android:value="org.kde.kdeconnect.UserInterface.MaterialActivity" />
</activity>
<activity
android:name="org.kde.kdeconnect.Plugins.SharePlugin.SendFileActivity"
android:label="KDE Connect"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MaterialActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MaterialActivity" />
</activity> </activity>
<activity <activity
android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity" android:name="org.kde.kdeconnect.UserInterface.PluginSettingsActivity"
android:label="@string/device_menu_plugins" android:label="@string/device_menu_plugins"
android:parentActivityName="org.kde.kdeconnect.UserInterface.SettingsActivity" android:parentActivityName="org.kde.kdeconnect.UserInterface.SettingsActivity" >
> <meta-data
<meta-data android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.SettingsActivity" /> android:value="org.kde.kdeconnect.UserInterface.SettingsActivity" />
</activity> </activity>
<receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver"> <receiver android:name="org.kde.kdeconnect.KdeConnectBroadcastReceiver" >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" /> <action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" android:path="org.kde.kdeconnect" />
<data
android:host="kdeconnect"
android:path="/"
android:scheme="package" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
@ -120,7 +101,8 @@
<intent-filter> <intent-filter>
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" /> <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
</intent-filter> </intent-filter>
<!--<intent-filter> <!--
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" /> <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
@ -128,7 +110,8 @@
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BATTERY_CHANGED" /> <action android:name="android.intent.action.BATTERY_CHANGED" />
</intent-filter>--> </intent-filter>
-->
</receiver> </receiver>
<!-- Plugin-related activities and services --> <!-- Plugin-related activities and services -->
@ -136,42 +119,45 @@
<activity <activity
android:name="org.kde.kdeconnect.Plugins.MprisPlugin.MprisActivity" android:name="org.kde.kdeconnect.Plugins.MprisPlugin.MprisActivity"
android:label="@string/remote_control" android:label="@string/remote_control"
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity" android:parentActivityName="org.kde.kdeconnect.UserInterface.MaterialActivity"
> >
<meta-data android:name="android.support.PARENT_ACTIVITY" <meta-data
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" /> android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.MaterialActivity" />
</activity> </activity>
<activity <activity
android:name="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity" android:name="org.kde.kdeconnect.Plugins.MousePadPlugin.MousePadActivity"
android:label="@string/remote_control"
android:windowSoftInputMode="stateHidden|adjustResize"
android:configChanges="orientation|keyboardHidden|screenSize" android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/remote_control"
android:parentActivityName="org.kde.kdeconnect.UserInterface.MaterialActivity"
android:screenOrientation="fullSensor" android:screenOrientation="fullSensor"
android:parentActivityName="org.kde.kdeconnect.UserInterface.DeviceActivity" android:windowSoftInputMode="stateHidden|adjustResize" >
> <meta-data
<meta-data android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" /> android:value="org.kde.kdeconnect.UserInterface.MaterialActivity" />
</activity> </activity>
<activity <activity
android:name="org.kde.kdeconnect.Plugins.SharePlugin.ShareActivity" android:name="org.kde.kdeconnect.Plugins.SharePlugin.ShareActivity"
android:label="KDE Connect" android:label="KDE Connect"
> >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" /> <data android:mimeType="*/*" />
</intent-filter> </intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" /> <action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" /> <data android:mimeType="*/*" />
</intent-filter> </intent-filter>
</activity> </activity>
<service
<service android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver" android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter> <intent-filter>
<action android:name="android.service.notification.NotificationListenerService" /> <action android:name="android.service.notification.NotificationListenerService" />
</intent-filter> </intent-filter>

View File

@ -3,7 +3,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.2.3' classpath 'com.android.tools.build:gradle:1.3.0'
} }
} }
@ -55,12 +55,14 @@ dependencies {
repositories { repositories {
mavenCentral() mavenCentral()
} }
compile 'com.android.support:support-v4:22.2.0' compile 'com.android.support:support-v4:22.2.1'
compile 'com.android.support:appcompat-v7:22.2.0' compile 'com.android.support:appcompat-v7:22.2.1'
compile 'com.android.support:design:22.2.1'
compile 'org.apache.sshd:sshd-core:0.8.0' compile 'org.apache.sshd:sshd-core:0.8.0'
compile 'com.madgag.spongycastle:pkix:1.52.0.0' compile 'com.madgag.spongycastle:pkix:1.52.0.0'
compile 'org.bouncycastle:bcprov-jdk16:1.46' compile 'org.bouncycastle:bcprov-jdk16:1.46'
// compile 'io.netty:netty-handler:4.0.30.Final' // We use a custom build netty in libs directory due to ssl related bug // compile 'io.netty:netty-handler:4.0.30.Final' // We use a custom-built netty in libs directory due to ssl related bug
compile fileTree(include: '*.jar', dir: 'libs') compile fileTree(include: '*.jar', dir: 'libs')
androidTestCompile 'org.mockito:mockito-core:1.10.19' androidTestCompile 'org.mockito:mockito-core:1.10.19'

262
icon.svg
View File

@ -14,8 +14,11 @@
height="274.43201" height="274.43201"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.48.4 r9939" inkscape:version="0.91 r13725"
sodipodi:docname="kdeconnect.svg"> sodipodi:docname="icon.svg"
inkscape:export-filename="/home/vaka/kdeconnect/kdeconnect-android/res/drawable-mdpi/icon.png"
inkscape:export-xdpi="15.741604"
inkscape:export-ydpi="15.741604">
<defs <defs
id="defs4"> id="defs4">
<linearGradient <linearGradient
@ -65,18 +68,6 @@
offset="1" offset="1"
id="stop4280" /> id="stop4280" />
</linearGradient> </linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4153">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4155" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="1"
id="stop4157" />
</linearGradient>
<linearGradient <linearGradient
id="linearGradient3994"> id="linearGradient3994">
<stop <stop
@ -516,43 +507,6 @@
y2="967.36218" y2="967.36218"
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
gradientTransform="translate(-296.98485,-295.9747)" /> gradientTransform="translate(-296.98485,-295.9747)" />
<filter
inkscape:collect="always"
id="filter3898"
x="-0.0093191492"
width="1.0186383"
y="-0.14599991"
height="1.2919998">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.91250003"
id="feGaussianBlur3900" />
</filter>
<filter
inkscape:collect="always"
id="filter3902">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.91250003"
id="feGaussianBlur3904" />
</filter>
<filter
inkscape:collect="always"
id="filter3935">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.5499999"
id="feGaussianBlur3937" />
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3994"
id="linearGradient4000"
x1="-61.213001"
y1="-85.49823"
x2="238.787"
y2="424.50177"
gradientUnits="userSpaceOnUse" />
<linearGradient <linearGradient
inkscape:collect="always" inkscape:collect="always"
xlink:href="#linearGradient3994-1" xlink:href="#linearGradient3994-1"
@ -613,52 +567,6 @@
offset="1" offset="1"
id="stop3998-2" /> id="stop3998-2" />
</linearGradient> </linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4153"
id="linearGradient4159"
x1="130"
y1="1052.3622"
x2="130"
y2="1032.3622"
gradientUnits="userSpaceOnUse" />
<filter
inkscape:collect="always"
id="filter4182"
x="-0.0093191487"
width="1.0186383"
y="-0.146"
height="1.292">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.91249998"
id="feGaussianBlur4184" />
</filter>
<filter
inkscape:collect="always"
id="filter4186">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.91249998"
id="feGaussianBlur4188" />
</filter>
<filter
color-interpolation-filters="sRGB"
inkscape:collect="always"
id="filter3935-5">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.5499999"
id="feGaussianBlur3937-7" />
</filter>
<filter
inkscape:collect="always"
id="filter4253">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="0.54999983"
id="feGaussianBlur4255" />
</filter>
<linearGradient <linearGradient
inkscape:collect="always" inkscape:collect="always"
xlink:href="#linearGradient4276" xlink:href="#linearGradient4276"
@ -680,7 +588,7 @@
gradientUnits="userSpaceOnUse" gradientUnits="userSpaceOnUse"
gradientTransform="translate(415.17269,-239.40615)" /> gradientTransform="translate(415.17269,-239.40615)" />
<linearGradient <linearGradient
gradientTransform="matrix(1.5765306,0,0,1.5765306,-240.12657,-563.0272)" gradientTransform="matrix(1.838244,0,0,1.760204,-248.41104,-733.67334)"
inkscape:collect="always" inkscape:collect="always"
xlink:href="#linearGradient4303-8" xlink:href="#linearGradient4303-8"
id="linearGradient4309-4" id="linearGradient4309-4"
@ -701,25 +609,6 @@
offset="1" offset="1"
id="stop4307-0" /> id="stop4307-0" />
</linearGradient> </linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4022"
id="linearGradient4028"
x1="338.78699"
y1="-145.49823"
x2="388.78699"
y2="-25.498228"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.5765306,0,0,1.5765306,-382.12995,246.43833)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4022"
id="linearGradient4052"
x1="141.07115"
y1="130.60065"
x2="243.84538"
y2="130.60065"
gradientUnits="userSpaceOnUse" />
<linearGradient <linearGradient
inkscape:collect="always" inkscape:collect="always"
xlink:href="#linearGradient4123" xlink:href="#linearGradient4123"
@ -839,34 +728,26 @@
offset="1" offset="1"
id="stop3998-0" /> id="stop3998-0" />
</linearGradient> </linearGradient>
<filter
inkscape:collect="always"
id="filter4199">
<feGaussianBlur
inkscape:collect="always"
stdDeviation="2.555"
id="feGaussianBlur4201" />
</filter>
</defs> </defs>
<sodipodi:namedview <sodipodi:namedview
id="base" id="base"
pagecolor="#000000" pagecolor="#ffffff"
bordercolor="#9d4f2f" bordercolor="#9d4f2f"
borderopacity="1" borderopacity="1"
inkscape:pageopacity="0.51372549" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="0.7" inkscape:zoom="0.49497475"
inkscape:cx="237.10108" inkscape:cx="-397.02564"
inkscape:cy="152.16765" inkscape:cy="247.25211"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="true" showgrid="true"
showguides="true" showguides="true"
inkscape:guide-bbox="true" inkscape:guide-bbox="true"
inkscape:window-width="1366" inkscape:window-width="1920"
inkscape:window-height="713" inkscape:window-height="1025"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="-3" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
fit-margin-top="0" fit-margin-top="0"
fit-margin-left="0" fit-margin-left="0"
@ -877,7 +758,7 @@
id="grid2985" id="grid2985"
empspacing="5" empspacing="5"
visible="true" visible="true"
enabled="true" enabled="false"
snapvisiblegridlinesonly="true" snapvisiblegridlinesonly="true"
originx="8.7869998px" originx="8.7869998px"
originy="9.9302358px" /> originy="9.9302358px" />
@ -885,10 +766,6 @@
orientation="1,0" orientation="1,0"
position="8.7869998,239.93024" position="8.7869998,239.93024"
id="guide2989" /> id="guide2989" />
<sodipodi:guide
orientation="0,1"
position="201.787,265.93024"
id="guide2991" />
<sodipodi:guide <sodipodi:guide
orientation="1,0" orientation="1,0"
position="263.787,-0.0697642" position="263.787,-0.0697642"
@ -899,11 +776,7 @@
id="guide2995" /> id="guide2995" />
<sodipodi:guide <sodipodi:guide
orientation="1,0" orientation="1,0"
position="118.787,309.93024" position="263.787,296.98485"
id="guide4512" />
<sodipodi:guide
orientation="1,0"
position="158.787,289.93024"
id="guide4514" /> id="guide4514" />
<sodipodi:guide <sodipodi:guide
orientation="0,1" orientation="0,1"
@ -911,20 +784,8 @@
id="guide3939" /> id="guide3939" />
<sodipodi:guide <sodipodi:guide
orientation="0,1" orientation="0,1"
position="148.787,89.930236" position="-434.64286,137.14286"
id="guide3941" /> id="guide3941" />
<sodipodi:guide
orientation="1,0"
position="223.787,174.93024"
id="guide3942" />
<sodipodi:guide
orientation="1,0"
position="203.787,179.93024"
id="guide3944" />
<sodipodi:guide
orientation="1,0"
position="63.787,39.930236"
id="guide4111" />
<sodipodi:guide <sodipodi:guide
orientation="1,0" orientation="1,0"
position="128.787,19.930236" position="128.787,19.930236"
@ -938,7 +799,7 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title /> <dc:title></dc:title>
<dc:creator> <dc:creator>
<cc:Agent> <cc:Agent>
<dc:title>Malcer</dc:title> <dc:title>Malcer</dc:title>
@ -952,83 +813,31 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(8.7869998,-787.86042)"> transform="translate(8.7869998,-787.86042)">
<rect
style="opacity:0.5;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4199)"
id="rect3908-2"
width="255"
height="256"
x="-7.9314225e-07"
y="796.36218"
rx="10"
ry="9.0819159"
transform="matrix(1.0053352,0,0,1.0053352,0.03271549,-4.2160417)" />
<rect
style="opacity:0.2;fill:#b3b3b3;fill-opacity:1;stroke:none"
id="rect3908-8"
width="259"
height="260"
x="-2"
y="794.36218"
rx="12"
ry="11.000009" />
<rect
style="fill:url(#linearGradient4000);fill-opacity:1;stroke:none"
id="rect3908"
width="255"
height="256"
x="8.7869997"
y="8.5017672"
transform="translate(-8.7869998,787.86042)"
rx="10"
ry="9.0819159" />
<rect
style="opacity:0.2;fill:url(#linearGradient4159);fill-opacity:1;stroke:none"
id="rect3908-1"
width="255"
height="229.99998"
x="0"
y="822.36218"
rx="10"
ry="9.0819159" />
<path
style="opacity:0.1;fill:#000000;fill-opacity:1;stroke:none"
d="m 33.787,244.50177 115,-219.999999 115,139.999999 0,90 0,0 0,0 c 0,0 0,-10 0,0 0,10 -10.11244,10 -10.11244,10 l -204.88756,0 z"
id="path4311"
inkscape:connector-curvature="0"
transform="translate(-8.7869998,787.86042)"
sodipodi:nodetypes="cccccczccc" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none;opacity:0.2"
d="m 18.787,8.5017668 c 10,0 227,8.4e-6 235,4.2e-6 8,-4.2e-6 10,7 10,9 0,2 0,2 0,2 -1,-4 -5,-6 -8,-6 -82.05383,0 -156.925223,0 -239,0 -4,0 -7.014667,3.536667 -8.0000003,6 0,0 10e-8,-1 10e-8,-2 0,-1 0,-9.0000042 10.0000002,-9.0000042 z"
id="rect4116"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zzzcccczz"
transform="translate(-8.7869998,787.86042)" />
<path <path
style="fill:#f2f2f2;fill-opacity:1;stroke:none" style="fill:#f2f2f2;fill-opacity:1;stroke:none"
d="m 27.883669,812.27851 110.357121,0 c 4.36699,0 7.88265,3.51567 7.88265,7.88266 l 0,39.41326 -9.45918,0 0,-36.2602 -107.204076,0 0,192.33677 107.204076,0 0,-156.07657 9.45918,0 0,165.53567 c 0,4.367 -3.51566,7.8827 -7.88265,7.8827 l -110.357121,0 c -4.367006,0 -7.882669,-3.5157 -7.882669,-7.8827 l 0,-204.94893 c 0,-4.36699 3.515663,-7.88266 7.882669,-7.88266 z" d="m 64.090487,801.86214 128.677053,0 c 5.09194,0 9.19121,3.92526 9.19121,8.80103 l 0,44.00509 -11.02945,0 0,-40.48469 -125.000587,0 0,214.74493 125.000587,0 0,-174.26024 11.02945,0 0,184.82144 c 0,4.8757 -4.09927,8.801 -9.19121,8.801 l -128.677053,0 c -5.091955,0 -9.191239,-3.9253 -9.191239,-8.801 l 0,-228.82653 c 0,-4.87577 4.099284,-8.80103 9.191239,-8.80103 z"
id="rect3099-1" id="rect3099-1"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="sssccccccccssssss" /> sodipodi:nodetypes="sssccccccccssssss" />
<rect <rect
style="fill:#2d2d2d;fill-opacity:1;stroke:none" style="fill:#2d2d2d;fill-opacity:1;stroke:none"
id="rect3907-1" id="rect3907-1"
width="107.20409" width="125.0006"
height="192.33675" height="214.7449"
x="29.460182" x="65.928711"
y="823.31403" y="814.18335"
rx="0" rx="0"
ry="0" /> ry="0" />
<rect <rect
style="fill:#1a1a1a;fill-opacity:1;stroke:none" style="fill:#1a1a1a;fill-opacity:1;stroke:none"
id="rect3946-5" id="rect3946-5"
width="31.530609" width="35.204079"
height="1.5765324" height="1.760206"
x="67.296921" x="110.82697"
y="817.00793" y="807.14258"
ry="0.78826618" /> ry="0.88010299" />
<g <g
transform="matrix(0.80336174,0,0,0.80336174,28.27591,877.43019)" transform="matrix(0.99703783,0,0,0.99703783,60.422321,868.29896)"
id="g3764-6-1" id="g3764-6-1"
style="fill:#f2f2f2;fill-opacity:1"> style="fill:#f2f2f2;fill-opacity:1">
<g <g
@ -1048,20 +857,9 @@
</g> </g>
<path <path
style="opacity:0.5;fill:url(#linearGradient4309-4);fill-opacity:1;stroke:none" style="opacity:0.5;fill:url(#linearGradient4309-4);fill-opacity:1;stroke:none"
d="m 29.460184,823.31423 77.249996,0 13.39023,-0.95204 -76.27562,194.86531 -15.941137,0 z" d="m 65.928713,814.18357 90.073947,0 15.61309,-1.06296 -88.937821,217.56809 -18.58746,0 z"
id="rect3907-9-4" id="rect3907-9-4"
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" /> sodipodi:nodetypes="cccccc" />
<g
transform="translate(-8.7869998,787.86042)"
style="font-size:144px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:url(#linearGradient4052);font-family:Nokia Pure Text;-inkscape-font-specification:Nokia Pure Text;stroke-width:7;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="text3247">
<path
d="m 175.48241,130.60064 c 0,-12.66854 -7.72782,-25.08374 -19.63624,-29.77109 -1.01349,-0.50675 -2.15367,-0.63344 -3.16714,-0.63344 -4.43399,0 -8.10788,3.54721 -8.10788,8.10788 0,9.62809 14.69552,6.96771 14.69552,22.29665 0,15.32892 -14.69552,12.66856 -14.69552,22.29665 0,4.56067 3.67389,8.10786 8.10788,8.10786 1.01347,0 2.15365,-0.12675 3.16714,-0.63341 11.90842,-4.81405 19.63624,-17.10257 19.63624,-29.7711 m 32.43149,0 c 0,-25.71712 -15.45564,-49.660734 -39.2725,-59.668882 -1.01349,-0.380054 -2.15365,-0.633418 -3.29383,-0.633418 -4.43399,0 -8.10787,3.673884 -8.10787,8.107876 0,3.547179 2.02697,5.95421 4.94073,7.474443 3.42051,1.773596 6.58766,3.293813 9.62811,5.574155 12.54186,9.121356 19.88962,23.690216 19.88962,39.145826 0,15.45561 -7.34776,30.02448 -19.88962,39.14582 -3.04045,2.28033 -6.2076,3.80056 -9.62811,5.57416 -2.91376,1.52023 -4.94073,3.92726 -4.94073,7.47444 0,4.43399 3.67388,8.10788 8.23456,8.10788 1.01349,0 2.15365,-0.25337 3.16714,-0.63344 23.81686,-10.00813 39.2725,-33.95173 39.2725,-59.66886 m 32.43149,0 c 0,-38.892415 -23.18348,-74.111022 -58.90875,-89.439944 -1.01349,-0.380071 -2.15365,-0.633435 -3.29383,-0.633435 -4.43398,0 -8.10786,3.673884 -8.10786,8.107877 0,3.673868 1.90028,5.700845 4.94072,7.474442 1.77359,1.013473 3.80057,1.646891 5.70086,2.66038 3.54718,1.900271 7.09439,4.053938 10.3882,6.460953 20.7764,15.328939 33.06492,39.525917 33.06492,65.369727 0,25.84382 -12.28852,50.04079 -33.06492,65.36973 -3.29381,2.40701 -6.84102,4.56066 -10.3882,6.46095 -1.90029,1.01349 -3.92727,1.64691 -5.70086,2.66039 -3.04044,1.7736 -4.94072,3.80058 -4.94072,7.47445 0,4.43399 3.67388,8.10787 8.10786,8.10787 1.14018,0 2.28034,-0.25338 3.29383,-0.63343 35.72527,-15.32892 58.90875,-50.54755 58.90875,-89.43996"
id="path3252"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsssssccccssccsccssccccsscccscccsscc"
style="fill:url(#linearGradient4028);fill-opacity:1;stroke:url(#linearGradient4052);stroke-opacity:1;stroke-width:7;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#FFFFFF" android:state_checked="true" />
<item android:color="#000000" android:state_checked="false" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/accent" />
<corners android:bottomRightRadius="2dip"
android:bottomLeftRadius="2dip"
android:topRightRadius="2dip"
android:topLeftRadius="2dip"/>
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/drawer_header"
android:gravity="center|clip_vertical|clip_horizontal"
/>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/primary" android:state_checked="true" />
<item android:drawable="@color/background_floating_material_light" android:state_checked="false" />
</selector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="1000dp" android:height="2dp" />
<solid android:color="#cecbce"/>
</shape>

View File

@ -1,13 +1,90 @@
<ListView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_vertical_margin"
tools:context=".DeviceFragment"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/pairing_buttons"
android:layout_gravity="center">
<ProgressBar
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/pair_progress" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dip"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/device_not_paired"
android:id="@+id/pair_message"
android:layout_gravity="left|center_vertical"
/>
<Button
android:id="@+id/pair_button"
android:background="@drawable/button_round"
android:textColor="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/request_pairing"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/pair_request"
android:layout_gravity="center">
<Button
android:background="@drawable/button_round"
android:textColor="@android:color/white"
android:layout_margin="4dip"
android:id="@+id/accept_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/pairing_accept"
android:layout_weight="1"
/>
<Button
android:background="@drawable/button_round"
android:textColor="@android:color/white"
android:layout_margin="4dip"
android:id="@+id/reject_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/pairing_reject"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/unpair_message"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/unreachable_description"
android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="center" />
<ListView
android:id="@+id/buttons_list" android:id="@+id/buttons_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:fillViewport="true" android:fillViewport="true"
android:divider="@null" tools:context=".DeviceActivity"
tools:context=".MainActivity"
/> />
</LinearLayout>

View File

@ -0,0 +1,10 @@
<ListView 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:padding="16dip"
tools:context=".MainActivity"
android:id="@+id/listView1"
android:addStatesFromChildren="true"
android:orientation="vertical">
</ListView>

View File

@ -1,14 +1,40 @@
<ListView xmlns:android="http://schemas.android.com/apk/res/android" <android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin" android:fitsSystemWindows="true"
android:paddingRight="@dimen/activity_horizontal_margin" tools:context="org.kde.kdeconnect.UserInterface.MaterialActivity"> <!-- fitSystemWindows to make the drawer slide below the Lollipop transparent status bar -->
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:id="@+id/listView1"
android:addStatesFromChildren="true"
android:orientation="vertical">
</ListView> <LinearLayout
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: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"
app:itemBackground="@drawable/state_list_drawer_background"
app:itemTextColor="@color/state_list_drawer_text"
app:itemIconTint="@color/state_list_drawer_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/nav_header"
/>
</android.support.v4.widget.DrawerLayout>

View File

@ -1,60 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android: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=".MainActivity"
android:orientation="vertical">
<ProgressBar
android:visibility="gone"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/pair_progress" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/device_not_paired"
android:id="@+id/pair_message"
android:layout_gravity="left|center_vertical"
/>
<Button
android:id="@+id/pair_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/request_pairing"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:id="@+id/pair_request"
android:layout_gravity="center">
<Button
android:id="@+id/accept_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/pairing_accept"
android:layout_weight="1"
/>
<Button
android:id="@+id/reject_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/pairing_reject"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>

View File

@ -30,7 +30,6 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/add_host" android:text="@string/add_host"
android:onClick="addNewIp"
android:id="@android:id/button1"/> android:id="@android:id/button1"/>

View File

@ -5,9 +5,20 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<include <TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:drawableBottom="@drawable/text_section_header"
android:textStyle="bold"
android:layout_marginTop="8dp"
android:textColor="?android:textColorSecondary"
android:drawablePadding="4dp"
android:textSize="14sp"
android:textAllCaps="true"
android:paddingLeft="8dip"
android:paddingRight="8dip"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/list_item_category_text" android:id="@+id/list_item_category_text"
layout="@android:layout/preference_category" /> />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -5,9 +5,11 @@
android:baselineAligned="false" android:baselineAligned="false"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="12dip" android:paddingLeft="12dip"
android:paddingStart="12dip"
android:minHeight="?android:attr/listPreferredItemHeight" android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize" android:paddingRight="?android:attr/scrollbarSize"
android:paddingEnd="?android:attr/scrollbarSize"
android:background="@drawable/abc_list_selector_holo_dark" android:background="@drawable/abc_list_selector_holo_dark"
android:orientation="vertical"> android:orientation="vertical">
<!-- We should use android:background="@android:/listChoiceBackgroundIndicator" <!-- We should use android:background="@android:/listChoiceBackgroundIndicator"

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:baselineAligned="false"
android:layout_height="wrap_content"
android:paddingLeft="12dip"
android:paddingStart="12dip"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize"
android:paddingEnd="?android:attr/scrollbarSize"
android:orientation="horizontal">
<ImageView android:id="@+id/list_item_entry_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_device_laptop"
android:layout_weight="0" />
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_weight="1"
android:layout_height="wrap_content">
<TextView android:id="@+id/list_item_entry_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dip"
android:layout_marginStart="8dip"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:text=""/>
<TextView android:id="@+id/list_item_entry_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
android:textColor="#CC2222"
android:visibility="gone"
android:text="" />
</LinearLayout>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:layout_weight="0"
android:indeterminate="true" />
<Button
android:layout_width="wrap_content"
android:minWidth="128dip"
android:layout_height="wrap_content"
android:text="@string/request_pairing"
android:padding="8dip"
android:background="@drawable/button_round"
android:textColor="#FFFFFF"
android:id="@+id/entry_pair_button"
android:layout_weight="0"
android:nestedScrollingEnabled="false" />
</LinearLayout>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:baselineAligned="false"
android:layout_height="wrap_content"
android:paddingLeft="12dip"
android:paddingStart="12dip"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center_vertical"
android:paddingRight="?android:attr/scrollbarSize"
android:paddingEnd="?android:attr/scrollbarSize"
android:clickable="true"
android:background="@drawable/abc_list_selector_holo_dark"
android:orientation="horizontal">
<ImageView android:id="@+id/list_item_entry_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_device_laptop"
/>
<LinearLayout
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView android:id="@+id/list_item_entry_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="8dip"
android:layout_marginStart="8dip"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:text="" />
<TextView android:id="@+id/list_item_entry_summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dip"
android:layout_marginStart="8dip"
android:textAppearance="?android:attr/textAppearanceSmall"
android:singleLine="true"
android:textColor="#CC2222"
android:visibility="gone"
android:text="" />
</LinearLayout>
</LinearLayout>

View File

@ -145,6 +145,7 @@
android:layout_height="30dip" android:layout_height="30dip"
android:maxWidth="30dip" android:maxWidth="30dip"
android:layout_marginRight="10dip" android:layout_marginRight="10dip"
android:layout_marginEnd="10dip"
android:id="@+id/imageView" android:id="@+id/imageView"
android:layout_weight="0" android:layout_weight="0"
android:layout_gravity="left|center_vertical" android:layout_gravity="left|center_vertical"

49
res/layout/nav_header.xml Normal file
View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="160dp"
android:padding="16dp"
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:paddingBottom="4dp"
android:layout_above="@+id/device_name"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My device"
android:id="@+id/device_name"
android:layout_marginBottom="8dp"
android:textColor="#fff"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
/>
<!--
<ImageView
android:src="@drawable/ic_action_image_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:clickable="true"
android:background="@drawable/abc_list_selector_holo_dark"
android:layout_gravity="center_vertical"
android:contentDescription="@string/rename_device"
android:id="@+id/rename"
/>
-->
</RelativeLayout>

View File

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Layout for a Preference in a PreferenceActivity. The
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" >
<!-- Preference should place its actual preference widget here. -->
<LinearLayout android:id="@+android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical" />
<ImageView
android:id="@+android:id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="15dip"
android:layout_marginLeft="15dip"
android:layout_marginEnd="15dip"
android:layout_marginRight="4dip"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_weight="1">
<TextView android:id="@+android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<TextView android:id="@+android:id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@android:id/title"
android:layout_alignStart="@android:id/title"
android:layout_alignLeft="@android:id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="3" />
</RelativeLayout>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/settingsButton"
android:padding="8dip"
android:background="@drawable/abc_btn_borderless_material"
android:src="@drawable/ic_action_settings_inverted"/>
</LinearLayout>

View File

@ -5,8 +5,8 @@
android:id="@+id/menu_refresh" android:id="@+id/menu_refresh"
android:icon="@drawable/ic_action_refresh" android:icon="@drawable/ic_action_refresh"
android:orderInCategory="200" android:orderInCategory="200"
kdeconnect:showAsAction="ifRoom" kdeconnect:showAsAction="never"
android:title="@string/reconnect" android:title="@string/refresh"
/> />
<item <item
@ -18,16 +18,14 @@
/> />
<item <item
android:id="@+id/menu_settings" android:id="@+id/menu_rename"
android:icon="@drawable/ic_action_settings"
android:orderInCategory="300" android:orderInCategory="300"
android:title="@string/settings" android:title="@string/device_rename_title"
kdeconnect:showAsAction="never" kdeconnect:showAsAction="never"
/> />
<item <item
android:id="@+id/menu_custom_device_list" android:id="@+id/menu_custom_device_list"
android:icon="@drawable/ic_action_settings"
android:orderInCategory="900" android:orderInCategory="900"
android:title="@string/custom_device_list" android:title="@string/custom_device_list"
kdeconnect:showAsAction="never" kdeconnect:showAsAction="never"

20
res/menu/refresh.xml Normal file
View File

@ -0,0 +1,20 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:kdeconnect="http://schemas.android.com/apk/res-auto/android">
<item
android:id="@+id/menu_refresh"
android:icon="@drawable/ic_action_refresh"
android:orderInCategory="200"
kdeconnect:showAsAction="ifRoom"
android:title="@string/refresh"
/>
<item
android:id="@+id/menu_progress"
android:orderInCategory="200"
android:visible="false"
kdeconnect:showAsAction="ifRoom"
kdeconnect:actionViewClass="android.widget.ProgressBar"
/>
</menu>

View File

@ -108,6 +108,8 @@
<string name="invalid_device_name">El nom del dispositiu no és vàlid</string> <string name="invalid_device_name">El nom del dispositiu no és vàlid</string>
<string name="shareplugin_text_saved">S\'ha rebut text i s\'ha desat al porta-retalls</string> <string name="shareplugin_text_saved">S\'ha rebut text i s\'ha desat al porta-retalls</string>
<string name="custom_devices_settings">Llista personalitzada de dispositius</string> <string name="custom_devices_settings">Llista personalitzada de dispositius</string>
<string name="pair_device_action">Aparella amb un dispositiu nou</string>
<string name="unpair_device_action">Desparella %s</string>
<string name="custom_device_list">Afegeix dispositius per la IP</string> <string name="custom_device_list">Afegeix dispositius per la IP</string>
<string name="share_notification_preference">Rebombori de les notificacions</string> <string name="share_notification_preference">Rebombori de les notificacions</string>
<string name="share_notification_preference_summary">Vibra i reprodueix un so en rebre un fitxer</string> <string name="share_notification_preference_summary">Vibra i reprodueix un so en rebre un fitxer</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">No s\'ha trobat cap reproductor</string> <string name="no_players_connected">No s\'ha trobat cap reproductor</string>
<string name="custom_dev_list_help">Empreu aquesta opció només si el dispositiu no és detectat automàticament. Introduïu a sota l\'adreça IP o nom de la màquina i premeu el botó per afegir-lo a la llista. Seleccioneu un element existent per eliminar-lo de la llista.</string> <string name="custom_dev_list_help">Empreu aquesta opció només si el dispositiu no és detectat automàticament. Introduïu a sota l\'adreça IP o nom de la màquina i premeu el botó per afegir-lo a la llista. Seleccioneu un element existent per eliminar-lo de la llista.</string>
<string name="mpris_player_on_device">%1$s sobre el %2$s</string> <string name="mpris_player_on_device">%1$s sobre el %2$s</string>
<string name="send_files">Envia els fitxers</string>
<string name="pairing_title">Dispositius del KDE Connect</string>
<string name="pairing_description">Els altres dispositius que executin el KDE Connect a la mateixa xarxa han d\'aparèixer aquí.</string>
<string name="device_paired">Dispositiu aparellat</string>
<string name="device_rename_title">Reanomena el dispositiu</string>
<string name="device_rename_confirm">Reanomena</string>
<string name="refresh">Refresca</string>
<string name="unreachable_description">Aquest dispositiu aparellat no és accessible. Assegureu-vos que està connectat a la mateixa xarxa.</string>
<string name="no_file_browser">No hi ha instal·lat cap explorador de fitxers.</string>
<string name="pref_plugin_telepathy">Envia un SMS</string>
<string name="pref_plugin_telepathy_desc">Envia missatges de text des de l\'escriptori</string>
<string name="plugin_not_supported">Aquest connector no és admès pel dispositiu</string>
</resources> </resources>

View File

@ -47,7 +47,7 @@
<string name="unknown_device">Neznámé 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_not_reachable">Zařízení je nedostupné</string>
<string name="error_already_requested">Párování již bylo vyžádáno</string> <string name="error_already_requested">Párování již bylo vyžádáno</string>
<string name="error_already_paired">Zařízení je již spárované</string> <string name="error_already_paired">Zařízení je již spárováno</string>
<string name="error_could_not_send_package">Balíček nelze poslat</string> <string name="error_could_not_send_package">Balíček nelze poslat</string>
<string name="error_timed_out">Čas vypršel</string> <string name="error_timed_out">Čas vypršel</string>
<string name="error_canceled_by_user">Přerušeno uživatelem</string> <string name="error_canceled_by_user">Přerušeno uživatelem</string>
@ -73,7 +73,7 @@
<string name="right_click">Poslat kliknutí pravým tlačítkem</string> <string name="right_click">Poslat kliknutí pravým tlačítkem</string>
<string name="middle_click">Poslat kliknutí prostředním tlačítkem</string> <string name="middle_click">Poslat kliknutí prostředním tlačítkem</string>
<string name="show_keyboard">Zobrazit klávesnici</string> <string name="show_keyboard">Zobrazit klávesnici</string>
<string name="device_not_paired">Zařízení není spárované</string> <string name="device_not_paired">Zařízení není spárováno</string>
<string name="request_pairing">Vyžádat párování</string> <string name="request_pairing">Vyžádat párování</string>
<string name="pairing_accept">Přijmout</string> <string name="pairing_accept">Přijmout</string>
<string name="pairing_reject">Odmítnout</string> <string name="pairing_reject">Odmítnout</string>
@ -108,6 +108,8 @@
<string name="invalid_device_name">Neplatný název zařízení</string> <string name="invalid_device_name">Neplatný název zařízení</string>
<string name="shareplugin_text_saved">Přijatý text byl uložen do schránky</string> <string name="shareplugin_text_saved">Přijatý text byl uložen do schránky</string>
<string name="custom_devices_settings">Seznam vlastních zařízení</string> <string name="custom_devices_settings">Seznam vlastních zařízení</string>
<string name="pair_device_action">Spárovat nové zařízení</string>
<string name="unpair_device_action">Zrušit párování %s</string>
<string name="custom_device_list">Přidat zařízení podle IP</string> <string name="custom_device_list">Přidat zařízení podle IP</string>
<string name="share_notification_preference">Hlasitá upozornění</string> <string name="share_notification_preference">Hlasitá upozornění</string>
<string name="share_notification_preference_summary">Vibrovat a přehrát melodii při přijímání souboru</string> <string name="share_notification_preference_summary">Vibrovat a přehrát melodii při přijímání souboru</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Přehrávač nenalezen</string> <string name="no_players_connected">Přehrávač nenalezen</string>
<string name="custom_dev_list_help">Použijte tuto možnost pouze pokud není vaše zařízení automaticky detekováno. Zadejte níže IP adresu a název hostitele a stiskněte tlačítko pro přidání do seznamu. Stiskněte existující položku pro odstranění ze seznamu.</string> <string name="custom_dev_list_help">Použijte tuto možnost pouze pokud není vaše zařízení automaticky detekováno. Zadejte níže IP adresu a název hostitele a stiskněte tlačítko pro přidání do seznamu. Stiskněte existující položku pro odstranění ze seznamu.</string>
<string name="mpris_player_on_device">%1$s na %2$s</string> <string name="mpris_player_on_device">%1$s na %2$s</string>
<string name="send_files">Odeslat soubory</string>
<string name="pairing_title">Zařízení KDE Connect</string>
<string name="pairing_description">Zde by se měla zobrazit další zařízení, na kterých běží KDE Connect ve stejné síti.</string>
<string name="device_paired">Zařízení je spárováno</string>
<string name="device_rename_title">Přejmenovat zařízení</string>
<string name="device_rename_confirm">Přejmenovat</string>
<string name="refresh">Obnovit</string>
<string name="unreachable_description">Toto spárované zařízení je nedosažitelné. Ujistěte se, že běží ve stejné síti.</string>
<string name="no_file_browser">Není nainstalován žádný prohlížeč souborů.</string>
<string name="pref_plugin_telepathy">Poslat SMS</string>
<string name="pref_plugin_telepathy_desc">Posílejte zprávy ze své pracovní plochy</string>
<string name="plugin_not_supported">Tento modul zařízení nepodporuje</string>
</resources> </resources>

View File

@ -108,6 +108,8 @@
<string name="invalid_device_name">Nombre de dispositivo no válido</string> <string name="invalid_device_name">Nombre de dispositivo no válido</string>
<string name="shareplugin_text_saved">Texto recibido y guardado en el portapapeles</string> <string name="shareplugin_text_saved">Texto recibido y guardado en el portapapeles</string>
<string name="custom_devices_settings">Lista de dispositivos personalizada</string> <string name="custom_devices_settings">Lista de dispositivos personalizada</string>
<string name="pair_device_action">Vincular un nuevo dispositivo</string>
<string name="unpair_device_action">Desvincular %s</string>
<string name="custom_device_list">Añadir dispositivos por IP</string> <string name="custom_device_list">Añadir dispositivos por IP</string>
<string name="share_notification_preference">Notificaciones ruidosas</string> <string name="share_notification_preference">Notificaciones ruidosas</string>
<string name="share_notification_preference_summary">Vibrar y reproducir un sonido cuando se reciba un archivo</string> <string name="share_notification_preference_summary">Vibrar y reproducir un sonido cuando se reciba un archivo</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Ningún reproductor encontrado</string> <string name="no_players_connected">Ningún reproductor encontrado</string>
<string name="custom_dev_list_help">Use esta opción solo si su dispositivo no es detectado automáticamente. Introduzca la dirección IP o nombre debajo y pulse el botón para añadirlo a la lista. Pulse un ítem existente para eliminarlo de la lista.</string> <string name="custom_dev_list_help">Use esta opción solo si su dispositivo no es detectado automáticamente. Introduzca la dirección IP o nombre debajo y pulse el botón para añadirlo a la lista. Pulse un ítem existente para eliminarlo de la lista.</string>
<string name="mpris_player_on_device">%1$s en %2$s</string> <string name="mpris_player_on_device">%1$s en %2$s</string>
<string name="send_files">Enviar archivos</string>
<string name="pairing_title">Dispositivos de KDE Connect</string>
<string name="pairing_description">Otros dispositivos ejecutando KDE Connect en su misma red deberían aparecer aquí.</string>
<string name="device_paired">Dispositivo vinculado</string>
<string name="device_rename_title">Renombrar dispositivo</string>
<string name="device_rename_confirm">Renombrar</string>
<string name="refresh">Actualizar</string>
<string name="unreachable_description">Este dispositivo vinculado no está disponible. Asegúrese que está conectado a su misma red.</string>
<string name="no_file_browser">No hay navegadores de archivos instalados.</string>
<string name="pref_plugin_telepathy">Enviar SMS</string>
<string name="pref_plugin_telepathy_desc">Enviar mensajes de texto desde el escritorio</string>
<string name="plugin_not_supported">Este complemento no está permitido por el dispositivo</string>
</resources> </resources>

View File

@ -108,6 +108,8 @@
<string name="invalid_device_name">Virheellinen laitenimi</string> <string name="invalid_device_name">Virheellinen laitenimi</string>
<string name="shareplugin_text_saved">Vastaanotettiin tekstiä, tallennettiin leikepöydälle</string> <string name="shareplugin_text_saved">Vastaanotettiin tekstiä, tallennettiin leikepöydälle</string>
<string name="custom_devices_settings">Omien laitteiden luettelo</string> <string name="custom_devices_settings">Omien laitteiden luettelo</string>
<string name="pair_device_action">Kytke uusi laite pariksi</string>
<string name="unpair_device_action">Poista laitepari %s</string>
<string name="custom_device_list">Lisää laitteita IP:llä</string> <string name="custom_device_list">Lisää laitteita IP:llä</string>
<string name="share_notification_preference">Äänekkäät ilmoitukset</string> <string name="share_notification_preference">Äänekkäät ilmoitukset</string>
<string name="share_notification_preference_summary">Värise ja soita ääni tiedoston saapuessa</string> <string name="share_notification_preference_summary">Värise ja soita ääni tiedoston saapuessa</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Soittimia ei löytynyt</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 kosketa painiketta lisätäksesi sen luetteloon. Kosketa olemassa olevaa kohtaa poistaaksesi sen luettelosta.</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="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">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 tavoitttavissa. Tarkista, että se on yhteydessä samaan verkkoon.</string>
<string name="no_file_browser">Tiedostoselaimia ei asennettu.</string>
<string name="pref_plugin_telepathy">Lähetä tekstiviesti</string>
<string name="pref_plugin_telepathy_desc">Lähetä tekstiviestejä työpöydältäsi</string>
<string name="plugin_not_supported">Laite ei tue tätä liitännäistä</string>
</resources> </resources>

View File

@ -108,6 +108,8 @@
<string name="invalid_device_name">Ongeldige apparaatnaam</string> <string name="invalid_device_name">Ongeldige apparaatnaam</string>
<string name="shareplugin_text_saved">Oontvangen tekst, opgeslagen op klembord</string> <string name="shareplugin_text_saved">Oontvangen tekst, opgeslagen op klembord</string>
<string name="custom_devices_settings">Aangepaste lijst apparaten</string> <string name="custom_devices_settings">Aangepaste lijst apparaten</string>
<string name="pair_device_action">Een paar maken met een nieuw apparaat</string>
<string name="unpair_device_action">Paar %s uit elkaar halen</string>
<string name="custom_device_list">Voeg apparaten toe per IP-adres</string> <string name="custom_device_list">Voeg apparaten toe per IP-adres</string>
<string name="share_notification_preference">Luidruchtige meldingen</string> <string name="share_notification_preference">Luidruchtige meldingen</string>
<string name="share_notification_preference_summary">Vibreer en speel een geluidje bij ontvangen van een bestand</string> <string name="share_notification_preference_summary">Vibreer en speel een geluidje bij ontvangen van een bestand</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Geen spelers gevonden</string> <string name="no_players_connected">Geen spelers gevonden</string>
<string name="custom_dev_list_help">Deze optie alleen gebruiken als uw apparaat niet automatisch gedetecteerd wordt. Voer IP-adres of hostnaam hieronder in en klik op de knop om het aan de lijst toe te voegen. Klik op een bestaand item om het uit de lijst te verwijderen.</string> <string name="custom_dev_list_help">Deze optie alleen gebruiken als uw apparaat niet automatisch gedetecteerd wordt. Voer IP-adres of hostnaam hieronder in en klik op de knop om het aan de lijst toe te voegen. Klik op een bestaand item om het uit de lijst te verwijderen.</string>
<string name="mpris_player_on_device">%1$s op %2$s</string> <string name="mpris_player_on_device">%1$s op %2$s</string>
<string name="send_files">Bestanden verzenden</string>
<string name="pairing_title">Apparaten van KDE Connect</string>
<string name="pairing_description">Andere apparaten met KDE Connect in uw zelfde netwerk zouden hier moeten verschijnen.</string>
<string name="device_paired">Gepaard apparaat</string>
<string name="device_rename_title">Apparaat hernoemen</string>
<string name="device_rename_confirm">Hernoemen</string>
<string name="refresh">Verversen</string>
<string name="unreachable_description">Dit gepaarde apparaat is niet bereikbaar. Ga na dat het is verbonden met uw zelfde netwerk.</string>
<string name="no_file_browser">Er zijn geen bestandsbrowsers geïnstalleerd.</string>
<string name="pref_plugin_telepathy">SMS verzenden</string>
<string name="pref_plugin_telepathy_desc">Stuur tekstberichten van uw bureaublad</string>
<string name="plugin_not_supported">Deze plug-in wordt niet ondersteund door het apparaat</string>
</resources> </resources>

View File

@ -108,6 +108,8 @@
<string name="invalid_device_name">Nome do dispositivo inválido</string> <string name="invalid_device_name">Nome do dispositivo inválido</string>
<string name="shareplugin_text_saved">Texto recebido e salvo na área de transferência</string> <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="custom_devices_settings">Lista de dispositivos personalizada</string>
<string name="pair_device_action">Emparelhar um novo dispositivo</string>
<string name="unpair_device_action">Desemparelhar o %s</string>
<string name="custom_device_list">Adicionar dispositivos pelo IP</string> <string name="custom_device_list">Adicionar dispositivos pelo IP</string>
<string name="share_notification_preference">Notificações barulhentas</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="share_notification_preference_summary">Vibrar e tocar um som ao receber um arquivo</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Nenhum reprodutor encontrado</string> <string name="no_players_connected">Nenhum reprodutor encontrado</string>
<string name="custom_dev_list_help">Use esta opção apenas se o seu dispositivo não for detectado automaticamente. Digite o endereço IP ou nome da máquina no campo abaixo e toque o botão para adicioná-lo à lista. Toque em um item existente para removê-lo da lista.</string> <string name="custom_dev_list_help">Use esta opção apenas se o seu dispositivo não for detectado automaticamente. Digite o endereço IP ou nome da máquina no campo abaixo e toque o botão para adicioná-lo à lista. Toque em um item existente para removê-lo da lista.</string>
<string name="mpris_player_on_device">%1$s em %2$s</string> <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">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>
<string name="refresh">Atualizar</string>
<string name="unreachable_description">Este dispositivo emparelhado não está acessível. Certifique-se de que está conectado à mesma rede que você.</string>
<string name="no_file_browser">Não há gerenciadores de arquivos instalados.</string>
<string name="pref_plugin_telepathy">Enviar SMS</string>
<string name="pref_plugin_telepathy_desc">Enviar mensagens de texto do seu Desktop</string>
<string name="plugin_not_supported">Este plugin não é suportado pelo dispositivo</string>
</resources> </resources>

View File

@ -108,6 +108,8 @@
<string name="invalid_device_name">Nome do dispositivo inválido</string> <string name="invalid_device_name">Nome do dispositivo inválido</string>
<string name="shareplugin_text_saved">Texto recebido e guardado na área de transferência</string> <string name="shareplugin_text_saved">Texto recebido e guardado na área de transferência</string>
<string name="custom_devices_settings">Lista de dispositivos personalizada</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 o %s</string>
<string name="custom_device_list">Adicionar dispositivos pelo IP</string> <string name="custom_device_list">Adicionar dispositivos pelo IP</string>
<string name="share_notification_preference">Notificações com ruído</string> <string name="share_notification_preference">Notificações com ruído</string>
<string name="share_notification_preference_summary">Vibrar e tocar um som ao receber um ficheiro</string> <string name="share_notification_preference_summary">Vibrar e tocar um som ao receber um ficheiro</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Não foram encontrados leitores</string> <string name="no_players_connected">Não foram encontrados leitores</string>
<string name="custom_dev_list_help">Use esta opção apenas se o seu dispositivo não for detectado automaticamente. Indique o endereço IP ou nome da máquina em baixo e carregue no botão para a adicionar à lista. Carregue sobre um item existente para o remover da lista.</string> <string name="custom_dev_list_help">Use esta opção apenas se o seu dispositivo não for detectado automaticamente. Indique o endereço IP ou nome da máquina em baixo e carregue no botão para a adicionar à lista. Carregue sobre um item existente para o remover da lista.</string>
<string name="mpris_player_on_device">%1$s em %2$s</string> <string name="mpris_player_on_device">%1$s em %2$s</string>
<string name="send_files">Enviar os ficheiros</string>
<string name="pairing_title">Dispositivos do KDE Connect</string>
<string name="pairing_description">Os outros dispositivos a executar o KDE Connect na mesma rede que você deverão aparecer aqui.</string>
<string name="device_paired">Dispositivo emparelhado</string>
<string name="device_rename_title">Mudar o nome do dispositivo</string>
<string name="device_rename_confirm">Mudar o Nome</string>
<string name="refresh">Actualizar</string>
<string name="unreachable_description">Este dispositivo emparelhado não está acessível. Certifique-se que está ligado à mesma rede que você.</string>
<string name="no_file_browser">Não existem gestores de ficheiros instalados.</string>
<string name="pref_plugin_telepathy">Enviar um SMS</string>
<string name="pref_plugin_telepathy_desc">Enviar mensagens de texto a partir do seu ambiente de trabalho</string>
<string name="plugin_not_supported">Este \'plugin\' não é suportado pelo dispositivo</string>
</resources> </resources>

View File

@ -5,7 +5,7 @@
<string name="pref_plugin_battery">Batterirapport</string> <string name="pref_plugin_battery">Batterirapport</string>
<string name="pref_plugin_battery_desc">Rapportera periodiskt batteriets status</string> <string name="pref_plugin_battery_desc">Rapportera periodiskt batteriets status</string>
<string name="pref_plugin_sftp">Exponera filsystem</string> <string name="pref_plugin_sftp">Exponera filsystem</string>
<string name="pref_plugin_sftp_desc">Gör det möjligt att bläddra i telefonens filsystem från annan enhet</string> <string name="pref_plugin_sftp_desc">Gör det möjligt att bläddra i telefonens filsystem från annan apparat</string>
<string name="pref_plugin_clipboard">Synkronisera klippbord</string> <string name="pref_plugin_clipboard">Synkronisera klippbord</string>
<string name="pref_plugin_clipboard_desc">Dela klippbordets innehåll</string> <string name="pref_plugin_clipboard_desc">Dela klippbordets innehåll</string>
<string name="pref_plugin_mousepad">Fjärrinmatning</string> <string name="pref_plugin_mousepad">Fjärrinmatning</string>
@ -17,7 +17,7 @@
<string name="pref_plugin_notifications">Synkronisering av underrättelser</string> <string name="pref_plugin_notifications">Synkronisering av underrättelser</string>
<string name="pref_plugin_notifications_desc">Kom åt underrättelser från andra apparater</string> <string name="pref_plugin_notifications_desc">Kom åt underrättelser från andra apparater</string>
<string name="pref_plugin_sharereceiver">Dela och ta emot</string> <string name="pref_plugin_sharereceiver">Dela och ta emot</string>
<string name="pref_plugin_sharereceiver_desc">Dela filer och webbadresser mellan enheter</string> <string name="pref_plugin_sharereceiver_desc">Dela filer och webbadresser mellan apparater</string>
<string name="plugin_not_available">Funktionen är inte tillgänglig i Android-versionen</string> <string name="plugin_not_available">Funktionen är inte tillgänglig i Android-versionen</string>
<string name="device_list_empty">Inga apparater</string> <string name="device_list_empty">Inga apparater</string>
<string name="ok">Ok</string> <string name="ok">Ok</string>
@ -38,12 +38,12 @@
<string name="mousepad_double_default">höger</string> <string name="mousepad_double_default">höger</string>
<string name="mousepad_triple_default">mitten</string> <string name="mousepad_triple_default">mitten</string>
<string name="category_connected_devices">Anslutna apparater</string> <string name="category_connected_devices">Anslutna apparater</string>
<string name="category_not_paired_devices">Tillgängliga enheter</string> <string name="category_not_paired_devices">Tillgängliga apparater</string>
<string name="category_remembered_devices">Ihågkomna apparater</string> <string name="category_remembered_devices">Ihågkomna apparater</string>
<string name="plugins_failed_to_load">Misslyckades ladda insticksprogram (rör för mer information):</string> <string name="plugins_failed_to_load">Misslyckades ladda insticksprogram (rör för mer information):</string>
<string name="device_menu_plugins">Inställningar av insticksprogram</string> <string name="device_menu_plugins">Inställningar av insticksprogram</string>
<string name="device_menu_unpair">Ta bort ihopparning</string> <string name="device_menu_unpair">Ta bort ihopparning</string>
<string name="device_not_reachable">Ihopparad enhet kan inte nås</string> <string name="device_not_reachable">Ihopparad apparat kan inte nås</string>
<string name="unknown_device">Okänd apparat</string> <string name="unknown_device">Okänd apparat</string>
<string name="error_not_reachable">Apparaten kan inte nås</string> <string name="error_not_reachable">Apparaten kan inte nås</string>
<string name="error_already_requested">Ihopparning redan begärd</string> <string name="error_already_requested">Ihopparning redan begärd</string>
@ -108,6 +108,8 @@
<string name="invalid_device_name">Ogiltigt apparatnamn</string> <string name="invalid_device_name">Ogiltigt apparatnamn</string>
<string name="shareplugin_text_saved">Tog emot text, spara på klippbordet</string> <string name="shareplugin_text_saved">Tog emot text, spara på klippbordet</string>
<string name="custom_devices_settings">Egen apparatlista</string> <string name="custom_devices_settings">Egen apparatlista</string>
<string name="pair_device_action">Para ihop med ny apparat</string>
<string name="unpair_device_action">Ta bort ihopparning %s</string>
<string name="custom_device_list">Lägg till apparater enligt IP-adress</string> <string name="custom_device_list">Lägg till apparater enligt IP-adress</string>
<string name="share_notification_preference">Ljudliga underrättelser</string> <string name="share_notification_preference">Ljudliga underrättelser</string>
<string name="share_notification_preference_summary">Vibrera och spela ett ljud när en fil tas emot</string> <string name="share_notification_preference_summary">Vibrera och spela ett ljud när en fil tas emot</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Inga spelare hittades</string> <string name="no_players_connected">Inga spelare hittades</string>
<string name="custom_dev_list_help">Använd bara alternativet om apparaten inte detekteras automatiskt. Skriv in IP-adress eller värddatornamn nedan och rör vid knappen för att lägga till den i listan. Rör vid ett befintligt objekt för att ta bort det från listan.</string> <string name="custom_dev_list_help">Använd bara alternativet om apparaten inte detekteras automatiskt. Skriv in IP-adress eller värddatornamn nedan och rör vid knappen för att lägga till den i listan. Rör vid ett befintligt objekt för att ta bort det från listan.</string>
<string name="mpris_player_on_device">%1$s på %2$s</string> <string name="mpris_player_on_device">%1$s på %2$s</string>
<string name="send_files">Skicka filer</string>
<string name="pairing_title">KDE-anslut apparater</string>
<string name="pairing_description">Andra apparater som kör KDE-anslut i samma nätverk ska synas här.</string>
<string name="device_paired">Apparat ihopparad</string>
<string name="device_rename_title">Byt namn på apparat</string>
<string name="device_rename_confirm">Byt namn</string>
<string name="refresh">Uppdatera</string>
<string name="unreachable_description">Den här ihopparade apparaten kan inte nås. Försälkra dig om att den är ansluten till samma nätverk.</string>
<string name="no_file_browser">Det finns inga filbläddrare installerade.</string>
<string name="pref_plugin_telepathy">Skicka SMS</string>
<string name="pref_plugin_telepathy_desc">Skicka textmeddelanden från skrivbordet</string>
<string name="plugin_not_supported">Insticksprogrammet stöds inte av apparaten</string>
</resources> </resources>

View File

@ -108,6 +108,8 @@
<string name="invalid_device_name">Некоректна назва пристрою</string> <string name="invalid_device_name">Некоректна назва пристрою</string>
<string name="shareplugin_text_saved">Отримано текст, збережено до буфера обміну даними</string> <string name="shareplugin_text_saved">Отримано текст, збережено до буфера обміну даними</string>
<string name="custom_devices_settings">Список нетипових пристроїв</string> <string name="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="custom_device_list">Додати пристрої за IP</string>
<string name="share_notification_preference">Звукові сповіщення</string> <string name="share_notification_preference">Звукові сповіщення</string>
<string name="share_notification_preference_summary">Вібрація і відтворення звуку у відповідь на отримання файла</string> <string name="share_notification_preference_summary">Вібрація і відтворення звуку у відповідь на отримання файла</string>
@ -124,4 +126,16 @@
<string name="no_players_connected">Не знайдено програвачів</string> <string name="no_players_connected">Не знайдено програвачів</string>
<string name="custom_dev_list_help">Цим пунктом слід користуватися, лише якщо ваш пристрій не було визначено у автоматичному режимі. Введіть IP-адресу або назву вузла нижче і натисніть кнопку, щоб додати пункт до списку. Натисніть наявний пункт, щоб вилучити його зі списку.</string> <string name="custom_dev_list_help">Цим пунктом слід користуватися, лише якщо ваш пристрій не було визначено у автоматичному режимі. Введіть IP-адресу або назву вузла нижче і натисніть кнопку, щоб додати пункт до списку. Натисніть наявний пункт, щоб вилучити його зі списку.</string>
<string name="mpris_player_on_device">%1$s на %2$s</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>
</resources> </resources>

View File

@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@ -129,6 +129,8 @@
<string name="invalid_device_name">Invalid device name</string> <string name="invalid_device_name">Invalid device name</string>
<string name="shareplugin_text_saved">Received text, saved to clipboard</string> <string name="shareplugin_text_saved">Received text, saved to clipboard</string>
<string name="custom_devices_settings">Custom device list</string> <string name="custom_devices_settings">Custom device list</string>
<string name="pair_device_action">Pair a new device</string>
<string name="unpair_device_action">Unpair %s</string>
<string name="custom_device_list">Add devices by IP</string> <string name="custom_device_list">Add devices by IP</string>
<string name="share_notification_preference">Noisy notifications</string> <string name="share_notification_preference">Noisy notifications</string>
<string name="share_notification_preference_summary">Vibrate and play a sound when receiving a file</string> <string name="share_notification_preference_summary">Vibrate and play a sound when receiving a file</string>
@ -145,5 +147,18 @@
<string name="no_players_connected">No players found</string> <string name="no_players_connected">No players found</string>
<string name="custom_dev_list_help">Use this option only if your device is not automatically detected. Enter IP address or hostname below and touch the button to add it to the list. Touch an existing item to remove it from the list.</string> <string name="custom_dev_list_help">Use this option only if your device is not automatically detected. Enter IP address or hostname below and touch the button to add it to the list. Touch an existing item to remove it from the list.</string>
<string name="mpris_player_on_device">%1$s on %2$s</string> <string name="mpris_player_on_device">%1$s on %2$s</string>
<string name="send_files">Send files</string>
<string name="pairing_title">KDE Connect Devices</string>
<string name="pairing_description">Other devices running KDE Connect in your same network should appear here.</string>
<string name="device_paired">Device paired</string>
<string name="device_rename_title">Rename device</string>
<string name="device_rename_confirm">Rename</string>
<string name="refresh">Refresh</string>
<string name="unreachable_description">This paired device is not reachable. Make sure it is connected to your same network.</string>
<string name="no_file_browser">There are no file browsers installed.</string>
<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>
</resources> </resources>

View File

@ -1,7 +1,21 @@
<resources> <resources>
<color name="primary">#F67400</color>
<color name="primaryDark">#BD5900</color>
<color name="accent">#4ebffa</color>
<style name="KdeConnectTheme" parent="Theme.AppCompat"> <!-- NoActionBar because we use a Toolbar widget as ActionBar -->
<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>
<item name="colorAccent">@color/accent</item>
<item name="android:textColorPrimary">@android:color/black</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="KdeConnectTheme.NoActionBar" parent="KdeConnectTheme">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style> </style>
</resources> </resources>

View File

@ -33,15 +33,30 @@ import java.util.ArrayList;
public abstract class BaseLink { public abstract class BaseLink {
protected final Context context; protected final Context context;
public enum ConnectionStarted {
Locally, Remotely;
};
public interface PackageReceiver {
void onPackageReceived(NetworkPackage np);
}
private final BaseLinkProvider linkProvider; private final BaseLinkProvider linkProvider;
private final String deviceId; private final String deviceId;
private final ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>(); private final ArrayList<PackageReceiver> receivers = new ArrayList<>();
protected PrivateKey privateKey; protected PrivateKey privateKey;
protected BaseLink(Context context,String deviceId, BaseLinkProvider linkProvider) { 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(Context context, String deviceId, BaseLinkProvider linkProvider, ConnectionStarted connectionSource) {
this.context = context; this.context = context;
this.linkProvider = linkProvider; this.linkProvider = linkProvider;
this.deviceId = deviceId; this.deviceId = deviceId;
this.connectionSource = connectionSource;
} }
/* To be implemented by each link for pairing handlers */ /* To be implemented by each link for pairing handlers */
@ -60,9 +75,8 @@ public abstract class BaseLink {
return linkProvider; return linkProvider;
} }
public ConnectionStarted getConnectionSource() {
public interface PackageReceiver { return connectionSource;
public void onPackageReceived(NetworkPackage np);
} }
public void addPackageReceiver(PackageReceiver pr) { public void addPackageReceiver(PackageReceiver pr) {
@ -79,8 +93,11 @@ public abstract class BaseLink {
} }
} }
public void disconnect() {
linkProvider.connectionLost(this);
}
//TO OVERRIDE, should be sync //TO OVERRIDE, should be sync
public abstract void sendPackage(NetworkPackage np,Device.SendPackageStatusCallback callback); public abstract void sendPackage(NetworkPackage np,Device.SendPackageStatusCallback callback);
public abstract void sendPackageEncrypted(NetworkPackage np,Device.SendPackageStatusCallback callback, PublicKey key); public abstract void sendPackageEncrypted(NetworkPackage np,Device.SendPackageStatusCallback callback, PublicKey key);
} }

View File

@ -26,10 +26,11 @@ import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class BaseLinkProvider { public abstract class BaseLinkProvider {
private final ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>(); private final CopyOnWriteArrayList<ConnectionReceiver> connectionReceivers = new CopyOnWriteArrayList<>();
protected BasePairingHandler pairingHandler; protected BasePairingHandler pairingHandler;
public BasePairingHandler getPairingHandler() { public BasePairingHandler getPairingHandler() {
@ -37,8 +38,8 @@ public abstract class BaseLinkProvider {
} }
public interface ConnectionReceiver { public interface ConnectionReceiver {
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link); void onConnectionReceived(NetworkPackage identityPackage, BaseLink link);
public void onConnectionLost(BaseLink link); void onConnectionLost(BaseLink link);
} }
public void addConnectionReceiver(ConnectionReceiver cr) { public void addConnectionReceiver(ConnectionReceiver cr) {

View File

@ -24,6 +24,8 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.util.Log; import android.util.Log;
import org.apache.mina.core.future.WriteFuture;
import org.apache.mina.core.session.IoSession;
import org.json.JSONObject; import org.json.JSONObject;
import org.kde.kdeconnect.Backends.BaseLink; import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider; import org.kde.kdeconnect.Backends.BaseLinkProvider;
@ -61,7 +63,12 @@ public class LanLink extends BaseLink {
// Using time stamp, because if both devices emit their identity package at the same time, both the links tends to cancel each other // Using time stamp, because if both devices emit their identity package at the same time, both the links tends to cancel each other
private long startTime = 0; private long startTime = 0;
@Override
public void disconnect() { public void disconnect() {
closeSocket();
}
public void closeSocket() {
if (channel == null) { if (channel == null) {
Log.e("KDE/LanLink", "Not yet connected"); Log.e("KDE/LanLink", "Not yet connected");
return; return;
@ -73,8 +80,8 @@ public class LanLink extends BaseLink {
this.onSsl = value; this.onSsl = value;
} }
public LanLink(Context context,Channel channel, String deviceId, BaseLinkProvider linkProvider) { public LanLink(Context context,Channel channel, String deviceId, BaseLinkProvider linkProvider, ConnectionStarted connectionSource) {
super(context, deviceId, linkProvider); super(context, deviceId, linkProvider, connectionSource);
this.channel = channel; this.channel = channel;
this.startTime = System.currentTimeMillis(); this.startTime = System.currentTimeMillis();
} }
@ -188,6 +195,10 @@ public class LanLink extends BaseLink {
if (callback != null) { if (callback != null) {
callback.sendFailure(e); callback.sendFailure(e);
} }
} finally {
//Make sure we close the payload stream, if any
InputStream stream = np.getPayload();
try { stream.close(); } catch (Exception e) { }
} }
} }

View File

@ -27,9 +27,23 @@ import android.support.v4.util.LongSparseArray;
import android.util.Base64; import android.util.Base64;
import android.util.Log; 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.Backends.BaseLinkProvider;
import org.kde.kdeconnect.BackgroundService; import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity; import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
@ -81,6 +95,7 @@ public class LanLinkProvider extends BaseLinkProvider {
private final static int port = 1714; private final static int port = 1714;
private final Context context; private final Context context;
private final HashMap<String, LanLink> visibleComputers = new HashMap<String, LanLink>(); private final HashMap<String, LanLink> visibleComputers = new HashMap<String, LanLink>();
private final LongSparseArray<LanLink> nioLinks = new LongSparseArray<LanLink>(); private final LongSparseArray<LanLink> nioLinks = new LongSparseArray<LanLink>();
private final LongSparseArray<Channel> nioChannels = new LongSparseArray<Channel>(); private final LongSparseArray<Channel> nioChannels = new LongSparseArray<Channel>();
@ -117,7 +132,7 @@ public class LanLinkProvider extends BaseLinkProvider {
nioLinks.remove(id); nioLinks.remove(id);
//Log.i("KDE/LanLinkProvider", "nioLinks.size(): " + nioLinks.size() + " (-)"); //Log.i("KDE/LanLinkProvider", "nioLinks.size(): " + nioLinks.size() + " (-)");
try { try {
brokenLink.disconnect(); brokenLink.closeSocket();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
Log.e("KDE/LanLinkProvider", "Exception. Already disconnected?"); Log.e("KDE/LanLinkProvider", "Exception. Already disconnected?");
@ -162,14 +177,14 @@ public class LanLinkProvider extends BaseLinkProvider {
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) { if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
NetworkPackage myIdentityPackage = NetworkPackage.createIdentityPackage(context); String myId = DeviceHelper.getDeviceId(context);
if (np.getString("deviceId").equals(myIdentityPackage.getString("deviceId"))) { if (np.getString("deviceId").equals(myId)) {
return; return;
} }
Log.i("KDE/LanLinkProvider", "Identity package received from " + np.getString("deviceName")); Log.i("KDE/LanLinkProvider", "Identity package received from " + np.getString("deviceName"));
final LanLink link = new LanLink(context, ctx.channel(), np.getString("deviceId"), LanLinkProvider.this); final LanLink link = new LanLink(context, ctx.channel(), np.getString("deviceId"), LanLinkProvider.this, BaseLink.ConnectionStarted.Locally);
nioLinks.put(ctx.channel().hashCode(), link); nioLinks.put(ctx.channel().hashCode(), link);
nioChannels.put(ctx.channel().hashCode(), ctx.channel()); nioChannels.put(ctx.channel().hashCode(), ctx.channel());
//Log.i("KDE/LanLinkProvider","nioLinks.size(): " + nioLinks.size()); //Log.i("KDE/LanLinkProvider","nioLinks.size(): " + nioLinks.size());
@ -241,7 +256,7 @@ public class LanLinkProvider extends BaseLinkProvider {
Log.e("KDE/LanLinkProvider", "Expecting an identity package (B)"); Log.e("KDE/LanLinkProvider", "Expecting an identity package (B)");
return; return;
} else { } else {
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId"); String myId = DeviceHelper.getDeviceId(context);
if (identityPackage.getString("deviceId").equals(myId)) { if (identityPackage.getString("deviceId").equals(myId)) {
return; return;
} }
@ -289,7 +304,7 @@ public class LanLinkProvider extends BaseLinkProvider {
channel.pipeline().addFirst(sslHandler); channel.pipeline().addFirst(sslHandler);
} }
final LanLink link = new LanLink(context, channel, identityPackage.getString("deviceId"), LanLinkProvider.this); final LanLink link = new LanLink(context, channel, identityPackage.getString("deviceId"), LanLinkProvider.this, BaseLink.ConnectionStarted.Remotely);
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context); NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
link.sendPackage(np2,new Device.SendPackageStatusCallback() { link.sendPackage(np2,new Device.SendPackageStatusCallback() {
@Override @Override
@ -387,11 +402,11 @@ public class LanLinkProvider extends BaseLinkProvider {
if (oldLink != null) { if (oldLink != null) {
if (link.getStartTime() < oldLink.getStartTime()) { if (link.getStartTime() < oldLink.getStartTime()) {
// New link is not so new, it just took more time to be establish successfully and get here // New link is not so new, it just took more time to be establish successfully and get here
link.disconnect(); link.closeSocket();
connectionLost(link); connectionLost(link);
} else { } else {
Log.i("KDE/LanLinkProvider", "Removing old connection to same device"); Log.i("KDE/LanLinkProvider", "Removing old connection to same device");
oldLink.disconnect(); oldLink.closeSocket();
connectionLost(oldLink); connectionLost(oldLink);
} }
} }
@ -462,7 +477,7 @@ public class LanLinkProvider extends BaseLinkProvider {
String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString( String deviceListPrefs = PreferenceManager.getDefaultSharedPreferences(context).getString(
KEY_CUSTOM_DEVLIST_PREFERENCE, ""); KEY_CUSTOM_DEVLIST_PREFERENCE, "");
ArrayList<String> iplist = new ArrayList<String>(); ArrayList<String> iplist = new ArrayList<>();
if (!deviceListPrefs.isEmpty()) { if (!deviceListPrefs.isEmpty()) {
iplist = CustomDevicesActivity.deserializeIpList(deviceListPrefs); iplist = CustomDevicesActivity.deserializeIpList(deviceListPrefs);
} }

View File

@ -35,7 +35,7 @@ import java.util.ArrayList;
public class LoopbackLink extends BaseLink { public class LoopbackLink extends BaseLink {
public LoopbackLink(Context context, BaseLinkProvider linkProvider) { public LoopbackLink(Context context, BaseLinkProvider linkProvider) {
super(context, "loopback", linkProvider); super(context, "loopback", linkProvider, ConnectionStarted.Remotely);
} }
@Override @Override

View File

@ -20,6 +20,7 @@
package org.kde.kdeconnect; package org.kde.kdeconnect;
import android.app.Activity;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -35,39 +36,95 @@ import org.kde.kdeconnect.Backends.BaseLinkProvider;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper; import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
public class BackgroundService extends Service { public class BackgroundService extends Service {
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<BaseLinkProvider>(); public interface DeviceListChangedCallback {
void onDeviceListChanged();
}
private final HashMap<String, Device> devices = new HashMap<String, Device>(); private final ConcurrentHashMap<String, DeviceListChangedCallback> deviceListChangedCallbacks = new ConcurrentHashMap<>();
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<>();
private final ConcurrentHashMap<String, Device> devices = new ConcurrentHashMap<>();
private final HashSet<Object> discoveryModeAcquisitions = new HashSet<>();
public boolean acquireDiscoveryMode(Object key) {
boolean wasEmpty = discoveryModeAcquisitions.isEmpty();
discoveryModeAcquisitions.add(key);
if (wasEmpty) {
onNetworkChange();
}
return wasEmpty;
}
public void releaseDiscoveryMode(Object key) {
boolean removed = discoveryModeAcquisitions.remove(key);
if (removed && discoveryModeAcquisitions.isEmpty()) {
cleanDevices();
}
}
public static void addGuiInUseCounter(Activity activity) {
addGuiInUseCounter(activity, false);
}
public static void addGuiInUseCounter(final Activity activity, final boolean forceNetworkRefresh) {
BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
boolean refreshed = service.acquireDiscoveryMode(activity);
if (!refreshed && forceNetworkRefresh) {
service.onNetworkChange();
}
}
});
}
public static void removeGuiInUseCounter(final Activity activity) {
BackgroundService.RunCommand(activity, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
//If no user interface is open, close the connections open to other devices
service.releaseDiscoveryMode(activity);
}
});
}
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() { private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
@Override @Override
public void incomingRequest() { public void incomingRequest() {
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged(); onDeviceListChanged();
} }
@Override @Override
public void pairingSuccessful() { public void pairingSuccessful() {
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged(); onDeviceListChanged();
} }
@Override @Override
public void pairingFailed(String error) { public void pairingFailed(String error) {
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged(); onDeviceListChanged();
} }
@Override @Override
public void unpaired() { public void unpaired() {
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged(); onDeviceListChanged();
} }
}; };
private void onDeviceListChanged() {
for(DeviceListChangedCallback callback : deviceListChangedCallbacks.values()) {
callback.onDeviceListChanged();
}
}
private void loadRememberedDevicesFromSettings() { private void loadRememberedDevicesFromSettings() {
//Log.e("BackgroundService", "Loading remembered trusted devices"); //Log.e("BackgroundService", "Loading remembered trusted devices");
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE); SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
@ -104,12 +161,18 @@ public class BackgroundService extends Service {
return devices.get(id); return devices.get(id);
} }
private void cleanDevices() {
for(Device d : devices.values()) {
if (!d.isPaired()) {
d.disconnect();
}
}
}
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() { private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
@Override @Override
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseLink link) { public void onConnectionReceived(final NetworkPackage identityPackage, final BaseLink link) {
Log.i("KDE/BackgroundService", "Connection accepted!");
String deviceId = identityPackage.getString("deviceId"); String deviceId = identityPackage.getString("deviceId");
Device device = devices.get(deviceId); Device device = devices.get(deviceId);
@ -120,11 +183,13 @@ public class BackgroundService extends Service {
} else { } else {
Log.i("KDE/BackgroundService", "addLink,unknown device: " + deviceId); Log.i("KDE/BackgroundService", "addLink,unknown device: " + deviceId);
device = new Device(BackgroundService.this, identityPackage, link); device = new Device(BackgroundService.this, identityPackage, link);
if (device.isPaired() || !discoveryModeAcquisitions.isEmpty()) {
devices.put(deviceId, device); devices.put(deviceId, device);
device.addPairingCallback(devicePairingCallback); device.addPairingCallback(devicePairingCallback);
} }
}
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged(); onDeviceListChanged();
} }
@Override @Override
@ -133,66 +198,47 @@ public class BackgroundService extends Service {
Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId()); Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
if (d != null) { if (d != null) {
d.removeLink(link); d.removeLink(link);
if (!d.isReachable() && !d.isPaired()) { if (!d.isReachable() && !d.isPaired() && (link.getConnectionSource() == BaseLink.ConnectionStarted.Locally)) {
//Log.e("onConnectionLost","Removing connection device because it was not paired"); //Log.e("onConnectionLost","Removing connection device because it was not paired");
devices.remove(link.getDeviceId()); devices.remove(link.getDeviceId());
d.removePairingCallback(devicePairingCallback); d.removePairingCallback(devicePairingCallback);
} }
} else { } else {
Log.e("KDE/onConnectionLost","Removing connection to unknown device, this should not happen"); //Log.d("KDE/onConnectionLost","Removing connection to unknown device");
} }
if (deviceListChangedCallback != null) deviceListChangedCallback.onDeviceListChanged(); onDeviceListChanged();
} }
}; };
public HashMap<String, Device> getDevices() { public ConcurrentHashMap<String, Device> getDevices() {
return devices; return devices;
} }
public void startDiscovery() {
Log.i("KDE/BackgroundService","StartDiscovery");
for (BaseLinkProvider a : linkProviders) {
a.onStart();
}
}
public void stopDiscovery() {
Log.i("KDE/BackgroundService","StopDiscovery");
for (BaseLinkProvider a : linkProviders) {
a.onStop();
}
}
public void onNetworkChange() { public void onNetworkChange() {
Log.i("KDE/BackgroundService","OnNetworkChange");
for (BaseLinkProvider a : linkProviders) { for (BaseLinkProvider a : linkProviders) {
a.onNetworkChange(); a.onNetworkChange();
} }
} }
public void addConnectionListener(BaseLinkProvider.ConnectionReceiver cr) { public void addConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
Log.i("KDE/BackgroundService","Registering connection listener");
for (BaseLinkProvider a : linkProviders) { for (BaseLinkProvider a : linkProviders) {
a.addConnectionReceiver(cr); a.addConnectionReceiver(cr);
} }
} }
public void removeConnectionListener(BaseLinkProvider.ConnectionReceiver cr) { public void removeConnectionListener(BaseLinkProvider.ConnectionReceiver cr) {
Log.i("KDE/BackgroundService","Removing connection listener");
for (BaseLinkProvider a : linkProviders) { for (BaseLinkProvider a : linkProviders) {
a.removeConnectionReceiver(cr); a.removeConnectionReceiver(cr);
} }
} }
public interface DeviceListChangedCallback { public void addDeviceListChangedCallback(String key, DeviceListChangedCallback callback) {
void onDeviceListChanged(); deviceListChangedCallbacks.put(key, callback);
} }
private DeviceListChangedCallback deviceListChangedCallback = null; public void removeDeviceListChangedCallback(String key) {
public void setDeviceListChangedCallback(DeviceListChangedCallback callback) { deviceListChangedCallbacks.remove(key);
this.deviceListChangedCallback = callback;
} }
//This will called only once, even if we launch the service intent several times //This will called only once, even if we launch the service intent several times
@Override @Override
public void onCreate() { public void onCreate() {
@ -202,17 +248,18 @@ public class BackgroundService extends Service {
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
registerReceiver(new KdeConnectBroadcastReceiver(), filter); registerReceiver(new KdeConnectBroadcastReceiver(), filter);
Log.i("KDE/BackgroundService","Service not started yet, initializing..."); Log.i("KDE/BackgroundService", "Service not started yet, initializing...");
initializeSecurityParameters(); initializeSecurityParameters();
MainSettingsActivity.initializeDeviceName(this);
loadRememberedDevicesFromSettings(); loadRememberedDevicesFromSettings();
registerLinkProviders(); registerLinkProviders();
//Link Providers need to be already registered //Link Providers need to be already registered
addConnectionListener(deviceListener); addConnectionListener(deviceListener);
startDiscovery();
for (BaseLinkProvider a : linkProviders) {
a.onStart();
}
} }
void initializeSecurityParameters() { void initializeSecurityParameters() {
@ -222,8 +269,9 @@ public class BackgroundService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
Log.i("KDE/BackgroundService", "Destroying"); for (BaseLinkProvider a : linkProviders) {
stopDiscovery(); a.onStop();
}
super.onDestroy(); super.onDestroy();
} }
@ -239,7 +287,7 @@ public class BackgroundService extends Service {
void onServiceStart(BackgroundService service); void onServiceStart(BackgroundService service);
} }
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>(); private final static ArrayList<InstanceCallback> callbacks = new ArrayList<>();
private final static Lock mutex = new ReentrantLock(true); private final static Lock mutex = new ReentrantLock(true);

View File

@ -32,6 +32,7 @@ import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
@ -39,24 +40,24 @@ import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BasePairingHandler; import org.kde.kdeconnect.Backends.BasePairingHandler;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.Plugins.PluginFactory; import org.kde.kdeconnect.Plugins.PluginFactory;
import org.kde.kdeconnect.UserInterface.PairActivity; import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import org.spongycastle.cert.X509CertificateHolder; import org.spongycastle.cert.X509CertificateHolder;
import org.spongycastle.cert.jcajce.JcaX509CertificateConverter; import org.spongycastle.cert.jcajce.JcaX509CertificateConverter;
import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.provider.BouncyCastleProvider;
import java.io.ByteArrayInputStream;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.cert.Certificate; import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
public class Device implements BaseLink.PackageReceiver { public class Device implements BaseLink.PackageReceiver {
@ -69,6 +70,38 @@ public class Device implements BaseLink.PackageReceiver {
private int notificationId; private int notificationId;
private int protocolVersion; private int protocolVersion;
private DeviceType deviceType;
private PairStatus pairStatus;
private final CopyOnWriteArrayList<PairingCallback> pairingCallback = new CopyOnWriteArrayList<>();
private Map<String, BasePairingHandler> pairingHandlers = new HashMap<String, BasePairingHandler>();
private final CopyOnWriteArrayList<BaseLink> links = new CopyOnWriteArrayList<>();
private ArrayList<String> incomingCapabilities;
private ArrayList<String> outgoingCapabilities;
private final HashMap<String, Plugin> plugins = new HashMap<>();
private final HashMap<String, Plugin> failedPlugins = new HashMap<>();
private ArrayList<String> unsupportedPlugins = new ArrayList<>();
private HashSet<String> supportedIncomingInterfaces = 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 {
void onPluginsChanged(Device device);
}
public enum PairStatus { public enum PairStatus {
NotPaired, NotPaired,
Paired Paired
@ -81,14 +114,14 @@ public class Device implements BaseLink.PackageReceiver {
public static DeviceType FromString(String s) { public static DeviceType FromString(String s) {
if ("tablet".equals(s)) return Tablet; if ("tablet".equals(s)) return Tablet;
if ("computer".equals(s)) return Computer; if ("phone".equals(s)) return Phone;
return Phone; //Default return Computer; //Default
} }
public String toString() { public String toString() {
switch (this) { switch (this) {
case Tablet: return "tablet"; case Tablet: return "tablet";
case Computer: return "computer"; case Phone: return "phone";
default: return "phone"; default: return "desktop";
} }
} }
} }
@ -100,17 +133,6 @@ public class Device implements BaseLink.PackageReceiver {
void unpaired(); void unpaired();
} }
private DeviceType deviceType;
private PairStatus pairStatus;
private ArrayList<PairingCallback> pairingCallback = new ArrayList<PairingCallback>();
private Map<String, BasePairingHandler> pairingHandlers = new HashMap<String, BasePairingHandler>();
private final ArrayList<BaseLink> links = new ArrayList<BaseLink>();
private final HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
private final HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
private final SharedPreferences settings;
//Remembered trusted device, we need to wait for a incoming devicelink to communicate //Remembered trusted device, we need to wait for a incoming devicelink to communicate
Device(Context context, String deviceId) { Device(Context context, String deviceId) {
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
@ -122,7 +144,7 @@ public class Device implements BaseLink.PackageReceiver {
this.name = settings.getString("deviceName", context.getString(R.string.unknown_device)); this.name = settings.getString("deviceName", context.getString(R.string.unknown_device));
this.pairStatus = PairStatus.Paired; this.pairStatus = PairStatus.Paired;
this.protocolVersion = NetworkPackage.ProtocolVersion; //We don't know it yet this.protocolVersion = NetworkPackage.ProtocolVersion; //We don't know it yet
this.deviceType = DeviceType.FromString(settings.getString("deviceType", "computer")); this.deviceType = DeviceType.FromString(settings.getString("deviceType", "desktop"));
try { try {
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0); byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
@ -143,10 +165,10 @@ public class Device implements BaseLink.PackageReceiver {
this.context = context; this.context = context;
this.deviceId = np.getString("deviceId"); this.deviceId = np.getString("deviceId");
this.name = np.getString("deviceName", context.getString(R.string.unknown_device)); this.name = context.getString(R.string.unknown_device); //We read it in addLink
this.pairStatus = PairStatus.NotPaired; this.pairStatus = PairStatus.NotPaired;
this.protocolVersion = np.getInt("protocolVersion"); this.protocolVersion = 0;
this.deviceType = DeviceType.FromString(np.getString("deviceType", "computer")); this.deviceType = DeviceType.Computer;
this.publicKey = null; this.publicKey = null;
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
@ -160,11 +182,13 @@ public class Device implements BaseLink.PackageReceiver {
public Drawable getIcon() public Drawable getIcon()
{ {
int drawableId;
switch (deviceType) { switch (deviceType) {
case Phone: return context.getResources().getDrawable(R.drawable.ic_device_phone); case Phone: drawableId = R.drawable.ic_device_phone; break;
case Tablet: return context.getResources().getDrawable(R.drawable.ic_device_tablet); case Tablet: drawableId = R.drawable.ic_device_tablet; break;
default: return context.getResources().getDrawable(R.drawable.ic_device_laptop); default: drawableId = R.drawable.ic_device_laptop;
} }
return ContextCompat.getDrawable(context, drawableId);
} }
public DeviceType getDeviceType() { public DeviceType getDeviceType() {
@ -187,7 +211,6 @@ public class Device implements BaseLink.PackageReceiver {
// //
// Pairing-related functions // Pairing-related functions
// //
@ -346,7 +369,7 @@ public class Device implements BaseLink.PackageReceiver {
notificationId = (int)System.currentTimeMillis(); notificationId = (int)System.currentTimeMillis();
Intent intent = new Intent(getContext(), PairActivity.class); Intent intent = new Intent(getContext(), MaterialActivity.class);
intent.putExtra("deviceId", getDeviceId()); intent.putExtra("deviceId", getDeviceId());
intent.putExtra("notificationId", notificationId); intent.putExtra("notificationId", notificationId);
PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT); PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
@ -358,7 +381,7 @@ public class Device implements BaseLink.PackageReceiver {
.setContentText(res.getString(R.string.tap_to_answer)) .setContentText(res.getString(R.string.tap_to_answer))
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.setTicker(res.getString(R.string.pair_requested)) .setTicker(res.getString(R.string.pair_requested))
.setSmallIcon(android.R.drawable.ic_menu_help) .setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true) .setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL) .setDefaults(Notification.DEFAULT_ALL)
.build(); .build();
@ -400,7 +423,7 @@ public class Device implements BaseLink.PackageReceiver {
} }
if (identityPackage.has("deviceType")) { if (identityPackage.has("deviceType")) {
this.deviceType = DeviceType.FromString(identityPackage.getString("deviceType", "computer")); this.deviceType = DeviceType.FromString(identityPackage.getString("deviceType", "desktop"));
} }
if (identityPackage.has("certificate")) { if (identityPackage.has("certificate")) {
@ -475,6 +498,8 @@ public class Device implements BaseLink.PackageReceiver {
link.addPackageReceiver(this); link.addPackageReceiver(this);
if (links.size() == 1) { if (links.size() == 1) {
incomingCapabilities = identityPackage.getStringList("IncomingCapabilties");
outgoingCapabilities = identityPackage.getStringList("OutgoingCapabilities");
reloadPluginsFromSettings(); reloadPluginsFromSettings();
} }
} }
@ -505,7 +530,7 @@ public class Device implements BaseLink.PackageReceiver {
@Override @Override
public void onPackageReceived(NetworkPackage np) { public void onPackageReceived(NetworkPackage 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");
@ -526,12 +551,10 @@ public class Device implements BaseLink.PackageReceiver {
e.printStackTrace(); e.printStackTrace();
Log.e("KDE/Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived()"); Log.e("KDE/Device", "Exception in "+plugin.getDisplayName()+"'s onPackageReceived()");
} }
} }
} else { } else {
Log.e("KDE/onPackageReceived","Device not paired, ignoring package!"); //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 it is pair package, it should be captured by "if" at start
// If not and device is paired, it should be captured by isPaired // If not and device is paired, it should be captured by isPaired
@ -539,6 +562,15 @@ public class Device implements BaseLink.PackageReceiver {
unpair(); 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");
}
}
} }
} }
@ -589,8 +621,7 @@ public class Device implements BaseLink.PackageReceiver {
boolean useEncryption = (!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 //Make a copy to avoid concurrent modification exception if the original list changes
ArrayList<BaseLink> mLinks = new ArrayList<BaseLink>(links); for (final BaseLink link : links) {
for (final BaseLink link : mLinks) {
if (link == null) continue; //Since we made a copy, maybe somebody destroyed the link in the meanwhile if (link == null) continue; //Since we made a copy, maybe somebody destroyed the link in the meanwhile
if (useEncryption) { if (useEncryption) {
link.sendPackageEncrypted(np, callback, publicKey); link.sendPackageEncrypted(np, callback, publicKey);
@ -601,7 +632,7 @@ public class Device implements BaseLink.PackageReceiver {
} }
if (!callback.success) { if (!callback.success) {
Log.e("KDE/sendPackage", "No device link (of "+mLinks.size()+" available) could send the package. Package lost!"); Log.e("KDE/sendPackage", "No device link (of "+links.size()+" available) could send the package. Package "+np.getType()+" to " + name + " lost!");
backtrace.printStackTrace(); backtrace.printStackTrace();
} }
@ -637,14 +668,14 @@ public class Device implements BaseLink.PackageReceiver {
private synchronized void addPlugin(final String pluginKey) { private synchronized void addPlugin(final String pluginKey) {
Plugin existing = plugins.get(pluginKey); Plugin existing = plugins.get(pluginKey);
if (existing != null) { if (existing != null) {
Log.w("KDE/addPlugin","plugin already present:" + pluginKey); //Log.w("KDE/addPlugin","plugin already present:" + pluginKey);
return; return;
} }
final Plugin plugin = PluginFactory.instantiatePluginForDevice(context, pluginKey, this); final Plugin plugin = PluginFactory.instantiatePluginForDevice(context, pluginKey, this);
if (plugin == null) { if (plugin == null) {
Log.e("KDE/addPlugin","could not instantiate plugin: "+pluginKey); Log.e("KDE/addPlugin","could not instantiate plugin: "+pluginKey);
failedPlugins.put(pluginKey, plugin); failedPlugins.put(pluginKey, null);
return; return;
} }
@ -671,10 +702,6 @@ public class Device implements BaseLink.PackageReceiver {
failedPlugins.put(pluginKey, plugin); failedPlugins.put(pluginKey, plugin);
} }
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(Device.this);
}
} }
}); });
@ -701,17 +728,12 @@ public class Device implements BaseLink.PackageReceiver {
Log.e("KDE/removePlugin","Exception calling onDestroy for plugin "+pluginKey); Log.e("KDE/removePlugin","Exception calling onDestroy for plugin "+pluginKey);
} }
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(this);
}
return true; return true;
} }
public void setPluginEnabled(String pluginKey, boolean value) { public void setPluginEnabled(String pluginKey, boolean value) {
settings.edit().putBoolean(pluginKey,value).apply(); settings.edit().putBoolean(pluginKey,value).apply();
if (value && isPaired() && isReachable()) addPlugin(pluginKey); reloadPluginsFromSettings();
else removePlugin(pluginKey);
} }
public boolean isPluginEnabled(String pluginKey) { public boolean isPluginEnabled(String pluginKey) {
@ -730,19 +752,83 @@ public class Device implements BaseLink.PackageReceiver {
Set<String> availablePlugins = PluginFactory.getAvailablePlugins(); Set<String> availablePlugins = PluginFactory.getAvailablePlugins();
for(String pluginKey : availablePlugins) { ArrayList<String> newUnsupportedPlugins = new ArrayList<>();
boolean enabled = false; HashSet<String> newSupportedIncomingInterfaces = new HashSet<>();
if (isPaired() && isReachable()) { HashMap<String, ArrayList<String>> newPluginsByIncomingInterface = new HashMap<>();
enabled = isPluginEnabled(pluginKey); HashMap<String, ArrayList<String>> newPluginsByOutgoingInterface = new HashMap<>();
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();
if ((isPaired() || listenToUnpaired) && isReachable()) {
pluginEnabled = isPluginEnabled(pluginKey);
} }
if (enabled) {
addPlugin(pluginKey); if (pluginEnabled) {
} else { newSupportedIncomingInterfaces.addAll(incomingInterfaces);
removePlugin(pluginKey); }
if ((incomingCapabilities != null && !incomingCapabilities.isEmpty()) ||
(incomingCapabilities != null && !outgoingCapabilities.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()) {
Log.w("ReloadPlugins", "not loading " + pluginKey + "because of unmatched capabilities");
newUnsupportedPlugins.add(pluginKey);
pluginEnabled = false;
} }
} }
//No need to call PluginsChangedListeners because addPlugin and removePlugin already do so if (pluginEnabled) {
addPlugin(pluginKey);
for (String packageType : incomingInterfaces) {
ArrayList<String> plugins = newPluginsByIncomingInterface.get(packageType);
if (plugins == null) plugins = new ArrayList<>();
plugins.add(pluginKey);
newPluginsByIncomingInterface.put(packageType, plugins);
}
for (String packageType : outgoingInterfaces) {
ArrayList<String> plugins = newPluginsByOutgoingInterface.get(packageType);
if (plugins == null) plugins = new ArrayList<>();
plugins.add(pluginKey);
newPluginsByOutgoingInterface.put(packageType, plugins);
}
} else {
removePlugin(pluginKey);
}
}
boolean capabilitiesChanged = false;
if (!newSupportedIncomingInterfaces.equals(supportedIncomingInterfaces) ||
!newPluginsByOutgoingInterface.equals(pluginsByOutgoingInterface)) {
capabilitiesChanged = true;
}
pluginsByOutgoingInterface = newPluginsByOutgoingInterface;
pluginsByIncomingInterface = newPluginsByIncomingInterface;
supportedIncomingInterfaces = newSupportedIncomingInterfaces;
unsupportedPlugins = newUnsupportedPlugins;
for (PluginsChangedListener listener : pluginsChangedListeners) {
listener.onPluginsChanged(Device.this);
}
if (capabilitiesChanged && isReachable() && isPaired()) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_CAPABILITIES);
np.set("IncomingCapabilities", new ArrayList<>(newSupportedIncomingInterfaces));
np.set("OutgoingCapabilities", new ArrayList<>(newPluginsByOutgoingInterface.keySet()));
sendPackage(np);
}
} }
public HashMap<String,Plugin> getLoadedPlugins() { public HashMap<String,Plugin> getLoadedPlugins() {
@ -753,12 +839,6 @@ public class Device implements BaseLink.PackageReceiver {
return failedPlugins; return failedPlugins;
} }
public interface PluginsChangedListener {
void onPluginsChanged(Device device);
}
private final ArrayList<PluginsChangedListener> pluginsChangedListeners = new ArrayList<PluginsChangedListener>();
public void addPluginsChangedListener(PluginsChangedListener listener) { public void addPluginsChangedListener(PluginsChangedListener listener) {
pluginsChangedListeners.add(listener); pluginsChangedListeners.add(listener);
} }
@ -767,4 +847,9 @@ public class Device implements BaseLink.PackageReceiver {
pluginsChangedListeners.remove(listener); pluginsChangedListeners.remove(listener);
} }
public void disconnect() {
for(BaseLink link : links) {
link.disconnect();
}
}
} }

View File

@ -20,19 +20,25 @@
package org.kde.kdeconnect.Helpers; package org.kde.kdeconnect.Helpers;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Build; import android.os.Build;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log; import android.util.Log;
import java.util.HashMap; import java.util.HashMap;
public class DeviceHelper { public class DeviceHelper {
public static final String KEY_DEVICE_NAME_PREFERENCE = "device_name_preference";
//from https://github.com/meetup/android-device-names //from https://github.com/meetup/android-device-names
//Converted to java using: //Converted to java using:
//cat android_models.properties | awk -F'=' '{sub(/ *$/, "", $1)} sub(/^ */, "", $2) { if ($2 != "") print "humanReadableNames.put(\""$1"\",\"" $2 "\");"}' //cat android_models.properties | awk -F'=' '{sub(/ *$/, "", $1)} sub(/^ */, "", $2) { if ($2 != "") print "humanReadableNames.put(\""$1"\",\"" $2 "\");"}'
private final static HashMap<String,String> humanReadableNames = new HashMap<String,String>(); private final static HashMap<String,String> humanReadableNames = new HashMap<>();
static { static {
humanReadableNames.put("5860E","Coolpad Quattro 4G"); humanReadableNames.put("5860E","Coolpad Quattro 4G");
humanReadableNames.put("831C","HTC One M8"); humanReadableNames.put("831C","HTC One M8");
@ -230,9 +236,9 @@ public class DeviceHelper {
humanReadableNames.put("MOTWX435KT","Motorola Triumph"); humanReadableNames.put("MOTWX435KT","Motorola Triumph");
humanReadableNames.put("N3","Star NO.1 N3"); humanReadableNames.put("N3","Star NO.1 N3");
humanReadableNames.put("N860","ZTE Warp N860"); humanReadableNames.put("N860","ZTE Warp N860");
humanReadableNames.put("NEXUS_4","Nexus 4"); humanReadableNames.put("NEXUS 4","Nexus 4");
humanReadableNames.put("NEXUS_5","Nexus 5"); humanReadableNames.put("NEXUS 5","Nexus 5");
humanReadableNames.put("NEXUS_6","Nexus 6"); humanReadableNames.put("NEXUS 6","Nexus 6");
humanReadableNames.put("Nexus_10","Google Nexus 10"); humanReadableNames.put("Nexus_10","Google Nexus 10");
humanReadableNames.put("Nexus_4","Google Nexus 4"); humanReadableNames.put("Nexus_4","Google Nexus 4");
humanReadableNames.put("Nexus_7","Asus Nexus 7"); humanReadableNames.put("Nexus_7","Asus Nexus 7");
@ -435,7 +441,7 @@ public class DeviceHelper {
} }
public static String getDeviceName() { public static String getAndroidDeviceName() {
String deviceName = null; String deviceName = null;
try { try {
String dictName = humanReadableNames.get(Build.MODEL.replace(' ', '_')); String dictName = humanReadableNames.get(Build.MODEL.replace(' ', '_'));
@ -465,4 +471,25 @@ public class DeviceHelper {
return isLarge; return isLarge;
} }
//It returns getAndroidDeviceName() if no user-defined name has been set with setDeviceName().
public static String getDeviceName(Context context){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
// Could use prefrences.contains but would need to check for empty String anyway.
String deviceName = preferences.getString(KEY_DEVICE_NAME_PREFERENCE, "");
if (deviceName.isEmpty()){
deviceName = DeviceHelper.getAndroidDeviceName();
Log.i("MainSettingsActivity", "New device name: " + deviceName);
preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, deviceName).apply();
}
return deviceName;
}
public static void setDeviceName(Context context, String name){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit().putString(KEY_DEVICE_NAME_PREFERENCE, name).apply();
}
public static String getDeviceId(Context context) {
return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
}
} }

View File

@ -42,11 +42,6 @@ public class FilesHelper {
/** /**
* Converts any string into a string that is safe to use as a file name. * Converts any string into a string that is safe to use as a file name.
* The result will only include ascii characters and numbers, and the "-","_", and "." characters. * The result will only include ascii characters and numbers, and the "-","_", and "." characters.
*
* @param name
* @param dirSeparators
* @param maxFileLength
* @return
*/ */
public static String toFileSystemSafeName(String name, boolean dirSeparators, int maxFileLength) { public static String toFileSystemSafeName(String name, boolean dirSeparators, int maxFileLength) {
int size = name.length(); int size = name.length();

View File

@ -0,0 +1,7 @@
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

@ -137,6 +137,7 @@ public class SslHelper {
if (isDeviceTrusted){ if (isDeviceTrusted){
SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE); SharedPreferences devicePreferences = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0); byte[] certificateBytes = Base64.decode(devicePreferences.getString("certificate", ""), 0);
Log.e("CERTIICATEBTYES", ""+certificateBytes);
X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes); X509CertificateHolder certificateHolder = new X509CertificateHolder(certificateBytes);
remoteDeviceCertificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder); remoteDeviceCertificate = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certificateHolder);
} }

View File

@ -61,7 +61,7 @@ public class StorageHelper {
*/ */
public static List<StorageInfo> getStorageList() { public static List<StorageInfo> getStorageList() {
List<StorageInfo> list = new ArrayList<StorageInfo>(); List<StorageInfo> list = new ArrayList<>();
String def_path = Environment.getExternalStorageDirectory().getPath(); String def_path = Environment.getExternalStorageDirectory().getPath();
boolean def_path_removable = Environment.isExternalStorageRemovable(); boolean def_path_removable = Environment.isExternalStorageRemovable();
String def_path_state = Environment.getExternalStorageState(); String def_path_state = Environment.getExternalStorageState();
@ -69,7 +69,7 @@ public class StorageHelper {
|| def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY); || def_path_state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY); boolean def_path_readonly = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY);
HashSet<String> paths = new HashSet<String>(); HashSet<String> paths = new HashSet<>();
int cur_removable_number = 1; int cur_removable_number = 1;
if (def_path_available) { if (def_path_available) {
@ -115,7 +115,7 @@ public class StorageHelper {
//Legacy code for Android < 4.0 that still didn't have /storage //Legacy code for Android < 4.0 that still didn't have /storage
ArrayList<String> entries = new ArrayList<String>(); ArrayList<String> entries = new ArrayList<>();
BufferedReader buf_reader = null; BufferedReader buf_reader = null;
try { try {
buf_reader = new BufferedReader(new FileReader("/proc/mounts")); buf_reader = new BufferedReader(new FileReader("/proc/mounts"));

View File

@ -28,7 +28,6 @@ import android.util.Log;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.kde.kdeconnect.Helpers.DeviceHelper; import org.kde.kdeconnect.Helpers.DeviceHelper;
import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.InputStream; import java.io.InputStream;
@ -51,6 +50,7 @@ public class NetworkPackage {
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris"; public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
public final static String PACKAGE_TYPE_MOUSEPAD = "kdeconnect.mousepad"; public final static String PACKAGE_TYPE_MOUSEPAD = "kdeconnect.mousepad";
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share"; public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
public static final String PACKAGE_TYPE_CAPABILITIES = "kdeconnect.capabilities";
private long mId; private long mId;
private String mType; private String mType;
@ -100,7 +100,8 @@ public class NetworkPackage {
public ArrayList<String> getStringList(String key) { public ArrayList<String> getStringList(String key) {
JSONArray jsonArray = mBody.optJSONArray(key); JSONArray jsonArray = mBody.optJSONArray(key);
ArrayList<String> list = new ArrayList<String>(); if (jsonArray == null) return null;
ArrayList<String> list = new ArrayList<>();
int length = jsonArray.length(); int length = jsonArray.length();
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
try { try {
@ -188,13 +189,10 @@ public class NetworkPackage {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY); NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
String deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID); String deviceId = DeviceHelper.getDeviceId(context);
try { try {
np.mBody.put("deviceId", deviceId); np.mBody.put("deviceId", deviceId);
np.mBody.put("deviceName", np.mBody.put("deviceName", DeviceHelper.getDeviceName(context));
PreferenceManager.getDefaultSharedPreferences(context).getString(
MainSettingsActivity.KEY_DEVICE_NAME_PREFERENCE,
DeviceHelper.getDeviceName()));
np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion); np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion);
np.mBody.put("deviceType", DeviceHelper.isTablet()? "tablet" : "phone"); np.mBody.put("deviceType", DeviceHelper.isTablet()? "tablet" : "phone");
} catch (Exception e) { } catch (Exception e) {

View File

@ -110,4 +110,16 @@ public class BatteryPlugin extends Plugin {
return true; return true;
} }
@Override
public String[] getSupportedPackageTypes() {
String[] packetTypes = {NetworkPackage.PACKAGE_TYPE_BATTERY};
return packetTypes;
}
@Override
public String[] getOutgoingPackageTypes() {
String[] packetTypes = {NetworkPackage.PACKAGE_TYPE_BATTERY};
return packetTypes;
}
} }

View File

@ -20,13 +20,16 @@
package org.kde.kdeconnect.Plugins.ClibpoardPlugin; package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.annotation.TargetApi;
import android.content.ClipData; import android.content.ClipData;
import android.content.Context; import android.content.Context;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.os.Build;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ClipboardListener { public class ClipboardListener {

View File

@ -23,6 +23,7 @@ package org.kde.kdeconnect.Plugins.ClibpoardPlugin;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.widget.Button; import android.widget.Button;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
@ -41,16 +42,6 @@ public class ClipboardPlugin extends Plugin {
return context.getResources().getString(R.string.pref_plugin_clipboard_desc); return context.getResources().getString(R.string.pref_plugin_clipboard_desc);
} }
@Override
public Drawable getIcon() {
return context.getResources().getDrawable(R.drawable.icon);
}
@Override
public boolean hasSettings() {
return false;
}
@Override @Override
public boolean isEnabledByDefault() { public boolean isEnabledByDefault() {
//Disabled by default due to just one direction sync(incoming clipboard change) in early version of android. //Disabled by default due to just one direction sync(incoming clipboard change) in early version of android.
@ -72,7 +63,6 @@ public class ClipboardPlugin extends Plugin {
@Override @Override
public boolean onPackageReceived(NetworkPackage np) { public boolean onPackageReceived(NetworkPackage np) {
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_CLIPBOARD)) { if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_CLIPBOARD)) {
return false; return false;
} }
@ -83,10 +73,16 @@ public class ClipboardPlugin extends Plugin {
} }
@Override @Override
public AlertDialog getErrorDialog(Activity deviceActivity) { return null; } public String[] getSupportedPackageTypes() {
String[] packetTypes = {NetworkPackage.PACKAGE_TYPE_CLIPBOARD};
return packetTypes;
}
@Override @Override
public Button getInterfaceButton(Activity activity) { public String[] getOutgoingPackageTypes() {
return null; String[] packetTypes = {NetworkPackage.PACKAGE_TYPE_CLIPBOARD};
return packetTypes;
} }
} }

View File

@ -0,0 +1,21 @@
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;
public KeyInputConnection(KeyListenerView targetView, boolean fullEditor) {
super(targetView, fullEditor);
view = targetView;
}
@Override public boolean commitText(CharSequence text, int newCursorPosition) {
view.sendChars(text);
return true;
}
}

View File

@ -33,8 +33,6 @@ import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect.Device; import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import java.util.HashMap;
public class KeyListenerView extends View { public class KeyListenerView extends View {
private String deviceId; private String deviceId;
@ -91,8 +89,10 @@ public class KeyListenerView extends View {
@Override @Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) { public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (android.os.Build.VERSION.SDK_INT >= 11) {
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
return null; }
return new KeyInputConnection(this, true);
} }
@Override @Override
@ -100,10 +100,27 @@ public class KeyListenerView extends View {
return true; return true;
} }
public void sendChars(CharSequence chars) {
final NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MOUSEPAD);
np.set("key", chars.toString());
sendKeyPressPackage(np);
}
private void sendKeyPressPackage(final NetworkPackage np) {
BackgroundService.RunCommand(getContext(), 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.sendKeyboardPacket(np);
}
});
}
@Override @Override
public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyUp(int keyCode, KeyEvent event) {
/* NOTE: Some keyboards, and specifically the Android default keyboard when /* NOTE: Some keyboards, and specifically the Android default keyboard when
* entering non-ascii characters, will not trigger KeyEvent events as documented * entering non-ascii characters, will not trigger KeyEvent events as documented
* here: http://developer.android.com/reference/android/view/KeyEvent.html * here: http://developer.android.com/reference/android/view/KeyEvent.html
@ -120,6 +137,7 @@ public class KeyListenerView extends View {
np.set("alt", true); np.set("alt", true);
modifier = true; modifier = true;
} }
if (Build.VERSION.SDK_INT >= 11) { if (Build.VERSION.SDK_INT >= 11) {
if (event.isCtrlPressed()) { if (event.isCtrlPressed()) {
np.set("ctrl", true); np.set("ctrl", true);
@ -127,62 +145,24 @@ public class KeyListenerView extends View {
} }
} }
if (modifier) {
//Only send shift in combination with other modifiers or special keys. Otherwise let it modify the letter itself and get the final result in utf.
if (event.isShiftPressed()) { if (event.isShiftPressed()) {
np.set("shift", true); np.set("shift", true);
} }
int specialKey = SpecialKeysMap.get(keyCode, -1); int specialKey = SpecialKeysMap.get(keyCode, -1);
if (specialKey != -1) { if (specialKey != -1) {
np.set("specialKey", specialKey); np.set("specialKey", specialKey);
} else if (event.getDisplayLabel() != 0) { } else if (event.getDisplayLabel() != 0 && modifier) {
//Alt will change the utf symbol to non-ascii characters, we want the plain original letter //Alt will change the utf symbol to non-ascii characters, we want the plain original letter
//Since getDisplayLabel will always have a value, we have to check for special keys before //Since getDisplayLabel will always have a value, we have to check for special keys before
char keyCharacter = event.getDisplayLabel(); char keyCharacter = event.getDisplayLabel();
np.set("key", new String(new char[]{keyCharacter}).toLowerCase()); np.set("key", new String(new char[]{keyCharacter}).toLowerCase());
} else { } else {
return false; //We don't know what to send, better send nothing. Probably this is the modifier key itself. return false; //normal keys will be handled by KeyInputConnection
} }
} else { sendKeyPressPackage(np);
//If it's not a modifier+key combination, we want the fancy (potentially utf) version of the key pressed
char utfChar = (char) event.getUnicodeChar();
//Workaround to send enter and tab as special keys instead of characters
if (utfChar == 9 || utfChar == 10) utfChar = 0;
if (utfChar != 0) {
String utfString = new String(new char[]{utfChar});
np.set("key", utfString);
} else {
int specialKey = SpecialKeysMap.get(keyCode, -1);
if (specialKey != -1) {
//Only send shift in combination with other modifiers or special keys. Otherwise let it modify the letter itself and get the final result in utf.
if (event.isShiftPressed()) {
np.set("shift", true);
}
//If it was not a displayable character, check if it was a special key
np.set("specialKey", specialKey);
} else {
return false; //We don't know what to send, better send nothing. Probably this is an unhandled special key.
}
}
}
BackgroundService.RunCommand(getContext(), new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MousePadPlugin mousePadPlugin = (MousePadPlugin) device.getPlugin(MousePadPlugin.class);
if (mousePadPlugin == null) return;
mousePadPlugin.sendKeyboardPacket(np);
}
});
return true; return true;
} }
} }

View File

@ -58,7 +58,7 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
KeyListenerView keyListenerView; KeyListenerView keyListenerView;
static enum ClickType { enum ClickType {
RIGHT, MIDDLE, NONE; RIGHT, MIDDLE, NONE;
static ClickType fromString(String s) { static ClickType fromString(String s) {
switch(s) { switch(s) {
@ -362,5 +362,17 @@ public class MousePadActivity extends ActionBarActivity implements GestureDetect
imm.toggleSoftInputFromWindow(keyListenerView.getWindowToken(), 0, 0); imm.toggleSoftInputFromWindow(keyListenerView.getWindowToken(), 0, 0);
} }
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
}
} }

View File

@ -23,6 +23,7 @@ package org.kde.kdeconnect.Plugins.MousePadPlugin;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
@ -42,7 +43,7 @@ public class MousePadPlugin extends Plugin {
@Override @Override
public Drawable getIcon() { public Drawable getIcon() {
return context.getResources().getDrawable(R.drawable.touchpad_plugin_action); return ContextCompat.getDrawable(context, R.drawable.touchpad_plugin_action);
} }
@Override @Override
@ -62,6 +63,16 @@ public class MousePadPlugin extends Plugin {
parentActivity.startActivity(intent); parentActivity.startActivity(intent);
} }
@Override
public String[] getSupportedPackageTypes() {
return new String[0];
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{NetworkPackage.PACKAGE_TYPE_MOUSEPAD};
}
@Override @Override
public String getActionName() { public String getActionName() {
return context.getString(R.string.open_mousepad); return context.getString(R.string.open_mousepad);

View File

@ -25,6 +25,7 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarActivity;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -120,7 +121,7 @@ public class MprisActivity extends ActionBarActivity {
@Override @Override
public void handleMessage(Message msg) { public void handleMessage(Message msg) {
final ArrayList<String> playerList = mpris.getPlayerList(); final ArrayList<String> playerList = mpris.getPlayerList();
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(MprisActivity.this, final ArrayAdapter<String> adapter = new ArrayAdapter<>(MprisActivity.this,
android.R.layout.simple_spinner_item, android.R.layout.simple_spinner_item,
playerList.toArray(new String[playerList.size()]) playerList.toArray(new String[playerList.size()])
); );
@ -133,11 +134,11 @@ public class MprisActivity extends ActionBarActivity {
//String prevPlayer = (String)spinner.getSelectedItem(); //String prevPlayer = (String)spinner.getSelectedItem();
spinner.setAdapter(adapter); spinner.setAdapter(adapter);
if(playerList.isEmpty()){ if (playerList.isEmpty()) {
findViewById(R.id.no_players).setVisibility(View.VISIBLE); findViewById(R.id.no_players).setVisibility(View.VISIBLE);
spinner.setVisibility(View.GONE); spinner.setVisibility(View.GONE);
((TextView) findViewById(R.id.now_playing_textview)).setText(""); ((TextView) findViewById(R.id.now_playing_textview)).setText("");
}else{ } else {
findViewById(R.id.no_players).setVisibility(View.GONE); findViewById(R.id.no_players).setVisibility(View.GONE);
spinner.setVisibility(View.VISIBLE); spinner.setVisibility(View.VISIBLE);
} }
@ -269,7 +270,7 @@ public class MprisActivity extends ActionBarActivity {
} }
@Override @Override
public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
switch (keyCode) { switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_UP:
return true; return true;
@ -452,9 +453,15 @@ public class MprisActivity extends ActionBarActivity {
} }
@Override @Override
protected void onPause() { protected void onStart() {
super.onPause(); super.onStart();
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable); BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
} }
} }

View File

@ -25,6 +25,7 @@ import android.content.Intent;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.util.Log; import android.util.Log;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
@ -43,10 +44,10 @@ public class MprisPlugin extends Plugin {
private long length = -1; private long length = -1;
private long lastPosition; private long lastPosition;
private long lastPositionTime; private long lastPositionTime;
private HashMap<String,Handler> playerStatusUpdated = new HashMap<String,Handler>(); private HashMap<String,Handler> playerStatusUpdated = new HashMap<>();
private ArrayList<String> playerList = new ArrayList<String>(); private ArrayList<String> playerList = new ArrayList<>();
private HashMap<String,Handler> playerListUpdated = new HashMap<String,Handler>(); private HashMap<String,Handler> playerListUpdated = new HashMap<>();
@Override @Override
public String getDisplayName() { public String getDisplayName() {
@ -60,7 +61,7 @@ public class MprisPlugin extends Plugin {
@Override @Override
public Drawable getIcon() { public Drawable getIcon() {
return context.getResources().getDrawable(R.drawable.mpris_plugin_action); return ContextCompat.getDrawable(context, R.drawable.mpris_plugin_action);
} }
@Override @Override
@ -169,6 +170,16 @@ public class MprisPlugin extends Plugin {
return true; return true;
} }
@Override
public String[] getSupportedPackageTypes() {
return new String[] {NetworkPackage.PACKAGE_TYPE_MPRIS};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[] {NetworkPackage.PACKAGE_TYPE_MPRIS};
}
public void setPlayerStatusUpdatedHandler(String id, Handler h) { public void setPlayerStatusUpdatedHandler(String id, Handler h) {
playerStatusUpdated.put(id, h); playerStatusUpdated.put(id, h);

View File

@ -30,6 +30,7 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ListView; import android.widget.ListView;
import org.kde.kdeconnect.BackgroundService;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
import java.util.List; import java.util.List;
@ -67,8 +68,8 @@ public class NotificationFilterActivity extends ActionBarActivity {
res.close(); res.close();
appDatabase.close(); appDatabase.close();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_list_item_multiple_choice,android.R.id.text1, appName); android.R.layout.simple_list_item_multiple_choice, android.R.id.text1, appName);
listView.setAdapter(adapter); listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
for (i = 0 ; i < isFiltered.length; i++){ for (i = 0 ; i < isFiltered.length; i++){
@ -157,4 +158,16 @@ public class NotificationFilterActivity extends ActionBarActivity {
return true; return true;
} }
@Override
protected void onStart() {
super.onStart();
BackgroundService.addGuiInUseCounter(this);
}
@Override
protected void onStop() {
super.onStop();
BackgroundService.removeGuiInUseCounter(this);
}
} }

View File

@ -37,7 +37,7 @@ public class NotificationReceiver extends NotificationListenerService {
void onNotificationRemoved(StatusBarNotification statusBarNotification); void onNotificationRemoved(StatusBarNotification statusBarNotification);
} }
private final ArrayList<NotificationListener> listeners = new ArrayList<NotificationListener>(); private final ArrayList<NotificationListener> listeners = new ArrayList<>();
public void addListener(NotificationListener listener) { public void addListener(NotificationListener listener) {
listeners.add(listener); listeners.add(listener);
@ -71,7 +71,7 @@ public class NotificationReceiver extends NotificationListenerService {
void onServiceStart(NotificationReceiver service); void onServiceStart(NotificationReceiver service);
} }
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<InstanceCallback>(); private final static ArrayList<InstanceCallback> callbacks = new ArrayList<>();
private final static Lock mutex = new ReentrantLock(true); private final static Lock mutex = new ReentrantLock(true);

View File

@ -20,6 +20,7 @@
package org.kde.kdeconnect.Plugins.NotificationsPlugin; package org.kde.kdeconnect.Plugins.NotificationsPlugin;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.Notification; import android.app.Notification;
@ -33,11 +34,12 @@ import android.util.Log;
import org.kde.kdeconnect.Helpers.AppsHelper; import org.kde.kdeconnect.Helpers.AppsHelper;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.DeviceActivity;
import org.kde.kdeconnect.UserInterface.SettingsActivity; import org.kde.kdeconnect.UserInterface.SettingsActivity;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener { public class NotificationsPlugin extends Plugin implements NotificationReceiver.NotificationListener {
/* /*
private boolean sendIcons = false; private boolean sendIcons = false;
@ -73,57 +75,6 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
return (notificationListenerList != null && notificationListenerList.contains(context.getPackageName())); return (notificationListenerList != null && notificationListenerList.contains(context.getPackageName()));
} }
static class NotificationId {
String packageName;
String tag;
int id;
public static NotificationId fromNotification(StatusBarNotification statusBarNotification) {
NotificationId nid = new NotificationId();
nid.packageName = statusBarNotification.getPackageName();
nid.tag = statusBarNotification.getTag();
nid.id = statusBarNotification.getId();
return nid;
}
public static NotificationId unserialize(String s) {
NotificationId nid = new NotificationId();
int first = s.indexOf(':');
int last = s.lastIndexOf(':');
nid.packageName = s.substring(0, first);
nid.tag = s.substring(first+1, last);
if (nid.tag.length() == 0) nid.tag = null;
String idString = s.substring(last+1);
try {
nid.id = Integer.parseInt(idString);
} catch(Exception e) {
nid.id = 0;
}
//Log.e("NotificationId","unserialize: " + nid.packageName+ ", "+nid.tag+ ", "+nid.id);
return nid;
}
public String serialize() {
//Log.e("NotificationId","serialize: " + packageName+ ", "+tag+ ", "+id);
String safePackageName = (packageName == null)? "" : packageName;
String safeTag = (tag == null)? "" : tag;
return safePackageName+":"+safeTag+":"+id;
}
public String getPackageName() {
return packageName;
}
public String getTag() {
return tag;
}
public int getId() {
return id;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof NotificationId)) return false;
NotificationId other = (NotificationId)o;
return other.getTag().equals(tag) && other.getId() == id && other.getPackageName().equals(packageName);
}
}
@Override @Override
public boolean onCreate() { public boolean onCreate() {
@ -141,8 +92,8 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
for (StatusBarNotification notification : notifications) { for (StatusBarNotification notification : notifications) {
sendNotification(notification, true); sendNotification(notification, true);
} }
} catch(Exception e) { } catch (Exception e) {
Log.e("NotificationsPlugin","Exception"); Log.e("NotificationsPlugin", "Exception");
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -172,10 +123,9 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override @Override
public void onNotificationRemoved(StatusBarNotification statusBarNotification) { public void onNotificationRemoved(StatusBarNotification statusBarNotification) {
NotificationId id = NotificationId.fromNotification(statusBarNotification); String id = getNotificationKeyCompat(statusBarNotification);
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION); NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_NOTIFICATION);
np.set("id", id.serialize()); np.set("id", id);
np.set("isCancel", true); np.set("isCancel", true);
device.sendPackage(np); device.sendPackage(np);
} }
@ -204,11 +154,14 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
} }
appDatabase.close(); appDatabase.close();
NotificationId id = NotificationId.fromNotification(statusBarNotification); String key = getNotificationKeyCompat(statusBarNotification);
String packageName = statusBarNotification.getPackageName(); String packageName = statusBarNotification.getPackageName();
String appName = AppsHelper.appNameLookup(context, packageName); String appName = AppsHelper.appNameLookup(context, packageName);
if (id.serialize().equals("com.facebook.orca::10012") && notification.tickerText == null && appName.equals("Messenger")) { if ("com.facebook.orca".equals(packageName) &&
(statusBarNotification.getId() == 10012) &&
"Messenger".equals(appName) &&
notification.tickerText == null) {
//HACK: Hide weird Facebook empty "Messenger" notification that is actually not shown in the phone //HACK: Hide weird Facebook empty "Messenger" notification that is actually not shown in the phone
return; return;
} }
@ -244,7 +197,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
} }
} }
*/ */
np.set("id", id.serialize()); np.set("id", key);
np.set("appName", appName == null? packageName : appName); np.set("appName", appName == null? packageName : appName);
np.set("isClearable", statusBarNotification.isClearable()); np.set("isClearable", statusBarNotification.isClearable());
np.set("ticker", getTickerText(notification)); np.set("ticker", getTickerText(notification));
@ -347,15 +300,14 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() { NotificationReceiver.RunCommand(context, new NotificationReceiver.InstanceCallback() {
@Override @Override
public void onServiceStart(NotificationReceiver service) { public void onServiceStart(NotificationReceiver service) {
String dismissedId = np.getString("cancel");
NotificationId dismissedId = NotificationId.unserialize(np.getString("cancel")); cancelNotificationCompat(service, dismissedId);
service.cancelNotification(dismissedId.getPackageName(), dismissedId.getTag(), dismissedId.getId());
} }
}); });
} else { } else {
Log.w("NotificationsPlugin","Nothing to do"); Log.w("NotificationsPlugin", "Nothing to do");
} }
@ -385,7 +337,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
@Override @Override
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
deviceActivity.startActivityForResult(intent, DeviceActivity.RESULT_NEEDS_RELOAD); deviceActivity.startActivityForResult(intent, MaterialActivity.RESULT_NEEDS_RELOAD);
} }
}) })
.setNegativeButton(R.string.cancel,new DialogInterface.OnClickListener() { .setNegativeButton(R.string.cancel,new DialogInterface.OnClickListener() {
@ -398,4 +350,48 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
} }
} }
@Override
public String[] getSupportedPackageTypes() {
return new String[]{NetworkPackage.PACKAGE_TYPE_NOTIFICATION};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{NetworkPackage.PACKAGE_TYPE_NOTIFICATION};
}
//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(':');
int last = compatKey.lastIndexOf(':');
String packageName = compatKey.substring(0, first);
String tag = compatKey.substring(first + 1, last);
if (tag.length() == 0) tag = null;
String idString = compatKey.substring(last + 1);
int id;
try {
id = Integer.parseInt(idString);
} catch (Exception e) {
id = 0;
}
service.cancelNotification(packageName, tag, id);
}
}
public static String getNotificationKeyCompat(StatusBarNotification statusBarNotification) {
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;
return safePackageName + ":" + safeTag + ":" + id;
}
}
} }

View File

@ -30,8 +30,8 @@ import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder; import android.support.v4.app.TaskStackBuilder;
import org.kde.kdeconnect.NetworkPackage; import org.kde.kdeconnect.NetworkPackage;
import org.kde.kdeconnect.UserInterface.MaterialActivity;
import org.kde.kdeconnect.Plugins.Plugin; import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect.UserInterface.MainActivity;
import org.kde.kdeconnect_tp.R; import org.kde.kdeconnect_tp.R;
@ -55,8 +55,8 @@ public class PingPlugin extends Plugin {
//Log.e("PingPackageReceiver", "was a ping!"); //Log.e("PingPackageReceiver", "was a ping!");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class); stackBuilder.addParentStack(MaterialActivity.class);
stackBuilder.addNextIntent(new Intent(context, MainActivity.class)); stackBuilder.addNextIntent(new Intent(context, MaterialActivity.class));
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(
0, 0,
PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_UPDATE_CURRENT
@ -77,7 +77,7 @@ public class PingPlugin extends Plugin {
.setContentText(message) .setContentText(message)
.setContentIntent(resultPendingIntent) .setContentIntent(resultPendingIntent)
.setTicker(message) .setTicker(message)
.setSmallIcon(android.R.drawable.ic_dialog_alert) .setSmallIcon(R.drawable.ic_notification)
.setAutoCancel(true) .setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL) .setDefaults(Notification.DEFAULT_ALL)
.build(); .build();
@ -117,4 +117,15 @@ public class PingPlugin extends Plugin {
public boolean displayInContextMenu() { public boolean displayInContextMenu() {
return true; return true;
} }
@Override
public String[] getSupportedPackageTypes() {
return new String[]{NetworkPackage.PACKAGE_TYPE_PING};
}
@Override
public String[] getOutgoingPackageTypes() {
return new String[]{NetworkPackage.PACKAGE_TYPE_PING};
}
} }

View File

@ -43,6 +43,22 @@ public abstract class Plugin {
this.context = context; this.context = context;
} }
/**
* To receive the network package from the unpaired device, override
* listensToUnpairedDevices to return true and this method.
*/
public boolean onUnpairedDevicePackageReceived(NetworkPackage np) {
return false;
}
/**
* Returns whether this plugin should be loaded or not, to listen to NetworkPackages
* from the unpaired devices. By default, returns false.
*/
public boolean listensToUnpairedDevices() {
return false;
}
/** /**
* Return the internal plugin name, that will be used as a * Return the internal plugin name, that will be used as a
* unique key to distinguish it. Use the class name as key. * unique key to distinguish it. Use the class name as key.
@ -158,6 +174,16 @@ public abstract class Plugin {
return null; 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 * Creates a button that will be displayed in the user interface
* It can open an activity or perform any other action that the * It can open an activity or perform any other action that the

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