diff --git a/discover/main.cpp b/discover/main.cpp index f3225b33..c75d35a9 100644 --- a/discover/main.cpp +++ b/discover/main.cpp @@ -1,184 +1,185 @@ /* * Copyright (C) 2012 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // #define QT_QML_DEBUG #include #include #include #include #include #include #include #include #include "DiscoverObject.h" #include #include "DiscoverVersion.h" #include #include typedef QHash StringCompactMode; Q_GLOBAL_STATIC_WITH_ARGS(StringCompactMode, s_decodeCompactMode, (StringCompactMode { { QLatin1String("auto"), DiscoverObject::Auto }, { QLatin1String("compact"), DiscoverObject::Compact }, { QLatin1String("full"), DiscoverObject::Full } })) QCommandLineParser* createParser() { QCommandLineParser* parser = new QCommandLineParser; parser->addOption(QCommandLineOption(QStringLiteral("application"), i18n("Directly open the specified application by its package name."), QStringLiteral("name"))); parser->addOption(QCommandLineOption(QStringLiteral("mime"), i18n("Open with a program that can deal with the given mimetype."), QStringLiteral("name"))); parser->addOption(QCommandLineOption(QStringLiteral("category"), i18n("Display a list of entries with a category."), QStringLiteral("name"))); parser->addOption(QCommandLineOption(QStringLiteral("mode"), i18n("Open Discover in a said mode. Modes correspond to the toolbar buttons."), QStringLiteral("name"))); parser->addOption(QCommandLineOption(QStringLiteral("listmodes"), i18n("List all the available modes."))); parser->addOption(QCommandLineOption(QStringLiteral("compact"), i18n("Compact Mode (auto/compact/full)."), QStringLiteral("mode"), QStringLiteral("auto"))); parser->addOption(QCommandLineOption(QStringLiteral("local-filename"), i18n("Local package file to install"), QStringLiteral("package"))); parser->addOption(QCommandLineOption(QStringLiteral("listbackends"), i18n("List all the available backends."))); parser->addOption(QCommandLineOption(QStringLiteral("search"), i18n("Search string."), QStringLiteral("text"))); parser->addOption(QCommandLineOption(QStringLiteral("feedback"), i18n("Lists the available options for user feedback"))); parser->addOption(QCommandLineOption(QStringLiteral("test"), QStringLiteral("Test file"), QStringLiteral("file.qml"))); parser->addPositionalArgument(QStringLiteral("urls"), i18n("Supports appstream: url scheme")); DiscoverBackendsFactory::setupCommandLine(parser); KAboutData::applicationData().setupCommandLine(parser); return parser; } void processArgs(QCommandLineParser* parser, DiscoverObject* mainWindow) { if(parser->isSet(QStringLiteral("application"))) mainWindow->openApplication(QUrl(parser->value(QStringLiteral("application")))); else if(parser->isSet(QStringLiteral("mime"))) mainWindow->openMimeType(parser->value(QStringLiteral("mime"))); else if(parser->isSet(QStringLiteral("category"))) mainWindow->openCategory(parser->value(QStringLiteral("category"))); if(parser->isSet(QStringLiteral("mode"))) mainWindow->openMode(parser->value(QStringLiteral("mode"))); else mainWindow->openMode(QStringLiteral("Browsing")); if(parser->isSet(QStringLiteral("search"))) Q_EMIT mainWindow->openSearch(parser->value(QStringLiteral("search"))); if(parser->isSet(QStringLiteral("local-filename"))) mainWindow->openLocalPackage(QUrl::fromUserInput(parser->value(QStringLiteral("local-filename")), {}, QUrl::AssumeLocalFile)); foreach(const QString &arg, parser->positionalArguments()) { const QUrl url = QUrl::fromUserInput(arg, {}, QUrl::AssumeLocalFile); if (url.isLocalFile()) mainWindow->openLocalPackage(url); else if (url.scheme() == QLatin1String("apt")) Q_EMIT mainWindow->openSearch(url.host()); else mainWindow->openApplication(url); } } int main(int argc, char** argv) { QApplication app(argc, argv); app.setWindowIcon(QIcon::fromTheme(QStringLiteral("plasmadiscover"))); app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); + app.setAttribute(Qt::AA_DisableSessionManager, true); KCrash::initialize(); KQuickAddons::QtQuickSettings::init(); KLocalizedString::setApplicationDomain("plasma-discover"); KAboutData about(QStringLiteral("discover"), i18n("Discover"), version, i18n("An application explorer"), KAboutLicense::GPL, i18n("© 2010-2019 Plasma Development Team")); about.addAuthor(i18n("Aleix Pol Gonzalez"), QString(), QStringLiteral("aleixpol@kde.org")); about.addAuthor(i18n("Jonathan Thomas"), QString(), QStringLiteral("echidnaman@kubuntu.org")); about.setProductName("discover/discover"); about.setProgramLogo(app.windowIcon()); about.setTranslator( i18ndc(nullptr, "NAME OF TRANSLATORS", "Your names"), i18ndc(nullptr, "EMAIL OF TRANSLATORS", "Your emails")); KAboutData::setApplicationData(about); DiscoverObject *mainWindow = nullptr; { QScopedPointer parser(createParser()); parser->process(app); about.processCommandLine(parser.data()); DiscoverBackendsFactory::processCommandLine(parser.data(), parser->isSet(QStringLiteral("test"))); if(parser->isSet(QStringLiteral("listbackends"))) { QTextStream(stdout) << i18n("Available backends:\n"); DiscoverBackendsFactory f; foreach(const QString& name, f.allBackendNames(false, true)) QTextStream(stdout) << " * " << name << '\n'; return 0; } if (parser->isSet(QStringLiteral("test"))) { QStandardPaths::setTestModeEnabled(true); } KDBusService* service = new KDBusService(KDBusService::Unique, &app); { auto options = parser->optionNames(); options.removeAll(QStringLiteral("backends")); options.removeAll(QStringLiteral("test")); QVariantMap initialProperties; if (!options.isEmpty() || !parser->positionalArguments().isEmpty()) initialProperties = {{QStringLiteral("currentTopLevel"), QStringLiteral("qrc:/qml/LoadingPage.qml")}}; mainWindow = new DiscoverObject(s_decodeCompactMode->value(parser->value(QStringLiteral("compact")), DiscoverObject::Full), initialProperties); } QObject::connect(&app, &QCoreApplication::aboutToQuit, mainWindow, &DiscoverObject::deleteLater); QObject::connect(service, &KDBusService::activateRequested, mainWindow, [mainWindow](const QStringList &arguments, const QString &/*workingDirectory*/){ if (!mainWindow->rootObject()) QCoreApplication::instance()->quit(); mainWindow->rootObject()->raise(); if (arguments.isEmpty()) return; QScopedPointer parser(createParser()); parser->parse(arguments); processArgs(parser.data(), mainWindow); }); processArgs(parser.data(), mainWindow); if(parser->isSet(QStringLiteral("listmodes"))) { QTextStream(stdout) << i18n("Available modes:\n"); foreach(const QString& mode, mainWindow->modes()) QTextStream(stdout) << " * " << mode << '\n'; delete mainWindow; return 0; } if(parser->isSet(QStringLiteral("feedback"))) { QTextStream(stdout) << mainWindow->describeSources() << '\n'; delete mainWindow; return 0; } if (parser->isSet(QStringLiteral("test"))) { const QUrl testFile = QUrl::fromUserInput(parser->value(QStringLiteral("test")), {}, QUrl::AssumeLocalFile); Q_ASSERT(!testFile.isEmpty() && testFile.isLocalFile()); mainWindow->loadTest(testFile); } } return app.exec(); } diff --git a/discover/org.kde.discover.appdata.xml b/discover/org.kde.discover.appdata.xml index f4a6bac7..3cb8d383 100644 --- a/discover/org.kde.discover.appdata.xml +++ b/discover/org.kde.discover.appdata.xml @@ -1,233 +1,235 @@ org.kde.discover.desktop CC0-1.0 GPL-2.0+ Discover استكشف Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discoperi Discover Discover 둘러보기 Discover ഡിസ്കവർ Ontdekken Discover ਖੋਜੋ Odkrywca Discover Discover Центр приложений Discover Discover Discover Oткривач Otkrivač Oткривач Otkrivač Discover Кашфиёт Keşfet Discover xxDiscoverxx 发现 探尋 Discover استكشف Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discover Discoperi Discover Discover 둘러보기 Discover ഡിസ്കവർ Ontdekken Discover ਖੋਜੋ Odkrywca Discover Discover Центр приложений Discover Discover Discover Oткривач Otkrivač Oткривач Otkrivač Discover Кашфиёт Keşfet Discover xxDiscoverxx 发现 探尋

Discover helps you find and install applications, games, and tools. You can search or browse by category, and look at screenshots and read reviews to help you pick the perfect app.

Discover ayúdate a alcontrar ya instalar aplicaciones y ferramientes. Pues guetar o restolar por estayes, mirar captures de pantalla y lleer reseñes p'ayudate a escoyer l'aplicación perfeuta.

El Discover us ajuda a instal·lar aplicacions, jocs i eines. Podeu cercar o explorar per categoria, i veure les captures de pantalla i llegir els comentaris per ajudar-vos a triar l'aplicació perfecta.

Discover us ajuda a instal·lar aplicacions, jocs i eines. Podeu buscar o explorar per categoria, i veure les captures de pantalla i llegir els comentaris per a ajudar-vos a triar l'aplicació perfecta.

