mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 18:07:55 +00:00
Merge branch 'filetransfer'
This commit is contained in:
commit
f8f58ef2a2
@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module external.linked.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||||
<component name="FacetManager">
|
<component name="FacetManager">
|
||||||
<facet type="android" name="Android">
|
<facet type="android" name="Android">
|
||||||
<configuration>
|
<configuration>
|
||||||
<option name="SELECTED_BUILD_VARIANT" value="Debug" />
|
<option name="SELECTED_BUILD_VARIANT" value="Debug" />
|
||||||
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
|
||||||
|
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebug" />
|
||||||
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleTest" />
|
<option name="ASSEMBLE_TEST_TASK_NAME" value="assembleTest" />
|
||||||
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
|
<option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
|
||||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||||
@ -60,7 +61,6 @@
|
|||||||
<excludeFolder url="file://$MODULE_DIR$/build/bundles" />
|
<excludeFolder url="file://$MODULE_DIR$/build/bundles" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/classes" />
|
<excludeFolder url="file://$MODULE_DIR$/build/classes" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
|
<excludeFolder url="file://$MODULE_DIR$/build/dependency-cache" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/exploded-bundles" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
|
<excludeFolder url="file://$MODULE_DIR$/build/incremental" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
|
<excludeFolder url="file://$MODULE_DIR$/build/libs" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
|
<excludeFolder url="file://$MODULE_DIR$/build/manifests" />
|
||||||
@ -70,10 +70,10 @@
|
|||||||
<orderEntry type="jdk" jdkName="Android 4.3 Platform" jdkType="Android SDK" />
|
<orderEntry type="jdk" jdkName="Android 4.3 Platform" jdkType="Android SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" exported="" name="android-support-v4" level="project" />
|
<orderEntry type="library" exported="" name="android-support-v4" level="project" />
|
||||||
<orderEntry type="library" exported="" name="mina-core-2.0.7" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="slf4j-api-1.6.6" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="support-v4-18.0.0" level="project" />
|
<orderEntry type="library" exported="" name="support-v4-18.0.0" level="project" />
|
||||||
<orderEntry type="library" exported="" name="ComAndroidSupportAppcompatV71800.aar" level="project" />
|
<orderEntry type="library" exported="" name="ComAndroidSupportAppcompatV71800.aar" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="slf4j-api-1.6.6" level="project" />
|
||||||
|
<orderEntry type="library" exported="" name="mina-core-2.0.7" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
|
@ -117,6 +117,23 @@
|
|||||||
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
android:value="org.kde.kdeconnect.UserInterface.DeviceActivity" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:theme="@style/Theme.AppCompat"
|
||||||
|
android:name="org.kde.kdeconnect.UserInterface.ShareToReceiver"
|
||||||
|
android:label="KDE Connect"
|
||||||
|
>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<service android:name="org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver"
|
<service 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>
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
package org.kde.kdeconnect.ComputerLinks;
|
package org.kde.kdeconnect.Backends;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
||||||
public abstract class BaseComputerLink {
|
public abstract class BaseLink {
|
||||||
|
|
||||||
private BaseLinkProvider linkProvider;
|
private BaseLinkProvider linkProvider;
|
||||||
private String deviceId;
|
private String deviceId;
|
||||||
private ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
|
private ArrayList<PackageReceiver> receivers = new ArrayList<PackageReceiver>();
|
||||||
|
protected PrivateKey privateKey;
|
||||||
|
|
||||||
protected BaseComputerLink(String deviceId, BaseLinkProvider linkProvider) {
|
protected BaseLink(String deviceId, BaseLinkProvider linkProvider) {
|
||||||
this.linkProvider = linkProvider;
|
this.linkProvider = linkProvider;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
}
|
}
|
||||||
@ -21,6 +29,10 @@ public abstract class BaseComputerLink {
|
|||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPrivateKey(PrivateKey key) {
|
||||||
|
privateKey = key;
|
||||||
|
}
|
||||||
|
|
||||||
public BaseLinkProvider getLinkProvider() {
|
public BaseLinkProvider getLinkProvider() {
|
||||||
return linkProvider;
|
return linkProvider;
|
||||||
}
|
}
|
||||||
@ -46,5 +58,6 @@ public abstract class BaseComputerLink {
|
|||||||
|
|
||||||
//TO OVERRIDE
|
//TO OVERRIDE
|
||||||
public abstract boolean sendPackage(NetworkPackage np);
|
public abstract boolean sendPackage(NetworkPackage np);
|
||||||
|
public abstract boolean sendPackageEncrypted(NetworkPackage np, PublicKey key);
|
||||||
|
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package org.kde.kdeconnect.LinkProviders;
|
package org.kde.kdeconnect.Backends;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -12,8 +12,8 @@ public abstract class BaseLinkProvider {
|
|||||||
private ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
|
private ArrayList<ConnectionReceiver> connectionReceivers = new ArrayList<ConnectionReceiver>();
|
||||||
|
|
||||||
public interface ConnectionReceiver {
|
public interface ConnectionReceiver {
|
||||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseComputerLink link);
|
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link);
|
||||||
public void onConnectionLost(BaseComputerLink link);
|
public void onConnectionLost(BaseLink link);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addConnectionReceiver(ConnectionReceiver cr) {
|
public void addConnectionReceiver(ConnectionReceiver cr) {
|
||||||
@ -25,13 +25,13 @@ public abstract class BaseLinkProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//These two should be called when the provider links to a new computer
|
//These two should be called when the provider links to a new computer
|
||||||
protected void connectionAccepted(NetworkPackage identityPackage, BaseComputerLink link) {
|
protected void connectionAccepted(NetworkPackage identityPackage, BaseLink link) {
|
||||||
Log.i("LinkProvider", "connectionAccepted");
|
Log.i("LinkProvider", "connectionAccepted");
|
||||||
for(ConnectionReceiver cr : connectionReceivers) {
|
for(ConnectionReceiver cr : connectionReceivers) {
|
||||||
cr.onConnectionReceived(identityPackage, link);
|
cr.onConnectionReceived(identityPackage, link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected void connectionLost(BaseComputerLink link) {
|
protected void connectionLost(BaseLink link) {
|
||||||
Log.i("LinkProvider", "connectionLost");
|
Log.i("LinkProvider", "connectionLost");
|
||||||
for(ConnectionReceiver cr : connectionReceivers) {
|
for(ConnectionReceiver cr : connectionReceivers) {
|
||||||
cr.onConnectionLost(link);
|
cr.onConnectionLost(link);
|
@ -0,0 +1,184 @@
|
|||||||
|
package org.kde.kdeconnect.Backends.LanBackend;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Base64;
|
||||||
|
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.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.NioSocketConnector;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
|
||||||
|
public class LanLink extends BaseLink {
|
||||||
|
|
||||||
|
private IoSession session = null;
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
Log.i("LanLink", "Disconnect: "+session.getRemoteAddress().toString());
|
||||||
|
session.close(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LanLink(IoSession session, String deviceId, BaseLinkProvider linkProvider) {
|
||||||
|
super(deviceId, linkProvider);
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject sendPayload(final InputStream stream) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
ServerSocket candidateServer = null;
|
||||||
|
boolean success = false;
|
||||||
|
int tcpPort = 1739;
|
||||||
|
while(!success) {
|
||||||
|
try {
|
||||||
|
candidateServer = new ServerSocket();
|
||||||
|
candidateServer.bind(new InetSocketAddress(tcpPort));
|
||||||
|
success = true;
|
||||||
|
} catch(Exception e) {
|
||||||
|
Log.e("LanLink", "Exception openning serversocket: "+e);
|
||||||
|
tcpPort++;
|
||||||
|
if (tcpPort >= 1764) return new JSONObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JSONObject payloadTransferInfo = new JSONObject();
|
||||||
|
payloadTransferInfo.put("port", tcpPort);
|
||||||
|
|
||||||
|
final ServerSocket server = candidateServer;
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
//TODO: Timeout when waiting for a connection and close the socket
|
||||||
|
OutputStream socket = null;
|
||||||
|
try {
|
||||||
|
socket = server.accept().getOutputStream();
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
int bytesRead;
|
||||||
|
Log.e("LanLink","Beginning to send payload");
|
||||||
|
while ((bytesRead = stream.read(buffer)) != -1) {
|
||||||
|
//Log.e("ok",""+bytesRead);
|
||||||
|
socket.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
Log.e("LanLink","Finished sending payload");
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("LanLink", "Exception with payload upload socket");
|
||||||
|
} finally {
|
||||||
|
if (socket != null) {
|
||||||
|
try { socket.close(); } catch(Exception e) { }
|
||||||
|
}
|
||||||
|
try { server.close(); } catch(Exception e) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
return payloadTransferInfo;
|
||||||
|
|
||||||
|
} catch(Exception e) {
|
||||||
|
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("LanLink", "Exception with payload upload socket");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean sendPackage(final NetworkPackage np) {
|
||||||
|
if (session == null) {
|
||||||
|
Log.e("LanLink", "sendPackage failed: not yet connected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (np.hasPayload()) {
|
||||||
|
JSONObject transferInfo = sendPayload(np.getPayload());
|
||||||
|
np.setPayloadTransferInfo(transferInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
session.write(np.serialize());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sendPackageEncrypted(NetworkPackage np, PublicKey key) {
|
||||||
|
|
||||||
|
if (session == null) {
|
||||||
|
Log.e("LanLink", "sendPackage failed: not yet connected");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (np.hasPayload()) {
|
||||||
|
JSONObject transferInfo = sendPayload(np.getPayload());
|
||||||
|
np.setPayloadTransferInfo(transferInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
np.encrypt(key);
|
||||||
|
|
||||||
|
session.write(np.serialize());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("LanLink", "Encryption exception");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void injectNetworkPackage(NetworkPackage np) {
|
||||||
|
|
||||||
|
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
np = np.decrypt(privateKey);
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("onPackageReceived","Exception reading the key needed to decrypt the package");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (np.hasPayloadTransferInfo()) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Socket socket = new Socket();
|
||||||
|
int tcpPort = np.getPayloadTransferInfo().getInt("port");
|
||||||
|
InetSocketAddress address = (InetSocketAddress)session.getRemoteAddress();
|
||||||
|
socket.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
||||||
|
np.setPayload(socket.getInputStream(), np.getPayloadSize());
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("LanLink", "Exception connecting to payload remote socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
packageReceived(np);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package org.kde.kdeconnect.LinkProviders;
|
package org.kde.kdeconnect.Backends.LanBackend;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
@ -16,7 +16,7 @@ import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
|
|||||||
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
|
import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
|
||||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||||
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
import org.apache.mina.transport.socket.nio.NioSocketConnector;
|
||||||
import org.kde.kdeconnect.ComputerLinks.LanComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
@ -31,8 +31,8 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
private final static int port = 1714;
|
private final static int port = 1714;
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
private HashMap<String, LanComputerLink> visibleComputers = new HashMap<String, LanComputerLink>();
|
private HashMap<String, LanLink> visibleComputers = new HashMap<String, LanLink>();
|
||||||
private HashMap<Long, LanComputerLink> nioSessions = new HashMap<Long, LanComputerLink>();
|
private HashMap<Long, LanLink> nioSessions = new HashMap<Long, LanLink>();
|
||||||
|
|
||||||
private NioSocketAcceptor tcpAcceptor = null;
|
private NioSocketAcceptor tcpAcceptor = null;
|
||||||
private NioDatagramAcceptor udpAcceptor = null;
|
private NioDatagramAcceptor udpAcceptor = null;
|
||||||
@ -41,7 +41,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void sessionClosed(IoSession session) throws Exception {
|
public void sessionClosed(IoSession session) throws Exception {
|
||||||
|
|
||||||
LanComputerLink brokenLink = nioSessions.remove(session.getId());
|
LanLink brokenLink = nioSessions.remove(session.getId());
|
||||||
if (brokenLink != null) {
|
if (brokenLink != null) {
|
||||||
connectionLost(brokenLink);
|
connectionLost(brokenLink);
|
||||||
brokenLink.disconnect();
|
brokenLink.disconnect();
|
||||||
@ -62,14 +62,18 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
String theMessage = (String) message;
|
String theMessage = (String) message;
|
||||||
NetworkPackage np = NetworkPackage.unserialize(theMessage);
|
NetworkPackage np = NetworkPackage.unserialize(theMessage);
|
||||||
|
|
||||||
LanComputerLink prevLink = nioSessions.get(session.getId());
|
LanLink prevLink = nioSessions.get(session.getId());
|
||||||
|
|
||||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||||
|
|
||||||
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
||||||
if (np.getString("deviceId").equals(myId)) {
|
if (np.getString("deviceId").equals(myId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LanComputerLink link = new LanComputerLink(session, np.getString("deviceId"), LanLinkProvider.this);
|
|
||||||
|
//Log.e("LanLinkProvider", "Identity package received from "+np.getString("deviceName"));
|
||||||
|
|
||||||
|
LanLink link = new LanLink(session, np.getString("deviceId"), LanLinkProvider.this);
|
||||||
nioSessions.put(session.getId(),link);
|
nioSessions.put(session.getId(),link);
|
||||||
addLink(np, link);
|
addLink(np, link);
|
||||||
} else {
|
} else {
|
||||||
@ -90,33 +94,23 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
|
|
||||||
//Log.e("LanLinkProvider", "Udp message received (" + message.getClass() + ") " + message.toString());
|
//Log.e("LanLinkProvider", "Udp message received (" + message.getClass() + ") " + message.toString());
|
||||||
|
|
||||||
NetworkPackage np = null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//We should receive a string thanks to the TextLineCodecFactory filter
|
//We should receive a string thanks to the TextLineCodecFactory filter
|
||||||
String theMessage = (String) message;
|
String theMessage = (String) message;
|
||||||
np = NetworkPackage.unserialize(theMessage);
|
final NetworkPackage identityPackage = NetworkPackage.unserialize(theMessage);
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e("LanLinkProvider", "Could not unserialize package");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (np != null) {
|
if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
||||||
|
|
||||||
final NetworkPackage identityPackage = np;
|
|
||||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
|
|
||||||
Log.e("LanLinkProvider", "1 Expecting an identity package");
|
Log.e("LanLinkProvider", "1 Expecting an identity package");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
|
||||||
if (np.getString("deviceId").equals(myId)) {
|
if (identityPackage.getString("deviceId").equals(myId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("LanLinkProvider", "Identity package received, creating link");
|
Log.i("LanLinkProvider", "Identity package received, creating link");
|
||||||
|
|
||||||
try {
|
|
||||||
final InetSocketAddress address = (InetSocketAddress) udpSession.getRemoteAddress();
|
final InetSocketAddress address = (InetSocketAddress) udpSession.getRemoteAddress();
|
||||||
|
|
||||||
final NioSocketConnector connector = new NioSocketConnector();
|
final NioSocketConnector connector = new NioSocketConnector();
|
||||||
@ -129,7 +123,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
);
|
);
|
||||||
connector.getSessionConfig().setKeepAlive(true);
|
connector.getSessionConfig().setKeepAlive(true);
|
||||||
|
|
||||||
int tcpPort = np.getInt("tcpPort",port);
|
int tcpPort = identityPackage.getInt("tcpPort",port);
|
||||||
ConnectFuture future = connector.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
ConnectFuture future = connector.connect(new InetSocketAddress(address.getAddress(), tcpPort));
|
||||||
future.addListener(new IoFutureListener<IoFuture>() {
|
future.addListener(new IoFutureListener<IoFuture>() {
|
||||||
@Override
|
@Override
|
||||||
@ -138,7 +132,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
|
|
||||||
Log.i("LanLinkProvider", "Connection successful: " + session.isConnected());
|
Log.i("LanLinkProvider", "Connection successful: " + session.isConnected());
|
||||||
|
|
||||||
LanComputerLink link = new LanComputerLink(session, identityPackage.getString("deviceId"), LanLinkProvider.this);
|
LanLink link = new LanLink(session, identityPackage.getString("deviceId"), LanLinkProvider.this);
|
||||||
|
|
||||||
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
|
NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
|
||||||
link.sendPackage(np2);
|
link.sendPackage(np2);
|
||||||
@ -149,18 +143,17 @@ public class LanLinkProvider extends BaseLinkProvider {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e("LanLinkProvider","Exception!!");
|
Log.e("LanLinkProvider","Exception receiving udp package!!");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private void addLink(NetworkPackage identityPackage, LanComputerLink link) {
|
private void addLink(NetworkPackage identityPackage, LanLink link) {
|
||||||
String deviceId = identityPackage.getString("deviceId");
|
String deviceId = identityPackage.getString("deviceId");
|
||||||
Log.i("LanLinkProvider","addLink to "+deviceId);
|
Log.i("LanLinkProvider","addLink to "+deviceId);
|
||||||
LanComputerLink oldLink = visibleComputers.get(deviceId);
|
LanLink oldLink = visibleComputers.get(deviceId);
|
||||||
visibleComputers.put(deviceId, link);
|
visibleComputers.put(deviceId, link);
|
||||||
connectionAccepted(identityPackage, link);
|
connectionAccepted(identityPackage, link);
|
||||||
if (oldLink != null) {
|
if (oldLink != null) {
|
@ -0,0 +1,43 @@
|
|||||||
|
package org.kde.kdeconnect.Backends.LoopbackBackend;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
|
public class LoopbackLink extends BaseLink {
|
||||||
|
|
||||||
|
public LoopbackLink(BaseLinkProvider linkProvider) {
|
||||||
|
super("loopback", linkProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sendPackage(NetworkPackage in) {
|
||||||
|
String s = in.serialize();
|
||||||
|
NetworkPackage out= NetworkPackage.unserialize(s);
|
||||||
|
if (in.hasPayload()) out.setPayload(in.getPayload(), in.getPayloadSize());
|
||||||
|
packageReceived(out);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean sendPackageEncrypted(NetworkPackage in, PublicKey key) {
|
||||||
|
try {
|
||||||
|
in.encrypt(key);
|
||||||
|
String s = in.serialize();
|
||||||
|
NetworkPackage out= NetworkPackage.unserialize(s);
|
||||||
|
out.decrypt(privateKey);
|
||||||
|
packageReceived(out);
|
||||||
|
if (in.hasPayload()) out.setPayload(in.getPayload(), in.getPayloadSize());
|
||||||
|
return true;
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("LoopbackLink", "Encryption exception");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package org.kde.kdeconnect.Backends.LoopbackBackend;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
|
||||||
|
public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||||
|
|
||||||
|
private Context context;
|
||||||
|
|
||||||
|
public LoopbackLinkProvider(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
onNetworkChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNetworkChange() {
|
||||||
|
|
||||||
|
NetworkPackage np = NetworkPackage.createIdentityPackage(context);
|
||||||
|
connectionAccepted(np, new LoopbackLink(this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "LoopbackLinkProvider";
|
||||||
|
}
|
||||||
|
}
|
@ -11,10 +11,10 @@ import android.preference.PreferenceManager;
|
|||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.LinkProviders.LanLinkProvider;
|
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||||
import org.kde.kdeconnect.LinkProviders.LoopbackLinkProvider;
|
import org.kde.kdeconnect.Backends.LoopbackBackend.LoopbackLinkProvider;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
@ -54,7 +54,7 @@ public class BackgroundService extends Service {
|
|||||||
Set<String> trustedDevices = preferences.getAll().keySet();
|
Set<String> trustedDevices = preferences.getAll().keySet();
|
||||||
for(String deviceId : trustedDevices) {
|
for(String deviceId : trustedDevices) {
|
||||||
if (preferences.getBoolean(deviceId, false)) {
|
if (preferences.getBoolean(deviceId, false)) {
|
||||||
Device device = new Device(getBaseContext(), deviceId);
|
Device device = new Device(this, deviceId);
|
||||||
devices.put(deviceId,device);
|
devices.put(deviceId,device);
|
||||||
device.addPairingCallback(devicePairingCallback);
|
device.addPairingCallback(devicePairingCallback);
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ public class BackgroundService extends Service {
|
|||||||
|
|
||||||
private BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
private BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseComputerLink link) {
|
public void onConnectionReceived(final NetworkPackage identityPackage, final BaseLink link) {
|
||||||
|
|
||||||
Log.i("BackgroundService", "Connection accepted!");
|
Log.i("BackgroundService", "Connection accepted!");
|
||||||
|
|
||||||
@ -91,11 +91,11 @@ public class BackgroundService extends Service {
|
|||||||
|
|
||||||
if (device != null) {
|
if (device != null) {
|
||||||
Log.i("BackgroundService", "addLink, known device: " + deviceId);
|
Log.i("BackgroundService", "addLink, known device: " + deviceId);
|
||||||
device.addLink(link);
|
device.addLink(identityPackage, link);
|
||||||
} else {
|
} else {
|
||||||
Log.i("BackgroundService", "addLink,unknown device: " + deviceId);
|
Log.i("BackgroundService", "addLink,unknown device: " + deviceId);
|
||||||
String name = identityPackage.getString("deviceName");
|
String name = identityPackage.getString("deviceName");
|
||||||
device = new Device(getBaseContext(), deviceId, name, link);
|
device = new Device(BackgroundService.this, identityPackage, link);
|
||||||
devices.put(deviceId, device);
|
devices.put(deviceId, device);
|
||||||
device.addPairingCallback(devicePairingCallback);
|
device.addPairingCallback(devicePairingCallback);
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ public class BackgroundService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionLost(BaseComputerLink link) {
|
public void onConnectionLost(BaseLink link) {
|
||||||
Device d = devices.get(link.getDeviceId());
|
Device d = devices.get(link.getDeviceId());
|
||||||
Log.i("onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
Log.i("onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
package org.kde.kdeconnect.ComputerLinks;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.apache.mina.core.session.IoSession;
|
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
|
||||||
|
|
||||||
public class LanComputerLink extends BaseComputerLink {
|
|
||||||
|
|
||||||
private IoSession session = null;
|
|
||||||
|
|
||||||
public void disconnect() {
|
|
||||||
Log.i("LanComputerLink","Disconnect: "+session.getRemoteAddress().toString());
|
|
||||||
session.close(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LanComputerLink(IoSession session, String deviceId, BaseLinkProvider linkProvider) {
|
|
||||||
super(deviceId, linkProvider);
|
|
||||||
this.session = session;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean sendPackage(NetworkPackage np) {
|
|
||||||
if (session == null) {
|
|
||||||
Log.e("LanComputerLink","sendPackage failed: not yet connected");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
session.write(np.serialize());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void injectNetworkPackage(NetworkPackage np) {
|
|
||||||
packageReceived(np);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
package org.kde.kdeconnect.ComputerLinks;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.apache.mina.core.session.IoSession;
|
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
|
||||||
|
|
||||||
public class LoopbackComputerLink extends BaseComputerLink {
|
|
||||||
|
|
||||||
public LoopbackComputerLink(BaseLinkProvider linkProvider) {
|
|
||||||
super("loopback", linkProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean sendPackage(NetworkPackage in) {
|
|
||||||
String s = in.serialize();
|
|
||||||
NetworkPackage out= NetworkPackage.unserialize(s);
|
|
||||||
packageReceived(out);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -15,7 +15,7 @@ import android.support.v4.app.NotificationCompat;
|
|||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
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.PairActivity;
|
||||||
@ -34,7 +34,7 @@ import java.util.Set;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
public class Device implements BaseComputerLink.PackageReceiver {
|
public class Device implements BaseLink.PackageReceiver {
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
@ -42,6 +42,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
private String name;
|
private String name;
|
||||||
private PublicKey publicKey;
|
private PublicKey publicKey;
|
||||||
private int notificationId;
|
private int notificationId;
|
||||||
|
private int protocolVersion;
|
||||||
|
|
||||||
private enum PairStatus {
|
private enum PairStatus {
|
||||||
NotPaired,
|
NotPaired,
|
||||||
@ -61,7 +62,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
private ArrayList<PairingCallback> pairingCallback = new ArrayList<PairingCallback>();
|
private ArrayList<PairingCallback> pairingCallback = new ArrayList<PairingCallback>();
|
||||||
private Timer pairingTimer;
|
private Timer pairingTimer;
|
||||||
|
|
||||||
private ArrayList<BaseComputerLink> links = new ArrayList<BaseComputerLink>();
|
private ArrayList<BaseLink> links = new ArrayList<BaseLink>();
|
||||||
private HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
|
private HashMap<String, Plugin> plugins = new HashMap<String, Plugin>();
|
||||||
private HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
|
private HashMap<String, Plugin> failedPlugins = new HashMap<String, Plugin>();
|
||||||
|
|
||||||
@ -77,6 +78,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.name = settings.getString("deviceName", "unknown device");
|
this.name = settings.getString("deviceName", "unknown device");
|
||||||
this.pairStatus = PairStatus.Paired;
|
this.pairStatus = PairStatus.Paired;
|
||||||
|
this.protocolVersion = NetworkPackage.ProtocolVersion; //We don't know it yet
|
||||||
|
|
||||||
try {
|
try {
|
||||||
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
|
byte[] publicKeyBytes = Base64.decode(settings.getString("publicKey", ""), 0);
|
||||||
@ -90,18 +92,19 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
//Device known via an incoming connection sent to us via a devicelink, we know everything but we don't trust it yet
|
||||||
Device(Context context, String deviceId, String name, BaseComputerLink dl) {
|
Device(Context context, NetworkPackage np, BaseLink dl) {
|
||||||
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
//Log.e("Device","Constructor B");
|
//Log.e("Device","Constructor B");
|
||||||
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.deviceId = deviceId;
|
this.deviceId = np.getString("deviceId");
|
||||||
this.name = name;
|
this.name = np.getString("deviceName");
|
||||||
|
this.protocolVersion = np.getInt("protocolVersion");
|
||||||
this.pairStatus = PairStatus.NotPaired;
|
this.pairStatus = PairStatus.NotPaired;
|
||||||
this.publicKey = null;
|
this.publicKey = null;
|
||||||
|
|
||||||
addLink(dl);
|
addLink(np, dl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -112,6 +115,10 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Returns 0 if the version matches, < 0 if it is older or > 0 if it is newer
|
||||||
|
public int compareProtocolVersion() {
|
||||||
|
return protocolVersion - NetworkPackage.ProtocolVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -158,23 +165,32 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
//Send our own public key
|
//Send our own public key
|
||||||
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
||||||
boolean success = sendPackage(np);
|
sendPackage(np, new SendPackageFinishedCallback(){
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(res.getString(R.string.error_could_not_send_package));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendSuccessful() {
|
||||||
pairingTimer = new Timer();
|
pairingTimer = new Timer();
|
||||||
pairingTimer.schedule(new TimerTask() {
|
pairingTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
for (PairingCallback cb : pairingCallback) cb.pairingFailed(context.getString(R.string.error_timed_out));
|
for (PairingCallback cb : pairingCallback) {
|
||||||
|
cb.pairingFailed(context.getString(R.string.error_timed_out));
|
||||||
|
}
|
||||||
pairStatus = PairStatus.NotPaired;
|
pairStatus = PairStatus.NotPaired;
|
||||||
}
|
}
|
||||||
}, 20*1000);
|
}, 20*1000);
|
||||||
|
|
||||||
pairStatus = PairStatus.Requested;
|
pairStatus = PairStatus.Requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendFailed() {
|
||||||
|
for (PairingCallback cb : pairingCallback) {
|
||||||
|
cb.pairingFailed(context.getString(R.string.error_could_not_send_package));
|
||||||
|
}
|
||||||
|
pairStatus = PairStatus.NotPaired;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,9 +223,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
//Send our own public key
|
//Send our own public key
|
||||||
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
NetworkPackage np = NetworkPackage.createPublicKeyPackage(context);
|
||||||
boolean success = sendPackage(np);
|
sendPackage(np); //TODO: Set a callback
|
||||||
|
|
||||||
if (!success) return;
|
|
||||||
|
|
||||||
pairStatus = PairStatus.Paired;
|
pairStatus = PairStatus.Paired;
|
||||||
|
|
||||||
@ -255,15 +269,27 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
return !links.isEmpty();
|
return !links.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addLink(BaseComputerLink link) {
|
public void addLink(NetworkPackage identityPackage, BaseLink link) {
|
||||||
|
|
||||||
|
this.protocolVersion = identityPackage.getInt("protocolVersion");
|
||||||
|
|
||||||
links.add(link);
|
links.add(link);
|
||||||
|
|
||||||
|
try {
|
||||||
|
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey", ""), 0);
|
||||||
|
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
||||||
|
link.setPrivateKey(privateKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("Device", "Exception reading our own private key"); //Should not happen
|
||||||
|
}
|
||||||
|
|
||||||
Log.i("Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size());
|
Log.i("Device","addLink "+link.getLinkProvider().getName()+" -> "+getName() + " active links: "+ links.size());
|
||||||
|
|
||||||
Collections.sort(links, new Comparator<BaseComputerLink>() {
|
Collections.sort(links, new Comparator<BaseLink>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(BaseComputerLink o, BaseComputerLink o2) {
|
public int compare(BaseLink o, BaseLink o2) {
|
||||||
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
|
return o2.getLinkProvider().getPriority() - o.getLinkProvider().getPriority();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -275,7 +301,7 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLink(BaseComputerLink link) {
|
public void removeLink(BaseLink link) {
|
||||||
link.removePackageReceiver(this);
|
link.removePackageReceiver(this);
|
||||||
links.remove(link);
|
links.remove(link);
|
||||||
Log.i("Device","removeLink: "+link.getLinkProvider().getName() + " -> "+getName() + " active links: "+ links.size());
|
Log.i("Device","removeLink: "+link.getLinkProvider().getName() + " -> "+getName() + " active links: "+ links.size());
|
||||||
@ -398,23 +424,6 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
Log.e("onPackageReceived","Device not paired, ignoring package!");
|
Log.e("onPackageReceived","Device not paired, ignoring package!");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
//TODO: Do not read the key every time
|
|
||||||
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
byte[] privateKeyBytes = Base64.decode(globalSettings.getString("privateKey",""), 0);
|
|
||||||
PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
|
|
||||||
np = np.decrypt(privateKey);
|
|
||||||
} catch(Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e("onPackageReceived","Exception reading the key needed to decrypt the package");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//TODO: The other side doesn't know that we are already paired, do something
|
|
||||||
Log.e("onPackageReceived","WARNING: Received unencrypted package from paired device!");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Plugin plugin : plugins.values()) {
|
for (Plugin plugin : plugins.values()) {
|
||||||
plugin.onPackageReceived(np);
|
plugin.onPackageReceived(np);
|
||||||
@ -423,36 +432,46 @@ public class Device implements BaseComputerLink.PackageReceiver {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface SendPackageFinishedCallback {
|
||||||
public boolean sendPackage(final NetworkPackage np) {
|
void sendSuccessful();
|
||||||
|
void sendFailed();
|
||||||
if (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired()) {
|
|
||||||
try {
|
|
||||||
np.encrypt(publicKey);
|
|
||||||
} catch(Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.e("Device","sendPackage exception - could not encrypt");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new AsyncTask<Void,Void,Void>() {
|
public void sendPackage(NetworkPackage np) {
|
||||||
|
sendPackage(np,null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPackage(final NetworkPackage np, final SendPackageFinishedCallback callback) {
|
||||||
|
|
||||||
|
new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
public void run() {
|
||||||
for(BaseComputerLink link : links) {
|
|
||||||
//Log.e("sendPackage","Trying "+link.getLinkProvider().getName());
|
boolean useEncryption = (!np.getType().equals(NetworkPackage.PACKAGE_TYPE_PAIR) && isPaired());
|
||||||
if (link.sendPackage(np)) {
|
|
||||||
//Log.e("sent using", link.getLinkProvider().getName());
|
//Log.e("sendPackage", "Sending...");
|
||||||
return null;
|
|
||||||
|
for(BaseLink link : links) {
|
||||||
|
|
||||||
|
boolean success;
|
||||||
|
if (useEncryption) {
|
||||||
|
success = link.sendPackageEncrypted(np, publicKey);
|
||||||
|
} else {
|
||||||
|
success = link.sendPackage(np);
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
//Log.e("sendPackage", "Sent");
|
||||||
|
if (callback != null) callback.sendSuccessful();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (callback != null) callback.sendFailed();
|
||||||
Log.e("sendPackage","Error: Package could not be sent ("+links.size()+" links available)");
|
Log.e("sendPackage","Error: Package could not be sent ("+links.size()+" links available)");
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}.execute();
|
}).start();
|
||||||
|
|
||||||
//TODO: Detect when unable to send a package and try again somehow
|
|
||||||
|
|
||||||
return !links.isEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
package org.kde.kdeconnect.LinkProviders;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
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.ComputerLinks.LanComputerLink;
|
|
||||||
import org.kde.kdeconnect.ComputerLinks.LoopbackComputerLink;
|
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
|
||||||
|
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class LoopbackLinkProvider extends BaseLinkProvider {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public LoopbackLinkProvider(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
onNetworkChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNetworkChange() {
|
|
||||||
|
|
||||||
NetworkPackage np = NetworkPackage.createIdentityPackage(context);
|
|
||||||
connectionAccepted(np, new LoopbackComputerLink(this));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPriority() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "LoopbackLinkProvider";
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,6 +11,10 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
@ -20,7 +24,7 @@ import javax.crypto.Cipher;
|
|||||||
|
|
||||||
public class NetworkPackage {
|
public class NetworkPackage {
|
||||||
|
|
||||||
public final static int ProtocolVersion = 3;
|
public final static int ProtocolVersion = 5;
|
||||||
|
|
||||||
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
|
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
|
||||||
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
|
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
|
||||||
@ -31,18 +35,26 @@ public class NetworkPackage {
|
|||||||
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
|
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
|
||||||
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
|
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
|
||||||
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
|
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
|
||||||
|
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
|
||||||
|
|
||||||
private long mId;
|
private long mId;
|
||||||
private String mType;
|
private String mType;
|
||||||
private JSONObject mBody;
|
private JSONObject mBody;
|
||||||
|
private InputStream mPayload;
|
||||||
|
private JSONObject mPayloadTransferInfo;
|
||||||
|
private int mPayloadSize;
|
||||||
|
|
||||||
private NetworkPackage() {
|
private NetworkPackage() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkPackage(String type) {
|
public NetworkPackage(String type) {
|
||||||
mId = System.currentTimeMillis();
|
mId = System.currentTimeMillis();
|
||||||
mType = type;
|
mType = type;
|
||||||
mBody = new JSONObject();
|
mBody = new JSONObject();
|
||||||
|
mPayload = null;
|
||||||
|
mPayloadSize = 0;
|
||||||
|
mPayloadTransferInfo = new JSONObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public String getType() {
|
||||||
@ -94,35 +106,60 @@ public class NetworkPackage {
|
|||||||
|
|
||||||
public boolean has(String key) { return mBody.has(key); }
|
public boolean has(String key) { return mBody.has(key); }
|
||||||
|
|
||||||
|
public boolean isEncrypted() { return mType.equals(PACKAGE_TYPE_ENCRYPTED); }
|
||||||
|
|
||||||
public String serialize() {
|
public String serialize() {
|
||||||
JSONObject jo = new JSONObject();
|
JSONObject jo = new JSONObject();
|
||||||
try {
|
try {
|
||||||
jo.put("id", mId);
|
jo.put("id", mId);
|
||||||
jo.put("type", mType);
|
jo.put("type", mType);
|
||||||
jo.put("body", mBody);
|
jo.put("body", mBody);
|
||||||
} catch(Exception e) {
|
if (hasPayload()) {
|
||||||
|
jo.put("payloadSize", mPayloadSize);
|
||||||
|
jo.put("payloadTransferInfo", mPayloadTransferInfo);
|
||||||
}
|
}
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("NetworkPackage", "Serialization exception");
|
||||||
|
}
|
||||||
|
|
||||||
//QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
|
//QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
|
||||||
String json = jo.toString().replace("\\/","/")+"\n";
|
String json = jo.toString().replace("\\/","/")+"\n";
|
||||||
|
|
||||||
|
if (!isEncrypted()) {
|
||||||
//Log.e("NetworkPackage.serialize", json);
|
//Log.e("NetworkPackage.serialize", json);
|
||||||
|
}
|
||||||
|
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
static public NetworkPackage unserialize(String s) {
|
static public NetworkPackage unserialize(String s) {
|
||||||
//Log.e("NetworkPackage.unserialize", s);
|
|
||||||
NetworkPackage np = new NetworkPackage();
|
NetworkPackage np = new NetworkPackage();
|
||||||
try {
|
try {
|
||||||
JSONObject jo = new JSONObject(s);
|
JSONObject jo = new JSONObject(s);
|
||||||
np.mId = jo.getLong("id");
|
np.mId = jo.getLong("id");
|
||||||
np.mType = jo.getString("type");
|
np.mType = jo.getString("type");
|
||||||
np.mBody = jo.getJSONObject("body");
|
np.mBody = jo.getJSONObject("body");
|
||||||
|
if (jo.has("payloadSize")) {
|
||||||
|
np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo");
|
||||||
|
np.mPayloadSize = jo.getInt("payloadSize");
|
||||||
|
} else {
|
||||||
|
np.mPayloadTransferInfo = new JSONObject();
|
||||||
|
np.mPayloadSize = 0;
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("NetworkPackage", "Unserialization exception unserializing "+s);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return np;
|
|
||||||
|
if (!np.isEncrypted()) {
|
||||||
|
//Log.e("NetworkPackage.unserialize", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return np;
|
||||||
|
}
|
||||||
|
|
||||||
public void encrypt(PublicKey publicKey) throws Exception {
|
public void encrypt(PublicKey publicKey) throws Exception {
|
||||||
|
|
||||||
@ -177,7 +214,6 @@ public class NetworkPackage {
|
|||||||
return unserialize(decryptedJson);
|
return unserialize(decryptedJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static public NetworkPackage createIdentityPackage(Context context) {
|
static public NetworkPackage createIdentityPackage(Context context) {
|
||||||
|
|
||||||
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
|
||||||
@ -208,4 +244,40 @@ public class NetworkPackage {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPayload(byte[] data) {
|
||||||
|
setPayload(new ByteArrayInputStream(data), data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayload(InputStream stream, int size) {
|
||||||
|
mPayload = stream;
|
||||||
|
mPayloadSize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public void setPayload(InputStream stream) {
|
||||||
|
setPayload(stream, -1);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public InputStream getPayload() {
|
||||||
|
return mPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPayloadSize() {
|
||||||
|
return mPayloadSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPayload() {
|
||||||
|
return (mPayload != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasPayloadTransferInfo() {
|
||||||
|
return (mPayloadTransferInfo.length() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject getPayloadTransferInfo() {
|
||||||
|
return mPayloadTransferInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayloadTransferInfo(JSONObject payloadTransferInfo) {
|
||||||
|
mPayloadTransferInfo = payloadTransferInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,10 @@ import android.widget.SeekBar;
|
|||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Backends.BaseLink;
|
||||||
import org.kde.kdeconnect.BackgroundService;
|
import org.kde.kdeconnect.BackgroundService;
|
||||||
import org.kde.kdeconnect.ComputerLinks.BaseComputerLink;
|
|
||||||
import org.kde.kdeconnect.Device;
|
import org.kde.kdeconnect.Device;
|
||||||
import org.kde.kdeconnect.LinkProviders.BaseLinkProvider;
|
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||||
import org.kde.kdeconnect.NetworkPackage;
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
import org.kde.kdeconnect_tp.R;
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
@ -121,12 +121,12 @@ public class MprisActivity extends Activity {
|
|||||||
|
|
||||||
BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
|
BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionReceived(NetworkPackage identityPackage, BaseComputerLink link) {
|
public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link) {
|
||||||
connectToPlugin();
|
connectToPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionLost(BaseComputerLink link) {
|
public void onConnectionLost(BaseLink link) {
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -172,10 +172,10 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
|
||||||
onNotificationPosted(statusBarNotification, false);
|
sendNotification(statusBarNotification, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onNotificationPosted(StatusBarNotification statusBarNotification, boolean requestAnswer) {
|
public void sendNotification(StatusBarNotification statusBarNotification, boolean requestAnswer) {
|
||||||
|
|
||||||
Notification notification = statusBarNotification.getNotification();
|
Notification notification = statusBarNotification.getNotification();
|
||||||
NotificationId id = NotificationId.fromNotification(statusBarNotification);
|
NotificationId id = NotificationId.fromNotification(statusBarNotification);
|
||||||
@ -185,20 +185,21 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
String packageName = statusBarNotification.getPackageName();
|
String packageName = statusBarNotification.getPackageName();
|
||||||
String appName = AppsHelper.appNameLookup(context, packageName);
|
String appName = AppsHelper.appNameLookup(context, packageName);
|
||||||
|
|
||||||
|
//TODO: Add support for displaying app icons to desktop plasmoid and uncomment this piece of code
|
||||||
|
/*
|
||||||
try {
|
try {
|
||||||
|
//TODO: Scale down app icon if too big and compress as JPG
|
||||||
Drawable drawableAppIcon = AppsHelper.appIconLookup(context, packageName);
|
Drawable drawableAppIcon = AppsHelper.appIconLookup(context, packageName);
|
||||||
Bitmap appIcon = ImagesHelper.drawableToBitmap(drawableAppIcon);
|
Bitmap appIcon = ImagesHelper.drawableToBitmap(drawableAppIcon);
|
||||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
|
appIcon.compress(Bitmap.CompressFormat.PNG, 90, outStream);
|
||||||
byte[] bitmapData = outStream.toByteArray();
|
byte[] bitmapData = outStream.toByteArray();
|
||||||
byte[] serializedBitmapData = Base64.encode(bitmapData, Base64.NO_WRAP);
|
np.setPayload(bitmapData);
|
||||||
String stringBitmapData = new String(serializedBitmapData, Charset.defaultCharset());
|
|
||||||
//The icon is super big, better sending it as a file transfer when we support that
|
|
||||||
//np.set("base64icon", stringBitmapData);
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Log.e("NotificationsPlugin","Error retrieving icon");
|
Log.e("NotificationsPlugin","Error retrieving icon");
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
np.set("id", id.serialize());
|
np.set("id", id.serialize());
|
||||||
np.set("appName", appName == null? packageName : appName);
|
np.set("appName", appName == null? packageName : appName);
|
||||||
@ -224,7 +225,7 @@ public class NotificationsPlugin extends Plugin implements NotificationReceiver.
|
|||||||
private void sendCurrentNotifications(NotificationReceiver service) {
|
private void sendCurrentNotifications(NotificationReceiver service) {
|
||||||
StatusBarNotification[] notifications = service.getActiveNotifications();
|
StatusBarNotification[] notifications = service.getActiveNotifications();
|
||||||
for (StatusBarNotification notification : notifications) {
|
for (StatusBarNotification notification : notifications) {
|
||||||
onNotificationPosted(notification, true);
|
sendNotification(notification, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,17 @@ public class DeviceItem implements ListAdapter.Item {
|
|||||||
|
|
||||||
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
|
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
|
||||||
if (titleView != null) titleView.setText(device.getName());
|
if (titleView != null) titleView.setText(device.getName());
|
||||||
|
if (device.compareProtocolVersion() != 0) {
|
||||||
|
TextView summaryView = (TextView)v.findViewById(R.id.list_item_entry_summary);
|
||||||
|
summaryView.setVisibility(View.VISIBLE);
|
||||||
|
if (device.compareProtocolVersion() > 0) {
|
||||||
|
summaryView.setText(R.string.protocol_version_newer);
|
||||||
|
} else {
|
||||||
|
summaryView.setText(R.string.protocol_version_older);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v.findViewById(R.id.list_item_entry_summary).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
v.setOnClickListener(new View.OnClickListener() {
|
v.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface.List;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.Device;
|
||||||
|
import org.kde.kdeconnect.UserInterface.DeviceActivity;
|
||||||
|
import org.kde.kdeconnect.UserInterface.PairActivity;
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
public class EntryItem implements ListAdapter.Item {
|
||||||
|
|
||||||
|
private final String title;
|
||||||
|
|
||||||
|
public EntryItem(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View inflateView(LayoutInflater layoutInflater) {
|
||||||
|
View v = layoutInflater.inflate(R.layout.list_item_entry, null);
|
||||||
|
|
||||||
|
TextView titleView = (TextView)v.findViewById(R.id.list_item_entry_title);
|
||||||
|
if (titleView != null) titleView.setText(title);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -13,6 +13,7 @@ public class SectionItem implements ListAdapter.Item {
|
|||||||
|
|
||||||
public SectionItem(String title) {
|
public SectionItem(String title) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
this.isEmpty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,276 @@
|
|||||||
|
package org.kde.kdeconnect.UserInterface;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.ActionBarActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import org.kde.kdeconnect.BackgroundService;
|
||||||
|
import org.kde.kdeconnect.Device;
|
||||||
|
import org.kde.kdeconnect.NetworkPackage;
|
||||||
|
import org.kde.kdeconnect.UserInterface.List.EntryItem;
|
||||||
|
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||||
|
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||||
|
import org.kde.kdeconnect_tp.R;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
|
||||||
|
public class ShareToReceiver extends ActionBarActivity {
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Action bar
|
||||||
|
//
|
||||||
|
|
||||||
|
private MenuItem menuProgress;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
MenuInflater inflater = getMenuInflater();
|
||||||
|
inflater.inflate(R.menu.main, menu);
|
||||||
|
menuProgress = menu.findItem(R.id.menu_progress);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.menu_refresh:
|
||||||
|
updateComputerList();
|
||||||
|
BackgroundService.RunCommand(ShareToReceiver.this, new BackgroundService.InstanceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServiceStart(BackgroundService service) {
|
||||||
|
service.onNetworkChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.setVisible(false);
|
||||||
|
menuProgress.setVisible(true);
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try { Thread.sleep(1500); } catch (InterruptedException e) { }
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
menuProgress.setVisible(false);
|
||||||
|
item.setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateComputerList() {
|
||||||
|
|
||||||
|
final Intent intent = getIntent();
|
||||||
|
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (!Intent.ACTION_SEND.equals(action) && !Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServiceStart(final BackgroundService service) {
|
||||||
|
|
||||||
|
Collection<Device> devices = service.getDevices().values();
|
||||||
|
final ArrayList<Device> devicesList = new ArrayList<Device>();
|
||||||
|
final ArrayList<ListAdapter.Item> items = new ArrayList<ListAdapter.Item>();
|
||||||
|
|
||||||
|
items.add(new SectionItem(getString(R.string.share_to)));
|
||||||
|
|
||||||
|
for (Device d : devices) {
|
||||||
|
if (d.isReachable() && d.isPaired()) {
|
||||||
|
devicesList.add(d);
|
||||||
|
items.add(new EntryItem(d.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ListView list = (ListView) findViewById(R.id.listView1);
|
||||||
|
list.setAdapter(new ListAdapter(ShareToReceiver.this, items));
|
||||||
|
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
|
||||||
|
Device device = devicesList.get(i-1); //NOTE: -1 because of the title!
|
||||||
|
|
||||||
|
Bundle extras = intent.getExtras();
|
||||||
|
if (extras.containsKey(Intent.EXTRA_STREAM)) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
ArrayList<Uri> uriList;
|
||||||
|
if (!Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||||
|
uriList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||||
|
} else {
|
||||||
|
Uri uri = extras.getParcelable(Intent.EXTRA_STREAM);
|
||||||
|
uriList = new ArrayList<Uri>();
|
||||||
|
uriList.add(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedSendUriList(device, uriList);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(this.getClass().getName(), e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (extras.containsKey(Intent.EXTRA_TEXT)) {
|
||||||
|
String text = extras.getString(Intent.EXTRA_TEXT);
|
||||||
|
boolean isUrl;
|
||||||
|
try {
|
||||||
|
new URL(text);
|
||||||
|
isUrl = true;
|
||||||
|
} catch(Exception e) {
|
||||||
|
isUrl = false;
|
||||||
|
}
|
||||||
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_SHARE);
|
||||||
|
if (isUrl) {
|
||||||
|
np.set("url", text);
|
||||||
|
} else {
|
||||||
|
np.set("text", text);
|
||||||
|
}
|
||||||
|
device.sendPackage(np);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queuedSendUriList(final Device device, final ArrayList<Uri> uriList) {
|
||||||
|
try {
|
||||||
|
Uri uri = uriList.remove(0);
|
||||||
|
ContentResolver cr = getContentResolver();
|
||||||
|
InputStream inputStream = cr.openInputStream(uri);
|
||||||
|
|
||||||
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_SHARE);
|
||||||
|
|
||||||
|
String[] proj = { MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.DISPLAY_NAME };
|
||||||
|
Cursor cursor = managedQuery(uri, proj, null, null, null);
|
||||||
|
|
||||||
|
int size = -1;
|
||||||
|
try {
|
||||||
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
size = cursor.getInt(column_index);
|
||||||
|
} catch(Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("ShareToReceiver", "Could not obtain file size");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.e("ShareToReceiver", "Size "+size);
|
||||||
|
np.setPayload(inputStream, size);
|
||||||
|
|
||||||
|
try {
|
||||||
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
String path = cursor.getString(column_index);
|
||||||
|
np.set("filename", Uri.parse(path).getLastPathSegment());
|
||||||
|
} catch(Exception _) {
|
||||||
|
try {
|
||||||
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
String name = cursor.getString(column_index);
|
||||||
|
np.set("filename", name);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("ShareToReceiver", "Could not obtain file name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device.sendPackage(np, new Device.SendPackageFinishedCallback() {
|
||||||
|
@Override
|
||||||
|
public void sendSuccessful() {
|
||||||
|
if (!uriList.isEmpty()) queuedSendUriList(device, uriList);
|
||||||
|
else Log.e("ShareToReceiver", "All files sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendFailed() {
|
||||||
|
Log.e("ShareToReceiver", "Failed to send attachment");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.e("ShareToReceiver", "Failed to send attachment");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServiceStart(BackgroundService service) {
|
||||||
|
service.onNetworkChange();
|
||||||
|
service.setDeviceListChangedCallback(new BackgroundService.DeviceListChangedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onDeviceListChanged() {
|
||||||
|
updateComputerList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onServiceStart(BackgroundService service) {
|
||||||
|
service.setDeviceListChangedCallback(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
super.onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
updateComputerList();
|
||||||
|
}
|
||||||
|
}
|
@ -6,43 +6,27 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingRight="?android:attr/scrollbarSize">
|
android:paddingRight="?android:attr/scrollbarSize"
|
||||||
<!--
|
android:orientation="vertical">
|
||||||
<ImageView
|
|
||||||
android:id="@+id/list_item_entry_drawable"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:src="@android:drawable/ic_menu_preferences"
|
|
||||||
android:paddingLeft="9dp"/>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="10dip"
|
|
||||||
android:layout_marginRight="6dip"
|
|
||||||
android:layout_marginTop="3dip"
|
|
||||||
android:layout_marginBottom="3dip"
|
|
||||||
android:layout_weight="0">
|
|
||||||
-->
|
|
||||||
<TextView android:id="@+id/list_item_entry_title"
|
<TextView android:id="@+id/list_item_entry_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
android:ellipsize="marquee"
|
android:ellipsize="marquee"
|
||||||
android:fadingEdge="horizontal" />
|
android:fadingEdge="horizontal"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
<!--
|
|
||||||
<TextView android:id="@+id/list_item_entry_summary"
|
<TextView android:id="@+id/list_item_entry_summary"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/list_item_entry_title"
|
|
||||||
android:layout_alignLeft="@id/list_item_entry_title"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="?android:attr/textColorSecondary" />
|
android:textColor="#CC2222"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:text="" />
|
||||||
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
-->
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -52,5 +52,9 @@
|
|||||||
<string name="mpris_previous">Previous</string>
|
<string name="mpris_previous">Previous</string>
|
||||||
<string name="mpris_next">Next</string>
|
<string name="mpris_next">Next</string>
|
||||||
<string name="mpris_volume">Volume</string>
|
<string name="mpris_volume">Volume</string>
|
||||||
|
<string name="share_to">Share To...</string>
|
||||||
|
<string name="protocol_version_older">This device uses an old protocol version</string>
|
||||||
|
<string name="protocol_version_newer">This device uses a newer protocol version</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<module external.linked.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
<exclude-output />
|
<exclude-output />
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user