[OpenDocString] kdeconnect-kde (cpp)
pluginloader.cpp
PluginLoader *PluginLoader::instance()
{
    static PluginLoader *instance = new PluginLoader();
    return instance;
}
This returns the singleton object of the class PluginLoader.
PluginLoader::PluginLoader()
{
#ifdef SAILFISHOS
    const QVector staticPlugins = QPluginLoader::staticPlugins();
    for (auto &staticPlugin : staticPlugins) {
        QJsonObject jsonMetadata = staticPlugin.metaData().value(QStringLiteral("MetaData")).toObject();
        KPluginMetaData metadata(jsonMetadata, QString());
        if (metadata.serviceTypes().contains(QStringLiteral("KdeConnect/Plugin"))) {
            plugins.insert(metadata.pluginId(), metadata);
            pluginsFactories.insert(metadata.pluginId(), qobject_cast(staticPlugin.instance()));
        }
    }
#else
    const QVector data = KPluginLoader::findPlugins(QStringLiteral("kdeconnect/"));
    for (const KPluginMetaData &metadata : data) {
        plugins[metadata.pluginId()] = metadata;
    }
#endif
}
This loads the plugins from the JSON.
QStringList PluginLoader::getPluginList() const
{
    return plugins.keys();
}
Returns the list of all plugins.
KPluginMetaData PluginLoader::getPluginInfo(const QString &name) const
{
    return plugins.value(name);
}
Returns the metadata for the plugin with the given name.
KdeConnectPlugin *PluginLoader::instantiatePluginForDevice(const QString &pluginName, Device *device) const
{
    KdeConnectPlugin *ret = nullptr;

    KPluginMetaData service = plugins.value(pluginName);
    if (!service.isValid()) {
        qCDebug(KDECONNECT_CORE) << "Plugin unknown" << pluginName;
        return ret;
    }

#ifdef SAILFISHOS
    KPluginFactory *factory = pluginsFactories.value(pluginName);
#else
    KPluginLoader loader(service.fileName());
    KPluginFactory *factory = loader.factory();
    if (!factory) {
        qCDebug(KDECONNECT_CORE) << "KPluginFactory could not load the plugin:" << service.pluginId() << loader.errorString();
        return ret;
    }
#endif

    const QStringList outgoingInterfaces = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-OutgoingPacketType"));

    QVariant deviceVariant = QVariant::fromValue(device);

    ret = factory->create(device, QVariantList() << deviceVariant << pluginName << outgoingInterfaces << service.iconName());
    if (!ret) {
        qCDebug(KDECONNECT_CORE) << "Error loading plugin";
        return ret;
    }

    // qCDebug(KDECONNECT_CORE) << "Loaded plugin:" << service.pluginId();
    return ret;
}
This constructs a KdeConnectPlugin object from a plugin name and a device. It takes the device object from the internal data list. It first checks that the plugin is valid, if it is a SailfishOS plugin, the factory is used. If the plugin is a SailfishOS plugin, the factory is used to create the plugin based on the service name, the outgoing interfaces of the plugin are added to the outgoing interfaces list.
QStringList PluginLoader::incomingCapabilities() const
{
    QSet ret;
    for (const KPluginMetaData &service : qAsConst(plugins)) {
        QStringList rawValues = service.value(QStringLiteral("X-KdeConnect-SupportedPacketType"), QStringList());
        ret += QSet(rawValues.begin(), rawValues.end());
    }
    return ret.values();
}
Returns a list of all incoming capabilities as QStringList.
QStringList PluginLoader::outgoingCapabilities() const
{
    QSet ret;
    for (const KPluginMetaData &service : qAsConst(plugins)) {
        QStringList rawValues = service.value(QStringLiteral("X-KdeConnect-OutgoingPacketType"), QStringList());
        ret += QSet(rawValues.begin(), rawValues.end());
    }
    return ret.values();
}
Returns a list of all outgoing capabilities.
QSet PluginLoader::pluginsForCapabilities(const QSet &incoming, const QSet &outgoing)
{
    QSet ret;

    QString myDeviceType = KdeConnectConfig::instance().deviceType();

    for (const KPluginMetaData &service : qAsConst(plugins)) {

        // Check if the plugin support this device type
        const QSet supportedDeviceTypes = KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-SupportedDeviceTypes")).toSet();
        if (!supportedDeviceTypes.isEmpty()) {
            if (!supportedDeviceTypes.contains(myDeviceType)) {
                qCDebug(KDECONNECT_CORE) << "Not loading plugin" << service.pluginId() << "because this device of type" << myDeviceType << "is not supported. Supports:" << supportedDeviceTypes.toList().join(QStringLiteral(", "));
                continue;
            }
        }

        // Check if capbilites intersect with the remote device
        const QSet pluginIncomingCapabilities =
            KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-SupportedPacketType")).toSet();
        const QSet pluginOutgoingCapabilities =
            KPluginMetaData::readStringList(service.rawData(), QStringLiteral("X-KdeConnect-OutgoingPacketType")).toSet();

        bool capabilitiesEmpty = (pluginIncomingCapabilities.isEmpty() && pluginOutgoingCapabilities.isEmpty());
        if (!capabilitiesEmpty) {
    #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
            bool capabilitiesIntersect = (outgoing.intersects(pluginIncomingCapabilities) || incoming.intersects(pluginOutgoingCapabilities));
    #else
            QSet commonIncoming = incoming;
            commonIncoming.intersect(pluginOutgoingCapabilities);
            QSet commonOutgoing = outgoing;
            commonOutgoing.intersect(pluginIncomingCapabilities);
            bool capabilitiesIntersect = (!commonIncoming.isEmpty() || !commonOutgoing.isEmpty());
    #endif

            if (!capabilitiesIntersect) {
                qCDebug(KDECONNECT_CORE) << "Not loading plugin" << service.pluginId() << "because device doesn't support it";
                continue;
            }
        }

        // If we get here, the plugin can be loaded
        ret += service.pluginId();

    }

    return ret;
}
This function returns a set of plugins that can handle incoming and outgoing capabilities. First, the function checks if the plugin is supported and supported by the current device type, because the remote device doesn't support it, because the plugin is not supported. Next, it iterates over the available plugins and checks if the incoming and outgoing capabilities intersect with the remote device, since the plugins can handle incoming and outgoing capabilities. Next, it iterates over the plugins and checks if the capabilities intersect. If the capabilities intersect, the plugin is loaded. If the plugin doesn't support the device type, the function returns null.