diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d88d9f0a..903ed8ab 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -18,6 +18,8 @@
+
+
diff --git a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java
new file mode 100644
index 00000000..4f092b3b
--- /dev/null
+++ b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2016 Saikrishna Arcot
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+package org.kde.kdeconnect.Backends.BluetoothBackend;
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.kde.kdeconnect.Backends.BaseLink;
+import org.kde.kdeconnect.Backends.BasePairingHandler;
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
+import org.kde.kdeconnect.NetworkPackage;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.security.PublicKey;
+import java.util.UUID;
+
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class BluetoothLink extends BaseLink {
+ private final BluetoothSocket socket;
+ private final BluetoothLinkProvider linkProvider;
+
+ private boolean continueAccepting = true;
+
+ private Thread receivingThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ StringBuilder sb = new StringBuilder();
+ try {
+ Reader reader = new InputStreamReader(socket.getInputStream(), "UTF-8");
+ char[] buf = new char[512];
+ while (continueAccepting) {
+ while (sb.indexOf("\n") == -1 && continueAccepting) {
+ int charsRead;
+ if ((charsRead = reader.read(buf)) > 0) {
+ sb.append(buf, 0, charsRead);
+ }
+ }
+
+ int endIndex = sb.indexOf("\n");
+ if (endIndex != -1) {
+ String message = sb.substring(0, endIndex + 1);
+ sb.delete(0, endIndex + 1);
+ processMessage(message);
+ }
+ }
+ } catch (IOException e) {
+ Log.e("BluetoothLink/receiving", "Connection to " + socket.getRemoteDevice().getAddress() + " likely broken.", e);
+ disconnect();
+ }
+ }
+
+ private void processMessage(String message) {
+ NetworkPackage np;
+ try {
+ np = NetworkPackage.unserialize(message);
+ } catch (JSONException e) {
+ Log.e("BluetoothLink/receiving", "Unable to parse message.", e);
+ return;
+ }
+
+ if (np.getType().equals(NetworkPackage.PACKAGE_TYPE_ENCRYPTED)) {
+ try {
+ np = RsaHelper.decrypt(np, privateKey);
+ } catch(Exception e) {
+ Log.e("BluetoothLink/receiving", "Exception decrypting the package", e);
+ }
+ }
+
+ if (np.hasPayloadTransferInfo()) {
+ BluetoothSocket transferSocket = null;
+ try {
+ UUID transferUuid = UUID.fromString(np.getPayloadTransferInfo().getString("uuid"));
+ transferSocket = socket.getRemoteDevice().createRfcommSocketToServiceRecord(transferUuid);
+ transferSocket.connect();
+ np.setPayload(transferSocket.getInputStream(), np.getPayloadSize());
+ } catch (Exception e) {
+ if (transferSocket != null) {
+ try { transferSocket.close(); } catch(IOException ignored) { }
+ }
+ Log.e("BluetoothLink/receiving", "Unable to get payload", e);
+ }
+ }
+
+ packageReceived(np);
+ }
+ });
+
+ public BluetoothLink(Context context, BluetoothSocket socket, String deviceId, BluetoothLinkProvider linkProvider) {
+ super(context, deviceId, linkProvider);
+ this.socket = socket;
+ this.linkProvider = linkProvider;
+ receivingThread.start();
+ }
+
+ @Override
+ public String getName() {
+ return "BluetoothLink";
+ }
+
+ @Override
+ public BasePairingHandler getPairingHandler(Device device, BasePairingHandler.PairingHandlerCallback callback) {
+ return new BluetoothPairingHandler(device, callback);
+ }
+
+ public void disconnect() {
+ if (socket == null) {
+ return;
+ }
+ continueAccepting = false;
+ try {
+ socket.close();
+ } catch (IOException e) {
+ }
+ linkProvider.disconnectedLink(this, getDeviceId(), socket);
+ }
+
+ private void sendMessage(NetworkPackage np) throws JSONException, IOException {
+ byte[] message = np.serialize().getBytes(Charset.forName("UTF-8"));
+ OutputStream socket = this.socket.getOutputStream();
+ Log.i("BluetoothLink","Beginning to send message");
+ socket.write(message);
+ Log.i("BluetoothLink","Finished sending message");
+ }
+
+ @Override
+ public boolean sendPackage(NetworkPackage np, Device.SendPackageStatusCallback callback) {
+ return sendPackageInternal(np, callback, null);
+ }
+
+ @Override
+ public boolean sendPackageEncrypted(NetworkPackage np, Device.SendPackageStatusCallback callback, PublicKey key) {
+ return sendPackageInternal(np, callback, key);
+ }
+
+ private boolean sendPackageInternal(NetworkPackage np, final Device.SendPackageStatusCallback callback, PublicKey key) {
+
+ /*if (!isConnected()) {
+ Log.e("BluetoothLink", "sendPackageEncrypted failed: not connected");
+ callback.sendFailure(new Exception("Not connected"));
+ return;
+ }*/
+
+ try {
+ BluetoothServerSocket serverSocket = null;
+ if (np.hasPayload()) {
+ UUID transferUuid = UUID.randomUUID();
+ serverSocket = BluetoothAdapter.getDefaultAdapter()
+ .listenUsingRfcommWithServiceRecord("KDE Connect Transfer", transferUuid);
+ JSONObject payloadTransferInfo = new JSONObject();
+ payloadTransferInfo.put("uuid", transferUuid.toString());
+ np.setPayloadTransferInfo(payloadTransferInfo);
+ }
+
+ if (key != null) {
+ try {
+ np = RsaHelper.encrypt(np, key);
+ } catch (Exception e) {
+ callback.onFailure(e);
+ return false;
+ }
+ }
+
+ sendMessage(np);
+
+ if (serverSocket != null) {
+ BluetoothSocket transferSocket = serverSocket.accept();
+ try {
+ serverSocket.close();
+
+ int idealBufferLength = 4096;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ idealBufferLength = transferSocket.getMaxReceivePacketSize();
+ }
+ byte[] buffer = new byte[idealBufferLength];
+ int bytesRead;
+ long progress = 0;
+ InputStream stream = np.getPayload();
+ while ((bytesRead = stream.read(buffer)) != -1) {
+ progress += bytesRead;
+ transferSocket.getOutputStream().write(buffer, 0, bytesRead);
+ if (np.getPayloadSize() > 0) {
+ callback.onProgressChanged((int) (100 * progress / np.getPayloadSize()));
+ }
+ }
+ transferSocket.getOutputStream().flush();
+ stream.close();
+ } catch (Exception e) {
+ callback.onFailure(e);
+ return false;
+ } finally {
+ try { transferSocket.close(); } catch (IOException ignored) { }
+ }
+ }
+
+ callback.onSuccess();
+ return true;
+ } catch (Exception e) {
+ callback.onFailure(e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean linkShouldBeKeptAlive() {
+ return receivingThread.isAlive();
+ }
+
+ /*
+ public boolean isConnected() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
+ return socket.isConnected();
+ } else {
+ return true;
+ }
+ }
+*/
+}
diff --git a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLinkProvider.java b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLinkProvider.java
new file mode 100644
index 00000000..20090ea7
--- /dev/null
+++ b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLinkProvider.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2016 Saikrishna Arcot
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+package org.kde.kdeconnect.Backends.BluetoothBackend;
+
+import android.annotation.TargetApi;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.bluetooth.BluetoothServerSocket;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Build;
+import android.os.Parcelable;
+import android.util.Log;
+
+import org.kde.kdeconnect.Backends.BaseLinkProvider;
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.NetworkPackage;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.Set;
+
+@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+public class BluetoothLinkProvider extends BaseLinkProvider {
+
+ private static final UUID SERVICE_UUID = UUID.fromString("185f3df4-3268-4e3f-9fca-d4d5059915bd");
+ private static final int REQUEST_ENABLE_BT = 48;
+
+ private final Context context;
+ private final Map visibleComputers = new HashMap<>();
+ private final Map sockets = new HashMap<>();
+
+ private BluetoothAdapter bluetoothAdapter = null;
+
+ private ServerRunnable serverRunnable;
+ private ClientRunnable clientRunnable;
+
+ private void addLink(NetworkPackage identityPackage, BluetoothLink link) {
+ String deviceId = identityPackage.getString("deviceId");
+ Log.i("BluetoothLinkProvider","addLink to "+deviceId);
+ BluetoothLink oldLink = visibleComputers.get(deviceId);
+ if (oldLink == link) {
+ Log.e("KDEConnect", "BluetoothLinkProvider: oldLink == link. This should not happen!");
+ return;
+ }
+ visibleComputers.put(deviceId, link);
+ connectionAccepted(identityPackage, link);
+ if (oldLink != null) {
+ Log.i("BluetoothLinkProvider","Removing old connection to same device");
+ oldLink.disconnect();
+ }
+ }
+
+ public BluetoothLinkProvider(Context context) {
+ this.context = context;
+
+ bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (bluetoothAdapter == null) {
+ Log.e("BluetoothLinkProvider","No bluetooth adapter found.");
+ }
+ }
+
+ @Override
+ public void onStart() {
+ if (bluetoothAdapter == null) {
+ return;
+ }
+
+ if (!bluetoothAdapter.isEnabled()) {
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ Log.e("BluetoothLinkProvider","Bluetooth adapter not enabled.");
+ // TODO: next line needs to be called from an existing activity, so move it?
+ // startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+ // TODO: Check result of the previous command, whether the user allowed bluetooth or not.
+ return;
+ }
+
+ //This handles the case when I'm the existing device in the network and receive a hello package
+ clientRunnable = new ClientRunnable();
+ new Thread(clientRunnable).start();
+
+ // I'm on a new network, let's be polite and introduce myself
+ serverRunnable = new ServerRunnable();
+ new Thread(serverRunnable).start();
+ }
+
+ @Override
+ public void onNetworkChange() {
+ onStop();
+ onStart();
+ }
+
+ @Override
+ public void onStop() {
+ if (bluetoothAdapter == null || clientRunnable == null || serverRunnable == null) {
+ return;
+ }
+
+ clientRunnable.stopProcessing();
+ serverRunnable.stopProcessing();
+ }
+
+ @Override
+ public String getName() {
+ return "BluetoothLinkProvider";
+ }
+
+ public void disconnectedLink(BluetoothLink link, String deviceId, BluetoothSocket socket) {
+ sockets.remove(socket.getRemoteDevice());
+ visibleComputers.remove(deviceId);
+ connectionLost(link);
+ }
+
+ private class ServerRunnable implements Runnable {
+
+ private boolean continueProcessing = true;
+ private BluetoothServerSocket serverSocket;
+
+ void stopProcessing() {
+ continueProcessing = false;
+ if (serverSocket != null) {
+ try {
+ serverSocket.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ serverSocket = bluetoothAdapter
+ .listenUsingRfcommWithServiceRecord("KDE Connect", SERVICE_UUID);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ if (continueProcessing) {
+ try {
+ BluetoothSocket socket = serverSocket.accept();
+ connect(socket);
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ private void connect(BluetoothSocket socket) throws Exception {
+ //socket.connect();
+ OutputStream outputStream = socket.getOutputStream();
+ if (sockets.containsKey(socket.getRemoteDevice())) {
+ Log.i("BTLinkProvider/Server", "Received duplicate connection from " + socket.getRemoteDevice().getAddress());
+ socket.close();
+ return;
+ } else {
+ sockets.put(socket.getRemoteDevice(), socket);
+ }
+
+ Log.i("BTLinkProvider/Server", "Received connection from " + socket.getRemoteDevice().getAddress());
+
+ NetworkPackage np = NetworkPackage.createIdentityPackage(context);
+ byte[] message = np.serialize().getBytes("UTF-8");
+ outputStream.write(message);
+
+ Log.i("BTLinkProvider/Server", "Sent identity package");
+
+ // Listen for the response
+ StringBuilder sb = new StringBuilder();
+ Reader reader = new InputStreamReader(socket.getInputStream(), "UTF-8");
+ int charsRead;
+ char[] buf = new char[512];
+ while(sb.lastIndexOf("\n") == -1 && (charsRead = reader.read(buf)) != -1) {
+ sb.append(buf, 0, charsRead);
+ }
+
+ String response = sb.toString();
+ final NetworkPackage identityPackage = NetworkPackage.unserialize(response);
+
+ if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
+ Log.e("BTLinkProvider/Server", "2 Expecting an identity package");
+ return;
+ }
+
+ Log.i("BTLinkProvider/Server", "Received identity package");
+
+ BluetoothLink link = new BluetoothLink(context, socket,
+ identityPackage.getString("deviceId"), BluetoothLinkProvider.this);
+
+ addLink(identityPackage, link);
+ }
+ }
+
+ private class ClientRunnable extends BroadcastReceiver implements Runnable {
+
+ private boolean continueProcessing = true;
+ private Map connectionThreads = new HashMap<>();
+
+ void stopProcessing() {
+ continueProcessing = false;
+ }
+
+ @Override
+ public void run() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_UUID);
+ context.registerReceiver(this, filter);
+ }
+
+ while (continueProcessing) {
+ connectToDevices();
+ try {
+ Thread.sleep(15000);
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ context.unregisterReceiver(this);
+ }
+ }
+
+ private void connectToDevices() {
+ Set pairedDevices = bluetoothAdapter.getBondedDevices();
+ Log.i("BluetoothLinkProvider", "Bluetooth adapter paired devices: " + pairedDevices.size());
+
+ // Loop through paired devices
+ for (BluetoothDevice device : pairedDevices) {
+ if (sockets.containsKey(device)) {
+ continue;
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ device.fetchUuidsWithSdp();
+ } else {
+ connectToDevice(device);
+ }
+ }
+ }
+
+ @Override
+ @TargetApi(value=Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(BluetoothDevice.ACTION_UUID)) {
+ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ Parcelable[] activeUuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
+
+ if (sockets.containsKey(device)) {
+ return;
+ }
+
+ if (activeUuids == null) {
+ return;
+ }
+
+ for (Parcelable uuid: activeUuids) {
+ if (uuid.toString().equals(SERVICE_UUID.toString())) {
+ connectToDevice(device);
+ return;
+ }
+ }
+ }
+ }
+
+ private void connectToDevice(BluetoothDevice device) {
+ if (!connectionThreads.containsKey(device) || !connectionThreads.get(device).isAlive()) {
+ Thread connectionThread = new Thread(new ClientConnect(device));
+ connectionThread.start();
+ connectionThreads.put(device, connectionThread);
+ }
+ }
+
+
+ }
+
+ private class ClientConnect implements Runnable {
+
+ private final BluetoothDevice device;
+
+ public ClientConnect(BluetoothDevice device) {
+ this.device = device;
+ }
+
+ @Override
+ public void run() {
+ connectToDevice();
+ }
+
+ private void connectToDevice() {
+ BluetoothSocket socket;
+ try {
+ socket = device.createRfcommSocketToServiceRecord(SERVICE_UUID);
+ socket.connect();
+ sockets.put(device, socket);
+ } catch (IOException e) {
+ Log.e("BTLinkProvider/Client", "Could not connect to KDE Connect service on " + device.getAddress(), e);
+ return;
+ }
+
+ Log.i("BTLinkProvider/Client", "Connected to " + device.getAddress());
+
+ try {
+ int character;
+ StringBuilder sb = new StringBuilder();
+ while(sb.lastIndexOf("\n") == -1 && (character = socket.getInputStream().read()) != -1) {
+ sb.append((char)character);
+ }
+
+ String message = sb.toString();
+ final NetworkPackage identityPackage = NetworkPackage.unserialize(message);
+
+ if (!identityPackage.getType().equals(NetworkPackage.PACKAGE_TYPE_IDENTITY)) {
+ Log.e("BTLinkProvider/Client", "1 Expecting an identity package");
+ socket.close();
+ return;
+ }
+
+ Log.i("BTLinkProvider/Client", "Received identity package");
+
+ String myId = NetworkPackage.createIdentityPackage(context).getString("deviceId");
+ if (identityPackage.getString("deviceId").equals(myId)) {
+ // Probably won't happen, but just to be safe
+ socket.close();
+ return;
+ }
+
+ if (visibleComputers.containsKey(identityPackage.getString("deviceId"))) {
+ return;
+ }
+
+ Log.i("BTLinkProvider/Client", "Identity package received, creating link");
+
+ final BluetoothLink link = new BluetoothLink(context, socket,
+ identityPackage.getString("deviceId"), BluetoothLinkProvider.this);
+
+ NetworkPackage np2 = NetworkPackage.createIdentityPackage(context);
+ link.sendPackage(np2,new Device.SendPackageStatusCallback() {
+ @Override
+ public void onSuccess() {
+ addLink(identityPackage, link);
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+
+ }
+ });
+ } catch (Exception e) {
+ Log.e("BTLinkProvider/Client", "Connection lost/disconnected on " + device.getAddress(), e);
+ }
+ }
+ }
+}
diff --git a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothPairingHandler.java b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothPairingHandler.java
new file mode 100644
index 00000000..1d380e05
--- /dev/null
+++ b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothPairingHandler.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2015 Vineet Garg
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License or (at your option) version 3 or any later version
+ * accepted by the membership of KDE e.V. (or its successor approved
+ * by the membership of KDE e.V.), which shall act as a proxy
+ * defined in Section 14 of version 3 of the license.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+*/
+
+package org.kde.kdeconnect.Backends.BluetoothBackend;
+
+import android.util.Log;
+import org.kde.kdeconnect.Backends.BasePairingHandler;
+import org.kde.kdeconnect.Device;
+import org.kde.kdeconnect.NetworkPackage;
+import org.kde.kdeconnect_tp.R;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class BluetoothPairingHandler extends BasePairingHandler {
+
+ Timer mPairingTimer;
+ public BluetoothPairingHandler(Device device, final PairingHandlerCallback callback) {
+ super(device, callback);
+
+ if (device.isPaired()) {
+ mPairStatus = PairStatus.Paired;
+ } else {
+ mPairStatus = PairStatus.NotPaired;
+ }
+ }
+
+// @Override
+ public NetworkPackage createPairPackage() {
+ NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
+ np.set("pair", true);
+ return np;
+ }
+
+ @Override
+ public void packageReceived(NetworkPackage np) throws Exception{
+
+ boolean wantsPair = np.getBoolean("pair");
+
+ if (wantsPair == isPaired()) {
+ if (mPairStatus == PairStatus.Requested) {
+ //Log.e("Device","Unpairing (pair rejected)");
+ mPairStatus = PairStatus.NotPaired;
+ hidePairingNotification();
+ mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
+ }
+ return;
+ }
+
+ if (wantsPair) {
+
+ if (mPairStatus == PairStatus.Requested) { //We started pairing
+ hidePairingNotification();
+ pairingDone();
+ } else {
+
+ // If device is already paired, accept pairing silently
+ if (mDevice.isPaired()) {
+ acceptPairing();
+ return;
+ }
+
+ // Pairing notifications are still managed by device as there is no other way to
+ // know about notificationId to cancel notification when PairActivity is started
+ // Even putting notificationId in intent does not work because PairActivity can be
+ // started from MainActivity too, so then notificationId cannot be set
+ hidePairingNotification();
+ mDevice.displayPairingNotification();
+
+ mPairingTimer = new Timer();
+
+ mPairingTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ Log.w("KDE/Device","Unpairing (timeout B)");
+ mPairStatus = PairStatus.NotPaired;
+ hidePairingNotification();
+ }
+ }, 25*1000); //Time to show notification, waiting for user to accept (peer will timeout in 30 seconds)
+ mPairStatus = PairStatus.RequestedByPeer;
+ mCallback.incomingRequest();
+
+ }
+ } else {
+ Log.i("KDE/Pairing", "Unpair request");
+
+ if (mPairStatus == PairStatus.Requested) {
+ hidePairingNotification();
+ mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_canceled_by_other_peer));
+ } else if (mPairStatus == PairStatus.Paired) {
+ mCallback.unpaired();
+ }
+
+ mPairStatus = PairStatus.NotPaired;
+
+ }
+
+ }
+
+ @Override
+ public void requestPairing() {
+
+ Device.SendPackageStatusCallback statusCallback = new Device.SendPackageStatusCallback() {
+ @Override
+ public void onSuccess() {
+ hidePairingNotification(); //Will stop the pairingTimer if it was running
+ mPairingTimer = new Timer();
+ mPairingTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_timed_out));
+ Log.w("KDE/Device","Unpairing (timeout A)");
+ mPairStatus = PairStatus.NotPaired;
+ }
+ }, 30*1000); //Time to wait for the other to accept
+ mPairStatus = PairStatus.Requested;
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_could_not_send_package));
+ }
+ };
+ mDevice.sendPackage(createPairPackage(), statusCallback);
+ }
+
+ public void hidePairingNotification() {
+ mDevice.hidePairingNotification();
+ if (mPairingTimer != null) {
+ mPairingTimer .cancel();
+ }
+ }
+
+ @Override
+ public void acceptPairing() {
+ hidePairingNotification();
+ Device.SendPackageStatusCallback statusCallback = new Device.SendPackageStatusCallback() {
+ @Override
+ public void onSuccess() {
+ pairingDone();
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ mCallback.pairingFailed(mDevice.getContext().getString(R.string.error_not_reachable));
+ }
+ };
+ mDevice.sendPackage(createPairPackage(), statusCallback);
+ }
+
+ @Override
+ public void rejectPairing() {
+ hidePairingNotification();
+ mPairStatus = PairStatus.NotPaired;
+ NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
+ np.set("pair", false);
+ mDevice.sendPackage(np);
+ }
+
+ //@Override
+ public void pairingDone() {
+ // Store device information needed to create a Device object in a future
+ //Log.e("KDE/PairingDone", "Pairing Done");
+ mPairStatus = PairStatus.Paired;
+ mCallback.pairingDone();
+
+ }
+
+ @Override
+ public void unpair() {
+ mPairStatus = PairStatus.NotPaired;
+ NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
+ np.set("pair", false);
+ mDevice.sendPackage(np);
+ }
+}
diff --git a/src/org/kde/kdeconnect/BackgroundService.java b/src/org/kde/kdeconnect/BackgroundService.java
index ca6c2cca..f10ac1d1 100644
--- a/src/org/kde/kdeconnect/BackgroundService.java
+++ b/src/org/kde/kdeconnect/BackgroundService.java
@@ -26,11 +26,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import org.kde.kdeconnect.Backends.BaseLink;
import org.kde.kdeconnect.Backends.BaseLinkProvider;
+//import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider;
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
@@ -140,11 +142,9 @@ public class BackgroundService extends Service {
}
private void registerLinkProviders() {
-
- //linkProviders.add(new LoopbackLinkProvider(this));
-
linkProviders.add(new LanLinkProvider(this));
-
+// linkProviders.add(new LoopbackLinkProvider(this));
+// linkProviders.add(new BluetoothLinkProvider(this));
}
public ArrayList getLinkProviders() {