Discover helps you find and install applications, games, and tools. You can search or browse by category, and look at screenshots and read reviews to help you pick the perfect app.

Discover le ayuda a encontrar e instalar aplicaciones, juegos y herramientas. Puede buscar o explorar por categorías, ver capturas de pantalla y leer reseñas que le ayudarán a escoger la aplicación perfecta.

Discover aitab leida ja paigaldada rakendusi, mänge ja tööriistu. Otsida või sirvida saab kategooriaid pidi, ekraanipiltide uurimine ja ülevaadete lugemine lubab leida just selle õige rakenduse.

Discover-rek aplikazioak, jokoak eta tresnak bilatzen eta instalatzen laguntzen dizu. Kategoria bidez bilatu eta arakatu dezakezu, eta pantaila-argazkiak ikusi eta iritziak irakur ditzakezu aplikazio egokia aukeratzen laguntzako.

Discover auttaa sinua etsimään ja asentamaan sovelluksia, pelejä ja apuohjelmia. Voit etsiä tai selata luokittain, katsella ruutukaappauskuvia ja lukea arvosteluja.

Discover vous aide à trouver et à installer des applications, des jeux et des outils. Vous pouvez chercher ou naviguer par catégorie, ou consulter les captures d'écran et les avis d'utilisateurs pour vous aider à trouver l'appli parfaite.

Discover axúdalle a atopar e instalar aplicacións, xogos e ferramentas. Pode buscar ou explorar por categoría, e ollar capturas de pantalla e ler recensións como axuda para escoller a aplicación perfecta.

A Discover segít az alkalmazások, játékok és eszközök keresésében és telepítésében. Kereshet vagy böngészhet kategóriák szerint, nézhet képernyőképeket és olvashat értékeléseket, hogy könnyen megtalálja a tökéletes alkalmazást.

Discover membantu kamu untuk menemukan dan menginstal aplikasi, permainan, dan peralatan. Kamu bisa mencari atau menelusuri berdasarkan kategori, dan melihat cuplikan dan membaca ulasan untuk membantu kamu memilih aplikasi yang sempurna.

Discover ti aiuta a trovare e installare applicazioni, giochi e strumenti. Puoi cercare o sfogliare per categoria, vedere schermate e leggere recensioni per aiutarti a scegliere l'applicazione perfetta.

둘러보기를 사용하여 프로그램, 게임, 도구를 찾고 설치할 수 있습니다. 분류별로 검색 및 탐색하고, 스크린샷과 리뷰를 참조하여 꼭 필요한 앱을 찾으십시오.

Discover padeda jums rasti ir įdiegti programas, žaidimus bei įrankius. Galite ieškoti ar naršyti pagal kategoriją ir žiūrėti ekrano kopijas bei skaityti apžvalgas, kurios padeda jums išsirinkti tobulą programą.

ആപ്ലിക്കേഷൻ, ഗെയിമുകൾ, ഉപകരണങ്ങൾ എന്നിവ കണ്ടെത്താനും ഇൻസ്റ്റാൾ ചെയ്യാനും ഡിസ്കവർ നിങ്ങളെ സഹായിക്കുന്നു. നിങ്ങൾക്ക് വിഭാഗം അനുസരിച്ച് തിരയാനോ ബ്രൗസുചെയ്യാനോ കഴിയും, കൂടാതെ മികച്ച ആപ്ലിക്കേഷൻ തിരഞ്ഞെടുക്കാൻ സഹായിക്കുന്നതിന് സ്‌ക്രീൻഷോട്ടുകൾ കാണാനും അവലോകനങ്ങൾ വായിക്കാനും കഴിയും.

Discover helpt om toepassingen, spellen en hulpmiddelen te zoeken en te installeren. U kunt zoeken of bladeren op categorie, kijken naar schermafdrukken en reviews lezen om u te helpen de perfecte toepassing te kiezen.

Discover hjelper deg å finna og installera program, spel og verktøy. Du kan søkja etter program eller bla gjennom program­kategoriar, sjå skjermbilete og lesa brukaromtalar for å finna det perfekte programmet.

Odkrywca pomaga znaleźć i wgrać aplikacje, gry oraz narzędzia. Można wyszukiwać lub przeglądać po kategorii. Można oglądać zrzuty ekranu i czytać oceny, aby wybrać najlepszą aplikację.

O Discover ajuda-o a instalar aplicações, jogos e ferramentas. Poderá pesquisar ou navegar por categoria, assim como ver as imagens e ler as revisões e comentários que o possam ajudar a escolher a aplicação perfeita.

O Discover ajuda-o a encontrar e instalar aplicativos, jogos e ferramentas. Você pode pesquisar ou navegar por categoria, assim como ver as capturas de tela e ler as avaliações e comentários que o possam ajudá-lo a escolher o aplicativo perfeito.

Центр программ Discover предназначен для поиска, в числе по категориям и установки приложений и игр. Описания приложений содержат снимки экрана и отзывы других пользователей.

Aplikácia Discover vám pomôže nájsť a nainštalovať aplikácie, hry a nástroje. Môžete vyhľadávať alebo prehliadať podľa kategórií, prezerať si snímky obrazoviek a čítať recenzie, ktoré vám pomôžu vybrať si perfektnú aplikáciu.

-

Discover vam pomaga najti in namestiti programe, igre in orodja. Lahko iščete ali brskate po kategorijah in si ogledate posnetke zaslona in preberete ocene, ki vam pomagajo izbrati popoln program.

Discover hjälper till att hitta och installera program, spel och verktyg. Man kan söka eller bläddra enligt kategori, och titta på skärmbilder och läsa recensioner för att hjälpa till att välja det perfekta programmet.

Кашфиёт ба шумо барои ёфтан ва насб кардани барномаҳо, бозиҳо ва абзорҳо кумак мерасонад. Шумо метавонед барномаҳоро аз рӯи навъ ҷустуҷӯ карда, аз назар гузаронед, аксҳои барномаҳоро бинед ва барои кумак ҳангоми интихоб намудани барномаҳои беҳтарин тақризҳоро хонед.

Discover допоможе вам знайти і встановити програми, ігри та інструменти. Ви можете виконувати пошук або навігацію за категоріями, переглядати знімки вікон і читати рецензії, щоб вибрати ту програму, яка пасує вам найкраще.

xxDiscover helps you find and install applications, games, and tools. You can search or browse by category, and look at screenshots and read reviews to help you pick the perfect app.xx

Discover 帮助您发现和安装应用,游戏和工具。您可以搜索关键字或浏览分类,通过屏幕截图和用户评论来选择最佳应用。

《探尋》協助您尋找及安裝應用程式、遊戲及工具。您可以搜尋,或者是瀏覽分類,看看螢幕截圖和閱讀評論,以協助你找到最好的 App。

With Discover, you can manage software from multiple sources, including your operating system's software repository, Flatpak repos, the Snap store, or even AppImages from store.kde.org.

Amb el Discover podeu gestionar programari de diverses fonts, incloent-hi el repositori de programari del sistema operatiu, repositoris del Flatpak, la botiga de l'Snap, o també AppImages des de «store.kde.org».

Amb Discover podeu gestionar programari de diverses fonts, incloent-hi el repositori de programari del sistema operatiu, repositoris de Flatpak, la botiga d'Snap, o també AppImages des de «store.kde.org».

With Discover, you can manage software from multiple sources, including your operating system's software repository, Flatpak repos, the Snap store, or even AppImages from store.kde.org.

Con Discover puede gestionar software de múltiples fuentes, incluyendo los repositorios de software de su sistema operativo, repositorios de Flatpak, la tienda de Snap, e incluso AppImages de store.kde.org.

Discoveriga saab hallata tarkvara, mis on pärit mitmest allikast, kaasa arvatud su enda operatsioonisüsteemi tarkvarahoidla, Flatpaki hoidlad, Snapi hoidla või isegi hoidlas store.kde.org pakutavad AppImage'id.

Discover-rekin, sorburu askotako softwarea kudeatu dezakezu, tartean zure sistema eragilearen software gordetegia, Flatpak gordetegiak, Snap biltegia, edo baita store.kde.org-eko AppImages-etik ere.

Discoverilla voit hallita ohjelmia eri lähteistä: käyttöjärjestelmän tai Flatpakin asennuslähteistä, Snap-kaupasta tai jopa store.kde.orgin AppImageista.

Grâce à Discover, vous pouvez gérer des logiciels provenant de plusieurs sources, dont le dépôt de logiciels de votre système d'exploitation, les dépôts Flatpak, la boutique Snap ou même des AppImages de store.kde.org.

Con Discover pode xestionar software de varias fontes, entre elas o repositorio de software do seu sistema operativo, os repositorios de Flatpak, a tenda de Snap, ou mesmo as AppImage de store.kde.org.

A Discoverrel egy helyen kezelheti több forrásból származó szoftvereit, legyen szó akár az operációs rendszer tárolóiról, a Flatpak tárolókról a Snap boltról vagy akár a store.kde.org-ról származó AppImage-ekről.

Dengan Discover, kamu bisa mengelola perangkat lunak dari banyak sumber, termasuk repositori perangkat lunak operasi sistemmu, repo Flatpak, Snap store, atau bahkan AppImages dari store.kde.org.

Con Discover, puoi gestire i programmi da diverse fonti, inclusi i depositi del software del tuo sistema operativo, depositi Flatpak, lo Snap Store o anche AppImages da store.kde.org.

