mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 01:51:47 +00:00
Working bluetooth implementation
This commit is contained in:
parent
22b6427a18
commit
19ec81eec4
@ -17,8 +17,10 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted
|
||||
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<!-- <uses-permission android:name="android.permission.BLUETOOTH" /> -->
|
||||
<!-- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
|
@ -55,4 +55,5 @@ public abstract class BaseLinkProvider {
|
||||
public abstract void onNetworkChange(@Nullable Network network);
|
||||
public abstract String getName();
|
||||
|
||||
public abstract int getPriority();
|
||||
}
|
||||
|
@ -189,10 +189,4 @@ public class BluetoothLink extends BaseLink {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public boolean isConnected() {
|
||||
return socket.isConnected();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* SPDX-FileCopyrightText: 2016 Saikrishna Arcot <saiarcot895@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Backends.BluetoothBackend;
|
||||
|
||||
@ -14,8 +14,11 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Network;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.Parcelable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
@ -29,6 +32,7 @@ import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.UserInterface.SettingsFragment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -39,6 +43,7 @@ import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -58,6 +63,8 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
private ServerRunnable serverRunnable;
|
||||
private ClientRunnable clientRunnable;
|
||||
|
||||
private Random randomiser;
|
||||
|
||||
private void addLink(NetworkPacket identityPacket, BluetoothLink link) throws CertificateException {
|
||||
String deviceId = identityPacket.getString("deviceId");
|
||||
Log.i("BluetoothLinkProvider", "addLink to " + deviceId);
|
||||
@ -66,7 +73,9 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
Log.e("BluetoothLinkProvider", "oldLink == link. This should not happen!");
|
||||
return;
|
||||
}
|
||||
synchronized (visibleDevices) {
|
||||
visibleDevices.put(deviceId, link);
|
||||
}
|
||||
onConnectionReceived(link);
|
||||
link.startListening();
|
||||
link.packetReceived(identityPacket);
|
||||
@ -79,6 +88,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
public BluetoothLinkProvider(Context context) {
|
||||
this.context = context;
|
||||
|
||||
this.randomiser = new Random();
|
||||
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bluetoothAdapter == null) {
|
||||
Log.e("BluetoothLinkProvider", "No bluetooth adapter found.");
|
||||
@ -87,6 +97,10 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (!preferences.getBoolean(SettingsFragment.KEY_BLUETOOTH_ENABLED, false)) {
|
||||
return;
|
||||
}
|
||||
if (bluetoothAdapter == null) {
|
||||
return;
|
||||
}
|
||||
@ -100,6 +114,8 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i("BluetoothLinkProvider", "onStart called");
|
||||
|
||||
//This handles the case when I'm the existing device in the network and receive a hello package
|
||||
clientRunnable = new ClientRunnable();
|
||||
ThreadHelper.execute(clientRunnable);
|
||||
@ -111,6 +127,8 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
|
||||
@Override
|
||||
public void onNetworkChange(@Nullable Network network) {
|
||||
Log.i("BluetoothLinkProvider", "onNetworkChange called");
|
||||
|
||||
onStop();
|
||||
onStart();
|
||||
}
|
||||
@ -120,7 +138,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
if (bluetoothAdapter == null || clientRunnable == null || serverRunnable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i("BluetoothLinkProvider", "onStop called");
|
||||
clientRunnable.stopProcessing();
|
||||
serverRunnable.stopProcessing();
|
||||
}
|
||||
@ -130,9 +148,20 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
return "BluetoothLinkProvider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
public void disconnectedLink(BluetoothLink link, BluetoothDevice remoteAddress) {
|
||||
|
||||
Log.i("BluetoothLinkProvider", "disconnectedLink called");
|
||||
synchronized (sockets) {
|
||||
sockets.remove(remoteAddress);
|
||||
}
|
||||
synchronized (visibleDevices) {
|
||||
visibleDevices.remove(link.getDeviceId());
|
||||
}
|
||||
onConnectionLost(link);
|
||||
}
|
||||
|
||||
@ -158,15 +187,18 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
} catch (IOException e) {
|
||||
Log.e("KDEConnect", "Exception", e);
|
||||
return;
|
||||
} catch (SecurityException e) {
|
||||
Log.e("KDEConnect", "Security Exception for CONNECT", e);
|
||||
return;
|
||||
}
|
||||
|
||||
while (continueProcessing) {
|
||||
try {
|
||||
while (continueProcessing) {
|
||||
BluetoothSocket socket = serverSocket.accept();
|
||||
connect(socket);
|
||||
} catch (Exception e) {
|
||||
Log.e("BTLinkProvider/Server", "Bluetooth error", e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.d("BTLinkProvider/Server", "Bluetooth Server error", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,6 +231,7 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
|
||||
DeviceInfo myDeviceInfo = DeviceHelper.getDeviceInfo(context);
|
||||
NetworkPacket np = myDeviceInfo.toIdentityPacket();
|
||||
|
||||
np.set("certificate", Base64.encodeToString(SslHelper.certificate.getEncoded(), 0));
|
||||
|
||||
byte[] message = np.serialize().getBytes(Charsets.UTF_8);
|
||||
@ -226,29 +259,38 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
|
||||
Log.i("BTLinkProvider/Server", "Received identity packet");
|
||||
|
||||
String certificateString = identityPacket.getString("certificate");
|
||||
byte[] certificateBytes = Base64.decode(certificateString, 0);
|
||||
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
|
||||
String PemEncodedCertificateString = identityPacket.getString("certificate");
|
||||
String base64CertificateString = PemEncodedCertificateString
|
||||
.replace("-----BEGIN CERTIFICATE-----\n","")
|
||||
.replace("-----END CERTIFICATE-----\n","");
|
||||
byte[] PEMEncodedCertificateBytes = Base64.decode(base64CertificateString, 0);
|
||||
Certificate certificate = SslHelper.parseCertificate(PEMEncodedCertificateBytes);
|
||||
|
||||
DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(identityPacket, certificate);
|
||||
|
||||
Log.i("BTLinkProvider/Server", "About to create link");
|
||||
BluetoothLink link = new BluetoothLink(context, connection,
|
||||
inputStream, outputStream, socket.getRemoteDevice(),
|
||||
deviceInfo, BluetoothLinkProvider.this);
|
||||
Log.i("BTLinkProvider/Server", "About to addLink");
|
||||
addLink(identityPacket, link);
|
||||
Log.i("BTLinkProvider/Server", "Link Added");
|
||||
} catch (Exception e) {
|
||||
synchronized (sockets) {
|
||||
sockets.remove(socket.getRemoteDevice());
|
||||
Log.i("BTLinkProvider/Server", "Exception thrown, removing socket", e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClientRunnableSingleton {
|
||||
|
||||
private static Map<BluetoothDevice, Thread> connectionThreads = new HashMap<>();
|
||||
}
|
||||
private class ClientRunnable extends BroadcastReceiver implements Runnable {
|
||||
|
||||
private boolean continueProcessing = true;
|
||||
private final Map<BluetoothDevice, Thread> connectionThreads = new HashMap<>();
|
||||
|
||||
void stopProcessing() {
|
||||
continueProcessing = false;
|
||||
@ -256,31 +298,57 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Log.i("ClientRunnable", "run called");
|
||||
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_UUID);
|
||||
context.registerReceiver(this, filter);
|
||||
|
||||
Log.i("ClientRunnable", "receiver registered");
|
||||
if (continueProcessing) {
|
||||
connectToDevices();
|
||||
Log.i("ClientRunnable", "before connectToDevices");
|
||||
discoverDeviceServices();
|
||||
Log.i("ClientRunnable", "after connectToDevices");
|
||||
try {
|
||||
Thread.sleep(15000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
Log.i("ClientRunnable", "unregisteringReceiver");
|
||||
context.unregisterReceiver(this);
|
||||
}
|
||||
|
||||
private void connectToDevices() {
|
||||
/**
|
||||
* Tell Android to use ServiceDiscoveryProtocol to update the
|
||||
* list of available UUIDs associated with Bluetooth devices
|
||||
* that are bluetooth-paired-but-not-yet-kde-paired
|
||||
*/
|
||||
private void discoverDeviceServices() {
|
||||
|
||||
Log.i("ClientRunnable", "connectToDevices called");
|
||||
|
||||
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
|
||||
if(pairedDevices == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i("BluetoothLinkProvider", "Bluetooth adapter paired devices: " + pairedDevices.size());
|
||||
|
||||
// Loop through paired devices
|
||||
// Loop through Bluetooth paired devices
|
||||
for (BluetoothDevice device : pairedDevices) {
|
||||
// If a socket exists for this, then it has been paired in KDE
|
||||
if (sockets.containsKey(device)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.i("ClientRunnable", "Calling fetchUuidsWithSdp for device: "+device);
|
||||
|
||||
device.fetchUuidsWithSdp();
|
||||
ParcelUuid[] deviceUuids = device.getUuids();
|
||||
|
||||
if(deviceUuids != null){
|
||||
for(ParcelUuid thisUuid : deviceUuids) {
|
||||
Log.i("ClientRunnable", "device "+device + " uuid: "+ thisUuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -309,14 +377,14 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
}
|
||||
|
||||
private void connectToDevice(BluetoothDevice device) {
|
||||
if (!connectionThreads.containsKey(device) || !connectionThreads.get(device).isAlive()) {
|
||||
synchronized (ClientRunnableSingleton.connectionThreads) {
|
||||
if (!ClientRunnableSingleton.connectionThreads.containsKey(device) || !ClientRunnableSingleton.connectionThreads.get(device).isAlive()) {
|
||||
Thread connectionThread = new Thread(new ClientConnect(device));
|
||||
connectionThread.start();
|
||||
connectionThreads.put(device, connectionThread);
|
||||
ClientRunnableSingleton.connectionThreads.put(device, connectionThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private class ClientConnect implements Runnable {
|
||||
@ -335,12 +403,21 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
private void connectToDevice() {
|
||||
BluetoothSocket socket;
|
||||
try {
|
||||
Log.i("BTLinkProvider/Client", "Cancelling Discovery");
|
||||
bluetoothAdapter.cancelDiscovery();
|
||||
Log.i("BTLinkProvider/Client", "Creating RFCommSocket to Service Record");
|
||||
socket = device.createRfcommSocketToServiceRecord(SERVICE_UUID);
|
||||
Log.i("BTLinkProvider/Client", "Connecting to ServiceRecord Socket");
|
||||
socket.connect();
|
||||
synchronized (sockets) {
|
||||
sockets.put(device, socket);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("BTLinkProvider/Client", "Could not connect to KDE Connect service on " + device.getAddress(), e);
|
||||
return;
|
||||
} catch (SecurityException e){
|
||||
Log.e("BTLinkProvider/Client", "Security Exception connecting to " + device.getAddress(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i("BTLinkProvider/Client", "Connected to " + device.getAddress());
|
||||
@ -348,19 +425,25 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
try {
|
||||
//Delay to let bluetooth initialize stuff correctly
|
||||
Thread.sleep(500);
|
||||
|
||||
ConnectionMultiplexer connection = new ConnectionMultiplexer(socket);
|
||||
OutputStream outputStream = connection.getDefaultOutputStream();
|
||||
InputStream inputStream = connection.getDefaultInputStream();
|
||||
|
||||
Log.i("BTLinkProvider/Client", "Device: " + device.getAddress() +" Before inputStream.read()");
|
||||
int character;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (sb.lastIndexOf("\n") == -1 && (character = inputStream.read()) != -1) {
|
||||
sb.append((char) character);
|
||||
}
|
||||
Log.i("BTLinkProvider/Client", "Device: " + device.getAddress() +" Before sb.toString()");
|
||||
|
||||
String message = sb.toString();
|
||||
Log.i("BTLinkProvider/Client", "Device: " + device.getAddress() +" Before unserialize (message: '"+message+"')");
|
||||
final NetworkPacket identityPacket = NetworkPacket.unserialize(message);
|
||||
|
||||
Log.i("BTLinkProvider/Client", "Device: " + device.getAddress() +" After unserialize");
|
||||
|
||||
if (!identityPacket.getType().equals(NetworkPacket.PACKET_TYPE_IDENTITY)) {
|
||||
Log.e("BTLinkProvider/Client", "1 Expecting an identity packet");
|
||||
socket.close();
|
||||
@ -382,9 +465,12 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
|
||||
Log.i("BTLinkProvider/Client", "identity packet received, creating link");
|
||||
|
||||
String certificateString = identityPacket.getString("certificate");
|
||||
byte[] certificateBytes = Base64.decode(certificateString, 0);
|
||||
Certificate certificate = SslHelper.parseCertificate(certificateBytes);
|
||||
String PemEncodedCertificateString = identityPacket.getString("certificate");
|
||||
String base64CertificateString = PemEncodedCertificateString
|
||||
.replace("-----BEGIN CERTIFICATE-----\n","")
|
||||
.replace("-----END CERTIFICATE-----\n","");
|
||||
byte[] PEMEncodedCertificateBytes = Base64.decode(base64CertificateString, 0);
|
||||
Certificate certificate = SslHelper.parseCertificate(PEMEncodedCertificateBytes);
|
||||
DeviceInfo deviceInfo = DeviceInfo.fromIdentityPacketAndCert(identityPacket, certificate);
|
||||
|
||||
final BluetoothLink link = new BluetoothLink(context, connection, inputStream, outputStream,
|
||||
@ -394,6 +480,8 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
NetworkPacket np2 = myDeviceInfo.toIdentityPacket();
|
||||
np2.set("certificate", Base64.encodeToString(SslHelper.certificate.getEncoded(), 0));
|
||||
|
||||
Log.i("BTLinkProvider/Client", "about to send packet np2");
|
||||
|
||||
link.sendPacket(np2, new Device.SendPacketStatusCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
@ -409,8 +497,12 @@ public class BluetoothLinkProvider extends BaseLinkProvider {
|
||||
|
||||
}
|
||||
}, true);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("BTLinkProvider/Client", "Connection lost/disconnected on " + device.getAddress(), e);
|
||||
synchronized (sockets) {
|
||||
sockets.remove(device, socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
package org.kde.kdeconnect.Backends.BluetoothBackend;
|
||||
|
||||
import android.bluetooth.BluetoothSocket;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
|
||||
@ -245,7 +246,7 @@ public final class ConnectionMultiplexer implements Closeable {
|
||||
channel.doClose();
|
||||
}
|
||||
channels.clear();
|
||||
if (socket.isConnected()) {
|
||||
if (socket != null && socket.isConnected()) {
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ignored) {
|
||||
@ -438,6 +439,16 @@ public final class ConnectionMultiplexer implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String byteArrayToHexString(final byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(byte b : bytes){
|
||||
sb.append(String.format("0x%02x ", b&0xff));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
private void read_message() throws IOException {
|
||||
byte[] data = new byte[BUFFER_SIZE];
|
||||
read_buffer(data, 19);
|
||||
@ -451,6 +462,10 @@ public final class ConnectionMultiplexer implements Closeable {
|
||||
UUID channel_id = new UUID(channel_id_msb, channel_id_lsb);
|
||||
|
||||
if (!receivedProtocolVersion && type != MESSAGE_PROTOCOL_VERSION) {
|
||||
Log.w("ConnectionMultiplexer", "Received invalid message '" + message+"'");
|
||||
Log.w("ConnectionMultiplexer", "'data_buffer:("+byteArrayToHexString(data)+") ");
|
||||
Log.w("ConnectionMultiplexer", "as string: '"+(new String(data, "UTF-8"))+"' ");
|
||||
|
||||
throw new IOException("Did not receive protocol version message!");
|
||||
}
|
||||
|
||||
@ -538,11 +553,15 @@ public final class ConnectionMultiplexer implements Closeable {
|
||||
public void run() {
|
||||
while (true) {
|
||||
synchronized (lock) {
|
||||
if (!open) return;
|
||||
if (!open) {
|
||||
Log.w("ConnectionMultiplexer", "connection not open, returning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
read_message();
|
||||
} catch (IOException e) {
|
||||
Log.w("ConnectionMultiplexer", "run caught IOException", e);
|
||||
handleException(e);
|
||||
return;
|
||||
}
|
||||
|
@ -494,4 +494,7 @@ public class LanLinkProvider extends BaseLinkProvider {
|
||||
return "LanLinkProvider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() { return 20; }
|
||||
|
||||
}
|
||||
|
@ -40,4 +40,7 @@ public class LoopbackLinkProvider extends BaseLinkProvider {
|
||||
public String getName() {
|
||||
return "LoopbackLinkProvider";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() { return 0; }
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
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.NotificationHelper;
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
|
||||
@ -82,7 +83,7 @@ public class BackgroundService extends Service {
|
||||
private void registerLinkProviders() {
|
||||
linkProviders.add(new LanLinkProvider(this));
|
||||
// linkProviders.add(new LoopbackLinkProvider(this));
|
||||
// linkProviders.add(new BluetoothLinkProvider(this));
|
||||
linkProviders.add(new BluetoothLinkProvider(this));
|
||||
}
|
||||
|
||||
public void onNetworkChange(@Nullable Network network) {
|
||||
|
@ -37,7 +37,10 @@ import org.kde.kdeconnect_tp.R;
|
||||
import java.io.IOException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -61,6 +64,7 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
private MultiValuedMap<String, String> pluginsByIncomingInterface = new ArrayListValuedHashMap<>();
|
||||
private final SharedPreferences settings;
|
||||
private final CopyOnWriteArrayList<PluginsChangedListener> pluginsChangedListeners = new CopyOnWriteArrayList<>();
|
||||
private String connectivityType;
|
||||
|
||||
public boolean supportsPacketType(String type) {
|
||||
if (deviceInfo.incomingCapabilities == null) {
|
||||
@ -70,6 +74,10 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
public String getConnectivityType() {
|
||||
return connectivityType;
|
||||
}
|
||||
|
||||
public interface PluginsChangedListener {
|
||||
void onPluginsChanged(@NonNull Device device);
|
||||
}
|
||||
@ -84,6 +92,7 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
this.deviceInfo = DeviceInfo.loadFromSettings(context, deviceId, settings);
|
||||
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.Paired);
|
||||
this.supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins()); // Assume all are supported until we receive capabilities
|
||||
this.connectivityType = "";
|
||||
Log.i("Device","Loading trusted device: " + deviceInfo.name);
|
||||
}
|
||||
|
||||
@ -98,6 +107,7 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
this.settings = context.getSharedPreferences(deviceInfo.id, Context.MODE_PRIVATE);
|
||||
this.pairingHandler = new PairingHandler(this, pairingCallback, PairingHandler.PairState.NotPaired);
|
||||
this.supportedPlugins = new Vector<>(PluginFactory.getAvailablePlugins()); // Assume all are supported until we receive capabilities
|
||||
this.connectivityType = link.getLinkProvider().getName();
|
||||
Log.i("Device","Creating untrusted device: "+ deviceInfo.name);
|
||||
addLink(link);
|
||||
}
|
||||
@ -302,7 +312,14 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
packetQueue = new DevicePacketQueue(this);
|
||||
}
|
||||
//FilesHelper.LogOpenFileCount();
|
||||
|
||||
links.add(link);
|
||||
|
||||
List linksToSort = Arrays.asList(links.toArray());
|
||||
Collections.sort(linksToSort, (Comparator<BaseLink>) (o1, o2) -> Integer.compare(o2.getLinkProvider().getPriority(), o1.getLinkProvider().getPriority()));
|
||||
links.clear();
|
||||
links.addAll(linksToSort);
|
||||
|
||||
link.addPacketReceiver(this);
|
||||
|
||||
boolean hasChanges = updateDeviceInfo(link.getDeviceInfo());
|
||||
|
@ -6,13 +6,17 @@
|
||||
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
@ -23,6 +27,7 @@ import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect_tp.BuildConfig;
|
||||
import org.slf4j.impl.HandroidLoggerAdapter;
|
||||
@ -62,6 +67,7 @@ public class KdeConnect extends Application {
|
||||
NotificationHelper.initializeChannels(this);
|
||||
LifecycleHelper.initializeObserver();
|
||||
loadRememberedDevicesFromSettings();
|
||||
|
||||
}
|
||||
|
||||
private void setupSL4JLogging() {
|
||||
|
@ -39,6 +39,7 @@ public class PairingDeviceItem implements ListAdapter.Item {
|
||||
final ListItemWithIconEntryBinding binding = ListItemWithIconEntryBinding.inflate(layoutInflater);
|
||||
|
||||
binding.listItemEntryIcon.setImageDrawable(device.getIcon());
|
||||
// binding.listItemEntryTitle.setText(device.getName() + " " + device.getConnectivityType());
|
||||
binding.listItemEntryTitle.setText(device.getName());
|
||||
|
||||
if (device.compareProtocolVersion() != 0) {
|
||||
|
@ -39,6 +39,7 @@ import org.kde.kdeconnect.UserInterface.About.AboutFragment
|
||||
import org.kde.kdeconnect.UserInterface.About.getApplicationAboutData
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityMainBinding
|
||||
import java.util.LinkedList
|
||||
|
||||
private const val MENU_ENTRY_ADD_DEVICE = 1 //0 means no-selection
|
||||
private const val MENU_ENTRY_SETTINGS = 2
|
||||
@ -195,14 +196,20 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
}
|
||||
}
|
||||
|
||||
val missingPermissions = mutableListOf<String>()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
val permissionResult = ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||
if (permissionResult != PackageManager.PERMISSION_GRANTED) {
|
||||
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.POST_NOTIFICATIONS)) {
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.POST_NOTIFICATIONS), RESULT_NOTIFICATIONS_ENABLED)
|
||||
missingPermissions.add(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(missingPermissions.size > 0){
|
||||
ActivityCompat.requestPermissions(this, missingPermissions.toTypedArray(), RESULT_NOTIFICATIONS_ENABLED)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
package org.kde.kdeconnect.UserInterface;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
@ -20,6 +22,7 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
@ -38,6 +41,7 @@ import org.kde.kdeconnect_tp.R;
|
||||
public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
public static final String KEY_UDP_BROADCAST_ENABLED = "udp_broadcast_enabled";
|
||||
public static final String KEY_BLUETOOTH_ENABLED = "bluetooth_enabled";
|
||||
public static final String KEY_APP_THEME = "theme_pref";
|
||||
|
||||
private EditTextPreference renameDevice;
|
||||
@ -178,6 +182,18 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
udpBroadcastDiscovery.setTitle(R.string.enable_udp_broadcast);
|
||||
screen.addPreference(udpBroadcastDiscovery);
|
||||
|
||||
final TwoStatePreference enableBluetoothSupport = new SwitchPreference(context);
|
||||
enableBluetoothSupport.setDefaultValue(false);
|
||||
enableBluetoothSupport.setKey(KEY_BLUETOOTH_ENABLED);
|
||||
enableBluetoothSupport.setTitle("Enable bluetooth (beta)");
|
||||
enableBluetoothSupport.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (boolean)newValue) {
|
||||
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN}, 2);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
screen.addPreference(enableBluetoothSupport);
|
||||
|
||||
// More settings text
|
||||
Preference moreSettingsText = new Preference(context);
|
||||
moreSettingsText.setPersistent(false);
|
||||
|
Loading…
x
Reference in New Issue
Block a user