mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 09:58:08 +00:00
Migrate PluginFactory to Kotlin
This commit is contained in:
parent
8fb545d620
commit
854b2a1c9f
@ -112,7 +112,7 @@ class Device : PacketReceiver {
|
||||
this.settings = context.getSharedPreferences(deviceId, Context.MODE_PRIVATE)
|
||||
this.deviceInfo = loadFromSettings(context, deviceId, settings)
|
||||
this.pairingHandler = PairingHandler(this, createDefaultPairingCallback(), PairingHandler.PairState.Paired)
|
||||
this.supportedPlugins = Vector(PluginFactory.getAvailablePlugins()) // Assume all are supported until we receive capabilities
|
||||
this.supportedPlugins = Vector(PluginFactory.availablePlugins) // Assume all are supported until we receive capabilities
|
||||
Log.i("Device", "Loading trusted device: ${deviceInfo.name}")
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ class Device : PacketReceiver {
|
||||
this.deviceInfo = link.deviceInfo
|
||||
this.settings = context.getSharedPreferences(deviceInfo.id, Context.MODE_PRIVATE)
|
||||
this.pairingHandler = PairingHandler(this, createDefaultPairingCallback(), PairingHandler.PairState.NotPaired)
|
||||
this.supportedPlugins = Vector(PluginFactory.getAvailablePlugins()) // Assume all are supported until we receive capabilities
|
||||
this.supportedPlugins = Vector(PluginFactory.availablePlugins) // Assume all are supported until we receive capabilities
|
||||
Log.i("Device", "Creating untrusted device: " + deviceInfo.name)
|
||||
addLink(link)
|
||||
}
|
||||
@ -622,7 +622,7 @@ class Device : PacketReceiver {
|
||||
|
||||
supportedPlugins.forEach { pluginKey ->
|
||||
val pluginInfo = PluginFactory.getPluginInfo(pluginKey)
|
||||
val listenToUnpaired = pluginInfo.listenToUnpaired()
|
||||
val listenToUnpaired = pluginInfo.listenToUnpaired
|
||||
|
||||
val pluginEnabled = (isPaired || listenToUnpaired) && this.isReachable && isPluginEnabled(pluginKey)
|
||||
|
||||
|
@ -160,8 +160,8 @@ object DeviceHelper {
|
||||
getDeviceName(context),
|
||||
deviceType,
|
||||
ProtocolVersion,
|
||||
PluginFactory.getIncomingCapabilities(),
|
||||
PluginFactory.getOutgoingCapabilities()
|
||||
PluginFactory.incomingCapabilities,
|
||||
PluginFactory.outgoingCapabilities
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1,179 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
package org.kde.kdeconnect.Plugins;
|
||||
|
||||
import static org.apache.commons.collections4.SetUtils.unmodifiableSet;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.atteo.classindex.ClassIndex;
|
||||
import org.atteo.classindex.IndexAnnotated;
|
||||
import org.kde.kdeconnect.Device;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PluginFactory {
|
||||
|
||||
public static void sortPluginList(@NonNull List<String> plugins) {
|
||||
plugins.sort(Comparator.comparing(o -> pluginInfo.get(o).displayName));
|
||||
}
|
||||
|
||||
@IndexAnnotated
|
||||
public @interface LoadablePlugin { } //Annotate plugins with this so PluginFactory finds them
|
||||
|
||||
public static class PluginInfo {
|
||||
|
||||
PluginInfo(@NonNull String displayName, @NonNull String description, @DrawableRes int icon,
|
||||
boolean enabledByDefault, boolean hasSettings, boolean supportsDeviceSpecificSettings,
|
||||
boolean listenToUnpaired, @NonNull String[] supportedPacketTypes, @NonNull String[] outgoingPacketTypes,
|
||||
@NonNull Class<? extends Plugin> instantiableClass) {
|
||||
this.displayName = displayName;
|
||||
this.description = description;
|
||||
this.icon = icon;
|
||||
this.enabledByDefault = enabledByDefault;
|
||||
this.hasSettings = hasSettings;
|
||||
this.supportsDeviceSpecificSettings = supportsDeviceSpecificSettings;
|
||||
this.listenToUnpaired = listenToUnpaired;
|
||||
this.supportedPacketTypes = unmodifiableSet(supportedPacketTypes);
|
||||
this.outgoingPacketTypes = unmodifiableSet(outgoingPacketTypes);
|
||||
this.instantiableClass = instantiableClass;
|
||||
}
|
||||
|
||||
public @NonNull String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public @NonNull String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public @DrawableRes int getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public boolean hasSettings() {
|
||||
return hasSettings;
|
||||
}
|
||||
|
||||
public boolean supportsDeviceSpecificSettings() { return supportsDeviceSpecificSettings; }
|
||||
|
||||
public boolean isEnabledByDefault() {
|
||||
return enabledByDefault;
|
||||
}
|
||||
|
||||
public boolean listenToUnpaired() {
|
||||
return listenToUnpaired;
|
||||
}
|
||||
|
||||
Set<String> getOutgoingPacketTypes() {
|
||||
return outgoingPacketTypes;
|
||||
}
|
||||
|
||||
public Set<String> getSupportedPacketTypes() {
|
||||
return supportedPacketTypes;
|
||||
}
|
||||
|
||||
Class<? extends Plugin> getInstantiableClass() {
|
||||
return instantiableClass;
|
||||
}
|
||||
|
||||
private final @NonNull String displayName;
|
||||
private final @NonNull String description;
|
||||
private final @DrawableRes int icon;
|
||||
private final boolean enabledByDefault;
|
||||
private final boolean hasSettings;
|
||||
private final boolean supportsDeviceSpecificSettings;
|
||||
private final boolean listenToUnpaired;
|
||||
private final @NonNull Set<String> supportedPacketTypes;
|
||||
private final @NonNull Set<String> outgoingPacketTypes;
|
||||
private final Class<? extends Plugin> instantiableClass;
|
||||
|
||||
}
|
||||
|
||||
private static final Map<String, PluginInfo> pluginInfo = new ConcurrentHashMap<>();
|
||||
|
||||
public static PluginInfo getPluginInfo(String pluginKey) {
|
||||
return pluginInfo.get(pluginKey);
|
||||
}
|
||||
|
||||
public static void initPluginInfo(Context context) {
|
||||
try {
|
||||
for (Class<?> pluginClass : ClassIndex.getAnnotated(LoadablePlugin.class)) {
|
||||
Plugin p = ((Plugin) pluginClass.newInstance());
|
||||
p.setContext(context, null);
|
||||
PluginInfo info = new PluginInfo(p.getDisplayName(), p.getDescription(), p.getIcon(),
|
||||
p.isEnabledByDefault(), p.hasSettings(), p.supportsDeviceSpecificSettings(),
|
||||
p.listensToUnpairedDevices(), p.getSupportedPacketTypes(),
|
||||
p.getOutgoingPacketTypes(), p.getClass());
|
||||
pluginInfo.put(p.getPluginKey(), info);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Log.i("PluginFactory","Loaded "+pluginInfo.size()+" plugins");
|
||||
}
|
||||
|
||||
public static @NonNull Set<String> getAvailablePlugins() {
|
||||
return pluginInfo.keySet();
|
||||
}
|
||||
|
||||
public static @Nullable Plugin instantiatePluginForDevice(Context context, String pluginKey, Device device) {
|
||||
PluginInfo info = pluginInfo.get(pluginKey);
|
||||
try {
|
||||
Plugin plugin = info.getInstantiableClass().newInstance();
|
||||
plugin.setContext(context, device);
|
||||
return plugin;
|
||||
} catch (Exception e) {
|
||||
Log.e("PluginFactory", "Could not instantiate plugin: " + pluginKey, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static @NonNull Set<String> getIncomingCapabilities() {
|
||||
HashSet<String> capabilities = new HashSet<>();
|
||||
for (PluginInfo plugin : pluginInfo.values()) {
|
||||
capabilities.addAll(plugin.getSupportedPacketTypes());
|
||||
}
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public static @NonNull Set<String> getOutgoingCapabilities() {
|
||||
HashSet<String> capabilities = new HashSet<>();
|
||||
for (PluginInfo plugin : pluginInfo.values()) {
|
||||
capabilities.addAll(plugin.getOutgoingPacketTypes());
|
||||
}
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public static @NonNull Set<String> pluginsForCapabilities(Set<String> incoming, Set<String> outgoing) {
|
||||
HashSet<String> plugins = new HashSet<>();
|
||||
for (Map.Entry<String, PluginInfo> entry : pluginInfo.entrySet()) {
|
||||
String pluginId = entry.getKey();
|
||||
PluginInfo info = entry.getValue();
|
||||
//Check incoming against outgoing
|
||||
if (Collections.disjoint(outgoing, info.getSupportedPacketTypes())
|
||||
&& Collections.disjoint(incoming, info.getOutgoingPacketTypes())) {
|
||||
Log.d("PluginFactory", "Won't load " + pluginId + " because of unmatched capabilities");
|
||||
continue; //No capabilities in common, do not load this plugin
|
||||
}
|
||||
plugins.add(pluginId);
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
|
||||
}
|
94
src/org/kde/kdeconnect/Plugins/PluginFactory.kt
Normal file
94
src/org/kde/kdeconnect/Plugins/PluginFactory.kt
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
package org.kde.kdeconnect.Plugins
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.annotation.DrawableRes
|
||||
import org.atteo.classindex.ClassIndex
|
||||
import org.atteo.classindex.IndexAnnotated
|
||||
import org.kde.kdeconnect.Device
|
||||
|
||||
object PluginFactory {
|
||||
private var pluginInfo: Map<String, PluginInfo> = mapOf()
|
||||
|
||||
fun initPluginInfo(context: Context) {
|
||||
try {
|
||||
val plugins = ClassIndex.getAnnotated(LoadablePlugin::class.java)
|
||||
.map { it.newInstance() as Plugin }
|
||||
.map { plugin -> plugin.apply { setContext(context, null) } }
|
||||
|
||||
pluginInfo = plugins.associate { plugin -> Pair(plugin.pluginKey, PluginInfo(plugin)) }
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
Log.i("PluginFactory", "Loaded " + pluginInfo.size + " plugins")
|
||||
}
|
||||
|
||||
val availablePlugins: Set<String>
|
||||
get() = pluginInfo.keys
|
||||
val incomingCapabilities: Set<String>
|
||||
get() = pluginInfo.values.flatMap { plugin -> plugin.supportedPacketTypes }.toSet()
|
||||
val outgoingCapabilities: Set<String>
|
||||
get() = pluginInfo.values.flatMap { plugin -> plugin.outgoingPacketTypes }.toSet()
|
||||
|
||||
@JvmStatic
|
||||
fun getPluginInfo(pluginKey: String): PluginInfo = pluginInfo[pluginKey]!!
|
||||
|
||||
@JvmStatic
|
||||
fun sortPluginList(plugins: List<String>): List<String> {
|
||||
return plugins.sortedBy { pluginInfo[it]?.displayName }
|
||||
}
|
||||
|
||||
fun instantiatePluginForDevice(context: Context, pluginKey: String, device: Device): Plugin? {
|
||||
try {
|
||||
val plugin = pluginInfo[pluginKey]?.instantiableClass?.newInstance()?.apply { setContext(context, device) }
|
||||
return plugin
|
||||
} catch (e: Exception) {
|
||||
Log.e("PluginFactory", "Could not instantiate plugin: $pluginKey", e)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun pluginsForCapabilities(incoming: Set<String>, outgoing: Set<String>): Set<String> {
|
||||
fun hasCommonCapabilities(info: PluginInfo): Boolean =
|
||||
outgoing.any { it in info.supportedPacketTypes } ||
|
||||
incoming.any { it in info.outgoingPacketTypes }
|
||||
|
||||
val (used, unused) = pluginInfo.entries.partition { hasCommonCapabilities(it.value) }
|
||||
|
||||
for (pluginId in unused.map { it.key }) {
|
||||
Log.d("PluginFactory", "Won't load $pluginId because of unmatched capabilities")
|
||||
}
|
||||
|
||||
return used.map { it.key }.toSet()
|
||||
}
|
||||
|
||||
@IndexAnnotated
|
||||
annotation class LoadablePlugin //Annotate plugins with this so PluginFactory finds them
|
||||
|
||||
class PluginInfo private constructor(
|
||||
val displayName: String,
|
||||
val description: String,
|
||||
// DrawableRes needs to be in the primary constructor, which is why we need 2 constructors
|
||||
@field:DrawableRes @get:DrawableRes @param:DrawableRes val icon: Int,
|
||||
val isEnabledByDefault: Boolean,
|
||||
val hasSettings: Boolean,
|
||||
val supportsDeviceSpecificSettings: Boolean,
|
||||
val listenToUnpaired: Boolean,
|
||||
supportedPacketTypes: Array<String>,
|
||||
outgoingPacketTypes: Array<String>,
|
||||
val instantiableClass: Class<out Plugin>,
|
||||
) {
|
||||
internal constructor(p: Plugin) : this(p.displayName, p.description, p.icon,
|
||||
p.isEnabledByDefault, p.hasSettings(), p.supportsDeviceSpecificSettings(),
|
||||
p.listensToUnpairedDevices(), p.supportedPacketTypes,
|
||||
p.outgoingPacketTypes, p.javaClass)
|
||||
|
||||
val supportedPacketTypes: Set<String> = supportedPacketTypes.toSet()
|
||||
val outgoingPacketTypes: Set<String> = outgoingPacketTypes.toSet()
|
||||
}
|
||||
}
|
@ -38,7 +38,7 @@ public class PluginPreference extends SwitchPreference {
|
||||
setSummary(info.getDescription());
|
||||
setChecked(device.isPluginEnabled(pluginKey));
|
||||
|
||||
if (info.hasSettings()) {
|
||||
if (info.getHasSettings()) {
|
||||
this.listener = v -> {
|
||||
Plugin plugin = device.getPluginIncludingWithoutPermissions(pluginKey);
|
||||
if (plugin != null) {
|
||||
|
@ -83,9 +83,8 @@ public class PluginSettingsListFragment extends PreferenceFragmentCompat {
|
||||
activity.runOnUiThread(activity::finish);
|
||||
return;
|
||||
}
|
||||
List<String> plugins = device.getSupportedPlugins();
|
||||
PluginFactory.sortPluginList(plugins);
|
||||
|
||||
List<String> plugins = PluginFactory.sortPluginList(device.getSupportedPlugins());
|
||||
for (final String pluginKey : plugins) {
|
||||
//TODO: Use PreferenceManagers context
|
||||
PluginPreference pref = new PluginPreference(requireContext(), pluginKey, device, callback);
|
||||
|
Loading…
x
Reference in New Issue
Block a user