둘러보기를 사용하면 여러 원본에서 제공되는 소프트웨어를 관리할 수 있습니다. 운영 체제 소프트웨어 저장소, Flatpak 저장소, Snap 스토어, store.kde.org의 AppImage를 한 곳에서 관리할 수 있습니다.

Naudodami Discover, galite tvarkyti programinę įrangą iš daugelio šaltinių, įskaitant jūsų operacinės sistemos programinės įrangos saugyklas, Flatpak saugyklas, Snap parduotuvę ar netgi AppImage paketus iš store.kde.org.

ഡിസ്കവർ ഉപയോഗിച്ച്, നിങ്ങളുടെ പ്രവര്‍ത്തകസംവിധാനത്തിന്റെ സോഫ്റ്റ്വെയർ ശേഖരം, ഫ്ലാറ്റ്പ്പാക്ക് ശേഖരണങ്ങൾ, സ്നാപ്പ് സ്റ്റോർ അല്ലെങ്കിൽ store.kde.org- ൽ നിന്നുള്ള AppImages എന്നിവ ഉൾപ്പെടെ ഒന്നിലധികം ഉറവിടങ്ങളിൽ നിന്നുള്ള സോഫ്റ്റ്വെയർ നിങ്ങൾക്ക് കൈകാര്യം ചെയ്യാൻ കഴിയും.

Met Discover kunt u software uit meerdere bronnen beheren, inclusief de opslagruimten van uw besturingssysteem, Flatpak-repo's, de Snap store of zelfs AppImages uit store.kde.org.

Men Discover kan du installera program frå ulike kjelder, blant anna standard pakkebrønnar for operativsystemet, Flatpak-depot, Snap-butikken og AppImages frå store.kde.org.

Dzięki Odkrywcy, można zarządzać oprogramowaniem z wielu źródeł, uwzględniając w tym źródło oprogramowania systemu operacyjnego, repozytoria Flatpak, sklep Snap lub nawet AppImage z store.kde.org.

Com o Discover, poderá gerir aplicações de várias fontes, incluindo os repositórios de aplicações do seu sistema operativo, os repositórios do Flatpak, a loja do Snap ou mesmo AppImages do store.kde.org.

Com o Discover você poderá gerenciar aplicativos de várias origens, incluindo os repositórios de aplicativos do seu sistema operacional, os repositórios do Flatpak, a loja do Snap ou mesmo AppImages do store.kde.org.

Discover позволяет управлять приложениями, получаемыми из различных источников: репозиториев установленной системы, репозиториев Flatpack, магазина приложений Snap, и даже приложений AppImages, расположенных по адресу store.kde.org.

S Discover môžete spravovať softvér z viacerých zdrojov vrátane úložiska softvéru vášho operačného systému, úložiska Flatpak, úložiska Snap alebo dokonca AppImages z obchodu store.kde.org.

S programom Discover lahko upravljate programsko opremo iz več virov, vključno s svojo shrambo programske opreme operacijskega sistema, programske pakete Flatpak, trgovina Snap ali celo programe AppImages iz store.kde.org.

Man kan hantera programvara från flera olika källor med Discover, inklusive operativsystemets programvaruarkiv, Flatpak-arkiv, Snap-butiken eller till och med programavbilder från store.kde.org.

Ба воситаи Кашфиёт шумо метавонед нармафзорро аз якчанд манбаъ идора намоед, аз он ҷумла тавассути анбори додаҳои нармафзори низоми шумо, анборҳои додаҳои Flatpak, дукони Snap ё ҳатто файлҳои AppImages аз сомонаи store.kde.org.

За допомогою Discover ви можете керувати програмним забезпеченням із багатьох джерел, зокрема зі сховищ програмного забезпечення вашої операційної системи, зі сховищ Flatpak, із крамниці Snap або навіть із образів AppImage з store.kde.org.

xxWith Discover, you can manage software from multiple sources, including your operating system's software repository, Flatpak repos, the Snap store, or even AppImages from store.kde.org.xx

使用 Discover,您可以管理来自不同来源的软件,包括系统软件源,Flatpak 仓库,Snap 商店,或者来自 store.kde.org 的 AppImage。

使用《探尋》,您可以管理來自多個來源的軟體,包含您作業系統的軟體庫、Flatpak 軟體庫、Snap 商店,或甚至是來自 store.kde.org 的 AppImage。

Finally, Discover also allows you to find, install, and manage add-ons for Plasma and all your favorite KDE apps!

P'acabar, Discover tamién te permite alcontrar, instalar y xestionar complementos de Plasma y toles aplicaciones de KDE.

Finalment, el Discover també permet cercar, instal·lar i gestionar complements per al Plasma i totes les aplicacions preferides del KDE!

Finalment, Discover també permet buscar, instal·lar i gestionar complements per a Plasma i totes les aplicacions preferides de KDE.

Finally, Discover also allows you to find, install, and manage add-ons for Plasma and all your favourite KDE apps!

Por último, Discover también le permite encontrar, instalar y gestionar complementos para Plasma y para todas sus aplicaciones favoritas de KDE.

Ja lõpuks lubab Discover leida, paigaldada ja hallata lisasid nii Plasmale kui ka kõigile KDE parimatele rakendustele!

Azkenik, Discover-rek uzten dizu Plasma eta zure KDEko aplikazio gogoko guztietarako gehigarriak bilatu, instalatu eta kudeatzen.

Discoverilla voit myös etsiä, asentaa ja hallita Plasman laajennusosia ja kaikkia suosikki-KDE-sovelluksiasi!

Enfin, Discover vous permet de trouver, d'installer et de gérer des modules complémentaires pour Plasma et toutes vos applis KDE préférées !

Por último, Discover tamén lle permite atopar, instalar e xestionar complementos de Plasma e das súas aplicacións favoritas de KDE!

Végül, de nem utolsósorban a Discoverrel kereshet, telepíthet és kezelhet kiegészítőket a Plasmához és a kedvenc KDE alkalmazásaihoz!

Terakhir, Discover juga memungkinkan kamu untuk menemukan, menginstal, dan mengelola pernik untuk Plasma dan semua aplikasi KDE favoritmu.

Infine, Discover ti consente di trovare, installare e gestire componenti aggiuntivi di Plasma e tutte le tue applicazioni preferite di KDE!

또한 둘러보기를 사용하면 Plasma와 KDE 프로그램의 추가 기능을 찾고, 설치하고, 관리할 수 있습니다!

Pagaliau, Discover taip pat leidžia jums rasti, įdiegti ir tvarkyti Plasma papildinius ir visas jūsų mėgstamas KDE programas!

അവസാനമായി, പ്ലാസ്മയ്ക്കും നിങ്ങളുടെ പ്രിയപ്പെട്ട എല്ലാ കെ‌ഡി‌ഇ അപ്ലിക്കേഷനുകൾക്കുമായുള്ള ആഡ്-ഓണുകൾ കണ്ടെത്താനും ഇൻസ്റ്റാൾ ചെയ്യാനും നിയന്ത്രിക്കാനും ഡിസ്കവർ നിങ്ങളെ അനുവദിക്കുന്നു!

Tenslotte biedt Discover ook om add-ons voor Plasma en al uw favoriete KDE toepassingen te vinden, te installeren en te beheren!

Discover lèt deg òg finna, installera og handsama programtillegg for Plasma og andre KDE-program.

Odkrywca umożliwia także znalezienie, wgranie i zrządzanie dodatkami do Plazmy i innych ulubionych aplikacji KDE!

Finalmente, o Discover também lhe permite procurar, instalar e gerir as extensões do Plasma e de todas as suas aplicações favoritas do KDE!

Finalmente, o Discover também lhe permite procurar, instalar e gerenciar as extensões do Plasma e de todos os seus aplicativos favoritos do KDE!

Кроме того, при помощи Discover возможно искать, устанавливать и управлять дополнениями рабочей среды Plasma и приложений KDE.

Konečne aplikácia Discover tiež umožňuje nájsť, nainštalovať a spravovať doplnky pre Plasmu a všetky vaše obľúbené aplikácie prostredia KDE!

+<<<<<<< HEAD

Končno Discover vam omogoča iskanje, namestitev in upravljanje dodatkov za Plazma in vse vaše najljubše programe za KDE!

+======= +>>>>>>> Plasma/5.18

Slutligen låter Discovery dig söka efter, installera och hantera tilläggsprogram för Plasma och alla favoritprogram i KDE.

Дар охир, Кашфиёт ба шумо барои ёфтан, насб ва идора кардани барномаҳои иловагӣ барои Плазма ва ҳамаи барномаҳои пазируфтаи шумо аз KDE имкон медиҳад!

Нарешті, Discover надає вам змогу шукати, встановлювати додатки до Плазми та усі ваші улюблені програми KDE та керувати ними!

xxFinally, Discover also allows you to find, install, and manage add-ons for Plasma and all your favorite KDE apps!xx

最后,Discover 还可以帮您找到,安装和管理 Plasma 附加组件,以及您喜爱的 KDE 应用!

最後,《探尋》也讓你能尋找、安裝和管理 Plasma 的外掛程式及你所有喜歡的 KDE 應用程式!

