File indexing completed on 2024-11-17 04:55:41
0001 /* 0002 * SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "AppPackageKitResource.h" 0008 #include "utils.h" 0009 #include <AppStreamQt/developer.h> 0010 #include <AppStreamQt/icon.h> 0011 #include <AppStreamQt/image.h> 0012 #include <AppStreamQt/release.h> 0013 #include <AppStreamQt/screenshot.h> 0014 #include <AppStreamQt/version.h> 0015 #include <KLocalizedString> 0016 #include <KService> 0017 #include <PackageKit/Daemon> 0018 #include <QDebug> 0019 #include <QFile> 0020 #include <QIcon> 0021 #include <QProcess> 0022 #include <QStandardPaths> 0023 #include <QUrlQuery> 0024 #include <appstream/AppStreamUtils.h> 0025 0026 using namespace Qt::StringLiterals; 0027 0028 AppPackageKitResource::AppPackageKitResource(const AppStream::Component &data, const QString &packageName, PackageKitBackend *parent) 0029 : PackageKitResource(packageName, QString(), parent) 0030 , m_appdata(data) 0031 { 0032 Q_ASSERT(data.isValid()); 0033 } 0034 0035 QString AppPackageKitResource::name() const 0036 { 0037 if (m_name.isEmpty()) { 0038 if (!m_appdata.extends().isEmpty()) { 0039 const auto components = backend()->componentsById(m_appdata.extends().constFirst()); 0040 0041 if (components.isEmpty()) { 0042 qWarning() << "couldn't find" << m_appdata.extends() << "which is supposedly extended by" << m_appdata.id(); 0043 } else { 0044 m_name = components.indexSafe(0)->name() + QLatin1String(" - ") + m_appdata.name(); 0045 } 0046 } 0047 0048 if (m_name.isEmpty()) { 0049 m_name = m_appdata.name(); 0050 } 0051 } 0052 return m_name; 0053 } 0054 0055 QString AppPackageKitResource::longDescription() 0056 { 0057 const auto desc = m_appdata.description(); 0058 if (!desc.isEmpty()) { 0059 return desc; 0060 } 0061 0062 return PackageKitResource::longDescription(); 0063 } 0064 0065 static QIcon componentIcon(const AppStream::Component &comp) 0066 { 0067 QIcon ret; 0068 const auto icons = comp.icons(); 0069 for (const AppStream::Icon &icon : icons) { 0070 QStringList stock; 0071 switch (icon.kind()) { 0072 case AppStream::Icon::KindLocal: 0073 ret.addFile(icon.url().toLocalFile(), icon.size()); 0074 break; 0075 case AppStream::Icon::KindCached: 0076 ret.addFile(icon.url().toLocalFile(), icon.size()); 0077 break; 0078 case AppStream::Icon::KindStock: { 0079 const auto ret = QIcon::fromTheme(icon.name()); 0080 if (!ret.isNull()) { 0081 return ret; 0082 } 0083 break; 0084 } 0085 default: 0086 break; 0087 } 0088 } 0089 if (ret.isNull()) { 0090 ret = QIcon::fromTheme(QStringLiteral("package-x-generic")); 0091 } 0092 return ret; 0093 } 0094 0095 QVariant AppPackageKitResource::icon() const 0096 { 0097 return componentIcon(m_appdata); 0098 } 0099 0100 QJsonArray AppPackageKitResource::licenses() 0101 { 0102 return m_appdata.projectLicense().isEmpty() ? PackageKitResource::licenses() : AppStreamUtils::licenses(m_appdata); 0103 } 0104 0105 QStringList AppPackageKitResource::mimetypes() const 0106 { 0107 return m_appdata.provided(AppStream::Provided::KindMimetype).items(); 0108 } 0109 0110 static constexpr auto s_addonKinds = {AppStream::Component::KindAddon, AppStream::Component::KindCodec}; 0111 0112 QStringList AppPackageKitResource::categories() 0113 { 0114 auto cats = m_appdata.categories(); 0115 if (!kContainsValue(s_addonKinds, m_appdata.kind())) { 0116 cats.append(QStringLiteral("Application")); 0117 } 0118 return cats; 0119 } 0120 0121 QString AppPackageKitResource::comment() 0122 { 0123 const auto summary = m_appdata.summary(); 0124 if (!summary.isEmpty()) { 0125 return summary; 0126 } 0127 0128 return PackageKitResource::comment(); 0129 } 0130 0131 QString AppPackageKitResource::appstreamId() const 0132 { 0133 return m_appdata.id(); 0134 } 0135 0136 QSet<QString> AppPackageKitResource::alternativeAppstreamIds() const 0137 { 0138 const AppStream::Provided::Kind AppStream_Provided_KindId = (AppStream::Provided::Kind)12; // Should be AppStream::Provided::KindId when released 0139 const auto ret = m_appdata.provided(AppStream_Provided_KindId).items(); 0140 return QSet<QString>(ret.begin(), ret.end()); 0141 } 0142 0143 QUrl AppPackageKitResource::url() const 0144 { 0145 QUrl ret(QStringLiteral("appstream://") + appstreamId()); 0146 const AppStream::Provided::Kind AppStream_Provided_KindId = (AppStream::Provided::Kind)12; // Should be AppStream::Provided::KindId when released 0147 auto provided = m_appdata.provided(AppStream_Provided_KindId).items(); 0148 provided.removeAll(appstreamId()); // Just in case, it has happened before 0149 if (!provided.isEmpty()) { 0150 QUrlQuery qq; 0151 qq.addQueryItem(u"alt"_s, provided.join(QLatin1Char(','))); 0152 ret.setQuery(qq); 0153 } 0154 return ret; 0155 } 0156 0157 QUrl AppPackageKitResource::homepage() 0158 { 0159 return m_appdata.url(AppStream::Component::UrlKindHomepage); 0160 } 0161 0162 QUrl AppPackageKitResource::helpURL() 0163 { 0164 return m_appdata.url(AppStream::Component::UrlKindHelp); 0165 } 0166 0167 QUrl AppPackageKitResource::bugURL() 0168 { 0169 return m_appdata.url(AppStream::Component::UrlKindBugtracker); 0170 } 0171 0172 QUrl AppPackageKitResource::donationURL() 0173 { 0174 return m_appdata.url(AppStream::Component::UrlKindDonation); 0175 } 0176 0177 QUrl AppPackageKitResource::contributeURL() 0178 { 0179 return m_appdata.url(AppStream::Component::UrlKindContribute); 0180 } 0181 0182 AbstractResource::Type AppPackageKitResource::type() const 0183 { 0184 static QString desktop = QString::fromUtf8(qgetenv("XDG_CURRENT_DESKTOP")); 0185 const auto desktops = m_appdata.compulsoryForDesktops(); 0186 return kContainsValue(s_addonKinds, m_appdata.kind()) ? Addon : (desktops.isEmpty() || !desktops.contains(desktop)) ? Application : Technical; 0187 } 0188 0189 void AppPackageKitResource::fetchScreenshots() 0190 { 0191 Q_EMIT screenshotsFetched(AppStreamUtils::fetchScreenshots(m_appdata)); 0192 } 0193 0194 QStringList AppPackageKitResource::allPackageNames() const 0195 { 0196 auto ret = m_appdata.packageNames(); 0197 if (ret.isEmpty()) { 0198 ret = QStringList{PackageKit::Daemon::packageName(availablePackageId())}; 0199 } 0200 return ret; 0201 } 0202 0203 QList<PackageState> AppPackageKitResource::addonsInformation() 0204 { 0205 const auto extendedBy = backend()->extendedBy(m_appdata.id()); 0206 const auto allPackageNamesCached = allPackageNames(); 0207 const auto resources = kFilter<QVector<AbstractResource *>>(extendedBy, [&](AbstractResource *r) { 0208 auto apkr = qobject_cast<AppPackageKitResource *>(r); 0209 return apkr && apkr->allPackageNames() != allPackageNamesCached; 0210 }); 0211 return kTransform<QList<PackageState>>(resources, [](AbstractResource *r) { 0212 return PackageState(r->packageName(), r->name(), r->comment(), r->isInstalled()); 0213 }); 0214 } 0215 0216 QStringList AppPackageKitResource::extends() const 0217 { 0218 return m_appdata.extends(); 0219 } 0220 0221 QString AppPackageKitResource::changelog() const 0222 { 0223 return PackageKitResource::changelog() + QLatin1String("<br />") + AppStreamUtils::changelogToHtml(m_appdata); 0224 } 0225 0226 bool AppPackageKitResource::canExecute() const 0227 { 0228 return !m_appdata.launchable(AppStream::Launchable::KindDesktopId).entries().isEmpty(); 0229 } 0230 0231 void AppPackageKitResource::invokeApplication() const 0232 { 0233 const QString launchable = m_appdata.launchable(AppStream::Launchable::KindDesktopId).entries().constFirst(); 0234 0235 KService::Ptr service = KService::serviceByStorageId(launchable); 0236 0237 if (!service) { 0238 Q_EMIT backend()->passiveMessage(i18n("Cannot launch %1", name())); 0239 return; 0240 } 0241 runService(service); 0242 } 0243 0244 QString AppPackageKitResource::versionString() 0245 { 0246 const QString version = isInstalled() ? installedVersion() : availableVersion(); 0247 return AppStreamUtils::versionString(version, m_appdata); 0248 } 0249 0250 QDate AppPackageKitResource::releaseDate() const 0251 { 0252 #if ASQ_CHECK_VERSION(1, 0, 0) 0253 if (!m_appdata.releasesPlain().isEmpty()) { 0254 const auto release = m_appdata.releasesPlain().indexSafe(0).value(); 0255 #else 0256 if (!m_appdata.releases().isEmpty()) { 0257 const auto release = m_appdata.releases().constFirst(); 0258 #endif 0259 return release.timestamp().date(); 0260 } 0261 0262 return {}; 0263 } 0264 0265 QString AppPackageKitResource::author() const 0266 { 0267 QString name = m_appdata.developer().name(); 0268 0269 if (name.isEmpty()) { 0270 name = m_appdata.projectGroup(); 0271 } 0272 0273 return name; 0274 } 0275 0276 void AppPackageKitResource::fetchChangelog() 0277 { 0278 Q_EMIT changelogFetched(changelog()); 0279 } 0280 0281 bool AppPackageKitResource::isCritical() const 0282 { 0283 return m_appdata.isCompulsoryForDesktop(qEnvironmentVariable("XDG_CURRENT_DESKTOP")); 0284 } 0285 0286 QString AppPackageKitResource::contentRatingDescription() const 0287 { 0288 return AppStreamUtils::contentRatingDescription(m_appdata); 0289 } 0290 0291 QString AppPackageKitResource::contentRatingText() const 0292 { 0293 return AppStreamUtils::contentRatingText(m_appdata); 0294 } 0295 0296 AbstractResource::ContentIntensity AppPackageKitResource::contentRatingIntensity() const 0297 { 0298 return AppStreamUtils::contentRatingIntensity(m_appdata); 0299 } 0300 0301 uint AppPackageKitResource::contentRatingMinimumAge() const 0302 { 0303 return AppStreamUtils::contentRatingMinimumAge(m_appdata); 0304 }