diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ab737ba8..99b01fcb 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -17,8 +17,10 @@ SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted - - + + + + diff --git a/src/org/kde/kdeconnect/Backends/BaseLinkProvider.java b/src/org/kde/kdeconnect/Backends/BaseLinkProvider.java index bcb27316..4ef61cd7 100644 --- a/src/org/kde/kdeconnect/Backends/BaseLinkProvider.java +++ b/src/org/kde/kdeconnect/Backends/BaseLinkProvider.java @@ -55,4 +55,5 @@ public abstract class BaseLinkProvider { public abstract void onNetworkChange(@Nullable Network network); public abstract String getName(); + public abstract int getPriority(); } diff --git a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java index 673f2709..5a332702 100644 --- a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java +++ b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLink.java @@ -189,10 +189,4 @@ public class BluetoothLink extends BaseLink { return false; } } - - /* - public boolean isConnected() { - return socket.isConnected(); - } -*/ } diff --git a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLinkProvider.java b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLinkProvider.java index 9558f6d4..2f3f8e68 100644 --- a/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLinkProvider.java +++ b/src/org/kde/kdeconnect/Backends/BluetoothBackend/BluetoothLinkProvider.java @@ -2,7 +2,7 @@ * SPDX-FileCopyrightText: 2016 Saikrishna Arcot * * 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; } - visibleDevices.put(deviceId, link); + 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) { - sockets.remove(remoteAddress); - visibleDevices.remove(link.getDeviceId()); + + 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 { + 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 connectionThreads = new HashMap<>(); + } private class ClientRunnable extends BroadcastReceiver implements Runnable { private boolean continueProcessing = true; - private final Map 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 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()) { - Thread connectionThread = new Thread(new ClientConnect(device)); - connectionThread.start(); - connectionThreads.put(device, connectionThread); + synchronized (ClientRunnableSingleton.connectionThreads) { + if (!ClientRunnableSingleton.connectionThreads.containsKey(device) || !ClientRunnableSingleton.connectionThreads.get(device).isAlive()) { + Thread connectionThread = new Thread(new ClientConnect(device)); + connectionThread.start(); + 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(); - sockets.put(device, socket); + 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); + } } } } diff --git a/src/org/kde/kdeconnect/Backends/BluetoothBackend/ConnectionMultiplexer.java b/src/org/kde/kdeconnect/Backends/BluetoothBackend/ConnectionMultiplexer.java index 97244854..c8c9514c 100644 --- a/src/org/kde/kdeconnect/Backends/BluetoothBackend/ConnectionMultiplexer.java +++ b/src/org/kde/kdeconnect/Backends/BluetoothBackend/ConnectionMultiplexer.java @@ -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; } diff --git a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java index f1508f21..f616623f 100644 --- a/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java +++ b/src/org/kde/kdeconnect/Backends/LanBackend/LanLinkProvider.java @@ -494,4 +494,7 @@ public class LanLinkProvider extends BaseLinkProvider { return "LanLinkProvider"; } + @Override + public int getPriority() { return 20; } + } diff --git a/src/org/kde/kdeconnect/Backends/LoopbackBackend/LoopbackLinkProvider.java b/src/org/kde/kdeconnect/Backends/LoopbackBackend/LoopbackLinkProvider.java index 0d0c1796..3f613d50 100644 --- a/src/org/kde/kdeconnect/Backends/LoopbackBackend/LoopbackLinkProvider.java +++ b/src/org/kde/kdeconnect/Backends/LoopbackBackend/LoopbackLinkProvider.java @@ -40,4 +40,7 @@ public class LoopbackLinkProvider extends BaseLinkProvider { public String getName() { return "LoopbackLinkProvider"; } + + @Override + public int getPriority() { return 0; } } diff --git a/src/org/kde/kdeconnect/BackgroundService.java b/src/org/kde/kdeconnect/BackgroundService.java index 1fba468f..df9498f8 100644 --- a/src/org/kde/kdeconnect/BackgroundService.java +++ b/src/org/kde/kdeconnect/BackgroundService.java @@ -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) { diff --git a/src/org/kde/kdeconnect/Device.java b/src/org/kde/kdeconnect/Device.java index ceccb748..c96ff05b 100644 --- a/src/org/kde/kdeconnect/Device.java +++ b/src/org/kde/kdeconnect/Device.java @@ -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 pluginsByIncomingInterface = new ArrayListValuedHashMap<>(); private final SharedPreferences settings; private final CopyOnWriteArrayList 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) (o1, o2) -> Integer.compare(o2.getLinkProvider().getPriority(), o1.getLinkProvider().getPriority())); + links.clear(); + links.addAll(linksToSort); + link.addPacketReceiver(this); boolean hasChanges = updateDeviceInfo(link.getDeviceInfo()); diff --git a/src/org/kde/kdeconnect/KdeConnect.java b/src/org/kde/kdeconnect/KdeConnect.java index f760d8c1..8a21f751 100644 --- a/src/org/kde/kdeconnect/KdeConnect.java +++ b/src/org/kde/kdeconnect/KdeConnect.java @@ -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() { diff --git a/src/org/kde/kdeconnect/UserInterface/List/PairingDeviceItem.java b/src/org/kde/kdeconnect/UserInterface/List/PairingDeviceItem.java index 468c8bc7..a70c526d 100644 --- a/src/org/kde/kdeconnect/UserInterface/List/PairingDeviceItem.java +++ b/src/org/kde/kdeconnect/UserInterface/List/PairingDeviceItem.java @@ -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) { diff --git a/src/org/kde/kdeconnect/UserInterface/MainActivity.kt b/src/org/kde/kdeconnect/UserInterface/MainActivity.kt index 51516dab..b871e922 100644 --- a/src/org/kde/kdeconnect/UserInterface/MainActivity.kt +++ b/src/org/kde/kdeconnect/UserInterface/MainActivity.kt @@ -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() + 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() { diff --git a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java index fef0a600..a095e2a0 100644 --- a/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java +++ b/src/org/kde/kdeconnect/UserInterface/SettingsFragment.java @@ -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);