aboutsummaryrefslogtreecommitdiff
path: root/src/widgets/MoreApps/moreapps.cpp
diff options
context:
space:
mode:
authorLibravatar Keshav Bhatt <keshavnrj@gmail.com>2022-12-19 02:25:44 +0530
committerLibravatar Keshav Bhatt <keshavnrj@gmail.com>2022-12-19 02:25:44 +0530
commit074b0f987330458698bcea14ac7b141e863600f5 (patch)
treee132d4a4a92a2ec3369016f701105d6173d86c64 /src/widgets/MoreApps/moreapps.cpp
parentd7f1faee3805f398d08a00ec132de5a8531de3af (diff)
downloadwhatsie-074b0f987330458698bcea14ac7b141e863600f5.tar.gz
whatsie-074b0f987330458698bcea14ac7b141e863600f5.zip
chore: add moreapps widget in lock screenv4.10.3
Diffstat (limited to 'src/widgets/MoreApps/moreapps.cpp')
-rw-r--r--src/widgets/MoreApps/moreapps.cpp373
1 files changed, 373 insertions, 0 deletions
diff --git a/src/widgets/MoreApps/moreapps.cpp b/src/widgets/MoreApps/moreapps.cpp
new file mode 100644
index 0000000..b28476a
--- /dev/null
+++ b/src/widgets/MoreApps/moreapps.cpp
@@ -0,0 +1,373 @@
+#include "moreapps.h"
+#include "ui_moreapps.h"
+
+#include <QDesktopServices>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QPushButton>
+#include <QRandomGenerator>
+#include <algorithm>
+#include <cstdlib>
+#include <ctime>
+
+MoreApps::MoreApps(QWidget *parent, QNetworkAccessManager *nam,
+ const QString &publisherName, const QUrl &remoteFilterUrl,
+ bool uIdebugMode, bool remoteIconPreCaching)
+ : QWidget(parent), ui(new Ui::MoreApps),
+ mFields("publisher,summary,title,media"), mPublisherName(publisherName),
+ mRemoteFilterUrl(remoteFilterUrl), mUiDebugMode(uIdebugMode),
+ mRemoteIconPreCaching(remoteIconPreCaching) {
+
+ init(nam);
+}
+
+void MoreApps::initNetworkManager(QNetworkAccessManager *nam) {
+
+ ui->setupUi(this);
+
+ // set to use external nam
+ mNetworkManager = nam;
+
+ QString diskCachePath =
+ QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+
+ // use internal nam
+ if (mNetworkManager == nullptr) {
+ mNetworkManager = new QNetworkAccessManager(this);
+ mNetworkManager->setObjectName("internal_nm");
+
+ QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
+ diskCache->setCacheDirectory(diskCachePath);
+ mNetworkManager->setCache(diskCache);
+ } else {
+ qDebug() << QT_STRINGIFY(MoreApps) << "using external network manager";
+ }
+
+ if (mNetworkManager->cache() == nullptr) {
+ QString nmType = (mNetworkManager->objectName() == "internal_nm")
+ ? "internal"
+ : "external";
+ qWarning() << "no cache set on" << nmType << "network manager"
+ << Q_FUNC_INFO;
+ }
+}
+
+void MoreApps::init(QNetworkAccessManager *nam) {
+
+ initNetworkManager(nam);
+
+ getAppsMeta();
+}
+
+void MoreApps::getAppsMeta() {
+
+ qDebug() << "getting apps meta...";
+
+ QUrlQuery query;
+ query.addQueryItem("q", mPublisherName);
+ query.addQueryItem("fields", mFields);
+
+ // TODO: check if snapcraft api provinding list by publisher name
+ QUrl reqUrl = QUrl("https://api.snapcraft.io/v2/snaps/find");
+ reqUrl.setQuery(query);
+
+ QNetworkRequest request(reqUrl);
+ request.setRawHeader("Snap-Device-Series", "16");
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::PreferCache);
+
+ QNetworkReply *reply = mNetworkManager->get(request);
+ ui->loadingLabel->show();
+ connect(reply, &QNetworkReply::finished, this, [=]() {
+ if (reply->error() == QNetworkReply::NoError) {
+ mAppMeta = reply->readAll();
+ } else {
+ qDebug() << "Error getting app meta from store";
+ this->hide();
+ }
+ reply->deleteLater();
+ applyFilter();
+ ui->loadingLabel->hide();
+ });
+}
+
+QList<AppItem> MoreApps::prepareAppsToShow(const QByteArray &bytes) {
+
+ QList<AppItem> appList;
+
+ QJsonDocument jsonResponse = QJsonDocument::fromJson(bytes);
+ if (jsonResponse.isEmpty()) {
+ return appList;
+ }
+
+ QJsonArray jsonArray = jsonResponse.object().value("results").toArray();
+ foreach (const QJsonValue &val, jsonArray) {
+ QJsonObject object = val.toObject();
+
+ // publisher
+ QString publisher = object.value("snap")
+ .toObject()
+ .value("publisher")
+ .toObject()
+ .value("username")
+ .toString();
+ publisher = publisher.isEmpty() ? "-" : publisher;
+
+ // name
+ QString name = object.value("name").toString();
+
+ // title
+ QString title = object.value("snap").toObject().value("title").toString();
+ title = title.isEmpty() ? "-" : title;
+
+ // icon
+ QJsonArray mediaArr =
+ object.value("snap").toObject().value("media").toArray();
+ QString iconUrl;
+ foreach (const QJsonValue &mediaItem, mediaArr) {
+ if (mediaItem.toObject().value("type") == "icon")
+ iconUrl = mediaItem.toObject().value("url").toString();
+ }
+
+ // summary
+ QString summary =
+ object.value("snap").toObject().value("summary").toString();
+ summary = summary.isEmpty() ? "-" : summary;
+
+ // link
+ QString storeUrl = "https://snapcraft.io/" + name;
+
+ // if filterList is set show only filtered items
+ if (mFilterList.isEmpty() == false && (publisher == mPublisherName) &&
+ (!name.isEmpty() || !storeUrl.isEmpty() || !iconUrl.isEmpty())) {
+ if (mFilterList.contains(name)) {
+ AppItem app(name, title, summary, QUrl(iconUrl), QUrl(storeUrl));
+ appList.append(app);
+ }
+ } else { // else show all items returned
+ AppItem app(name, title, summary, QUrl(iconUrl), QUrl(storeUrl));
+ appList.append(app);
+ }
+ }
+
+ return appList;
+}
+
+void MoreApps::applyFilter() {
+
+ if (mRemoteFilterUrl.isEmpty() == false) {
+
+ applyRemoteFilter(mRemoteFilterUrl);
+
+ } else {
+
+ mAppList = prepareAppsToShow(mAppMeta);
+
+ showApps();
+ }
+}
+
+void MoreApps::applyRemoteFilter(const QUrl &remoteFilterUrl) {
+
+ qDebug() << "getting remote filter...";
+
+ QNetworkRequest request(remoteFilterUrl);
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::PreferCache);
+
+ QNetworkReply *reply = mNetworkManager->get(QNetworkRequest(request));
+ ui->loadingLabel->show();
+
+ connect(reply, &QNetworkReply::finished, this, [=]() {
+ if (reply->error() == QNetworkReply::NoError) {
+ auto replyBytes = reply->readAll();
+ foreach (QString line, replyBytes.split('\n')) {
+ // ignore commented line
+ if (line.startsWith("#")) {
+ continue;
+ }
+ addToAppFilterList(line.trimmed());
+ }
+ } else {
+ qDebug() << "Error getting filter list";
+ }
+
+ ui->loadingLabel->hide();
+
+ reply->deleteLater();
+
+ mAppList = prepareAppsToShow(mAppMeta);
+
+ showApps();
+ });
+}
+
+void MoreApps::setRemoteIcon(const QUrl &iconUrl, QLabel *lb) {
+
+ QNetworkRequest request(iconUrl);
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::PreferCache);
+
+ QNetworkReply *reply = mNetworkManager->get(QNetworkRequest(request));
+ connect(reply, &QNetworkReply::finished, this, [=]() {
+ if (reply->error() == QNetworkReply::NoError) {
+ if (lb != nullptr) {
+ auto replyBytes = reply->readAll();
+ QPixmap pixmap;
+ pixmap.loadFromData(replyBytes);
+ qDebug() << "after load" << lb->size();
+ lb->setPixmap(pixmap.scaled(lb->size(), Qt::KeepAspectRatio,
+ Qt::SmoothTransformation));
+ }
+ } else {
+ qDebug() << "Error getting icon" << iconUrl.toString();
+ if (lb != nullptr) {
+ QByteArray data = QByteArray::fromBase64(mSnapIconBin.toLatin1());
+ QPixmap pixmap;
+ pixmap.loadFromData(data, "PNG");
+ lb->setPixmap(QPixmap(pixmap.scaled(lb->size(), Qt::KeepAspectRatio,
+ Qt::SmoothTransformation)));
+ }
+ }
+ reply->deleteLater();
+ });
+}
+
+void MoreApps::showApps() {
+
+ qDebug() << "showing apps...";
+
+ std::srand(unsigned(std::time(0)));
+ // rand generator
+ auto rand = [](auto i) { return std::rand() % i; };
+ // shuffle appList before adding
+ std::random_shuffle(mAppList.begin(), mAppList.end(), rand);
+
+ auto fallbackIconUrl =
+ QUrl("https://dashboard.snapcraft.io/site_media/appmedia/"
+ "2019/09/snapd.png");
+
+ if (mRemoteIconPreCaching) {
+ // cache fallback icon
+ setRemoteIcon(fallbackIconUrl, nullptr);
+ foreach (auto a, mAppList) {
+ auto iconUrl = a.getIconUrl();
+ qDebug() << "pre-caching icon for" << a.getName();
+ setRemoteIcon(iconUrl, nullptr);
+ }
+ }
+
+ // calculate icon height width
+ double pheight = this->height();
+ double ratio = pheight / pheight;
+ double height = this->height() / 1.8;
+ double width = ratio * height;
+
+ // add appItem to appItemWidget
+ for (int i = 0; i < getAppsToShowCount(); ++i) {
+
+ auto appItem = mAppList.at(i);
+
+ QLabel *iconLabel = new QLabel();
+ iconLabel->setFixedSize(width, height);
+ if (appItem.getIconUrl().isEmpty() == false) {
+ setRemoteIcon(appItem.getIconUrl(), iconLabel);
+ } else {
+ qDebug() << "icon empty for " << appItem.getName();
+ setRemoteIcon(fallbackIconUrl, iconLabel);
+ }
+
+ QPushButton *pb = new QPushButton();
+ pb->setText(appItem.getTitle());
+ connect(pb, &QPushButton::clicked, this,
+ [=]() { QDesktopServices::openUrl(appItem.getStoreUrl()); });
+
+ QVBoxLayout *vl = new QVBoxLayout();
+ vl->addWidget(iconLabel);
+ vl->addWidget(pb);
+ vl->setAlignment(iconLabel, Qt::AlignCenter);
+ vl->setContentsMargins(0, 0, 0, 0);
+ vl->setSpacing(6);
+
+ QWidget *appItemWidget = new QWidget();
+ if (mUiDebugMode)
+ appItemWidget->setStyleSheet("border: 1px solid red;");
+ appItemWidget->setToolTip(appItem.getSummary());
+ appItemWidget->setLayout(vl);
+ appItemWidget->setMaximumWidth(
+ getMaxWidth(iconLabel->sizeHint().width(), pb->sizeHint().width()) +
+ (2 * 3));
+ appItemWidget->setMinimumWidth(
+ getMaxWidth(iconLabel->sizeHint().width(), pb->sizeHint().width()));
+ appItemWidget->setMinimumHeight(iconLabel->sizeHint().height() +
+ pb->sizeHint().height() + (2 * 3));
+ ui->horizontalLayout->setSpacing(9);
+ ui->horizontalLayout->addWidget(appItemWidget);
+ }
+}
+
+int MoreApps::getMaxWidth(int x, int y) {
+ int z = x;
+ x > y ? z = x : z = y;
+ return z;
+}
+
+int MoreApps::getAppsToShowCount() {
+
+ int min = 0;
+ int max = mAppList.count();
+ int proposed = 3;
+
+ if (max > min && max >= proposed) {
+ return proposed;
+ } else {
+ return max;
+ }
+}
+
+void MoreApps::addToAppFilterList(const QString &appName) {
+
+ // filter out if appname string contains running application name substring
+ // or appname is empty or already added to filter list
+ if (mFilterList.contains(appName) == false &&
+ appName.trimmed().isEmpty() == false &&
+ appName.contains(QApplication::applicationName(), Qt::CaseInsensitive) ==
+ false)
+ mFilterList.append(appName);
+}
+
+MoreApps::~MoreApps() { delete ui; }
+
+QString AppItem::getName() const { return name; }
+
+void AppItem::setName(const QString &newName) { name = newName; }
+
+QString AppItem::getSummary() const {
+ return QString(summary).replace(" & ", " and ");
+}
+
+void AppItem::setSummary(const QString &newSummary) { summary = newSummary; }
+
+QUrl AppItem::getIconUrl() const { return iconUrl; }
+
+void AppItem::setIconUrl(const QUrl &newIconUrl) { iconUrl = newIconUrl; }
+
+QUrl AppItem::getStoreUrl() const { return storeUrl; }
+
+void AppItem::setStoreUrl(const QUrl &newLink) { storeUrl = newLink; }
+
+QString AppItem::getTitle() const {
+ return QString(title).replace(" & ", " and ");
+}
+
+void AppItem::setTitle(const QString &newTitle) { title = newTitle; }
+
+AppItem::AppItem() {}
+
+AppItem::AppItem(const QString &name, const QString &title,
+ const QString &summary, const QUrl &iconUrl,
+ const QUrl &storeUrl)
+ : name(name), title(title), summary(summary), iconUrl(iconUrl),
+ storeUrl(storeUrl) {}