mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Refactor BackgroundService
Added a new KdeConnect Application class that holds the Devices now, while BackgroundService "only" takes care of the LinkProviders. Since KdeConnect subclasses Application we have the guarantee that it will exist as long as our process does, so we can use it as a singleton. This removes the "BackgroundService.RunCommand" hack (which sent an Intent that would awake BackgroundService in case it wasn't running already and then call our code in a callback). This saves lots of round trips between the system and us and makes things simpler (and stack traces useful) by making the code sequential. We already had an Application subclass that I moved to a new helper, which now the KdeConnect class initializes together with all the other helpers.
This commit is contained in:
parent
a6eea8e996
commit
ae23413971
@ -52,7 +52,7 @@
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:theme="@style/KdeConnectTheme.NoActionBar"
|
||||
android:name="org.kde.kdeconnect.MyApplication"
|
||||
android:name="org.kde.kdeconnect.KdeConnect"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
<receiver
|
||||
|
@ -53,11 +53,6 @@ public abstract class BaseLink {
|
||||
return linkProvider;
|
||||
}
|
||||
|
||||
//The daemon will periodically destroy unpaired links if this returns false
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPacketReceiver(PacketReceiver pr) {
|
||||
receivers.add(pr);
|
||||
}
|
||||
|
@ -186,11 +186,6 @@ public class BluetoothLink extends BaseLink {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
return receivingThread.isAlive();
|
||||
}
|
||||
|
||||
/*
|
||||
public boolean isConnected() {
|
||||
return socket.isConnected();
|
||||
|
@ -252,14 +252,4 @@ public class LanLink extends BaseLink {
|
||||
packetReceived(np);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean linkShouldBeKeptAlive() {
|
||||
|
||||
return true; //FIXME: Current implementation is broken, so for now we will keep links always established
|
||||
|
||||
//We keep the remotely initiated connections, since the remotes require them if they want to request
|
||||
//pairing to us, or connections that are already paired.
|
||||
//return (connectionSource == ConnectionStarted.Remotely);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -14,12 +14,12 @@ import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.UserInterface.CustomDevicesActivity;
|
||||
|
||||
@ -196,13 +196,13 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
|
||||
if (isDeviceTrusted && !SslHelper.isCertificateStored(context, deviceId)) {
|
||||
//Device paired with and old version, we can't use it as we lack the certificate
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
Device device = service.getDevice(deviceId);
|
||||
if (device == null) return;
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
device.unpair();
|
||||
//Retry as unpaired
|
||||
identityPacketReceived(identityPacket, socket, connectionStarted);
|
||||
});
|
||||
}
|
||||
|
||||
Log.i("KDE/LanLinkProvider", "Starting SSL handshake with " + identityPacket.getString("deviceName") + " trusted:" + isDeviceTrusted);
|
||||
@ -217,11 +217,11 @@ public class LanLinkProvider extends BaseLinkProvider implements LanLink.LinkDis
|
||||
addLink(identityPacket, sslsocket, connectionStarted);
|
||||
} catch (Exception e) {
|
||||
Log.e("KDE/LanLinkProvider", "Handshake as " + mode + " failed with " + identityPacket.getString("deviceName"), e);
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
Device device = service.getDevice(deviceId);
|
||||
if (device == null) return;
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
device.unpair();
|
||||
});
|
||||
}
|
||||
});
|
||||
//Handshake is blocking, so do it on another thread and free this thread to keep receiving new connection
|
||||
|
@ -14,34 +14,26 @@ import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper;
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper;
|
||||
import org.kde.kdeconnect.Helpers.ThreadHelper;
|
||||
import org.kde.kdeconnect.Plugins.ClibpoardPlugin.ClipboardFloatingActivity;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandActivity;
|
||||
import org.kde.kdeconnect.Plugins.RunCommandPlugin.RunCommandPlugin;
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.SendFileActivity;
|
||||
@ -49,92 +41,34 @@ import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
//import org.kde.kdeconnect.Backends.BluetoothBackend.BluetoothLinkProvider;
|
||||
|
||||
/*
|
||||
* This class (still) does 3 things:
|
||||
* - Keeps the app running by creating a foreground notification.
|
||||
* - Holds references to the active LinkProviders, but doesn't handle the DeviceLink those create (the KdeConnect class does that).
|
||||
* - Listens for network connectivity changes and tells the LinkProviders to re-check for devices.
|
||||
* It can be started by the KdeConnectBroadcastReceiver on some events or when the MainActivity is launched.
|
||||
*/
|
||||
public class BackgroundService extends Service {
|
||||
private static final int FOREGROUND_NOTIFICATION_ID = 1;
|
||||
|
||||
private static BackgroundService instance;
|
||||
|
||||
public interface DeviceListChangedCallback {
|
||||
void onDeviceListChanged(boolean isConnectedToNonCellularNetwork);
|
||||
}
|
||||
|
||||
public interface PluginCallback<T extends Plugin> {
|
||||
void run(T plugin);
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<String, DeviceListChangedCallback> deviceListChangedCallbacks = new ConcurrentHashMap<>();
|
||||
private KdeConnect applicationInstance;
|
||||
|
||||
private final ArrayList<BaseLinkProvider> linkProviders = new ArrayList<>();
|
||||
|
||||
private final ConcurrentHashMap<String, Device> devices = new ConcurrentHashMap<>();
|
||||
|
||||
private final HashSet<Object> discoveryModeAcquisitions = new HashSet<>();
|
||||
|
||||
public static BackgroundService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
boolean isConnectedToNonCellularNetwork; // True when connected over wifi/usb/bluetooth/(anything other than cellular)
|
||||
|
||||
private boolean acquireDiscoveryMode(Object key) {
|
||||
boolean wasEmpty = discoveryModeAcquisitions.isEmpty();
|
||||
discoveryModeAcquisitions.add(key);
|
||||
if (wasEmpty) {
|
||||
onNetworkChange();
|
||||
}
|
||||
//Log.e("acquireDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
|
||||
return wasEmpty;
|
||||
}
|
||||
|
||||
private void releaseDiscoveryMode(Object key) {
|
||||
boolean removed = discoveryModeAcquisitions.remove(key);
|
||||
//Log.e("releaseDiscoveryMode",key.getClass().getName() +" ["+discoveryModeAcquisitions.size()+"]");
|
||||
if (removed && discoveryModeAcquisitions.isEmpty()) {
|
||||
cleanDevices();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInDiscoveryMode() {
|
||||
//return !discoveryModeAcquisitions.isEmpty();
|
||||
return true; // Keep it always on for now
|
||||
}
|
||||
|
||||
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
||||
@Override
|
||||
public void incomingRequest() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingSuccessful() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingFailed(String error) {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpaired() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public void onDeviceListChanged() {
|
||||
for (DeviceListChangedCallback callback : deviceListChangedCallbacks.values()) {
|
||||
callback.onDeviceListChanged(isConnectedToNonCellularNetwork);
|
||||
// This indicates when connected over wifi/usb/bluetooth/(anything other than cellular)
|
||||
private final MutableLiveData<Boolean> connectedToNonCellularNetwork = new MutableLiveData<>();
|
||||
public LiveData<Boolean> isConnectedToNonCellularNetwork() {
|
||||
return connectedToNonCellularNetwork;
|
||||
}
|
||||
|
||||
public void updateForegroundNotification() {
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
//Update the foreground notification with the currently connected device list
|
||||
NotificationManager nm = ContextCompat.getSystemService(this, NotificationManager.class);
|
||||
@ -142,97 +76,14 @@ public class BackgroundService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void loadRememberedDevicesFromSettings() {
|
||||
//Log.e("BackgroundService", "Loading remembered trusted devices");
|
||||
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
Set<String> trustedDevices = preferences.getAll().keySet();
|
||||
for (String deviceId : trustedDevices) {
|
||||
//Log.e("BackgroundService", "Loading device "+deviceId);
|
||||
if (preferences.getBoolean(deviceId, false)) {
|
||||
Device device = new Device(this, deviceId);
|
||||
devices.put(deviceId, device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerLinkProviders() {
|
||||
linkProviders.add(new LanLinkProvider(this));
|
||||
// linkProviders.add(new LoopbackLinkProvider(this));
|
||||
// linkProviders.add(new BluetoothLinkProvider(this));
|
||||
}
|
||||
|
||||
public ArrayList<BaseLinkProvider> getLinkProviders() {
|
||||
return linkProviders;
|
||||
}
|
||||
|
||||
public Device getDevice(String id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
private void cleanDevices() {
|
||||
ThreadHelper.execute(() -> {
|
||||
for (Device d : devices.values()) {
|
||||
if (!d.isPaired() && !d.isPairRequested() && !d.isPairRequestedByPeer() && !d.deviceShouldBeKeptAlive()) {
|
||||
d.disconnect();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private final BaseLinkProvider.ConnectionReceiver deviceListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||
@Override
|
||||
public void onConnectionReceived(final NetworkPacket identityPacket, final BaseLink link) {
|
||||
|
||||
String deviceId = identityPacket.getString("deviceId");
|
||||
|
||||
Device device = devices.get(deviceId);
|
||||
|
||||
if (device != null) {
|
||||
Log.i("KDE/BackgroundService", "addLink, known device: " + deviceId);
|
||||
device.addLink(identityPacket, link);
|
||||
} else {
|
||||
Log.i("KDE/BackgroundService", "addLink,unknown device: " + deviceId);
|
||||
device = new Device(BackgroundService.this, identityPacket, link);
|
||||
if (device.isPaired() || device.isPairRequested() || device.isPairRequestedByPeer()
|
||||
|| link.linkShouldBeKeptAlive()
|
||||
|| isInDiscoveryMode()) {
|
||||
devices.put(deviceId, device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
} else {
|
||||
device.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(BaseLink link) {
|
||||
Device d = devices.get(link.getDeviceId());
|
||||
Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
||||
if (d != null) {
|
||||
d.removeLink(link);
|
||||
if (!d.isReachable() && !d.isPaired()) {
|
||||
//Log.e("onConnectionLost","Removing connection device because it was not paired");
|
||||
devices.remove(link.getDeviceId());
|
||||
d.removePairingCallback(devicePairingCallback);
|
||||
}
|
||||
} else {
|
||||
//Log.d("KDE/onConnectionLost","Removing connection to unknown device");
|
||||
}
|
||||
onDeviceListChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public ConcurrentHashMap<String, Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public void onNetworkChange() {
|
||||
Log.d("KDE/BackgroundService", "onNetworkChange");
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onNetworkChange();
|
||||
}
|
||||
@ -250,22 +101,14 @@ public class BackgroundService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
public void addDeviceListChangedCallback(String key, DeviceListChangedCallback callback) {
|
||||
deviceListChangedCallbacks.put(key, callback);
|
||||
}
|
||||
|
||||
public void removeDeviceListChangedCallback(String key) {
|
||||
deviceListChangedCallbacks.remove(key);
|
||||
}
|
||||
|
||||
//This will called only once, even if we launch the service intent several times
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Log.d("KdeConnect/BgService", "onCreate");
|
||||
instance = this;
|
||||
|
||||
DeviceHelper.initializeDeviceId(this);
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("BackgroundService", this::updateForegroundNotification);
|
||||
|
||||
// Register screen on listener
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||
@ -281,29 +124,19 @@ public class BackgroundService extends Service {
|
||||
cm.registerNetworkCallback(networkRequestBuilder.build(), new ConnectivityManager.NetworkCallback() {
|
||||
@Override
|
||||
public void onAvailable(Network network) {
|
||||
isConnectedToNonCellularNetwork = true;
|
||||
onDeviceListChanged();
|
||||
connectedToNonCellularNetwork.postValue(true);
|
||||
onNetworkChange();
|
||||
}
|
||||
@Override
|
||||
public void onLost(Network network) {
|
||||
isConnectedToNonCellularNetwork = false;
|
||||
onDeviceListChanged();
|
||||
connectedToNonCellularNetwork.postValue(false);
|
||||
}
|
||||
});
|
||||
|
||||
Log.i("KDE/BackgroundService", "Service not started yet, initializing...");
|
||||
applicationInstance = KdeConnect.getInstance();
|
||||
|
||||
PluginFactory.initPluginInfo(getBaseContext());
|
||||
initializeSecurityParameters();
|
||||
NotificationHelper.initializeChannels(this);
|
||||
loadRememberedDevicesFromSettings();
|
||||
migratePluginSettings();
|
||||
registerLinkProviders();
|
||||
|
||||
//Link Providers need to be already registered
|
||||
addConnectionListener(deviceListener);
|
||||
|
||||
addConnectionListener(applicationInstance.getConnectionListener()); // Link Providers need to be already registered
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStart();
|
||||
}
|
||||
@ -325,36 +158,11 @@ public class BackgroundService extends Service {
|
||||
return networkRequestBuilder;
|
||||
}
|
||||
|
||||
private void migratePluginSettings() {
|
||||
SharedPreferences globalPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
for (String pluginKey : PluginFactory.getAvailablePlugins()) {
|
||||
if (PluginFactory.getPluginInfo(pluginKey).supportsDeviceSpecificSettings()) {
|
||||
Iterator<Device> it = devices.values().iterator();
|
||||
|
||||
while (it.hasNext()) {
|
||||
Device device = it.next();
|
||||
Plugin plugin = PluginFactory.instantiatePluginForDevice(getBaseContext(), pluginKey, device);
|
||||
|
||||
if (plugin == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
plugin.copyGlobalToDeviceSpecificSettings(globalPrefs);
|
||||
if (!it.hasNext()) {
|
||||
plugin.removeSettings(globalPrefs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void changePersistentNotificationVisibility(boolean visible) {
|
||||
NotificationManager nm = ContextCompat.getSystemService(this, NotificationManager.class);
|
||||
if (visible) {
|
||||
nm.notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
updateForegroundNotification();
|
||||
} else {
|
||||
stopForeground(true);
|
||||
Stop();
|
||||
Start(this);
|
||||
}
|
||||
}
|
||||
@ -365,7 +173,7 @@ public class BackgroundService extends Service {
|
||||
|
||||
ArrayList<String> connectedDevices = new ArrayList<>();
|
||||
ArrayList<String> connectedDeviceIds = new ArrayList<>();
|
||||
for (Device device : getDevices().values()) {
|
||||
for (Device device : applicationInstance.getDevices().values()) {
|
||||
if (device.isReachable() && device.isPaired()) {
|
||||
connectedDeviceIds.add(device.getDeviceId());
|
||||
connectedDevices.add(device.getName());
|
||||
@ -409,7 +217,7 @@ public class BackgroundService extends Service {
|
||||
|
||||
if (connectedDeviceIds.size() == 1) {
|
||||
String deviceId = connectedDeviceIds.get(0);
|
||||
Device device = getDevice(deviceId);
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device != null) {
|
||||
// Adding two action buttons only when there is a single device connected.
|
||||
// Setting up Send File Intent.
|
||||
@ -432,49 +240,24 @@ public class BackgroundService extends Service {
|
||||
return notification.build();
|
||||
}
|
||||
|
||||
private void initializeSecurityParameters() {
|
||||
RsaHelper.initialiseRsaKeys(this);
|
||||
SslHelper.initialiseCertificate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
stopForeground(true);
|
||||
Log.d("KdeConnect/BgService", "onDestroy");
|
||||
for (BaseLinkProvider a : linkProviders) {
|
||||
a.onStop();
|
||||
}
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("BackgroundService");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new Binder();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
//To use the service from the gui
|
||||
|
||||
public interface InstanceCallback {
|
||||
void onServiceStart(BackgroundService service);
|
||||
}
|
||||
|
||||
private final static ArrayList<InstanceCallback> callbacks = new ArrayList<>();
|
||||
|
||||
private final static Lock mutex = new ReentrantLock(true);
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
//This will be called for each intent launch, even if the service is already started and it is reused
|
||||
mutex.lock();
|
||||
try {
|
||||
for (InstanceCallback c : callbacks) {
|
||||
c.onServiceStart(this);
|
||||
}
|
||||
callbacks.clear();
|
||||
} finally {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
Log.d("KDE/BackgroundService", "onStartCommand");
|
||||
if (NotificationHelper.isPersistentNotificationEnabled(this)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
|
||||
@ -482,43 +265,26 @@ public class BackgroundService extends Service {
|
||||
startForeground(FOREGROUND_NOTIFICATION_ID, createForegroundNotification());
|
||||
}
|
||||
}
|
||||
if (intent.hasExtra("refresh")) {
|
||||
onNetworkChange();
|
||||
}
|
||||
return Service.START_STICKY;
|
||||
}
|
||||
|
||||
private static void Start(Context c) {
|
||||
RunCommand(c, null);
|
||||
public static void Start(Context context) {
|
||||
Log.d("KDE/BackgroundService", "Start");
|
||||
ContextCompat.startForegroundService(context, new Intent(context, BackgroundService.class));
|
||||
}
|
||||
|
||||
public static void RunCommand(final Context c, final InstanceCallback callback) {
|
||||
ThreadHelper.execute(() -> {
|
||||
if (callback != null) {
|
||||
mutex.lock();
|
||||
try {
|
||||
callbacks.add(callback);
|
||||
} finally {
|
||||
mutex.unlock();
|
||||
}
|
||||
}
|
||||
ContextCompat.startForegroundService(c, new Intent(c, BackgroundService.class));
|
||||
});
|
||||
public static void ForceRefreshConnections(Context context) {
|
||||
Log.d("KDE/BackgroundService", "ForceRefreshConnections");
|
||||
Intent i = new Intent(context, BackgroundService.class);
|
||||
i.putExtra("refresh", true);
|
||||
ContextCompat.startForegroundService(context, i);
|
||||
}
|
||||
|
||||
public static <T extends Plugin> void RunWithPlugin(final Context c, final String deviceId, final Class<T> pluginClass, final PluginCallback<T> cb) {
|
||||
RunCommand(c, service -> {
|
||||
Device device = service.getDevice(deviceId);
|
||||
|
||||
if (device == null) {
|
||||
Log.e("BackgroundService", "Device " + deviceId + " not found");
|
||||
return;
|
||||
public void Stop() {
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
final T plugin = device.getPlugin(pluginClass);
|
||||
|
||||
if (plugin == null) {
|
||||
Log.e("BackgroundService", "Device " + device.getName() + " does not have plugin " + pluginClass.getName());
|
||||
return;
|
||||
}
|
||||
cb.run(plugin);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -883,22 +883,6 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean deviceShouldBeKeptAlive() {
|
||||
|
||||
SharedPreferences preferences = context.getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
if (preferences.contains(getDeviceId())) {
|
||||
//Log.e("DeviceShouldBeKeptAlive", "because it's a paired device");
|
||||
return true; //Already paired
|
||||
}
|
||||
|
||||
for (BaseLink l : links) {
|
||||
if (l.linkShouldBeKeptAlive()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<String> getSupportedPlugins() {
|
||||
return supportedPlugins;
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import android.os.Build;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import org.kde.kdeconnect.MyApplication;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class IntentHelper {
|
||||
@ -27,8 +26,8 @@ public class IntentHelper {
|
||||
* @param intent the Intent to be started
|
||||
* @param title a title which is shown in the notification on Android 10+
|
||||
*/
|
||||
public static void startActivityFromBackground(Context context, Intent intent, String title) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !MyApplication.isInForeground()) {
|
||||
public static void startActivityFromBackgroundOrCreateNotification(Context context, Intent intent, String title) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !LifecycleHelper.isInForeground()) {
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE);
|
||||
Notification notification = new NotificationCompat
|
||||
.Builder(context, NotificationHelper.Channels.HIGHPRIORITY)
|
||||
|
@ -1,15 +1,12 @@
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.app.Application;
|
||||
package org.kde.kdeconnect.Helpers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
public class LifecycleHelper {
|
||||
|
||||
public class MyApplication extends Application {
|
||||
private static class LifecycleObserver implements DefaultLifecycleObserver {
|
||||
private boolean inForeground = false;
|
||||
|
||||
@ -28,16 +25,13 @@ public class MyApplication extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
private static final LifecycleObserver foregroundTracker = new LifecycleObserver();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
ThemeUtil.setUserPreferredTheme(this);
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(foregroundTracker);
|
||||
}
|
||||
private final static LifecycleObserver foregroundTracker = new LifecycleObserver();
|
||||
|
||||
public static boolean isInForeground() {
|
||||
return foregroundTracker.isInForeground();
|
||||
}
|
||||
|
||||
public static void initializeObserver() {
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(foregroundTracker);
|
||||
}
|
||||
}
|
174
src/org/kde/kdeconnect/KdeConnect.java
Normal file
174
src/org/kde/kdeconnect/KdeConnect.java
Normal file
@ -0,0 +1,174 @@
|
||||
package org.kde.kdeconnect;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.LifecycleHelper;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
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.ThemeUtil;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/*
|
||||
* This class holds all the active devices and makes them accessible from every other class.
|
||||
* It also takes care of initializing all classes that need so when the app boots.
|
||||
* It provides a ConnectionReceiver that the BackgroundService uses to ping this class every time a new DeviceLink is created.
|
||||
*/
|
||||
public class KdeConnect extends Application {
|
||||
|
||||
public interface DeviceListChangedCallback {
|
||||
void onDeviceListChanged();
|
||||
}
|
||||
|
||||
private static KdeConnect instance = null;
|
||||
|
||||
private final ConcurrentHashMap<String, Device> devices = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, DeviceListChangedCallback> deviceListChangedCallbacks = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
instance = this;
|
||||
Log.d("KdeConnect/Application", "onCreate");
|
||||
ThemeUtil.setUserPreferredTheme(this);
|
||||
DeviceHelper.initializeDeviceId(this);
|
||||
RsaHelper.initialiseRsaKeys(this);
|
||||
SslHelper.initialiseCertificate(this);
|
||||
PluginFactory.initPluginInfo(this);
|
||||
NotificationHelper.initializeChannels(this);
|
||||
LifecycleHelper.initializeObserver();
|
||||
loadRememberedDevicesFromSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTerminate() {
|
||||
Log.d("KdeConnect/Application", "onTerminate");
|
||||
super.onTerminate();
|
||||
}
|
||||
|
||||
public void addDeviceListChangedCallback(String key, DeviceListChangedCallback callback) {
|
||||
deviceListChangedCallbacks.put(key, callback);
|
||||
}
|
||||
|
||||
public void removeDeviceListChangedCallback(String key) {
|
||||
deviceListChangedCallbacks.remove(key);
|
||||
}
|
||||
|
||||
private void onDeviceListChanged() {
|
||||
for (DeviceListChangedCallback callback : deviceListChangedCallbacks.values()) {
|
||||
callback.onDeviceListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, Device> getDevices() {
|
||||
return devices;
|
||||
}
|
||||
|
||||
public Device getDevice(String id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
return devices.get(id);
|
||||
}
|
||||
|
||||
public <T extends Plugin> T getDevicePlugin(String deviceId, Class<T> pluginClass) {
|
||||
if (deviceId == null) {
|
||||
return null;
|
||||
}
|
||||
Device device = devices.get(deviceId);
|
||||
if (device == null) {
|
||||
return null;
|
||||
}
|
||||
return device.getPlugin(pluginClass);
|
||||
}
|
||||
|
||||
public static KdeConnect getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private void loadRememberedDevicesFromSettings() {
|
||||
//Log.e("BackgroundService", "Loading remembered trusted devices");
|
||||
SharedPreferences preferences = getSharedPreferences("trusted_devices", Context.MODE_PRIVATE);
|
||||
Set<String> trustedDevices = preferences.getAll().keySet();
|
||||
for (String deviceId : trustedDevices) {
|
||||
//Log.e("BackgroundService", "Loading device "+deviceId);
|
||||
if (preferences.getBoolean(deviceId, false)) {
|
||||
Device device = new Device(this, deviceId);
|
||||
devices.put(deviceId, device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Device.PairingCallback devicePairingCallback = new Device.PairingCallback() {
|
||||
@Override
|
||||
public void incomingRequest() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingSuccessful() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pairingFailed(String error) {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unpaired() {
|
||||
onDeviceListChanged();
|
||||
}
|
||||
};
|
||||
|
||||
private final BaseLinkProvider.ConnectionReceiver connectionListener = new BaseLinkProvider.ConnectionReceiver() {
|
||||
@Override
|
||||
public void onConnectionReceived(final NetworkPacket identityPacket, final BaseLink link) {
|
||||
String deviceId = identityPacket.getString("deviceId");
|
||||
Device device = devices.get(deviceId);
|
||||
if (device != null) {
|
||||
Log.i("KDE/Application", "addLink, known device: " + deviceId);
|
||||
device.addLink(identityPacket, link);
|
||||
} else {
|
||||
Log.i("KDE/Application", "addLink,unknown device: " + deviceId);
|
||||
device = new Device(KdeConnect.this, identityPacket, link);
|
||||
devices.put(deviceId, device);
|
||||
device.addPairingCallback(devicePairingCallback);
|
||||
}
|
||||
onDeviceListChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(BaseLink link) {
|
||||
Device d = devices.get(link.getDeviceId());
|
||||
Log.i("KDE/onConnectionLost", "removeLink, deviceId: " + link.getDeviceId());
|
||||
if (d != null) {
|
||||
d.removeLink(link);
|
||||
if (!d.isReachable() && !d.isPaired()) {
|
||||
//Log.e("onConnectionLost","Removing connection device because it was not paired");
|
||||
devices.remove(link.getDeviceId());
|
||||
d.removePairingCallback(devicePairingCallback);
|
||||
}
|
||||
} else {
|
||||
//Log.d("KDE/onConnectionLost","Removing connection to unknown device");
|
||||
}
|
||||
onDeviceListChanged();
|
||||
}
|
||||
};
|
||||
public BaseLinkProvider.ConnectionReceiver getConnectionListener() {
|
||||
return connectionListener;
|
||||
}
|
||||
|
||||
}
|
@ -15,7 +15,6 @@ import android.util.Log;
|
||||
|
||||
public class KdeConnectBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
//Log.e("KdeConnect", "Broadcast event: "+intent.getAction());
|
||||
@ -25,9 +24,7 @@ public class KdeConnectBroadcastReceiver extends BroadcastReceiver {
|
||||
switch (action) {
|
||||
case Intent.ACTION_MY_PACKAGE_REPLACED:
|
||||
Log.i("KdeConnect", "MyUpdateReceiver");
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
|
||||
});
|
||||
BackgroundService.Start(context);
|
||||
break;
|
||||
case Intent.ACTION_PACKAGE_REPLACED:
|
||||
Log.i("KdeConnect", "UpdateReceiver");
|
||||
@ -35,27 +32,20 @@ public class KdeConnectBroadcastReceiver extends BroadcastReceiver {
|
||||
Log.i("KdeConnect", "Ignoring, it's not me!");
|
||||
return;
|
||||
}
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
|
||||
});
|
||||
BackgroundService.Start(context);
|
||||
break;
|
||||
case Intent.ACTION_BOOT_COMPLETED:
|
||||
Log.i("KdeConnect", "KdeConnectBroadcastReceiver");
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
|
||||
});
|
||||
BackgroundService.Start(context);
|
||||
break;
|
||||
case WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION:
|
||||
case WifiManager.WIFI_STATE_CHANGED_ACTION:
|
||||
case ConnectivityManager.CONNECTIVITY_ACTION:
|
||||
Log.i("KdeConnect", "Connection state changed, trying to connect");
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
service.onDeviceListChanged();
|
||||
service.onNetworkChange();
|
||||
});
|
||||
BackgroundService.ForceRefreshConnections(context);
|
||||
break;
|
||||
case Intent.ACTION_SCREEN_ON:
|
||||
BackgroundService.RunCommand(context, BackgroundService::onNetworkChange);
|
||||
BackgroundService.ForceRefreshConnections(context);
|
||||
break;
|
||||
default:
|
||||
Log.i("BroadcastReceiver", "Ignoring broadcast event: " + intent.getAction());
|
||||
|
@ -17,10 +17,9 @@ import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity;
|
||||
import org.kde.kdeconnect.UserInterface.PermissionsAlertDialogFragment;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityBigscreenBinding;
|
||||
|
||||
@ -49,7 +48,12 @@ public class BigscreenActivity extends AppCompatActivity {
|
||||
binding.micButton.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
BackgroundService.RunWithPlugin(this, deviceId, BigscreenPlugin.class, plugin -> runOnUiThread(() -> {
|
||||
BigscreenPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, BigscreenPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
binding.leftButton.setOnClickListener(v -> plugin.sendLeft());
|
||||
binding.rightButton.setOnClickListener(v -> plugin.sendRight());
|
||||
binding.upButton.setOnClickListener(v -> plugin.sendUp());
|
||||
@ -70,7 +74,6 @@ public class BigscreenActivity extends AppCompatActivity {
|
||||
.create().show(getSupportFragmentManager(), null);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
public void activateSTT() {
|
||||
@ -89,9 +92,12 @@ public class BigscreenActivity extends AppCompatActivity {
|
||||
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
|
||||
if (result.get(0) != null) {
|
||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
||||
BackgroundService.RunWithPlugin(this, deviceId, BigscreenPlugin.class, plugin ->
|
||||
runOnUiThread(() -> plugin.sendSTT(result.get(0)))
|
||||
);
|
||||
BigscreenPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, BigscreenPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendSTT(result.get(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.service.quicksettings.TileService
|
||||
import androidx.annotation.RequiresApi
|
||||
import org.kde.kdeconnect.BackgroundService
|
||||
import org.kde.kdeconnect.KdeConnect
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
class ClipboardTileService : TileService() {
|
||||
@ -20,12 +20,9 @@ class ClipboardTileService : TileService() {
|
||||
startActivityAndCollapse(Intent(this, ClipboardFloatingActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
var ids : List<String> = emptyList()
|
||||
val service = BackgroundService.getInstance()
|
||||
if (service != null) {
|
||||
ids = service.devices.values
|
||||
ids = KdeConnect.getInstance().devices.values
|
||||
.filter { it.isReachable && it.isPaired }
|
||||
.map { it.deviceId }
|
||||
}
|
||||
putExtra("connectedDeviceIds", ArrayList(ids))
|
||||
})
|
||||
}
|
||||
|
@ -12,8 +12,7 @@ import android.view.WindowManager;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityFindMyPhoneBinding;
|
||||
|
||||
import java.util.Objects;
|
||||
@ -21,7 +20,7 @@ import java.util.Objects;
|
||||
public class FindMyPhoneActivity extends AppCompatActivity {
|
||||
static final String EXTRA_DEVICE_ID = "deviceId";
|
||||
|
||||
private FindMyPhonePlugin plugin;
|
||||
String deviceId;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -39,8 +38,7 @@ public class FindMyPhoneActivity extends AppCompatActivity {
|
||||
finish();
|
||||
}
|
||||
|
||||
String deviceId = getIntent().getStringExtra(EXTRA_DEVICE_ID);
|
||||
plugin = BackgroundService.getInstance().getDevice(deviceId).getPlugin(FindMyPhonePlugin.class);
|
||||
deviceId = getIntent().getStringExtra(EXTRA_DEVICE_ID);
|
||||
|
||||
Window window = this.getWindow();
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
|
||||
@ -53,11 +51,10 @@ public class FindMyPhoneActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
/*
|
||||
For whatever reason when Android launches this activity as a SystemAlertWindow it calls:
|
||||
onCreate(), onStart(), onResume(), onStop(), onStart(), onResume().
|
||||
When using BackgroundService.RunWithPlugin we get into concurrency problems and sometimes no sound will be played
|
||||
*/
|
||||
FindMyPhonePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, FindMyPhonePlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
plugin.startPlaying();
|
||||
plugin.hideNotification();
|
||||
}
|
||||
@ -65,7 +62,10 @@ public class FindMyPhoneActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
FindMyPhonePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, FindMyPhonePlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
plugin.stopPlaying();
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
||||
import org.kde.kdeconnect.Helpers.LifecycleHelper;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.MyApplication;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
@ -107,7 +107,7 @@ public class FindMyPhonePlugin extends Plugin {
|
||||
|
||||
@Override
|
||||
public boolean onPacketReceived(NetworkPacket np) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || MyApplication.isInForeground()) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || LifecycleHelper.isInForeground()) {
|
||||
Intent intent = new Intent(context, FindMyPhoneActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(FindMyPhoneActivity.EXTRA_DEVICE_ID, device.getDeviceId());
|
||||
|
@ -5,7 +5,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
|
||||
public class FindMyPhoneReceiver extends BroadcastReceiver {
|
||||
final static String ACTION_FOUND_IT = "org.kde.kdeconnect.Plugins.FindMyPhonePlugin.foundIt";
|
||||
@ -29,7 +29,10 @@ public class FindMyPhoneReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
String deviceId = intent.getStringExtra(EXTRA_DEVICE_ID);
|
||||
|
||||
BackgroundService.RunWithPlugin(context, deviceId, FindMyPhonePlugin.class, FindMyPhonePlugin::stopPlaying);
|
||||
FindMyPhonePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, FindMyPhonePlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
plugin.stopPlaying();
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.accompanist.themeadapter.material3.Mdc3Theme
|
||||
import org.kde.kdeconnect.BackgroundService
|
||||
import org.kde.kdeconnect.KdeConnect
|
||||
import org.kde.kdeconnect.NetworkPacket
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTextButton
|
||||
import org.kde.kdeconnect.UserInterface.compose.KdeTextField
|
||||
@ -72,9 +72,12 @@ class ComposeSendActivity : AppCompatActivity() {
|
||||
} catch (e: Exception) {
|
||||
Log.e("KDE/ComposeSend", "Exception", e)
|
||||
}
|
||||
BackgroundService.RunWithPlugin(
|
||||
this, deviceId, MousePadPlugin::class.java
|
||||
) { plugin: MousePadPlugin -> plugin.sendKeyboardPacket(np) }
|
||||
val plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin::class.java)
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendKeyboardPacket(np);
|
||||
}
|
||||
|
||||
private fun sendComposed() {
|
||||
|
@ -14,7 +14,7 @@ import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
|
||||
public class KeyListenerView extends View {
|
||||
@ -89,7 +89,11 @@ public class KeyListenerView extends View {
|
||||
}
|
||||
|
||||
private void sendKeyPressPacket(final NetworkPacket np) {
|
||||
BackgroundService.RunWithPlugin(getContext(), deviceId, MousePadPlugin.class, plugin -> plugin.sendKeyboardPacket(np));
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
plugin.sendKeyboardPacket(np);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,10 +22,12 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
@ -115,7 +117,12 @@ public class MousePadActivity
|
||||
final float nX = X;
|
||||
final float nY = Y;
|
||||
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> plugin.sendMouseDelta(nX, nY));
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendMouseDelta(nX, nY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -235,24 +242,30 @@ public class MousePadActivity
|
||||
startActivity(intent);
|
||||
return true;
|
||||
} else if (id == R.id.menu_show_keyboard) {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> {
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
if (plugin.isKeyboardEnabled()) {
|
||||
showKeyboard();
|
||||
} else {
|
||||
Toast toast = Toast.makeText(this, R.string.mousepad_keyboard_input_not_supported, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else if (id == R.id.menu_open_compose_send) {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> {
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
if (plugin.isKeyboardEnabled()) {
|
||||
showCompose();
|
||||
} else {
|
||||
Toast toast = Toast.makeText(this, R.string.mousepad_keyboard_input_not_supported, Toast.LENGTH_SHORT);
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
@ -288,7 +301,12 @@ public class MousePadActivity
|
||||
mCurrentX = event.getX();
|
||||
mCurrentY = event.getY();
|
||||
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> {
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
float deltaX = (mCurrentX - mPrevX) * displayDpiMultiplier * mCurrentSensitivity;
|
||||
float deltaY = (mCurrentY - mPrevY) * displayDpiMultiplier * mCurrentSensitivity;
|
||||
|
||||
@ -300,8 +318,6 @@ public class MousePadActivity
|
||||
|
||||
mPrevX = mCurrentX;
|
||||
mPrevY = mCurrentY;
|
||||
});
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
@ -361,7 +377,12 @@ public class MousePadActivity
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
getWindow().getDecorView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendSingleHold);
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendSingleHold();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -388,7 +409,12 @@ public class MousePadActivity
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendDoubleClick);
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
plugin.sendDoubleClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -438,19 +464,39 @@ public class MousePadActivity
|
||||
|
||||
|
||||
private void sendLeftClick() {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendLeftClick);
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendLeftClick();
|
||||
}
|
||||
|
||||
private void sendMiddleClick() {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendMiddleClick);
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendMiddleClick();
|
||||
}
|
||||
|
||||
private void sendRightClick() {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, MousePadPlugin::sendRightClick);
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendRightClick();
|
||||
}
|
||||
|
||||
private void sendScroll(final float y) {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, MousePadPlugin.class, plugin -> plugin.sendScroll(0, y));
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendScroll(0, y);
|
||||
}
|
||||
|
||||
private void showKeyboard() {
|
||||
|
@ -19,11 +19,11 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.SafeTextChecker;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.UserInterface.List.EntryItemWithIcon;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivitySendkeystrokesBinding;
|
||||
|
||||
@ -89,7 +89,7 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
|
||||
// If we trust the sending app, check if there is only one device paired / reachable...
|
||||
if (contentIsOkay) {
|
||||
List<Device> reachableDevices = BackgroundService.getInstance().getDevices().values().stream()
|
||||
List<Device> reachableDevices = KdeConnect.getInstance().getDevices().values().stream()
|
||||
.filter(Device::isReachable)
|
||||
.limit(2) // we only need the first two; if its more than one, we need to show the user the device-selection
|
||||
.collect(Collectors.toList());
|
||||
@ -103,16 +103,9 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// subscribe to new connected devices
|
||||
BackgroundService.RunCommand(this, service -> {
|
||||
service.onNetworkChange();
|
||||
service.addDeviceListChangedCallback("SendKeystrokesToHostActivity", unused -> updateDeviceList());
|
||||
});
|
||||
|
||||
// list all currently connected devices
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("SendKeystrokesToHostActivity", () -> runOnUiThread(this::updateDeviceList));
|
||||
BackgroundService.ForceRefreshConnections(this); // force a network re-discover
|
||||
updateDeviceList();
|
||||
|
||||
} else {
|
||||
Toast.makeText(getApplicationContext(), R.string.sendkeystrokes_wrong_data, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
@ -122,7 +115,7 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
BackgroundService.RunCommand(this, service -> service.removeDeviceListChangedCallback("SendKeystrokesToHostActivity"));
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("SendKeystrokesToHostActivity");
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@ -131,7 +124,12 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
if (binding.textToSend.getText() != null && (toSend = binding.textToSend.getText().toString().trim()).length() > 0) {
|
||||
final NetworkPacket np = new NetworkPacket(MousePadPlugin.PACKET_TYPE_MOUSEPAD_REQUEST);
|
||||
np.set("key", toSend);
|
||||
BackgroundService.RunWithPlugin(this, deviceId.getDeviceId(), MousePadPlugin.class, plugin -> plugin.sendKeyboardPacket(np));
|
||||
MousePadPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId.getDeviceId(), MousePadPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendKeyboardPacket(np);
|
||||
Toast.makeText(
|
||||
getApplicationContext(),
|
||||
getString(R.string.sendkeystrokes_sent_text, toSend, deviceId.getName()),
|
||||
@ -143,9 +141,7 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
|
||||
|
||||
private void updateDeviceList() {
|
||||
BackgroundService.RunCommand(this, service -> {
|
||||
|
||||
Collection<Device> devices = service.getDevices().values();
|
||||
Collection<Device> devices = KdeConnect.getInstance().getDevices().values();
|
||||
final ArrayList<Device> devicesList = new ArrayList<>();
|
||||
final ArrayList<ListAdapter.Item> items = new ArrayList<>();
|
||||
|
||||
@ -159,14 +155,13 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
section.isEmpty = false;
|
||||
}
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
|
||||
binding.devicesList.setAdapter(new ListAdapter(SendKeystrokesToHostActivity.this, items));
|
||||
binding.devicesList.setOnItemClickListener((adapterView, view, i, l) -> {
|
||||
Device device = devicesList.get(i - 1); // NOTE: -1 because of the title!
|
||||
sendKeys(device);
|
||||
this.finish(); // close the activity
|
||||
});
|
||||
});
|
||||
|
||||
// only one device is connected and we trust the text to send -> send it and close the activity.
|
||||
// Usually we already check it in `onStart` - but if the BackgroundService was not started/connected to the host
|
||||
@ -177,7 +172,6 @@ public class SendKeystrokesToHostActivity extends AppCompatActivity {
|
||||
sendKeys(device);
|
||||
this.finish(); // close the activity
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
|
||||
/**
|
||||
* Called when the mpris media notification's buttons are pressed
|
||||
@ -29,7 +28,7 @@ public class MprisMediaNotificationReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
//First case: buttons send by other applications via the media session APIs
|
||||
//First case: buttons send by other applications via the media session APIs. They don't target a specific device.
|
||||
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
|
||||
//Route these buttons to the media session, which will handle them
|
||||
MediaSessionCompat mediaSession = MprisMediaSession.getMediaSession();
|
||||
@ -39,13 +38,10 @@ public class MprisMediaNotificationReceiver extends BroadcastReceiver {
|
||||
//Second case: buttons on the notification, which we created ourselves
|
||||
|
||||
//Get the correct device, the mpris plugin and the mpris player
|
||||
BackgroundService service = BackgroundService.getInstance();
|
||||
if (service == null) return;
|
||||
Device device = service.getDevice(intent.getStringExtra(EXTRA_DEVICE_ID));
|
||||
if (device == null) return;
|
||||
MprisPlugin mpris = device.getPlugin(MprisPlugin.class);
|
||||
if (mpris == null) return;
|
||||
MprisPlugin.MprisPlayer player = mpris.getPlayerStatus(intent.getStringExtra(EXTRA_MPRIS_PLAYER));
|
||||
String deviceId = intent.getStringExtra(EXTRA_DEVICE_ID);
|
||||
MprisPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MprisPlugin.class);
|
||||
if (plugin == null) return;
|
||||
MprisPlugin.MprisPlayer player = plugin.getPlayerStatus(intent.getStringExtra(EXTRA_MPRIS_PLAYER));
|
||||
if (player == null) return;
|
||||
|
||||
//Forward the action to the player
|
||||
@ -65,7 +61,9 @@ public class MprisMediaNotificationReceiver extends BroadcastReceiver {
|
||||
case ACTION_CLOSE_NOTIFICATION:
|
||||
//The user dismissed the notification: actually handle its removal correctly
|
||||
MprisMediaSession.getInstance().closeMediaNotification();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
@ -27,9 +28,9 @@ import androidx.core.app.TaskStackBuilder;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.media.app.NotificationCompat.MediaStyle;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.NotificationHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.Plugins.NotificationsPlugin.NotificationReceiver;
|
||||
import org.kde.kdeconnect.Plugins.SystemVolumePlugin.SystemVolumePlugin;
|
||||
import org.kde.kdeconnect.Plugins.SystemVolumePlugin.SystemVolumeProvider;
|
||||
@ -112,20 +113,20 @@ public class MprisMediaSession implements
|
||||
* <p>
|
||||
* Can be called multiple times, once for each device
|
||||
*
|
||||
* @param _context The context
|
||||
* @param mpris The mpris plugin
|
||||
* @param context The context
|
||||
* @param plugin The mpris plugin
|
||||
* @param device The device id
|
||||
*/
|
||||
public void onCreate(Context _context, MprisPlugin mpris, String device) {
|
||||
public void onCreate(Context context, MprisPlugin plugin, String device) {
|
||||
if (mprisDevices.isEmpty()) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(_context);
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
context = _context;
|
||||
this.context = context;
|
||||
mprisDevices.add(device);
|
||||
|
||||
mpris.setPlayerListUpdatedHandler("media_notification", this::updateMediaNotification);
|
||||
mpris.setPlayerStatusUpdatedHandler("media_notification", this::updateMediaNotification);
|
||||
plugin.setPlayerListUpdatedHandler("media_notification", this::updateMediaNotification);
|
||||
plugin.setPlayerStatusUpdatedHandler("media_notification", this::updateMediaNotification);
|
||||
|
||||
NotificationReceiver.RunCommand(context, service -> {
|
||||
|
||||
@ -137,8 +138,6 @@ public class MprisMediaSession implements
|
||||
onListenerConnected(service);
|
||||
}
|
||||
});
|
||||
|
||||
updateMediaNotification();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,13 +145,13 @@ public class MprisMediaSession implements
|
||||
* <p>
|
||||
* Can be called multiple times, once for each device
|
||||
*
|
||||
* @param mpris The mpris plugin
|
||||
* @param plugin The mpris plugin
|
||||
* @param device The device id
|
||||
*/
|
||||
public void onDestroy(MprisPlugin mpris, String device) {
|
||||
public void onDestroy(MprisPlugin plugin, String device) {
|
||||
mprisDevices.remove(device);
|
||||
mpris.removePlayerStatusUpdatedHandler("media_notification");
|
||||
mpris.removePlayerListUpdatedHandler("media_notification");
|
||||
plugin.removePlayerStatusUpdatedHandler("media_notification");
|
||||
plugin.removePlayerListUpdatedHandler("media_notification");
|
||||
updateMediaNotification();
|
||||
|
||||
if (mprisDevices.isEmpty()) {
|
||||
@ -166,21 +165,19 @@ public class MprisMediaSession implements
|
||||
* <p>
|
||||
* Prefers playing devices/mpris players, but tries to keep displaying the same
|
||||
* player and device, while possible.
|
||||
*
|
||||
* @param service The background service
|
||||
*/
|
||||
private void updateCurrentPlayer(BackgroundService service) {
|
||||
Pair<Device, MprisPlugin.MprisPlayer> player = findPlayer(service);
|
||||
private void updateCurrentPlayer() {
|
||||
Pair<Device, MprisPlugin.MprisPlayer> player = findPlayer();
|
||||
|
||||
//Update the last-displayed device and player
|
||||
notificationDevice = player.first == null ? null : player.first.getDeviceId();
|
||||
notificationPlayer = player.second;
|
||||
}
|
||||
|
||||
private Pair<Device, MprisPlugin.MprisPlayer> findPlayer(BackgroundService service) {
|
||||
private Pair<Device, MprisPlugin.MprisPlayer> findPlayer() {
|
||||
//First try the previously displayed player (if still playing) or the previous displayed device (otherwise)
|
||||
if (notificationDevice != null && mprisDevices.contains(notificationDevice)) {
|
||||
Device device = service.getDevice(notificationDevice);
|
||||
Device device = KdeConnect.getInstance().getDevice(notificationDevice);
|
||||
|
||||
MprisPlugin.MprisPlayer player;
|
||||
if (notificationPlayer != null && notificationPlayer.isPlaying()) {
|
||||
@ -194,7 +191,7 @@ public class MprisMediaSession implements
|
||||
}
|
||||
|
||||
// Try a different player from another device
|
||||
for (Device otherDevice : service.getDevices().values()) {
|
||||
for (Device otherDevice : KdeConnect.getInstance().getDevices().values()) {
|
||||
MprisPlugin.MprisPlayer player = getPlayerFromDevice(otherDevice, null);
|
||||
if (player != null) {
|
||||
return new Pair<>(otherDevice, player);
|
||||
@ -205,7 +202,7 @@ public class MprisMediaSession implements
|
||||
// This will succeed if it's paused:
|
||||
// that allows pausing and subsequently resuming via the notification
|
||||
if (notificationDevice != null && mprisDevices.contains(notificationDevice)) {
|
||||
Device device = service.getDevice(notificationDevice);
|
||||
Device device = KdeConnect.getInstance().getDevice(notificationDevice);
|
||||
|
||||
MprisPlugin.MprisPlayer player = getPlayerFromDevice(device, notificationPlayer);
|
||||
if (player != null) {
|
||||
@ -244,19 +241,20 @@ public class MprisMediaSession implements
|
||||
}
|
||||
|
||||
private void updateRemoteDeviceVolumeControl() {
|
||||
// Volume control feature is only available from Lollipop onwards
|
||||
BackgroundService.RunWithPlugin(context, notificationDevice, SystemVolumePlugin.class, plugin -> {
|
||||
SystemVolumePlugin plugin = KdeConnect.getInstance().getDevicePlugin(notificationDevice, SystemVolumePlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
SystemVolumeProvider systemVolumeProvider = SystemVolumeProvider.fromPlugin(plugin);
|
||||
systemVolumeProvider.addStateListener(this);
|
||||
systemVolumeProvider.startTrackingVolumeKeys();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the media control notification
|
||||
*/
|
||||
private void updateMediaNotification() {
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
|
||||
//If the user disabled the media notification, do not show it
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (!prefs.getBoolean(context.getString(R.string.mpris_notification_key), true)) {
|
||||
@ -264,8 +262,15 @@ public class MprisMediaSession implements
|
||||
return;
|
||||
}
|
||||
|
||||
if (mediaSession == null) {
|
||||
mediaSession = new MediaSessionCompat(context, MPRIS_MEDIA_SESSION_TAG);
|
||||
mediaSession.setCallback(mediaSessionCallback, new Handler(context.getMainLooper()));
|
||||
// Deprecated flags not required in Build.VERSION_CODES.O and later
|
||||
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||
}
|
||||
|
||||
//Make sure our information is up-to-date
|
||||
updateCurrentPlayer(service);
|
||||
updateCurrentPlayer();
|
||||
|
||||
//If the player disappeared (and no other playing one found), just remove the notification
|
||||
if (notificationPlayer == null) {
|
||||
@ -273,14 +278,6 @@ public class MprisMediaSession implements
|
||||
return;
|
||||
}
|
||||
|
||||
//Update the metadata and playback status
|
||||
if (mediaSession == null) {
|
||||
mediaSession = new MediaSessionCompat(context, MPRIS_MEDIA_SESSION_TAG);
|
||||
mediaSession.setCallback(mediaSessionCallback);
|
||||
// Deprecated flags not required in Build.VERSION_CODES.O and later
|
||||
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||
}
|
||||
|
||||
updateRemoteDeviceVolumeControl();
|
||||
|
||||
MediaMetadataCompat.Builder metadata = new MediaMetadataCompat.Builder();
|
||||
@ -317,41 +314,41 @@ public class MprisMediaSession implements
|
||||
}
|
||||
|
||||
//Create all actions (previous/play/pause/next)
|
||||
Intent iPlay = new Intent(service, MprisMediaNotificationReceiver.class);
|
||||
Intent iPlay = new Intent(context, MprisMediaNotificationReceiver.class);
|
||||
iPlay.setAction(MprisMediaNotificationReceiver.ACTION_PLAY);
|
||||
iPlay.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
|
||||
iPlay.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayer());
|
||||
PendingIntent piPlay = PendingIntent.getBroadcast(service, 0, iPlay, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
iPlay.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
|
||||
PendingIntent piPlay = PendingIntent.getBroadcast(context, 0, iPlay, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
NotificationCompat.Action.Builder aPlay = new NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_play_white, service.getString(R.string.mpris_play), piPlay);
|
||||
R.drawable.ic_play_white, context.getString(R.string.mpris_play), piPlay);
|
||||
|
||||
Intent iPause = new Intent(service, MprisMediaNotificationReceiver.class);
|
||||
Intent iPause = new Intent(context, MprisMediaNotificationReceiver.class);
|
||||
iPause.setAction(MprisMediaNotificationReceiver.ACTION_PAUSE);
|
||||
iPause.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
|
||||
iPause.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayer());
|
||||
PendingIntent piPause = PendingIntent.getBroadcast(service, 0, iPause, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
iPause.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
|
||||
PendingIntent piPause = PendingIntent.getBroadcast(context, 0, iPause, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
NotificationCompat.Action.Builder aPause = new NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_pause_white, service.getString(R.string.mpris_pause), piPause);
|
||||
R.drawable.ic_pause_white, context.getString(R.string.mpris_pause), piPause);
|
||||
|
||||
Intent iPrevious = new Intent(service, MprisMediaNotificationReceiver.class);
|
||||
Intent iPrevious = new Intent(context, MprisMediaNotificationReceiver.class);
|
||||
iPrevious.setAction(MprisMediaNotificationReceiver.ACTION_PREVIOUS);
|
||||
iPrevious.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
|
||||
iPrevious.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayer());
|
||||
PendingIntent piPrevious = PendingIntent.getBroadcast(service, 0, iPrevious, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
iPrevious.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
|
||||
PendingIntent piPrevious = PendingIntent.getBroadcast(context, 0, iPrevious, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
NotificationCompat.Action.Builder aPrevious = new NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_previous_white, service.getString(R.string.mpris_previous), piPrevious);
|
||||
R.drawable.ic_previous_white, context.getString(R.string.mpris_previous), piPrevious);
|
||||
|
||||
Intent iNext = new Intent(service, MprisMediaNotificationReceiver.class);
|
||||
Intent iNext = new Intent(context, MprisMediaNotificationReceiver.class);
|
||||
iNext.setAction(MprisMediaNotificationReceiver.ACTION_NEXT);
|
||||
iNext.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
|
||||
iNext.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayer());
|
||||
PendingIntent piNext = PendingIntent.getBroadcast(service, 0, iNext, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
iNext.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
|
||||
PendingIntent piNext = PendingIntent.getBroadcast(context, 0, iNext, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
NotificationCompat.Action.Builder aNext = new NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_next_white, service.getString(R.string.mpris_next), piNext);
|
||||
R.drawable.ic_next_white, context.getString(R.string.mpris_next), piNext);
|
||||
|
||||
Intent iOpenActivity = new Intent(service, MprisActivity.class);
|
||||
Intent iOpenActivity = new Intent(context, MprisActivity.class);
|
||||
iOpenActivity.putExtra("deviceId", notificationDevice);
|
||||
iOpenActivity.putExtra("player", notificationPlayer.getPlayer());
|
||||
iOpenActivity.putExtra("player", notificationPlayer.getPlayerName());
|
||||
|
||||
PendingIntent piOpenActivity = TaskStackBuilder.create(context)
|
||||
.addNextIntentWithParentStack(iOpenActivity)
|
||||
@ -364,9 +361,9 @@ public class MprisMediaSession implements
|
||||
.setContentIntent(piOpenActivity)
|
||||
.setSmallIcon(R.drawable.ic_play_white)
|
||||
.setShowWhen(false)
|
||||
.setColor(ContextCompat.getColor(service, R.color.primary))
|
||||
.setColor(ContextCompat.getColor(context, R.color.primary))
|
||||
.setVisibility(androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setSubText(service.getDevice(notificationDevice).getName());
|
||||
.setSubText(KdeConnect.getInstance().getDevice(notificationDevice).getName());
|
||||
|
||||
if (!notificationPlayer.getTitle().isEmpty()) {
|
||||
notification.setContentTitle(notificationPlayer.getTitle());
|
||||
@ -375,13 +372,13 @@ public class MprisMediaSession implements
|
||||
}
|
||||
//Only set the notification body text if we have an author and/or album
|
||||
if (!notificationPlayer.getArtist().isEmpty() && !notificationPlayer.getAlbum().isEmpty()) {
|
||||
notification.setContentText(notificationPlayer.getArtist() + " - " + notificationPlayer.getAlbum() + " (" + notificationPlayer.getPlayer() + ")");
|
||||
notification.setContentText(notificationPlayer.getArtist() + " - " + notificationPlayer.getAlbum() + " (" + notificationPlayer.getPlayerName() + ")");
|
||||
} else if (!notificationPlayer.getArtist().isEmpty()) {
|
||||
notification.setContentText(notificationPlayer.getArtist() + " (" + notificationPlayer.getPlayer() + ")");
|
||||
notification.setContentText(notificationPlayer.getArtist() + " (" + notificationPlayer.getPlayerName() + ")");
|
||||
} else if (!notificationPlayer.getAlbum().isEmpty()) {
|
||||
notification.setContentText(notificationPlayer.getAlbum() + " (" + notificationPlayer.getPlayer() + ")");
|
||||
notification.setContentText(notificationPlayer.getAlbum() + " (" + notificationPlayer.getPlayerName() + ")");
|
||||
} else {
|
||||
notification.setContentText(notificationPlayer.getPlayer());
|
||||
notification.setContentText(notificationPlayer.getPlayerName());
|
||||
}
|
||||
|
||||
if (albumArt != null) {
|
||||
@ -389,11 +386,11 @@ public class MprisMediaSession implements
|
||||
}
|
||||
|
||||
if (!notificationPlayer.isPlaying()) {
|
||||
Intent iCloseNotification = new Intent(service, MprisMediaNotificationReceiver.class);
|
||||
Intent iCloseNotification = new Intent(context, MprisMediaNotificationReceiver.class);
|
||||
iCloseNotification.setAction(MprisMediaNotificationReceiver.ACTION_CLOSE_NOTIFICATION);
|
||||
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_DEVICE_ID, notificationDevice);
|
||||
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayer());
|
||||
PendingIntent piCloseNotification = PendingIntent.getBroadcast(service, 0, iCloseNotification, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
iCloseNotification.putExtra(MprisMediaNotificationReceiver.EXTRA_MPRIS_PLAYER, notificationPlayer.getPlayerName());
|
||||
PendingIntent piCloseNotification = PendingIntent.getBroadcast(context, 0, iCloseNotification, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
notification.setDeleteIntent(piCloseNotification);
|
||||
}
|
||||
|
||||
@ -449,7 +446,6 @@ public class MprisMediaSession implements
|
||||
mediaSession.setActive(true);
|
||||
final NotificationManager nm = ContextCompat.getSystemService(context, NotificationManager.class);
|
||||
nm.notify(MPRIS_MEDIA_NOTIFICATION_ID, notification.build());
|
||||
});
|
||||
}
|
||||
|
||||
public void closeMediaNotification() {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.kde.kdeconnect.Plugins.MprisPlugin;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
@ -10,7 +9,6 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@ -28,12 +26,9 @@ import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.kde.kdeconnect.Backends.BaseLink;
|
||||
import org.kde.kdeconnect.Backends.BaseLinkProvider;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Helpers.VideoUrlsHelper;
|
||||
import org.kde.kdeconnect.Helpers.VolumeHelperKt;
|
||||
import org.kde.kdeconnect.NetworkPacket;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.MprisControlBinding;
|
||||
import org.kde.kdeconnect_tp.databinding.MprisNowPlayingBinding;
|
||||
@ -49,18 +44,9 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
private MprisNowPlayingBinding activityMprisBinding;
|
||||
private String deviceId;
|
||||
private Runnable positionSeekUpdateRunnable = null;
|
||||
|
||||
private String targetPlayerName = "";
|
||||
private MprisPlugin.MprisPlayer targetPlayer = null;
|
||||
private final BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() {
|
||||
@Override
|
||||
public void onConnectionReceived(NetworkPacket identityPacket, BaseLink link) {
|
||||
connectToPlugin(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionLost(BaseLink link) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
public static MprisNowPlayingFragment newInstance(String deviceId) {
|
||||
MprisNowPlayingFragment mprisNowPlayingFragment = new MprisNowPlayingFragment();
|
||||
@ -94,32 +80,27 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
if (activityMprisBinding == null) {
|
||||
activityMprisBinding = MprisNowPlayingBinding.inflate(inflater);
|
||||
mprisControlBinding = activityMprisBinding.mprisControl;
|
||||
|
||||
String targetPlayerName = "";
|
||||
Intent activityIntent = requireActivity().getIntent();
|
||||
activityIntent.getStringExtra("player");
|
||||
activityIntent.removeExtra("player");
|
||||
deviceId = requireArguments().getString(MprisPlugin.DEVICE_ID_KEY);
|
||||
|
||||
if (TextUtils.isEmpty(targetPlayerName)) {
|
||||
if (savedInstanceState != null) {
|
||||
targetPlayerName = "";
|
||||
Intent activityIntent = requireActivity().getIntent();
|
||||
if (activityIntent.hasExtra("player")) {
|
||||
targetPlayerName = activityIntent.getStringExtra("player");
|
||||
activityIntent.removeExtra("player");
|
||||
} else if (savedInstanceState != null) {
|
||||
targetPlayerName = savedInstanceState.getString("targetPlayer");
|
||||
}
|
||||
}
|
||||
|
||||
deviceId = requireArguments().getString(MprisPlugin.DEVICE_ID_KEY);
|
||||
connectToPlugin();
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(requireContext());
|
||||
String interval_time_str = prefs.getString(getString(R.string.mpris_time_key),
|
||||
getString(R.string.mpris_time_default));
|
||||
final int interval_time = Integer.parseInt(interval_time_str);
|
||||
|
||||
BackgroundService.RunCommand(requireContext(), service -> service.addConnectionListener(connectionReceiver));
|
||||
connectToPlugin(targetPlayerName);
|
||||
|
||||
performActionOnClick(mprisControlBinding.loopButton, p -> {
|
||||
switch (p.getLoopStatus()) {
|
||||
case "None":
|
||||
@ -159,23 +140,18 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(final SeekBar seekBar) {
|
||||
BackgroundService.RunCommand(requireContext(), service -> {
|
||||
if (targetPlayer == null) return;
|
||||
targetPlayer.setVolume(seekBar.getProgress());
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
positionSeekUpdateRunnable = () -> {
|
||||
Context context = getContext();
|
||||
if (context == null) return; // Fragment was already detached
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
if (!isAdded()) return; // Fragment was already detached
|
||||
if (targetPlayer != null) {
|
||||
mprisControlBinding.positionSeek.setProgress((int) (targetPlayer.getPosition()));
|
||||
}
|
||||
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable);
|
||||
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 1000);
|
||||
});
|
||||
};
|
||||
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200);
|
||||
|
||||
@ -192,36 +168,54 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(final SeekBar seekBar) {
|
||||
BackgroundService.RunCommand(requireContext(), service -> {
|
||||
if (targetPlayer != null) {
|
||||
targetPlayer.setPosition(seekBar.getProgress());
|
||||
}
|
||||
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
mprisControlBinding.nowPlayingTextview.setSelected(true);
|
||||
|
||||
}
|
||||
|
||||
return activityMprisBinding.getRoot();
|
||||
}
|
||||
|
||||
private void connectToPlugin(final String targetPlayerName) {
|
||||
BackgroundService.RunWithPlugin(requireContext(), deviceId, MprisPlugin.class, mpris -> {
|
||||
targetPlayer = mpris.getPlayerStatus(targetPlayerName);
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
disconnectFromPlugin();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
mpris.setPlayerStatusUpdatedHandler("activity", () -> requireActivity().runOnUiThread(() -> updatePlayerStatus(mpris)));
|
||||
mpris.setPlayerListUpdatedHandler("activity", () -> {
|
||||
final List<String> playerList = mpris.getPlayerList();
|
||||
private void disconnectFromPlugin() {
|
||||
MprisPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MprisPlugin.class);
|
||||
if (plugin != null) {
|
||||
plugin.removePlayerListUpdatedHandler("activity");
|
||||
plugin.removePlayerStatusUpdatedHandler("activity");
|
||||
}
|
||||
}
|
||||
|
||||
private void connectToPlugin() {
|
||||
MprisPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, MprisPlugin.class);
|
||||
if (plugin == null) {
|
||||
if (isAdded()) {
|
||||
requireActivity().finish();
|
||||
}
|
||||
return;
|
||||
}
|
||||
targetPlayer = plugin.getPlayerStatus(targetPlayerName);
|
||||
|
||||
plugin.setPlayerStatusUpdatedHandler("activity", () -> requireActivity().runOnUiThread(() -> {
|
||||
updatePlayerStatus(plugin);
|
||||
}));
|
||||
plugin.setPlayerListUpdatedHandler("activity", () -> requireActivity().runOnUiThread(() -> {
|
||||
final List<String> playerList = plugin.getPlayerList();
|
||||
final ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(),
|
||||
android.R.layout.simple_spinner_item,
|
||||
playerList.toArray(ArrayUtils.EMPTY_STRING_ARRAY)
|
||||
);
|
||||
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
|
||||
mprisControlBinding.playerSpinner.setAdapter(adapter);
|
||||
|
||||
if (playerList.isEmpty()) {
|
||||
@ -240,11 +234,12 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
if (pos >= playerList.size()) return;
|
||||
|
||||
String player = playerList.get(pos);
|
||||
if (targetPlayer != null && player.equals(targetPlayer.getPlayer())) {
|
||||
if (targetPlayer != null && player.equals(targetPlayer.getPlayerName())) {
|
||||
return; //Player hasn't actually changed
|
||||
}
|
||||
targetPlayer = mpris.getPlayerStatus(player);
|
||||
updatePlayerStatus(mpris);
|
||||
targetPlayer = plugin.getPlayerStatus(player);
|
||||
targetPlayerName = targetPlayer.getPlayerName();
|
||||
updatePlayerStatus(plugin);
|
||||
|
||||
if (targetPlayer != null && targetPlayer.isPlaying()) {
|
||||
MprisMediaSession.getInstance().playerSelected(targetPlayer);
|
||||
@ -259,11 +254,11 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
|
||||
if (targetPlayer == null) {
|
||||
//If no player is selected, try to select a playing player
|
||||
targetPlayer = mpris.getPlayingPlayer();
|
||||
targetPlayer = plugin.getPlayingPlayer();
|
||||
}
|
||||
//Try to select the specified player
|
||||
if (targetPlayer != null) {
|
||||
int targetIndex = adapter.getPosition(targetPlayer.getPlayer());
|
||||
int targetIndex = adapter.getPosition(targetPlayer.getPlayerName());
|
||||
if (targetIndex >= 0) {
|
||||
mprisControlBinding.playerSpinner.setSelection(targetIndex);
|
||||
} else {
|
||||
@ -272,33 +267,30 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
}
|
||||
//If no player selected, select the first one (if any)
|
||||
if (targetPlayer == null && !playerList.isEmpty()) {
|
||||
targetPlayer = mpris.getPlayerStatus(playerList.get(0));
|
||||
targetPlayer = plugin.getPlayerStatus(playerList.get(0));
|
||||
mprisControlBinding.playerSpinner.setSelection(0);
|
||||
}
|
||||
updatePlayerStatus(mpris);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
BackgroundService.RunCommand(requireContext(), service -> service.removeConnectionListener(connectionReceiver));
|
||||
}
|
||||
|
||||
private void performActionOnClick(View v, MprisPlayerCallback l) {
|
||||
v.setOnClickListener(view -> BackgroundService.RunCommand(requireContext(), service -> {
|
||||
if (targetPlayer == null) return;
|
||||
l.performAction(targetPlayer);
|
||||
updatePlayerStatus(plugin);
|
||||
}));
|
||||
}
|
||||
|
||||
private void updatePlayerStatus(MprisPlugin mpris) {
|
||||
private void performActionOnClick(View v, MprisPlayerCallback l) {
|
||||
v.setOnClickListener(view -> {
|
||||
if (targetPlayer == null) return;
|
||||
l.performAction(targetPlayer);
|
||||
});
|
||||
}
|
||||
|
||||
private void updatePlayerStatus(MprisPlugin plugin) {
|
||||
if (!isAdded()) {
|
||||
//Fragment is not attached to an activity. We will crash if we try to do anything here.
|
||||
return;
|
||||
}
|
||||
|
||||
MprisPlugin.MprisPlayer playerStatus = targetPlayer;
|
||||
if (playerStatus == null) {
|
||||
//No player with that name found, just display "empty" data
|
||||
playerStatus = mpris.getEmptyPlayer();
|
||||
playerStatus = plugin.getEmptyPlayer();
|
||||
}
|
||||
String song = playerStatus.getCurrentSong();
|
||||
|
||||
@ -433,7 +425,7 @@ public class MprisNowPlayingFragment extends Fragment implements VolumeKeyListen
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
if (targetPlayer != null) {
|
||||
outState.putString("targetPlayer", targetPlayer.getPlayer());
|
||||
outState.putString("targetPlayer", targetPlayerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
@ -77,12 +75,12 @@ public class MprisPlugin extends Plugin {
|
||||
return album;
|
||||
}
|
||||
|
||||
public String getPlayer() {
|
||||
public String getPlayerName() {
|
||||
return player;
|
||||
}
|
||||
|
||||
boolean isSpotify() {
|
||||
return getPlayer().equalsIgnoreCase("spotify");
|
||||
return getPlayerName().equalsIgnoreCase("spotify");
|
||||
}
|
||||
|
||||
public String getLoopStatus() {
|
||||
@ -165,55 +163,55 @@ public class MprisPlugin extends Plugin {
|
||||
|
||||
public void playPause() {
|
||||
if (isPauseAllowed() || isPlayAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "action", "PlayPause");
|
||||
sendCommand(getPlayerName(), "action", "PlayPause");
|
||||
}
|
||||
}
|
||||
|
||||
public void play() {
|
||||
if (isPlayAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "action", "Play");
|
||||
sendCommand(getPlayerName(), "action", "Play");
|
||||
}
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
if (isPauseAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "action", "Pause");
|
||||
sendCommand(getPlayerName(), "action", "Pause");
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "action", "Stop");
|
||||
sendCommand(getPlayerName(), "action", "Stop");
|
||||
}
|
||||
|
||||
public void previous() {
|
||||
if (isGoPreviousAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "action", "Previous");
|
||||
sendCommand(getPlayerName(), "action", "Previous");
|
||||
}
|
||||
}
|
||||
|
||||
public void next() {
|
||||
if (isGoNextAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "action", "Next");
|
||||
sendCommand(getPlayerName(), "action", "Next");
|
||||
}
|
||||
}
|
||||
|
||||
public void setLoopStatus(String loopStatus) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "setLoopStatus", loopStatus);
|
||||
sendCommand(getPlayerName(), "setLoopStatus", loopStatus);
|
||||
}
|
||||
|
||||
public void setShuffle(boolean shuffle) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "setShuffle", shuffle);
|
||||
sendCommand(getPlayerName(), "setShuffle", shuffle);
|
||||
}
|
||||
|
||||
public void setVolume(int volume) {
|
||||
if (isSetVolumeAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "setVolume", volume);
|
||||
sendCommand(getPlayerName(), "setVolume", volume);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPosition(int position) {
|
||||
if (isSeekAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "SetPosition", position);
|
||||
sendCommand(getPlayerName(), "SetPosition", position);
|
||||
|
||||
lastPosition = position;
|
||||
lastPositionTime = System.currentTimeMillis();
|
||||
@ -222,7 +220,7 @@ public class MprisPlugin extends Plugin {
|
||||
|
||||
public void seek(int offset) {
|
||||
if (isSeekAllowed()) {
|
||||
MprisPlugin.this.sendCommand(getPlayer(), "Seek", offset);
|
||||
sendCommand(getPlayerName(), "Seek", offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -522,7 +520,7 @@ public class MprisPlugin extends Plugin {
|
||||
|
||||
if (player.albumArtUrl.equals(url)) {
|
||||
NetworkPacket np = new NetworkPacket(PACKET_TYPE_MPRIS_REQUEST);
|
||||
np.set("player", player.getPlayer());
|
||||
np.set("player", player.getPlayerName());
|
||||
np.set("albumArtUrl", url);
|
||||
device.sendPacket(np);
|
||||
return true;
|
||||
|
@ -9,7 +9,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -58,13 +58,17 @@ public class PhotoActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
BackgroundService.RunWithPlugin(this, getIntent().getStringExtra("deviceId"), PhotoPlugin.class, plugin -> {
|
||||
String deviceId = getIntent().getStringExtra("deviceId");
|
||||
PhotoPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, PhotoPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (resultCode == -1) {
|
||||
plugin.sendPhoto(photoURI);
|
||||
} else {
|
||||
plugin.sendCancel();
|
||||
}
|
||||
});
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,7 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.media.VolumeProviderCompat;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityPresenterBinding;
|
||||
|
||||
@ -90,16 +89,14 @@ public class PresenterActivity extends AppCompatActivity implements SensorEventL
|
||||
Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
final String deviceId = getIntent().getStringExtra("deviceId");
|
||||
String deviceId = getIntent().getStringExtra("deviceId");
|
||||
this.plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, PresenterPlugin.class);
|
||||
|
||||
BackgroundService.RunWithPlugin(this, deviceId, PresenterPlugin.class, plugin -> runOnUiThread(() -> {
|
||||
this.plugin = plugin;
|
||||
binding.nextButton.setOnClickListener(v -> plugin.sendNext());
|
||||
binding.previousButton.setOnClickListener(v -> plugin.sendPrevious());
|
||||
if (plugin.isPointerSupported()) {
|
||||
enablePointer();
|
||||
}
|
||||
}));
|
||||
}
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
|
@ -23,9 +23,8 @@ import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityRunCommandBinding;
|
||||
|
||||
@ -38,11 +37,16 @@ import java.util.Objects;
|
||||
public class RunCommandActivity extends AppCompatActivity {
|
||||
private ActivityRunCommandBinding binding;
|
||||
private String deviceId;
|
||||
private final RunCommandPlugin.CommandsChangedCallback commandsChangedCallback = this::updateView;
|
||||
private final RunCommandPlugin.CommandsChangedCallback commandsChangedCallback = () -> runOnUiThread(this::updateView);
|
||||
private List<CommandEntry> commandItems;
|
||||
|
||||
private void updateView() {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, RunCommandPlugin.class, plugin -> runOnUiThread(() -> {
|
||||
RunCommandPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, RunCommandPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
registerForContextMenu(binding.runCommandsList);
|
||||
|
||||
commandItems = new ArrayList<>();
|
||||
@ -69,7 +73,6 @@ public class RunCommandActivity extends AppCompatActivity {
|
||||
}
|
||||
binding.addComandExplanation.setText(text);
|
||||
binding.addComandExplanation.setVisibility(commandItems.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,27 +87,22 @@ public class RunCommandActivity extends AppCompatActivity {
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
deviceId = getIntent().getStringExtra("deviceId");
|
||||
|
||||
boolean canAddCommands = false;
|
||||
try {
|
||||
canAddCommands = BackgroundService.getInstance().getDevice(deviceId).getPlugin(RunCommandPlugin.class).canAddCommand();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
if (canAddCommands) {
|
||||
RunCommandPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId,RunCommandPlugin.class);
|
||||
if (plugin != null) {
|
||||
if (plugin.canAddCommand()) {
|
||||
binding.addCommandButton.show();
|
||||
} else {
|
||||
binding.addCommandButton.hide();
|
||||
}
|
||||
|
||||
binding.addCommandButton.setOnClickListener(v -> BackgroundService.RunWithPlugin(RunCommandActivity.this, deviceId, RunCommandPlugin.class, plugin -> {
|
||||
binding.addCommandButton.setOnClickListener(v -> {
|
||||
plugin.sendSetupPacket();
|
||||
new AlertDialog.Builder(RunCommandActivity.this)
|
||||
.setTitle(R.string.add_command)
|
||||
.setMessage(R.string.add_command_description)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}));
|
||||
});
|
||||
}
|
||||
updateView();
|
||||
}
|
||||
|
||||
@ -134,14 +132,23 @@ public class RunCommandActivity extends AppCompatActivity {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
BackgroundService.RunWithPlugin(this, deviceId, RunCommandPlugin.class, plugin -> plugin.addCommandsUpdatedCallback(commandsChangedCallback));
|
||||
RunCommandPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, RunCommandPlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.addCommandsUpdatedCallback(commandsChangedCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
BackgroundService.RunWithPlugin(this, deviceId, RunCommandPlugin.class, plugin -> plugin.removeCommandsUpdatedCallback(commandsChangedCallback));
|
||||
RunCommandPlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, RunCommandPlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
plugin.removeCommandsUpdatedCallback(commandsChangedCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,8 +23,8 @@ import io.reactivex.Flowable
|
||||
import io.reactivex.processors.ReplayProcessor
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.kde.kdeconnect.BackgroundService
|
||||
import org.kde.kdeconnect.Device
|
||||
import org.kde.kdeconnect.KdeConnect
|
||||
import org.kde.kdeconnect.UserInterface.MainActivity
|
||||
import org.kde.kdeconnect_tp.R
|
||||
import org.reactivestreams.FlowAdapters
|
||||
@ -92,12 +92,10 @@ class RunCommandControlsProviderService : ControlsProviderService() {
|
||||
if (action is CommandAction) {
|
||||
val commandEntry = getCommandByControlId(controlId)
|
||||
if (commandEntry != null) {
|
||||
val plugin = BackgroundService.getInstance().getDevice(controlId.split(":")[0]).getPlugin(RunCommandPlugin::class.java)
|
||||
val deviceId = controlId.split(":")[0]
|
||||
val plugin = KdeConnect.getInstance().getDevicePlugin(deviceId ,RunCommandPlugin::class.java)
|
||||
if (plugin != null) {
|
||||
BackgroundService.RunCommand(this) {
|
||||
plugin.runCommand(commandEntry.key)
|
||||
}
|
||||
|
||||
consumer.accept(ControlAction.RESPONSE_OK)
|
||||
} else {
|
||||
consumer.accept(ControlAction.RESPONSE_FAIL)
|
||||
@ -141,9 +139,7 @@ class RunCommandControlsProviderService : ControlsProviderService() {
|
||||
private fun getAllCommandsList(): List<CommandEntryWithDevice> {
|
||||
val commandList = mutableListOf<CommandEntryWithDevice>()
|
||||
|
||||
val service = BackgroundService.getInstance() ?: return commandList
|
||||
|
||||
for (device in service.devices.values) {
|
||||
for (device in KdeConnect.getInstance().devices.values) {
|
||||
if (!device.isReachable) {
|
||||
commandList.addAll(getSavedCommandsList(device))
|
||||
continue
|
||||
@ -169,9 +165,7 @@ class RunCommandControlsProviderService : ControlsProviderService() {
|
||||
private fun getCommandByControlId(controlId: String): CommandEntryWithDevice? {
|
||||
val controlIdParts = controlId.split(":")
|
||||
|
||||
val service = BackgroundService.getInstance() ?: return null
|
||||
|
||||
val device = service.getDevice(controlIdParts[0])
|
||||
val device = KdeConnect.getInstance().getDevice(controlIdParts[0])
|
||||
|
||||
if (device == null || !device.isPaired) return null
|
||||
|
||||
|
@ -10,9 +10,8 @@ import android.widget.TextView;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
public class RunCommandUrlActivity extends AppCompatActivity {
|
||||
@ -26,8 +25,7 @@ public class RunCommandUrlActivity extends AppCompatActivity {
|
||||
Uri uri = getIntent().getData();
|
||||
String deviceId = uri.getPathSegments().get(0);
|
||||
|
||||
BackgroundService.RunCommand(this, service -> {
|
||||
final Device device = service.getDevice(deviceId);
|
||||
final Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
|
||||
if(device == null) {
|
||||
error(R.string.runcommand_nosuchdevice);
|
||||
@ -54,12 +52,11 @@ public class RunCommandUrlActivity extends AppCompatActivity {
|
||||
RunCommandUrlActivity.this.finish();
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
Vibrator vibrator = RunCommandUrlActivity.this.getSystemService(Vibrator.class);
|
||||
Vibrator vibrator = getSystemService(Vibrator.class);
|
||||
if(vibrator != null && vibrator.hasVibrator()) {
|
||||
vibrator.vibrate(100);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
Log.e("RuncommandPlugin", "Exception", e);
|
||||
}
|
||||
|
@ -12,10 +12,12 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class RunCommandWidget extends AppWidgetProvider {
|
||||
|
||||
public static final String RUN_COMMAND_ACTION = "RUN_COMMAND_ACTION";
|
||||
@ -35,8 +37,7 @@ public class RunCommandWidget extends AppWidgetProvider {
|
||||
final String targetCommand = intent.getStringExtra(TARGET_COMMAND);
|
||||
final String targetDevice = intent.getStringExtra(TARGET_DEVICE);
|
||||
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
RunCommandPlugin plugin = service.getDevice(targetDevice).getPlugin(RunCommandPlugin.class);
|
||||
RunCommandPlugin plugin = KdeConnect.getInstance().getDevicePlugin(targetDevice, RunCommandPlugin.class);
|
||||
|
||||
if (plugin != null) {
|
||||
try {
|
||||
@ -46,7 +47,6 @@ public class RunCommandWidget extends AppWidgetProvider {
|
||||
Log.e("RunCommandWidget", "Error running command", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (intent != null && TextUtils.equals(intent.getAction(), SET_CURRENT_DEVICE)) {
|
||||
setCurrentDevice(context);
|
||||
}
|
||||
@ -70,14 +70,13 @@ public class RunCommandWidget extends AppWidgetProvider {
|
||||
|
||||
private void updateWidget(final Context context) {
|
||||
|
||||
if (getCurrentDevice() == null || !getCurrentDevice().isReachable()) {
|
||||
Device device = getCurrentDevice();
|
||||
|
||||
BackgroundService.RunCommand(context, service -> {
|
||||
if (service.getDevices().size() > 0)
|
||||
currentDeviceId = service.getDevices().elements().nextElement().getDeviceId();
|
||||
|
||||
updateWidgetImpl(context);
|
||||
});
|
||||
if (device == null || !device.isReachable()) {
|
||||
ConcurrentHashMap<String, Device> devices = KdeConnect.getInstance().getDevices();
|
||||
if (devices.size() > 0) {
|
||||
currentDeviceId = devices.elements().nextElement().getDeviceId();
|
||||
}
|
||||
}
|
||||
|
||||
updateWidgetImpl(context);
|
||||
@ -99,12 +98,13 @@ public class RunCommandWidget extends AppWidgetProvider {
|
||||
pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
views.setOnClickPendingIntent(R.id.runcommandWidgetTitleHeader, pendingIntent);
|
||||
|
||||
if (getCurrentDevice() == null || !getCurrentDevice().isReachable()) {
|
||||
Device device = getCurrentDevice();
|
||||
if (device == null || !device.isReachable()) {
|
||||
views.setTextViewText(R.id.runcommandWidgetTitle, context.getString(R.string.kde_connect));
|
||||
views.setViewVisibility(R.id.run_commands_list, View.GONE);
|
||||
views.setViewVisibility(R.id.not_reachable_message, View.VISIBLE);
|
||||
} else {
|
||||
views.setTextViewText(R.id.runcommandWidgetTitle, getCurrentDevice().getName());
|
||||
views.setTextViewText(R.id.runcommandWidgetTitle, device.getName());
|
||||
views.setViewVisibility(R.id.run_commands_list, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.not_reachable_message, View.GONE);
|
||||
}
|
||||
@ -129,21 +129,15 @@ public class RunCommandWidget extends AppWidgetProvider {
|
||||
Log.e("RunCommandWidget", "Error updating widget", ex);
|
||||
}
|
||||
|
||||
if (BackgroundService.getInstance() != null) {
|
||||
BackgroundService.getInstance().addDeviceListChangedCallback("RunCommandWidget", unused -> {
|
||||
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("RunCommandWidget", () -> {
|
||||
Intent updateWidget = new Intent(context, RunCommandWidget.class);
|
||||
context.sendBroadcast(updateWidget);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static Device getCurrentDevice() {
|
||||
|
||||
try {
|
||||
return BackgroundService.getInstance().getDevice(currentDeviceId);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
return KdeConnect.getInstance().getDevice(currentDeviceId);
|
||||
}
|
||||
|
||||
public static void setCurrentDevice(final String DeviceId) {
|
||||
|
@ -6,10 +6,9 @@ import android.view.Window;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect_tp.databinding.WidgetRemoteCommandPluginDialogBinding;
|
||||
|
||||
import java.util.Comparator;
|
||||
@ -27,8 +26,7 @@ public class RunCommandWidgetDeviceSelector extends AppCompatActivity {
|
||||
WidgetRemoteCommandPluginDialogBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
BackgroundService.RunCommand(this, service -> runOnUiThread(() -> {
|
||||
final List<CommandEntry> deviceItems = service.getDevices().values().stream()
|
||||
final List<CommandEntry> deviceItems = KdeConnect.getInstance().getDevices().values().stream()
|
||||
.filter(Device::isPaired).filter(Device::isReachable)
|
||||
.map(device -> new CommandEntry(device.getName(), null, device.getDeviceId()))
|
||||
.sorted(Comparator.comparing(CommandEntry::getName))
|
||||
@ -46,6 +44,5 @@ public class RunCommandWidgetDeviceSelector extends AppCompatActivity {
|
||||
|
||||
finish();
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
@ -30,8 +30,8 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.UserInterface.PluginSettingsActivity;
|
||||
import org.kde.kdeconnect.UserInterface.PluginSettingsFragment;
|
||||
@ -326,21 +326,11 @@ public class SftpSettingsFragment
|
||||
|
||||
addStoragePreferences(preferenceCategory);
|
||||
|
||||
Device device = getDeviceOrThrow();
|
||||
Device device = KdeConnect.getInstance().getDevice(getDeviceId());
|
||||
|
||||
device.reloadPluginsFromSettings();
|
||||
}
|
||||
|
||||
private Device getDeviceOrThrow() {
|
||||
Device device = BackgroundService.getInstance().getDevice(getDeviceId());
|
||||
|
||||
if (device == null) {
|
||||
throw new RuntimeException("SftpSettingsFragment.getDeviceOrThrow(): No device with id: " + getDeviceId());
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
|
||||
SftpPlugin.StorageInfo newStorageInfo = (SftpPlugin.StorageInfo) newValue;
|
||||
|
@ -16,8 +16,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -69,7 +68,12 @@ public class SendFileActivity extends AppCompatActivity {
|
||||
if (uris.isEmpty()) {
|
||||
Log.w("SendFileActivity", "No files to send?");
|
||||
} else {
|
||||
BackgroundService.RunWithPlugin(this, mDeviceId, SharePlugin.class, plugin -> plugin.sendUriList(uris));
|
||||
SharePlugin plugin = KdeConnect.getInstance().getDevicePlugin(mDeviceId, SharePlugin.class);
|
||||
if (plugin == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
plugin.sendUriList(uris);
|
||||
}
|
||||
}
|
||||
finish();
|
||||
|
@ -17,10 +17,10 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.List.EntryItemWithIcon;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
import org.kde.kdeconnect.UserInterface.ThemeUtil;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ActivityShareBinding;
|
||||
|
||||
@ -42,19 +42,17 @@ public class ShareActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_refresh) {
|
||||
updateDeviceListAction();
|
||||
refreshDevicesAction();
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDeviceListAction() {
|
||||
updateDeviceList();
|
||||
BackgroundService.RunCommand(ShareActivity.this, BackgroundService::onNetworkChange);
|
||||
private void refreshDevicesAction() {
|
||||
BackgroundService.ForceRefreshConnections(this);
|
||||
|
||||
binding.devicesListLayout.refreshListLayout.setRefreshing(true);
|
||||
|
||||
binding.devicesListLayout.refreshListLayout.postDelayed(() -> {
|
||||
binding.devicesListLayout.refreshListLayout.setRefreshing(false);
|
||||
}, 1500);
|
||||
@ -69,9 +67,7 @@ public class ShareActivity extends AppCompatActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
BackgroundService.RunCommand(this, service -> {
|
||||
|
||||
Collection<Device> devices = service.getDevices().values();
|
||||
Collection<Device> devices = KdeConnect.getInstance().getDevices().values();
|
||||
final ArrayList<Device> devicesList = new ArrayList<>();
|
||||
final ArrayList<ListAdapter.Item> items = new ArrayList<>();
|
||||
|
||||
@ -86,15 +82,15 @@ public class ShareActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
runOnUiThread(() -> {
|
||||
binding.devicesListLayout.devicesList.setAdapter(new ListAdapter(ShareActivity.this, items));
|
||||
binding.devicesListLayout.devicesList.setOnItemClickListener((adapterView, view, i, l) -> {
|
||||
Device device = devicesList.get(i - 1); //NOTE: -1 because of the title!
|
||||
BackgroundService.RunWithPlugin(this, device.getDeviceId(), SharePlugin.class, plugin -> plugin.share(intent));
|
||||
SharePlugin plugin = KdeConnect.getInstance().getDevicePlugin(device.getDeviceId(), SharePlugin.class);
|
||||
if (plugin != null) {
|
||||
plugin.share(intent);
|
||||
}
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -109,7 +105,7 @@ public class ShareActivity extends AppCompatActivity {
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
binding.devicesListLayout.refreshListLayout.setOnRefreshListener(this::updateDeviceListAction);
|
||||
binding.devicesListLayout.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_CUSTOM);
|
||||
}
|
||||
@ -123,22 +119,21 @@ public class ShareActivity extends AppCompatActivity {
|
||||
final String deviceId = intent.getStringExtra("deviceId");
|
||||
|
||||
if (deviceId != null) {
|
||||
BackgroundService.RunWithPlugin(this, deviceId, SharePlugin.class, plugin -> {
|
||||
SharePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, SharePlugin.class);
|
||||
if (plugin != null) {
|
||||
plugin.share(intent);
|
||||
}
|
||||
finish();
|
||||
});
|
||||
} else {
|
||||
BackgroundService.RunCommand(this, service -> {
|
||||
service.onNetworkChange();
|
||||
service.addDeviceListChangedCallback("ShareActivity", unused -> updateDeviceList());
|
||||
});
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("ShareActivity", () -> runOnUiThread(this::updateDeviceList));
|
||||
BackgroundService.ForceRefreshConnections(this); // force a network re-discover
|
||||
updateDeviceList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
BackgroundService.RunCommand(this, service -> service.removeDeviceListChangedCallback("ShareActivity"));
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("ShareActivity");
|
||||
super.onStop();
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
|
||||
public class ShareBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
@ -35,6 +35,10 @@ public class ShareBroadcastReceiver extends BroadcastReceiver {
|
||||
long jobId = intent.getLongExtra(SharePlugin.CANCEL_SHARE_BACKGROUND_JOB_ID_EXTRA, -1);
|
||||
String deviceId = intent.getStringExtra(SharePlugin.CANCEL_SHARE_DEVICE_ID_EXTRA);
|
||||
|
||||
BackgroundService.RunWithPlugin(context, deviceId, SharePlugin.class, plugin -> plugin.cancelJob(jobId));
|
||||
SharePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, SharePlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
plugin.cancelJob(jobId);
|
||||
}
|
||||
}
|
||||
|
@ -14,8 +14,8 @@ import android.service.chooser.ChooserTarget;
|
||||
import android.service.chooser.ChooserTargetService;
|
||||
import android.util.Log;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -26,7 +26,7 @@ public class ShareChooserTargetService extends ChooserTargetService {
|
||||
public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
|
||||
Log.d("DirectShare", "invoked");
|
||||
final List<ChooserTarget> targets = new ArrayList<>();
|
||||
for (Device d : BackgroundService.getInstance().getDevices().values()) {
|
||||
for (Device d : KdeConnect.getInstance().getDevices().values()) {
|
||||
if (d.isReachable() && d.isPaired()) {
|
||||
Log.d("DirectShare", d.getName());
|
||||
final String targetName = d.getName();
|
||||
|
@ -15,7 +15,6 @@ import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
@ -157,7 +156,7 @@ public class SharePlugin extends Plugin {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
IntentHelper.startActivityFromBackground(context, browserIntent, url);
|
||||
IntentHelper.startActivityFromBackgroundOrCreateNotification(context, browserIntent, url);
|
||||
}
|
||||
|
||||
private void receiveText(NetworkPacket np) {
|
||||
|
@ -11,7 +11,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.core.util.Consumer;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
import org.kde.kdeconnect_tp.databinding.ListItemSystemvolumeBinding;
|
||||
|
||||
@ -49,8 +48,7 @@ class SinkItemHolder extends RecyclerView.ViewHolder
|
||||
@Override
|
||||
public void onProgressChanged(final SeekBar seekBar, int i, boolean triggeredByUser) {
|
||||
if (triggeredByUser) {
|
||||
BackgroundService.RunCommand(seekBar.getContext(),
|
||||
service -> plugin.sendVolume(sink.getName(), seekBar.getProgress()));
|
||||
plugin.sendVolume(sink.getName(), seekBar.getProgress());
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,8 +60,7 @@ class SinkItemHolder extends RecyclerView.ViewHolder
|
||||
@Override
|
||||
public void onStopTrackingTouch(final SeekBar seekBar) {
|
||||
seekBarTracking.accept(false);
|
||||
BackgroundService.RunCommand(seekBar.getContext(),
|
||||
service -> plugin.sendVolume(sink.getName(), seekBar.getProgress()));
|
||||
plugin.sendVolume(sink.getName(), seekBar.getProgress());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
package org.kde.kdeconnect.Plugins.SystemVolumePlugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -20,8 +19,8 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Helpers.VolumeHelperKt;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.Plugins.MprisPlugin.MprisPlugin;
|
||||
import org.kde.kdeconnect.Plugins.MprisPlugin.VolumeKeyListener;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
@ -73,19 +72,15 @@ public class SystemVolumeFragment
|
||||
recyclerView.setAdapter(recyclerAdapter);
|
||||
}
|
||||
|
||||
connectToPlugin(getDeviceId());
|
||||
|
||||
return systemVolumeFragmentBinding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
connectToPlugin(getDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
public void onDestroyView() {
|
||||
disconnectFromPlugin(getDeviceId());
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -99,16 +94,21 @@ public class SystemVolumeFragment
|
||||
}
|
||||
|
||||
private void connectToPlugin(final String deviceId) {
|
||||
BackgroundService.RunWithPlugin(requireActivity(), deviceId, SystemVolumePlugin.class, plugin -> {
|
||||
SystemVolumePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, SystemVolumePlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
this.plugin = plugin;
|
||||
plugin.addSinkListener(SystemVolumeFragment.this);
|
||||
plugin.requestSinkList();
|
||||
});
|
||||
}
|
||||
|
||||
private void disconnectFromPlugin(final String deviceId) {
|
||||
BackgroundService.RunWithPlugin(requireActivity(), deviceId, SystemVolumePlugin.class, plugin ->
|
||||
plugin.removeSinkListener(SystemVolumeFragment.this));
|
||||
SystemVolumePlugin plugin = KdeConnect.getInstance().getDevicePlugin(deviceId, SystemVolumePlugin.class);
|
||||
if (plugin == null) {
|
||||
return;
|
||||
}
|
||||
plugin.removeSinkListener(SystemVolumeFragment.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,11 +18,11 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.kde.kdeconnect.BackgroundService
|
||||
import org.kde.kdeconnect.Device
|
||||
import org.kde.kdeconnect.Device.PairingCallback
|
||||
import org.kde.kdeconnect.Device.PluginsChangedListener
|
||||
import org.kde.kdeconnect.Helpers.SecurityHelpers.SslHelper
|
||||
import org.kde.kdeconnect.KdeConnect
|
||||
import org.kde.kdeconnect.Plugins.BatteryPlugin.BatteryPlugin
|
||||
import org.kde.kdeconnect.Plugins.Plugin
|
||||
import org.kde.kdeconnect.UserInterface.List.PluginAdapter
|
||||
@ -98,9 +98,7 @@ class DeviceFragment : Fragment() {
|
||||
// ...and for when pairing doesn't (or can't) work
|
||||
errorBinding = deviceBinding.pairError
|
||||
|
||||
BackgroundService.RunCommand(mActivity) {
|
||||
device = it.getDevice(deviceId)
|
||||
}
|
||||
device = KdeConnect.getInstance().getDevice(deviceId)
|
||||
|
||||
requirePairingBinding().pairButton.setOnClickListener {
|
||||
with(requirePairingBinding()) {
|
||||
@ -128,36 +126,35 @@ class DeviceFragment : Fragment() {
|
||||
mActivity?.onDeviceSelected(null)
|
||||
}
|
||||
setHasOptionsMenu(true)
|
||||
BackgroundService.RunCommand(mActivity) { service: BackgroundService ->
|
||||
device = service.getDevice(deviceId) ?: let {
|
||||
Log.e(TAG, "Trying to display a device fragment but the device is not present")
|
||||
mActivity?.onDeviceSelected(null)
|
||||
return@RunCommand
|
||||
}
|
||||
mActivity?.supportActionBar?.title = device?.name
|
||||
device?.addPairingCallback(pairingCallback)
|
||||
device?.addPluginsChangedListener(pluginsChangedListener)
|
||||
refreshUI()
|
||||
}
|
||||
|
||||
requireDeviceBinding().pluginsList.layoutManager =
|
||||
GridLayoutManager(requireContext(), resources.getInteger(R.integer.plugins_columns))
|
||||
requireDeviceBinding().permissionsList.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
device?.apply {
|
||||
mActivity?.supportActionBar?.title = name
|
||||
addPairingCallback(pairingCallback)
|
||||
addPluginsChangedListener(pluginsChangedListener)
|
||||
} ?: run { // device is null
|
||||
Log.e(TAG, "Trying to display a device fragment but the device is not present")
|
||||
mActivity?.onDeviceSelected(null)
|
||||
}
|
||||
|
||||
refreshUI()
|
||||
|
||||
return deviceBinding.root
|
||||
}
|
||||
|
||||
private val pluginsChangedListener = PluginsChangedListener { refreshUI() }
|
||||
override fun onDestroyView() {
|
||||
BackgroundService.RunCommand(mActivity) { service: BackgroundService ->
|
||||
val device = service.getDevice(deviceId) ?: return@RunCommand
|
||||
device.removePluginsChangedListener(pluginsChangedListener)
|
||||
device.removePairingCallback(pairingCallback)
|
||||
device?.apply {
|
||||
removePluginsChangedListener(pluginsChangedListener)
|
||||
removePairingCallback(pairingCallback)
|
||||
}
|
||||
super.onDestroyView()
|
||||
pairingBinding = null
|
||||
errorBinding = null
|
||||
deviceBinding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
|
@ -25,6 +25,7 @@ import org.apache.commons.lang3.ArrayUtils
|
||||
import org.kde.kdeconnect.BackgroundService
|
||||
import org.kde.kdeconnect.Device
|
||||
import org.kde.kdeconnect.Helpers.DeviceHelper
|
||||
import org.kde.kdeconnect.KdeConnect
|
||||
import org.kde.kdeconnect.Plugins.SharePlugin.ShareSettingsFragment
|
||||
import org.kde.kdeconnect.UserInterface.About.AboutFragment.Companion.newInstance
|
||||
import org.kde.kdeconnect.UserInterface.About.getApplicationAboutData
|
||||
@ -194,18 +195,16 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
private fun onPairResultFromNotification(deviceId: String?, pairStatus: String): String? {
|
||||
assert(deviceId != null)
|
||||
if (pairStatus != PAIRING_PENDING) {
|
||||
BackgroundService.RunCommand(this) { service: BackgroundService ->
|
||||
val device = service.getDevice(deviceId)
|
||||
val device = KdeConnect.getInstance().getDevice(deviceId)
|
||||
if (device == null) {
|
||||
Log.w(this::class.simpleName, "Reject pairing - device no longer exists: $deviceId")
|
||||
return@RunCommand
|
||||
return null
|
||||
}
|
||||
when (pairStatus) {
|
||||
PAIRING_ACCEPTED -> device.acceptPairing()
|
||||
PAIRING_REJECTED -> device.rejectPairing()
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (pairStatus == PAIRING_ACCEPTED || pairStatus == PAIRING_PENDING) deviceId else null
|
||||
}
|
||||
|
||||
@ -228,13 +227,12 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
}
|
||||
|
||||
private fun updateDeviceList() {
|
||||
BackgroundService.RunCommand(this@MainActivity) { service: BackgroundService ->
|
||||
val menu = mNavigationView.menu
|
||||
menu.clear()
|
||||
mMapMenuToDeviceId.clear()
|
||||
val devicesMenu = menu.addSubMenu(R.string.devices)
|
||||
var id = MENU_ENTRY_DEVICE_FIRST_ID
|
||||
val devices: Collection<Device> = service.devices.values
|
||||
val devices: Collection<Device> = KdeConnect.getInstance().devices.values
|
||||
for (device in devices) {
|
||||
if (device.isReachable && device.isPaired) {
|
||||
val item = devicesMenu.add(Menu.FIRST, id++, 1, device.name)
|
||||
@ -259,14 +257,11 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
}
|
||||
mNavigationView.setCheckedItem(mCurrentMenuEntry)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
BackgroundService.RunCommand(this) { service: BackgroundService ->
|
||||
service.onNetworkChange()
|
||||
service.addDeviceListChangedCallback(this::class.simpleName) { updateDeviceList() }
|
||||
}
|
||||
BackgroundService.Start(applicationContext);
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback(this::class.simpleName) { runOnUiThread { updateDeviceList() } }
|
||||
updateDeviceList()
|
||||
onBackPressedDispatcher.addCallback(mainFragmentCallback)
|
||||
onBackPressedDispatcher.addCallback(closeDrawerCallback)
|
||||
@ -274,10 +269,10 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
BackgroundService.RunCommand(this) { service: BackgroundService -> service.removeDeviceListChangedCallback(this::class.simpleName) }
|
||||
super.onStop()
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback(this::class.simpleName)
|
||||
mainFragmentCallback.remove()
|
||||
closeDrawerCallback.remove()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
@ -315,11 +310,9 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when {
|
||||
requestCode == RESULT_NEEDS_RELOAD -> BackgroundService.RunCommand(this) { service: BackgroundService ->
|
||||
val device = service.getDevice(mCurrentDevice)
|
||||
device.reloadPluginsFromSettings()
|
||||
requestCode == RESULT_NEEDS_RELOAD -> {
|
||||
KdeConnect.getInstance().getDevice(mCurrentDevice)?.reloadPluginsFromSettings()
|
||||
}
|
||||
|
||||
requestCode == STORAGE_LOCATION_CONFIGURED && resultCode == RESULT_OK && data != null -> {
|
||||
val uri = data.data
|
||||
ShareSettingsFragment.saveStorageLocationPreference(this, uri)
|
||||
@ -344,17 +337,14 @@ class MainActivity : AppCompatActivity(), OnSharedPreferenceChangeListener {
|
||||
}
|
||||
|
||||
//New permission granted, reload plugins
|
||||
BackgroundService.RunCommand(this) { service: BackgroundService ->
|
||||
val device = service.getDevice(mCurrentDevice)
|
||||
device.reloadPluginsFromSettings()
|
||||
}
|
||||
KdeConnect.getInstance().getDevice(mCurrentDevice)?.reloadPluginsFromSettings()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
if (DeviceHelper.KEY_DEVICE_NAME_PREFERENCE == key) {
|
||||
mNavViewDeviceName.text = DeviceHelper.getDeviceName(this)
|
||||
BackgroundService.RunCommand(this) { obj: BackgroundService -> obj.onNetworkChange() } //Re-send our identity packet
|
||||
BackgroundService.ForceRefreshConnections(this) //Re-send our identity packet
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ import androidx.fragment.app.Fragment;
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.Helpers.TrustedNetworkHelper;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.UserInterface.List.ListAdapter;
|
||||
import org.kde.kdeconnect.UserInterface.List.PairingDeviceItem;
|
||||
import org.kde.kdeconnect.UserInterface.List.SectionItem;
|
||||
@ -59,7 +60,6 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
private TextView headerText;
|
||||
private TextView noWifiHeader;
|
||||
private TextView notTrustedText;
|
||||
private boolean isConnectedToNonCellularNetwork = true;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
@ -73,7 +73,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
pairingExplanationTextBinding = PairingExplanationTextBinding.inflate(inflater);
|
||||
pairingExplanationTextNoWifiBinding = PairingExplanationTextNoWifiBinding.inflate(inflater);
|
||||
|
||||
devicesListBinding.refreshListLayout.setOnRefreshListener(this::updateDeviceListAction);
|
||||
devicesListBinding.refreshListLayout.setOnRefreshListener(this::refreshDevicesAction);
|
||||
|
||||
notTrustedText = pairingExplanationNotTrustedBinding.getRoot();
|
||||
notTrustedText.setOnClickListener(null);
|
||||
@ -105,24 +105,18 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
mActivity = ((MainActivity) getActivity());
|
||||
}
|
||||
|
||||
private void updateDeviceListAction() {
|
||||
updateDeviceList();
|
||||
BackgroundService.RunCommand(mActivity, BackgroundService::onNetworkChange);
|
||||
private void refreshDevicesAction() {
|
||||
BackgroundService.ForceRefreshConnections(requireContext());
|
||||
|
||||
devicesListBinding.refreshListLayout.setRefreshing(true);
|
||||
|
||||
devicesListBinding.refreshListLayout.postDelayed(() -> {
|
||||
// the view might be destroyed by now
|
||||
if (devicesListBinding == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (devicesListBinding != null) { // the view might be destroyed by now
|
||||
devicesListBinding.refreshListLayout.setRefreshing(false);
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
private void updateDeviceList() {
|
||||
BackgroundService.RunCommand(mActivity, service -> mActivity.runOnUiThread(() -> {
|
||||
|
||||
if (!isAdded()) {
|
||||
//Fragment is not attached to an activity. We will crash if we try to do anything here.
|
||||
return;
|
||||
@ -136,27 +130,12 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
}
|
||||
listRefreshCalledThisFrame = true;
|
||||
|
||||
Collection<Device> devices = service.getDevices().values();
|
||||
boolean someDevicesReachable = false;
|
||||
for (Device device : devices) {
|
||||
if (device.isReachable()) {
|
||||
someDevicesReachable = true;
|
||||
}
|
||||
}
|
||||
|
||||
devicesListBinding.devicesList.removeHeaderView(headerText);
|
||||
devicesListBinding.devicesList.removeHeaderView(noWifiHeader);
|
||||
devicesListBinding.devicesList.removeHeaderView(notTrustedText);
|
||||
|
||||
//Check if we're on Wi-Fi/Local network. If we still see a device, don't do anything special
|
||||
if (someDevicesReachable || isConnectedToNonCellularNetwork) {
|
||||
if (TrustedNetworkHelper.isTrustedNetwork(getContext())) {
|
||||
devicesListBinding.devicesList.addHeaderView(headerText);
|
||||
BackgroundService service = BackgroundService.getInstance();
|
||||
if (service == null) {
|
||||
updateConnectivityInfoHeader(true);
|
||||
} else {
|
||||
devicesListBinding.devicesList.addHeaderView(notTrustedText);
|
||||
}
|
||||
} else {
|
||||
devicesListBinding.devicesList.addHeaderView(noWifiHeader);
|
||||
service.isConnectedToNonCellularNetwork().observe(this, this::updateConnectivityInfoHeader);
|
||||
}
|
||||
|
||||
try {
|
||||
@ -167,6 +146,8 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
|
||||
connectedSection = new SectionItem(res.getString(R.string.category_connected_devices));
|
||||
items.add(connectedSection);
|
||||
|
||||
Collection<Device> devices = KdeConnect.getInstance().getDevices().values();
|
||||
for (Device device : devices) {
|
||||
if (device.isReachable() && device.isPaired()) {
|
||||
items.add(new PairingDeviceItem(device, PairingFragment.this));
|
||||
@ -215,26 +196,43 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
} finally {
|
||||
listRefreshCalledThisFrame = false;
|
||||
}
|
||||
|
||||
}));
|
||||
}
|
||||
|
||||
void updateConnectivityInfoHeader(boolean isConnectedToNonCellularNetwork) {
|
||||
Collection<Device> devices = KdeConnect.getInstance().getDevices().values();
|
||||
boolean someDevicesReachable = false;
|
||||
for (Device device : devices) {
|
||||
if (device.isReachable()) {
|
||||
someDevicesReachable = true;
|
||||
}
|
||||
}
|
||||
|
||||
devicesListBinding.devicesList.removeHeaderView(headerText);
|
||||
devicesListBinding.devicesList.removeHeaderView(noWifiHeader);
|
||||
devicesListBinding.devicesList.removeHeaderView(notTrustedText);
|
||||
|
||||
if (someDevicesReachable || isConnectedToNonCellularNetwork) {
|
||||
if (TrustedNetworkHelper.isTrustedNetwork(getContext())) {
|
||||
devicesListBinding.devicesList.addHeaderView(headerText);
|
||||
} else {
|
||||
devicesListBinding.devicesList.addHeaderView(notTrustedText);
|
||||
}
|
||||
} else {
|
||||
devicesListBinding.devicesList.addHeaderView(noWifiHeader);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
devicesListBinding.refreshListLayout.setEnabled(true);
|
||||
BackgroundService.RunCommand(mActivity, service -> service.addDeviceListChangedCallback("PairingFragment", newIsConnectedToNonCellularNetwork -> {
|
||||
isConnectedToNonCellularNetwork = newIsConnectedToNonCellularNetwork;
|
||||
updateDeviceList();
|
||||
}));
|
||||
KdeConnect.getInstance().addDeviceListChangedCallback("PairingFragment", () -> mActivity.runOnUiThread(this::updateDeviceList));
|
||||
BackgroundService.ForceRefreshConnections(requireContext()); // force a network re-discover
|
||||
updateDeviceList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
KdeConnect.getInstance().removeDeviceListChangedCallback("PairingFragment");
|
||||
super.onStop();
|
||||
devicesListBinding.refreshListLayout.setEnabled(false);
|
||||
BackgroundService.RunCommand(mActivity, service -> service.removeDeviceListChangedCallback("PairingFragment"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -265,7 +263,7 @@ public class PairingFragment extends Fragment implements PairingDeviceItem.Callb
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.menu_refresh) {
|
||||
updateDeviceListAction();
|
||||
refreshDevicesAction();
|
||||
return true;
|
||||
} else if (id == R.id.menu_custom_device_list) {
|
||||
startActivity(new Intent(mActivity, CustomDevicesActivity.class));
|
||||
|
@ -13,8 +13,8 @@ import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
@ -55,7 +55,7 @@ public class PluginSettingsActivity
|
||||
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragmentPlaceHolder);
|
||||
if (fragment == null) {
|
||||
if (pluginKey != null) {
|
||||
Device device = BackgroundService.getInstance().getDevice(deviceId);
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device != null) {
|
||||
Plugin plugin = device.getPluginIncludingWithoutPermissions(pluginKey);
|
||||
if (plugin != null) {
|
||||
|
@ -13,8 +13,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.Plugins.Plugin;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
@ -56,7 +56,7 @@ public class PluginSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
this.pluginKey = getArguments().getString(ARG_PLUGIN_KEY);
|
||||
this.layout = getArguments().getInt(ARG_LAYOUT);
|
||||
this.device = getDeviceOrThrow(getDeviceId());
|
||||
this.device = KdeConnect.getInstance().getDevice(getDeviceId());
|
||||
this.plugin = device.getPluginIncludingWithoutPermissions(pluginKey);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -85,13 +85,4 @@ public class PluginSettingsFragment extends PreferenceFragmentCompat {
|
||||
return ((PluginSettingsActivity)requireActivity()).getDeviceId();
|
||||
}
|
||||
|
||||
private Device getDeviceOrThrow(String deviceId) {
|
||||
Device device = BackgroundService.getInstance().getDevice(deviceId);
|
||||
|
||||
if (device == null) {
|
||||
throw new RuntimeException("PluginSettingsFragment.onCreatePreferences() - No device with id " + getDeviceId());
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.kde.kdeconnect.BackgroundService;
|
||||
import org.kde.kdeconnect.Device;
|
||||
import org.kde.kdeconnect.KdeConnect;
|
||||
import org.kde.kdeconnect.Plugins.PluginFactory;
|
||||
import org.kde.kdeconnect_tp.R;
|
||||
|
||||
@ -77,8 +77,7 @@ public class PluginSettingsListFragment extends PreferenceFragmentCompat {
|
||||
|
||||
final String deviceId = getArguments().getString(ARG_DEVICE_ID);
|
||||
|
||||
BackgroundService.RunCommand(requireContext(), service -> {
|
||||
final Device device = service.getDevice(deviceId);
|
||||
Device device = KdeConnect.getInstance().getDevice(deviceId);
|
||||
if (device == null) {
|
||||
final FragmentActivity activity = requireActivity();
|
||||
activity.runOnUiThread(activity::finish);
|
||||
@ -92,7 +91,6 @@ public class PluginSettingsListFragment extends PreferenceFragmentCompat {
|
||||
PluginPreference pref = new PluginPreference(requireContext(), pluginKey, device, callback);
|
||||
preferenceScreen.addPreference(pref);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
@ -125,8 +125,10 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
final boolean isChecked = (Boolean) newValue;
|
||||
|
||||
NotificationHelper.setPersistentNotificationEnabled(context, isChecked);
|
||||
BackgroundService.RunCommand(context,
|
||||
service -> service.changePersistentNotificationVisibility(isChecked));
|
||||
BackgroundService service = BackgroundService.getInstance();
|
||||
if (service != null) {
|
||||
service.changePersistentNotificationVisibility(isChecked);
|
||||
}
|
||||
|
||||
NotificationHelper.setPersistentNotificationEnabled(context, isChecked);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user