https://bugs.kde.org/enter_bug.cgi?format=guided&product=Discover Plasma Discover Plasma Discover Discover del Plasma Discover de Plasma Plasma Discover Plasma Discover Plasma Discover Plasma Discover Plasma Discover Plasma Discover Plasma Discover Plasma Discover Plasma Discover Plasma Discover (Discoperi de Plasma) Discover Plasma Plasma Discover Plasma 둘러보기 Plasma Discover പ്ലാസ്മ ഡിസ്കവർ Plasma Discover Plasma Discover Odkrywca Plazmy Plasma Discover Plasma Discover Центр приложений Discover Plasma Discover Plasma Discover Plasma Discover Кашфиёти Плазма Discover у Плазмі xxPlasma Discoverxx Plasma Discover Plasma 探詢 https://cdn.kde.org/screenshots/plasma-discover/plasma-discover.png KDE plasma-discover
diff --git a/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp b/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp index dc501085..c0e1d3b9 100644 --- a/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp +++ b/libdiscover/backends/FlatpakBackend/FlatpakBackend.cpp @@ -1,1330 +1,1334 @@ /*************************************************************************** * Copyright © 2013 Aleix Pol Gonzalez * * Copyright © 2017 Jan Grulich * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "FlatpakBackend.h" #include "FlatpakFetchDataJob.h" #include "FlatpakResource.h" #include "FlatpakSourcesBackend.h" #include "FlatpakJobTransaction.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DISCOVER_BACKEND_PLUGIN(FlatpakBackend) QDebug operator<<(QDebug debug, const FlatpakResource::Id& id) { QDebugStateSaver saver(debug); debug.nospace() << "FlatpakResource::Id("; debug.nospace() << "name:" << id.id << ','; debug.nospace() << "branch:" << id.branch << ','; debug.nospace() << "origin:" << id.origin << ','; debug.nospace() << "type:" << id.type; debug.nospace() << ')'; return debug; } static FlatpakResource::Id idForInstalledRef(FlatpakInstallation *installation, FlatpakInstalledRef *ref, const QString &postfix) { const FlatpakResource::ResourceType appType = flatpak_ref_get_kind(FLATPAK_REF(ref)) == FLATPAK_REF_KIND_APP ? FlatpakResource::DesktopApp : FlatpakResource::Runtime; const QString appId = QLatin1String(flatpak_ref_get_name(FLATPAK_REF(ref))) + postfix; const QString arch = QString::fromUtf8(flatpak_ref_get_arch(FLATPAK_REF(ref))); const QString branch = QString::fromUtf8(flatpak_ref_get_branch(FLATPAK_REF(ref))); return { installation, QString::fromUtf8(flatpak_installed_ref_get_origin(ref)), appType, appId, branch, arch }; } FlatpakBackend::FlatpakBackend(QObject* parent) : AbstractResourcesBackend(parent) , m_updater(new StandardBackendUpdater(this)) , m_reviews(AppStreamIntegration::global()->reviews()) , m_refreshAppstreamMetadataJobs(0) , m_cancellable(g_cancellable_new()) , m_threadPool(new QThreadPool(this)) { g_autoptr(GError) error = nullptr; connect(m_updater, &StandardBackendUpdater::updatesCountChanged, this, &FlatpakBackend::updatesCountChanged); // Load flatpak installation if (!setupFlatpakInstallations(&error)) { qWarning() << "Failed to setup flatpak installations:" << error->message; } else { loadAppsFromAppstreamData(); m_sources = new FlatpakSourcesBackend(m_installations, this); SourcesModel::global()->addSourcesBackend(m_sources); } connect(m_reviews.data(), &OdrsReviewsBackend::ratingsReady, this, [this] { m_reviews->emitRatingFetched(this, kTransform>(m_resources.values(), [] (AbstractResource* r) { return r; })); }); /* Override the umask to 022 to make it possible to share files between * the plasma-discover process and flatpak system helper process. * * See https://github.com/flatpak/flatpak/pull/2856/ */ umask(022); } FlatpakBackend::~FlatpakBackend() { g_cancellable_cancel(m_cancellable); m_threadPool.waitForDone(200); m_threadPool.clear(); for(auto inst : qAsConst(m_installations)) g_object_unref(inst); g_object_unref(m_cancellable); } bool FlatpakBackend::isValid() const { return m_sources && !m_installations.isEmpty(); } class FlatpakFetchRemoteResourceJob : public QNetworkAccessManager { Q_OBJECT public: FlatpakFetchRemoteResourceJob(const QUrl &url, FlatpakBackend *backend) : QNetworkAccessManager(backend) , m_backend(backend) , m_url(url) { } void start() { QNetworkRequest req(m_url); req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); auto replyGet = get(req); connect(replyGet, &QNetworkReply::finished, this, [this, replyGet] { QScopedPointer replyPtr(replyGet); const QUrl originalUrl = replyGet->request().url(); if (replyGet->error() != QNetworkReply::NoError) { qWarning() << "couldn't download" << originalUrl << replyGet->errorString(); Q_EMIT jobFinished(false, nullptr); return; } const QUrl fileUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1Char('/') + originalUrl.fileName()); auto replyPut = put(QNetworkRequest(fileUrl), replyGet->readAll()); connect(replyPut, &QNetworkReply::finished, this, [this, originalUrl, fileUrl, replyPut]() { QScopedPointer replyPtr(replyPut); if (replyPut->error() != QNetworkReply::NoError) { qWarning() << "couldn't save" << originalUrl << replyPut->errorString(); Q_EMIT jobFinished(false, nullptr); return; } if (!fileUrl.isLocalFile()) { Q_EMIT jobFinished(false, nullptr); return; } FlatpakResource *resource = nullptr; if (fileUrl.path().endsWith(QLatin1String(".flatpak"))) { resource = m_backend->addAppFromFlatpakBundle(fileUrl); } else if (fileUrl.path().endsWith(QLatin1String(".flatpakref"))) { resource = m_backend->addAppFromFlatpakRef(fileUrl); } else if (fileUrl.path().endsWith(QLatin1String(".flatpakrepo"))) { resource = m_backend->addSourceFromFlatpakRepo(fileUrl); } if (resource) { resource->setResourceFile(originalUrl); Q_EMIT jobFinished(true, resource); } else { qWarning() << "couldn't create resource from" << fileUrl.toLocalFile(); Q_EMIT jobFinished(false, nullptr); } } ); }); } Q_SIGNALS: void jobFinished(bool success, FlatpakResource *resource); private: FlatpakBackend *const m_backend; const QUrl m_url; }; FlatpakRemote * FlatpakBackend::getFlatpakRemoteByUrl(const QString &url, FlatpakInstallation *installation) const { auto remotes = flatpak_installation_list_remotes(installation, m_cancellable, nullptr); if (!remotes) { return nullptr; } const QByteArray comparableUrl = url.toUtf8(); for (uint i = 0; i < remotes->len; i++) { FlatpakRemote *remote = FLATPAK_REMOTE(g_ptr_array_index(remotes, i)); if (comparableUrl == flatpak_remote_get_url(remote)) { return remote; } } return nullptr; } FlatpakInstalledRef * FlatpakBackend::getInstalledRefForApp(FlatpakInstallation *flatpakInstallation, FlatpakResource *resource) const { FlatpakInstalledRef *ref = nullptr; g_autoptr(GError) localError = nullptr; if (!flatpakInstallation) { return ref; } const auto type = resource->resourceType() == FlatpakResource::DesktopApp ? FLATPAK_REF_KIND_APP : FLATPAK_REF_KIND_RUNTIME; return flatpak_installation_get_installed_ref(flatpakInstallation, type, resource->flatpakName().toUtf8().constData(), resource->arch().toUtf8().constData(), resource->branch().toUtf8().constData(), m_cancellable, &localError); } FlatpakResource * FlatpakBackend::getAppForInstalledRef(FlatpakInstallation *flatpakInstallation, FlatpakInstalledRef *ref) const { auto r = m_resources.value(idForInstalledRef(flatpakInstallation, ref, {})); if (!r) r = m_resources.value(idForInstalledRef(flatpakInstallation, ref, QStringLiteral(".desktop"))); // if (!r) { // qDebug() << "no" << flatpak_ref_get_name(FLATPAK_REF(ref)); // } return r; } FlatpakResource * FlatpakBackend::getRuntimeForApp(FlatpakResource *resource) const { FlatpakResource *runtime = nullptr; const QString runtimeName = resource->runtime(); const auto runtimeInfo = runtimeName.splitRef(QLatin1Char('/')); if (runtimeInfo.count() != 3) { return runtime; } for(auto it = m_resources.constBegin(), itEnd = m_resources.constEnd(); it!=itEnd; ++it) { const auto& id = it.key(); if (id.type == FlatpakResource::Runtime && id.id == runtimeInfo.at(0) && id.branch == runtimeInfo.at(2)) { runtime = *it; break; } } // TODO if runtime wasn't found, create a new one from available info if (!runtime) { qWarning() << "could not find runtime" << runtimeInfo << resource; } return runtime; } FlatpakResource * FlatpakBackend::addAppFromFlatpakBundle(const QUrl &url) { g_autoptr(GBytes) appstreamGz = nullptr; g_autoptr(GError) localError = nullptr; g_autoptr(GFile) file = nullptr; g_autoptr(FlatpakBundleRef) bundleRef = nullptr; AppStream::Component asComponent; file = g_file_new_for_path(url.toLocalFile().toUtf8().constData()); bundleRef = flatpak_bundle_ref_new(file, &localError); if (!bundleRef) { qWarning() << "Failed to load bundle:" << localError->message; return nullptr; } g_autoptr(GBytes) metadata = flatpak_bundle_ref_get_metadata(bundleRef); appstreamGz = flatpak_bundle_ref_get_appstream(bundleRef); if (appstreamGz) { g_autoptr(GZlibDecompressor) decompressor = nullptr; g_autoptr(GInputStream) streamGz = nullptr; g_autoptr(GInputStream) streamData = nullptr; g_autoptr(GBytes) appstream = nullptr; /* decompress data */ decompressor = g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); streamGz = g_memory_input_stream_new_from_bytes (appstreamGz); if (!streamGz) { return nullptr; } streamData = g_converter_input_stream_new (streamGz, G_CONVERTER (decompressor)); appstream = g_input_stream_read_bytes (streamData, 0x100000, m_cancellable, &localError); if (!appstream) { qWarning() << "Failed to extract appstream metadata from bundle:" << localError->message; return nullptr; } gsize len = 0; gconstpointer data = g_bytes_get_data(appstream, &len); AppStream::Metadata metadata; metadata.setFormatStyle(AppStream::Metadata::FormatStyleCollection); AppStream::Metadata::MetadataError error = metadata.parse(QString::fromUtf8((char*)data, len), AppStream::Metadata::FormatKindXml); if (error != AppStream::Metadata::MetadataErrorNoError) { qWarning() << "Failed to parse appstream metadata: " << error; return nullptr; } const QList components = metadata.components(); if (components.size()) { asComponent = AppStream::Component(components.first()); } else { qWarning() << "Failed to parse appstream metadata"; return nullptr; } } else { qWarning() << "No appstream metadata in bundle"; QTemporaryFile tempFile; tempFile.setAutoRemove(false); if (!tempFile.open()) { qWarning() << "Failed to get metadata file"; return nullptr; } gsize len = 0; QByteArray metadataContent = QByteArray((char *)g_bytes_get_data(metadata, &len)); tempFile.write(metadataContent); tempFile.close(); // Parse the temporary file QSettings setting(tempFile.fileName(), QSettings::NativeFormat); setting.beginGroup(QLatin1String("Application")); asComponent.setName(setting.value(QLatin1String("name")).toString()); tempFile.remove(); } FlatpakResource *resource = new FlatpakResource(asComponent, preferredInstallation(), this); gsize len = 0; QByteArray metadataContent = QByteArray((char *)g_bytes_get_data(metadata, &len)); if (!updateAppMetadata(resource, metadataContent)) { delete resource; qWarning() << "Failed to update metadata from app bundle"; return nullptr; } g_autoptr(GBytes) iconData = flatpak_bundle_ref_get_icon(bundleRef, 128); if (!iconData) { iconData = flatpak_bundle_ref_get_icon(bundleRef, 64); } if (iconData) { gsize len = 0; char * data = (char *)g_bytes_get_data(iconData, &len); QPixmap pixmap; pixmap.loadFromData(QByteArray(data, len), "PNG"); resource->setBundledIcon(pixmap); } const QString origin = QString::fromUtf8(flatpak_bundle_ref_get_origin(bundleRef)); resource->setDownloadSize(0); resource->setInstalledSize(flatpak_bundle_ref_get_installed_size(bundleRef)); resource->setPropertyState(FlatpakResource::DownloadSize, FlatpakResource::AlreadyKnown); resource->setPropertyState(FlatpakResource::InstalledSize, FlatpakResource::AlreadyKnown); resource->setFlatpakFileType(QStringLiteral("flatpak")); resource->setOrigin(origin.isEmpty() ? i18n("Local bundle") : origin); resource->setResourceFile(url); resource->setState(FlatpakResource::None); resource->setType(FlatpakResource::DesktopApp); addResource(resource); return resource; } FlatpakResource * FlatpakBackend::addAppFromFlatpakRef(const QUrl &url) { QSettings settings(url.toLocalFile(), QSettings::NativeFormat); const QString refurl = settings.value(QStringLiteral("Flatpak Ref/Url")).toString(); const QString name = settings.value(QStringLiteral("Flatpak Ref/Name")).toString(); auto item = m_sources->sourceByUrl(refurl); if (item) { const auto resources = resourcesByAppstreamName(name); for (auto resource : resources) { if (resource->origin() == item->data(AbstractSourcesBackend::IdRole)) { return static_cast(resource); } } } g_autoptr(GError) error = nullptr; g_autoptr(FlatpakRemoteRef) remoteRef = nullptr; { QFile f(url.toLocalFile()); if (!f.open(QFile::ReadOnly | QFile::Text)) { return nullptr; } QByteArray contents = f.readAll(); g_autoptr(GBytes) bytes = g_bytes_new (contents.data(), contents.size()); remoteRef = flatpak_installation_install_ref_file (preferredInstallation(), bytes, m_cancellable, &error); if (!remoteRef) { qWarning() << "Failed to create install ref file:" << error->message; const auto resources = resourcesByAppstreamName(name); if (!resources.isEmpty()) { return qobject_cast(resources.constFirst()); } return nullptr; } } const auto remoteName = flatpak_remote_ref_get_remote_name(remoteRef); auto ref = FLATPAK_REF(remoteRef); AppStream::Component asComponent; asComponent.addUrl(AppStream::Component::UrlKindHomepage, settings.value(QStringLiteral("Flatpak Ref/Homepage")).toString()); asComponent.setDescription(settings.value(QStringLiteral("Flatpak Ref/Description")).toString()); asComponent.setName(settings.value(QStringLiteral("Flatpak Ref/Title")).toString()); asComponent.setSummary(settings.value(QStringLiteral("Flatpak Ref/Comment")).toString()); asComponent.setId(name); const QString iconUrl = settings.value(QStringLiteral("Flatpak Ref/Icon")).toString(); if (!iconUrl.isEmpty()) { AppStream::Icon icon; icon.setKind(AppStream::Icon::KindRemote); icon.setUrl(QUrl(iconUrl)); asComponent.addIcon(icon); } auto resource = new FlatpakResource(asComponent, preferredInstallation(), this); resource->setFlatpakFileType(QStringLiteral("flatpakref")); resource->setOrigin(QString::fromUtf8(remoteName)); resource->updateFromRef(ref); QUrl runtimeUrl = QUrl(settings.value(QStringLiteral("Flatpak Ref/RuntimeRepo")).toString()); if (!runtimeUrl.isEmpty()) { auto installation = preferredInstallation(); // We need to fetch metadata to find information about required runtime auto fw = new QFutureWatcher(this); connect(fw, &QFutureWatcher::finished, this, [this, installation, resource, fw, runtimeUrl]() { const auto metadata = fw->result(); // Even when we failed to fetch information about runtime we still want to show the application if (metadata.isEmpty()) { onFetchMetadataFinished(installation, resource, metadata); } else { updateAppMetadata(resource, metadata); auto runtime = getRuntimeForApp(resource); if (!runtime || (runtime && !runtime->isInstalled())) { FlatpakFetchRemoteResourceJob *fetchRemoteResource = new FlatpakFetchRemoteResourceJob(runtimeUrl, this); connect(fetchRemoteResource, &FlatpakFetchRemoteResourceJob::jobFinished, this, [this, resource] (bool success, FlatpakResource *repoResource) { if (success) { installApplication(repoResource); } addResource(resource); }); fetchRemoteResource->start(); return; } else { addResource(resource); } } fw->deleteLater(); }); fw->setFuture(QtConcurrent::run(&m_threadPool, &FlatpakRunnables::fetchMetadata, installation, resource)); } else { addResource(resource); } return resource; } FlatpakResource * FlatpakBackend::addSourceFromFlatpakRepo(const QUrl &url) { Q_ASSERT(url.isLocalFile()); QSettings settings(url.toLocalFile(), QSettings::NativeFormat); const QString gpgKey = settings.value(QStringLiteral("Flatpak Repo/GPGKey")).toString(); const QString title = settings.value(QStringLiteral("Flatpak Repo/Title")).toString(); const QString repoUrl = settings.value(QStringLiteral("Flatpak Repo/Url")).toString(); if (gpgKey.isEmpty() || title.isEmpty() || repoUrl.isEmpty()) { return nullptr; } if (gpgKey.startsWith(QLatin1String("http://")) || gpgKey.startsWith(QLatin1String("https://"))) { return nullptr; } AppStream::Component asComponent; asComponent.addUrl(AppStream::Component::UrlKindHomepage, settings.value(QStringLiteral("Flatpak Repo/Homepage")).toString()); asComponent.setSummary(settings.value(QStringLiteral("Flatpak Repo/Comment")).toString()); asComponent.setDescription(settings.value(QStringLiteral("Flatpak Repo/Description")).toString()); asComponent.setName(title); asComponent.setId(settings.value(QStringLiteral("Flatpak Ref/Name")).toString()); const QString iconUrl = settings.value(QStringLiteral("Flatpak Repo/Icon")).toString(); if (!iconUrl.isEmpty()) { AppStream::Icon icon; icon.setKind(AppStream::Icon::KindRemote); icon.setUrl(QUrl(iconUrl)); asComponent.addIcon(icon); } auto resource = new FlatpakResource(asComponent, preferredInstallation(), this); // Use metadata only for stuff which are not common for all resources resource->addMetadata(QStringLiteral("gpg-key"), gpgKey); resource->addMetadata(QStringLiteral("repo-url"), repoUrl); resource->setBranch(settings.value(QStringLiteral("Flatpak Repo/DefaultBranch")).toString()); resource->setFlatpakName(url.fileName().remove(QStringLiteral(".flatpakrepo"))); resource->setType(FlatpakResource::Source); auto repo = flatpak_installation_get_remote_by_name(preferredInstallation(), resource->flatpakName().toUtf8().constData(), m_cancellable, nullptr); if (!repo) { resource->setState(AbstractResource::State::None); } else { resource->setState(AbstractResource::State::Installed); } return resource; } void FlatpakBackend::addResource(FlatpakResource *resource) { // Update app with all possible information we have if (!parseMetadataFromAppBundle(resource)) { qWarning() << "Failed to parse metadata from app bundle for" << resource->name(); } auto installation = resource->installation(); updateAppState(installation, resource); // This will update also metadata (required runtime) updateAppSize(installation, resource); m_resources.insert(resource->uniqueId(), resource); if (!resource->extends().isEmpty()) { m_extends.append(resource->extends()); m_extends.removeDuplicates(); } } class FlatpakSource { public: FlatpakSource(FlatpakRemote* remote) : m_remote(remote) {} bool isEnabled() const { return !flatpak_remote_get_disabled(m_remote); } QString appstreamDir() const { g_autoptr(GFile) appstreamDir = flatpak_remote_get_appstream_dir(m_remote, nullptr); if (!appstreamDir) { qWarning() << "No appstream dir for" << flatpak_remote_get_name(m_remote); return {}; } return QString::fromUtf8(g_file_get_path(appstreamDir)); } QString name() const { return QString::fromUtf8(flatpak_remote_get_name(m_remote)); } private: FlatpakRemote* m_remote; }; void FlatpakBackend::loadAppsFromAppstreamData() { for (auto installation : qAsConst(m_installations)) { // Load applications from appstream metadata if (g_cancellable_is_cancelled(m_cancellable)) break; if (!loadAppsFromAppstreamData(installation)) { qWarning() << "Failed to load packages from appstream data from installation" << installation; } } } bool FlatpakBackend::loadAppsFromAppstreamData(FlatpakInstallation *flatpakInstallation) { Q_ASSERT(flatpakInstallation); GPtrArray *remotes = flatpak_installation_list_remotes(flatpakInstallation, m_cancellable, nullptr); if (!remotes) { return false; } m_refreshAppstreamMetadataJobs += remotes->len; for (uint i = 0; i < remotes->len; i++) { FlatpakRemote *remote = FLATPAK_REMOTE(g_ptr_array_index(remotes, i)); g_autoptr(GFile) fileTimestamp = flatpak_remote_get_appstream_timestamp(remote, flatpak_get_default_arch()); QFileInfo fileInfo = QFileInfo(QString::fromUtf8(g_file_get_path(fileTimestamp))); // Refresh appstream metadata in case they have never been refreshed or the cache is older than 6 hours if (!fileInfo.exists() || fileInfo.lastModified().toUTC().secsTo(QDateTime::currentDateTimeUtc()) > 21600) { refreshAppstreamMetadata(flatpakInstallation, remote); } else { integrateRemote(flatpakInstallation, remote); } } return true; } void FlatpakBackend::metadataRefreshed() { m_refreshAppstreamMetadataJobs--; if (m_refreshAppstreamMetadataJobs == 0) { loadInstalledApps(); checkForUpdates(); } } void FlatpakBackend::integrateRemote(FlatpakInstallation *flatpakInstallation, FlatpakRemote *remote) { Q_ASSERT(m_refreshAppstreamMetadataJobs != 0); FlatpakSource source(remote); if (!source.isEnabled() || flatpak_remote_get_noenumerate(remote)) { metadataRefreshed(); return; } const QString appstreamDirPath = source.appstreamDir(); const QString appstreamIconsPath = source.appstreamDir() + QLatin1String("/icons/"); const QString appDirFileName = appstreamDirPath + QLatin1String("/appstream.xml.gz"); if (!QFile::exists(appDirFileName)) { qWarning() << "No" << appDirFileName << "appstream metadata found for" << source.name(); metadataRefreshed(); return; } auto fw = new QFutureWatcher>(this); const auto sourceName = source.name(); connect(fw, &QFutureWatcher>::finished, this, [this, fw, flatpakInstallation, appstreamIconsPath, sourceName]() { const auto components = fw->result(); foreach (const AppStream::Component& appstreamComponent, components) { FlatpakResource *resource = new FlatpakResource(appstreamComponent, flatpakInstallation, this); resource->setIconPath(appstreamIconsPath); resource->setOrigin(sourceName); addResource(resource); } metadataRefreshed(); acquireFetching(false); fw->deleteLater(); }); acquireFetching(true); fw->setFuture(QtConcurrent::run(&m_threadPool, [appDirFileName]() -> QList { AppStream::Metadata metadata; metadata.setFormatStyle(AppStream::Metadata::FormatStyleCollection); AppStream::Metadata::MetadataError error = metadata.parseFile(appDirFileName, AppStream::Metadata::FormatKindXml); if (error != AppStream::Metadata::MetadataErrorNoError) { qWarning() << "Failed to parse appstream metadata: " << error; return {}; } return metadata.components(); })); } void FlatpakBackend::loadInstalledApps() { for (auto installation : qAsConst(m_installations)) { // Load installed applications and update existing resources with info from installed application if (!loadInstalledApps(installation)) { qWarning() << "Failed to load installed packages from installation" << installation; } } } bool FlatpakBackend::loadInstalledApps(FlatpakInstallation *flatpakInstallation) { Q_ASSERT(flatpakInstallation); g_autoptr(GError) localError = nullptr; g_autoptr(GPtrArray) refs = flatpak_installation_list_installed_refs(flatpakInstallation, m_cancellable, &localError); if (!refs) { qWarning() << "Failed to get list of installed refs for listing updates:" << localError->message; return false; } const QString pathExports = FlatpakResource::installationPath(flatpakInstallation) + QLatin1String("/exports/"); const QString pathApps = pathExports + QLatin1String("share/applications/"); for (uint i = 0; i < refs->len; i++) { FlatpakInstalledRef *ref = FLATPAK_INSTALLED_REF(g_ptr_array_index(refs, i)); const auto name = QLatin1String(flatpak_ref_get_name(FLATPAK_REF(ref))); if (name.endsWith(QLatin1String(".Debug")) || name.endsWith(QLatin1String(".Locale")) || name.endsWith(QLatin1String(".BaseApp")) || name.endsWith(QLatin1String(".Docs"))) continue; const auto res = getAppForInstalledRef(flatpakInstallation, ref); if (res) { res->setState(AbstractResource::Installed); continue; } AppStream::Component cid; AppStream::Metadata metadata; const QString fnDesktop = pathApps + name + QLatin1String(".desktop"); AppStream::Metadata::MetadataError error = metadata.parseFile(fnDesktop, AppStream::Metadata::FormatKindDesktopEntry); if (error != AppStream::Metadata::MetadataErrorNoError) { if (QFile::exists(fnDesktop)) qDebug() << "Failed to parse appstream metadata:" << error << fnDesktop; cid.setId(QString::fromLatin1(flatpak_ref_get_name(FLATPAK_REF(ref)))); #if FLATPAK_CHECK_VERSION(1,1,2) cid.setName(QString::fromLatin1(flatpak_installed_ref_get_appdata_name(ref))); #endif } else cid = metadata.component(); FlatpakResource *resource = new FlatpakResource(cid, flatpakInstallation, this); resource->setIconPath(pathExports); resource->setState(AbstractResource::Installed); resource->setOrigin(QString::fromUtf8(flatpak_installed_ref_get_origin(ref))); resource->updateFromRef(FLATPAK_REF(ref)); addResource(resource); } return true; } void FlatpakBackend::loadLocalUpdates(FlatpakInstallation *flatpakInstallation) { g_autoptr(GError) localError = nullptr; g_autoptr(GPtrArray) refs = nullptr; refs = flatpak_installation_list_installed_refs(flatpakInstallation, m_cancellable, &localError); if (!refs) { qWarning() << "Failed to get list of installed refs for listing updates:" << localError->message; return; } for (uint i = 0; i < refs->len; i++) { FlatpakInstalledRef *ref = FLATPAK_INSTALLED_REF(g_ptr_array_index(refs, i)); const gchar *latestCommit = flatpak_installed_ref_get_latest_commit(ref); if (!latestCommit) { qWarning() << "Couldn't get latest commit for" << flatpak_ref_get_name(FLATPAK_REF(ref)); continue; } const gchar *commit = flatpak_ref_get_commit(FLATPAK_REF(ref)); if (g_strcmp0(commit, latestCommit) == 0) { continue; } FlatpakResource *resource = getAppForInstalledRef(flatpakInstallation, ref); if (resource) { resource->setState(AbstractResource::Upgradeable); updateAppSize(flatpakInstallation, resource); } } } void FlatpakBackend::loadRemoteUpdates(FlatpakInstallation* installation) { auto fw = new QFutureWatcher(this); connect(fw, &QFutureWatcher::finished, this, [this, installation, fw](){ g_autoptr(GPtrArray) refs = fw->result(); onFetchUpdatesFinished(installation, refs); fw->deleteLater(); acquireFetching(false); }); acquireFetching(true); fw->setFuture(QtConcurrent::run(&m_threadPool, [installation, this]() -> GPtrArray * { g_autoptr(GError) localError = nullptr; + if (g_cancellable_is_cancelled(m_cancellable)) { + qWarning() << "don't issue commands after cancelling"; + return {}; + } GPtrArray *refs = flatpak_installation_list_installed_refs_for_update(installation, m_cancellable, &localError); if (!refs) { qWarning() << "Failed to get list of installed refs for listing updates: " << localError->message; } return refs; })); } void FlatpakBackend::onFetchUpdatesFinished(FlatpakInstallation *flatpakInstallation, GPtrArray *fetchedUpdates) { if (!fetchedUpdates) { qWarning() << "could not get updates for" << flatpakInstallation; return; } for (uint i = 0; i < fetchedUpdates->len; i++) { FlatpakInstalledRef *ref = FLATPAK_INSTALLED_REF(g_ptr_array_index(fetchedUpdates, i)); FlatpakResource *resource = getAppForInstalledRef(flatpakInstallation, ref); if (resource) { resource->setState(AbstractResource::Upgradeable); updateAppSize(flatpakInstallation, resource); } else qWarning() << "could not find updated resource" << flatpak_ref_get_name(FLATPAK_REF(ref)) << m_resources.size(); } } bool FlatpakBackend::parseMetadataFromAppBundle(FlatpakResource *resource) { g_autoptr(FlatpakRef) ref = nullptr; g_autoptr(GError) localError = nullptr; AppStream::Bundle bundle = resource->appstreamComponent().bundle(AppStream::Bundle::KindFlatpak); // Get arch/branch/commit/name from FlatpakRef if (!bundle.isEmpty()) { ref = flatpak_ref_parse(bundle.id().toUtf8().constData(), &localError); if (!ref) { qWarning() << "Failed to parse" << bundle.id() << localError->message; return false; } else { resource->updateFromRef(ref); } } return true; } class FlatpakRefreshAppstreamMetadataJob : public QThread { Q_OBJECT public: FlatpakRefreshAppstreamMetadataJob(FlatpakInstallation *installation, FlatpakRemote *remote) : QThread() , m_cancellable(g_cancellable_new()) , m_installation(installation) , m_remote(remote) { connect(this, &FlatpakRefreshAppstreamMetadataJob::finished, this, &QObject::deleteLater); } ~FlatpakRefreshAppstreamMetadataJob() { g_object_unref(m_cancellable); } void cancel() { g_cancellable_cancel(m_cancellable); } void run() override { g_autoptr(GError) localError = nullptr; #if FLATPAK_CHECK_VERSION(0,9,4) // With Flatpak 0.9.4 we can use flatpak_installation_update_appstream_full_sync() providing progress reporting which we don't use at this moment, but still // better to use newer function in case the previous one gets deprecated if (!flatpak_installation_update_appstream_full_sync(m_installation, flatpak_remote_get_name(m_remote), nullptr, nullptr, nullptr, nullptr, m_cancellable, &localError)) { #else if (!flatpak_installation_update_appstream_sync(m_installation, flatpak_remote_get_name(m_remote), nullptr, nullptr, m_cancellable, &localError)) { #endif const QString error = localError ? QString::fromUtf8(localError->message) : QStringLiteral(""); qWarning() << "Failed to refresh appstream metadata for " << flatpak_remote_get_name(m_remote) << ": " << error; Q_EMIT jobRefreshAppstreamMetadataFailed(error); } else { Q_EMIT jobRefreshAppstreamMetadataFinished(m_installation, m_remote); } } Q_SIGNALS: void jobRefreshAppstreamMetadataFailed(const QString &errorMessage); void jobRefreshAppstreamMetadataFinished(FlatpakInstallation *installation, FlatpakRemote *remote); private: GCancellable *m_cancellable; FlatpakInstallation *m_installation; FlatpakRemote *m_remote; }; void FlatpakBackend::refreshAppstreamMetadata(FlatpakInstallation *installation, FlatpakRemote *remote) { FlatpakRefreshAppstreamMetadataJob *job = new FlatpakRefreshAppstreamMetadataJob(installation, remote); connect(job, &FlatpakRefreshAppstreamMetadataJob::jobRefreshAppstreamMetadataFailed, this, &FlatpakBackend::metadataRefreshed); connect(job, &FlatpakRefreshAppstreamMetadataJob::jobRefreshAppstreamMetadataFailed, this, [this] (const QString &errorMessage) { Q_EMIT passiveMessage(errorMessage); }); connect(job, &FlatpakRefreshAppstreamMetadataJob::jobRefreshAppstreamMetadataFinished, this, &FlatpakBackend::integrateRemote); connect(job, &FlatpakRefreshAppstreamMetadataJob::finished, this, [this] { acquireFetching(false); }); acquireFetching(true); job->start(); } bool FlatpakBackend::setupFlatpakInstallations(GError **error) { if (qEnvironmentVariableIsSet("FLATPAK_TEST_MODE")) { const QString path = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1String("/discover-flatpak-test"); qDebug() << "running flatpak backend on test mode" << path; g_autoptr(GFile) file = g_file_new_for_path(QFile::encodeName(path).constData()); m_installations << flatpak_installation_new_for_path(file, true, m_cancellable, error); return true; } GPtrArray *installations = flatpak_get_system_installations(m_cancellable, error); if (*error) { qWarning() << "Failed to call flatpak_get_system_installations:" << (*error)->message; } for (uint i = 0; installations && i < installations->len; i++) { m_installations << FLATPAK_INSTALLATION(g_ptr_array_index(installations, i)); } auto user = flatpak_installation_new_user(m_cancellable, error); if (user) { m_installations << user; } return !m_installations.isEmpty(); } void FlatpakBackend::updateAppInstalledMetadata(FlatpakInstalledRef *installedRef, FlatpakResource *resource) { // Update the rest resource->updateFromRef(FLATPAK_REF(installedRef)); resource->setInstalledSize(flatpak_installed_ref_get_installed_size(installedRef)); resource->setOrigin(QString::fromUtf8(flatpak_installed_ref_get_origin(installedRef))); if (resource->state() < AbstractResource::Installed) resource->setState(AbstractResource::Installed); } bool FlatpakBackend::updateAppMetadata(FlatpakInstallation* flatpakInstallation, FlatpakResource *resource) { if (resource->resourceType() != FlatpakResource::DesktopApp) { return true; } const QString path = resource->installPath() + QStringLiteral("/metadata"); if (QFile::exists(path)) { return updateAppMetadata(resource, path); } else { auto fw = new QFutureWatcher(this); connect(fw, &QFutureWatcher::finished, this, [this, flatpakInstallation, resource, fw]() { const auto metadata = fw->result(); if (!metadata.isEmpty()) onFetchMetadataFinished(flatpakInstallation, resource, metadata); fw->deleteLater(); }); fw->setFuture(QtConcurrent::run(&m_threadPool, &FlatpakRunnables::fetchMetadata, flatpakInstallation, resource)); // Return false to indicate we cannot continue (right now used only in updateAppSize()) return false; } } void FlatpakBackend::onFetchMetadataFinished(FlatpakInstallation *flatpakInstallation, FlatpakResource *resource, const QByteArray &metadata) { updateAppMetadata(resource, metadata); // Right now we attempt to update metadata for calculating the size so call updateSizeFromRemote() // as it's what we want. In future if there are other reason to update metadata we will need to somehow // distinguish between these calls updateAppSizeFromRemote(flatpakInstallation, resource); } bool FlatpakBackend::updateAppMetadata(FlatpakResource *resource, const QString &path) { // Parse the temporary file QSettings setting(path, QSettings::NativeFormat); setting.beginGroup(QLatin1String("Application")); // Set the runtime in form of name/arch/version which can be later easily parsed resource->setRuntime(setting.value(QLatin1String("runtime")).toString()); // TODO get more information? return true; } bool FlatpakBackend::updateAppMetadata(FlatpakResource *resource, const QByteArray &data) { //We just find the runtime with a regex, QSettings only can read from disk (and so does KConfig) const QRegularExpression rx(QStringLiteral("runtime=(.*)")); const auto match = rx.match(QString::fromUtf8(data)); if (!match.isValid()) { return false; } resource->setRuntime(match.captured(1)); return true; } bool FlatpakBackend::updateAppSize(FlatpakInstallation *flatpakInstallation, FlatpakResource *resource) { // Check if the size is already set, we should also distinguish between download and installed size, // right now it doesn't matter whether we get size for installed or not installed app, but if we // start making difference then for not installed app check download and install size separately if (resource->state() == AbstractResource::Installed) { // The size appears to be already set (from updateAppInstalledMetadata() apparently) if (resource->installedSize() > 0) { return true; } } else { if (resource->installedSize() > 0 && resource->downloadSize() > 0) { return true; } } // Check if we know the needed runtime which is needed for calculating the size if (resource->runtime().isEmpty()) { if (!updateAppMetadata(flatpakInstallation, resource)) { return false; } } return updateAppSizeFromRemote(flatpakInstallation, resource); } bool FlatpakBackend::updateAppSizeFromRemote(FlatpakInstallation *flatpakInstallation, FlatpakResource *resource) { // Calculate the runtime size if (resource->state() == AbstractResource::None && resource->resourceType() == FlatpakResource::DesktopApp) { auto runtime = getRuntimeForApp(resource); if (runtime) { // Re-check runtime state if case a new one was created updateAppState(flatpakInstallation, runtime); if (!runtime->isInstalled()) { if (!updateAppSize(flatpakInstallation, runtime)) { qWarning() << "Failed to get runtime size needed for total size of" << resource->name(); return false; } // Set required download size to include runtime size even now, in case we fail to // get the app size (e.g. when installing bundles where download size is 0) resource->setDownloadSize(runtime->downloadSize()); } } } if (resource->state() == AbstractResource::Installed) { g_autoptr(FlatpakInstalledRef) ref = nullptr; ref = getInstalledRefForApp(flatpakInstallation, resource); if (!ref) { qWarning() << "Failed to get installed size of" << resource->name(); return false; } resource->setInstalledSize(flatpak_installed_ref_get_installed_size(ref)); } else { if (resource->origin().isEmpty()) { qWarning() << "Failed to get size of" << resource->name() << " because of missing origin"; return false; } auto futureWatcher = new QFutureWatcher(this); connect(futureWatcher, &QFutureWatcher::finished, this, [this, resource, futureWatcher]() { auto value = futureWatcher->result(); if (value.valid) { onFetchSizeFinished(resource, value.downloadSize, value.installedSize); } else { resource->setPropertyState(FlatpakResource::DownloadSize, FlatpakResource::UnknownOrFailed); resource->setPropertyState(FlatpakResource::InstalledSize, FlatpakResource::UnknownOrFailed); } futureWatcher->deleteLater(); }); futureWatcher->setFuture(QtConcurrent::run(&m_threadPool, &FlatpakRunnables::fetchFlatpakSize, flatpakInstallation, resource)); } return true; } void FlatpakBackend::onFetchSizeFinished(FlatpakResource *resource, guint64 downloadSize, guint64 installedSize) { FlatpakResource *runtime = nullptr; if (resource->state() == AbstractResource::None && resource->resourceType() == FlatpakResource::DesktopApp) { runtime = getRuntimeForApp(resource); } if (runtime && !runtime->isInstalled()) { resource->setDownloadSize(runtime->downloadSize() + downloadSize); } else { resource->setDownloadSize(downloadSize); } resource->setInstalledSize(installedSize); } void FlatpakBackend::updateAppState(FlatpakInstallation *flatpakInstallation, FlatpakResource *resource) { FlatpakInstalledRef *ref = getInstalledRefForApp(flatpakInstallation, resource); if (ref) { // If the app is installed, we can set information about commit, arch etc. updateAppInstalledMetadata(ref, resource); } else { // TODO check if the app is actually still available resource->setState(AbstractResource::None); } } void FlatpakBackend::acquireFetching(bool f) { if (f) m_isFetching++; else m_isFetching--; if ((!f && m_isFetching==0) || (f && m_isFetching==1)) { emit fetchingChanged(); } if (m_isFetching==0) Q_EMIT initialized(); } int FlatpakBackend::updatesCount() const { return m_updater->updatesCount(); } bool FlatpakBackend::flatpakResourceLessThan(AbstractResource* l, AbstractResource* r) const { return (l->isInstalled() != r->isInstalled()) ? l->isInstalled() : (l->origin() != r->origin()) ? m_sources->originIndex(l->origin()) < m_sources->originIndex(r->origin()) : l < r; } ResultsStream * FlatpakBackend::search(const AbstractResourcesBackend::Filters &filter) { if (filter.resourceUrl.fileName().endsWith(QLatin1String(".flatpakrepo")) || filter.resourceUrl.fileName().endsWith(QLatin1String(".flatpakref")) || filter.resourceUrl.fileName().endsWith(QLatin1String(".flatpak"))) { auto stream = new ResultsStream(QLatin1String("FlatpakStream-http-")+filter.resourceUrl.fileName()); FlatpakFetchRemoteResourceJob *fetchResourceJob = new FlatpakFetchRemoteResourceJob(filter.resourceUrl, this); connect(fetchResourceJob, &FlatpakFetchRemoteResourceJob::jobFinished, this, [fetchResourceJob, stream] (bool success, FlatpakResource *resource) { if (success) { Q_EMIT stream->resourcesFound({resource}); } stream->finish(); fetchResourceJob->deleteLater(); }); fetchResourceJob->start(); return stream; } else if(filter.resourceUrl.scheme() == QLatin1String("appstream")) { return findResourceByPackageName(filter.resourceUrl); } else if (!filter.resourceUrl.isEmpty() || (!filter.extends.isEmpty() && !m_extends.contains(filter.extends))) return new ResultsStream(QStringLiteral("FlatpakStream-void"), {}); auto stream = new ResultsStream(QStringLiteral("FlatpakStream")); auto f = [this, stream, filter] () { QVector ret; for (auto r : qAsConst(m_resources)) { const bool matchById = r->appstreamId().compare(filter.search, Qt::CaseInsensitive) == 0; if (r->type() == AbstractResource::Technical && filter.state != AbstractResource::Upgradeable && !matchById) { continue; } if (r->state() < filter.state) continue; if (!filter.extends.isEmpty() && !r->extends().contains(filter.extends)) continue; if (filter.search.isEmpty() || r->name().contains(filter.search, Qt::CaseInsensitive) || r->comment().contains(filter.search, Qt::CaseInsensitive) || matchById) { ret += r; } } auto f = [this](AbstractResource* l, AbstractResource* r) { return flatpakResourceLessThan(l,r); }; std::sort(ret.begin(), ret.end(), f); if (!ret.isEmpty()) Q_EMIT stream->resourcesFound(ret); stream->finish(); }; if (isFetching()) { connect(this, &FlatpakBackend::initialized, stream, f); } else { QTimer::singleShot(0, this, f); } return stream; } QVector FlatpakBackend::resourcesByAppstreamName(const QString& name) const { QVector resources; foreach(FlatpakResource* res, m_resources) { if (QString::compare(res->appstreamId(), name, Qt::CaseInsensitive)==0 || QString::compare(res->appstreamId(), name + QLatin1String(".desktop"), Qt::CaseInsensitive)==0) resources << res; } auto f = [this](AbstractResource* l, AbstractResource* r) { return flatpakResourceLessThan(l, r); }; std::sort(resources.begin(), resources.end(), f); return resources; } ResultsStream * FlatpakBackend::findResourceByPackageName(const QUrl &url) { if (url.scheme() == QLatin1String("appstream")) { const auto appstreamIds = AppStreamUtils::appstreamIds(url); if (appstreamIds.isEmpty()) Q_EMIT passiveMessage(i18n("Malformed appstream url '%1'", url.toDisplayString())); else { auto stream = new ResultsStream(QStringLiteral("FlatpakStream")); auto f = [this, stream, appstreamIds] () { const auto resources = kAppend>(appstreamIds, [this] (const QString appstreamId) { return resourcesByAppstreamName(appstreamId); }); if (!resources.isEmpty()) Q_EMIT stream->resourcesFound(resources); stream->finish(); }; if (isFetching()) { connect(this, &FlatpakBackend::initialized, stream, f); } else { QTimer::singleShot(0, this, f); } return stream; } } return new ResultsStream(QStringLiteral("FlatpakStream-packageName-void"), {}); } AbstractBackendUpdater * FlatpakBackend::backendUpdater() const { return m_updater; } AbstractReviewsBackend * FlatpakBackend::reviewsBackend() const { return m_reviews.data(); } Transaction* FlatpakBackend::installApplication(AbstractResource *app, const AddonList &addons) { Q_UNUSED(addons); FlatpakResource *resource = qobject_cast(app); if (resource->resourceType() == FlatpakResource::Source) { // Let source backend handle this FlatpakRemote *remote = m_sources->installSource(resource); if (remote) { resource->setState(AbstractResource::Installed); m_refreshAppstreamMetadataJobs++; // Make sure we update appstream metadata first // FIXME we have to let flatpak to return the remote as the one created by FlatpakSourcesBackend will not have appstream directory refreshAppstreamMetadata(preferredInstallation(), flatpak_installation_get_remote_by_name(preferredInstallation(), flatpak_remote_get_name(remote), nullptr, nullptr)); } return nullptr; } FlatpakJobTransaction *transaction = new FlatpakJobTransaction(resource, Transaction::InstallRole); connect(transaction, &FlatpakJobTransaction::statusChanged, this, [this, resource] (Transaction::Status status) { if (status == Transaction::Status::DoneStatus) { FlatpakInstallation *installation = resource->installation(); updateAppState(installation, resource); } }); return transaction; } Transaction* FlatpakBackend::installApplication(AbstractResource *app) { return installApplication(app, {}); } Transaction* FlatpakBackend::removeApplication(AbstractResource *app) { FlatpakResource *resource = qobject_cast(app); if (resource->resourceType() == FlatpakResource::Source) { // Let source backend handle this if (m_sources->removeSource(resource->flatpakName())) { resource->setState(AbstractResource::None); } return nullptr; } FlatpakInstallation *installation = resource->installation(); FlatpakJobTransaction *transaction = new FlatpakJobTransaction(resource, Transaction::RemoveRole); connect(transaction, &FlatpakJobTransaction::statusChanged, this, [this, installation, resource] (Transaction::Status status) { if (status == Transaction::Status::DoneStatus) { updateAppSize(installation, resource); } }); return transaction; } void FlatpakBackend::checkForUpdates() { for (auto installation : qAsConst(m_installations)) { // Load local updates, comparing current and latest commit loadLocalUpdates(installation); if (g_cancellable_is_cancelled(m_cancellable)) break; // Load updates from remote repositories loadRemoteUpdates(installation); if (g_cancellable_is_cancelled(m_cancellable)) break; } } QString FlatpakBackend::displayName() const { return QStringLiteral("Flatpak"); } #include "FlatpakBackend.moc"