aboutsummaryrefslogtreecommitdiff
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
parentd7f1faee3805f398d08a00ec132de5a8531de3af (diff)
downloadwhatsie-074b0f987330458698bcea14ac7b141e863600f5.tar.gz
whatsie-074b0f987330458698bcea14ac7b141e863600f5.zip
chore: add moreapps widget in lock screenv4.10.3
-rw-r--r--src/WhatsApp.pro6
-rw-r--r--src/lock.cpp10
-rw-r--r--src/lock.ui23
-rw-r--r--src/widgets/MoreApps/MoreApps.pri4
-rw-r--r--src/widgets/MoreApps/moreapps.cpp373
-rw-r--r--src/widgets/MoreApps/moreapps.h272
-rw-r--r--src/widgets/MoreApps/moreapps.ui57
7 files changed, 741 insertions, 4 deletions
diff --git a/src/WhatsApp.pro b/src/WhatsApp.pro
index 1e36224..3dc1f41 100644
--- a/src/WhatsApp.pro
+++ b/src/WhatsApp.pro
@@ -6,7 +6,7 @@
QT += core gui webengine webenginewidgets positioning
-CONFIG += c++11
+CONFIG += c++17
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
@@ -25,6 +25,8 @@ LIBS += -L/usr/X11/lib -lX11
include(singleapplication/singleapplication.pri)
DEFINES += QAPPLICATION_CLASS=QApplication
+include(widgets/MoreApps/MoreApps.pri)
+
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Refer to the documentation for the
@@ -48,7 +50,7 @@ BUILD_TIMESTAMP="\\\"$$system(date -u +\""%Y-%m-%dT%H:%M:%SUTC\"")\\\""
DEFINES += GIT_HASH=$$GIT_HASH GIT_BRANCH=$$GIT_BRANCH BUILD_TIMESTAMP=$$BUILD_TIMESTAMP
# Set program version
-VERSION = 4.9
+VERSION = 4.10
DEFINES += VERSIONSTR=\\\"$${VERSION}\\\"
# You can also make your code fail to compile if you use deprecated APIs.
diff --git a/src/lock.cpp b/src/lock.cpp
index 62c4604..757e6dd 100644
--- a/src/lock.cpp
+++ b/src/lock.cpp
@@ -4,6 +4,7 @@
#include <QGraphicsOpacityEffect>
#include <QKeyEvent>
#include <QPropertyAnimation>
+#include "moreapps.h"
#include "X11/XKBlib.h" // keep this header at bottom
@@ -12,6 +13,15 @@ Lock::Lock(QWidget *parent) : QWidget(parent), ui(new Ui::Lock) {
ui->setPass->setEnabled(false);
ui->wrong->hide();
+ MoreApps *moreApps =
+ new MoreApps(this, nullptr, "keshavnrj",
+ QUrl("https://raw.githubusercontent.com/keshavbhatt/appdata/"
+ "main/moreapps.txt"),
+ false);
+ moreApps->setWindowTitle("More Applications by developer");
+ moreApps->setFixedHeight(98);
+ ui->moreAppsLayout->addWidget(moreApps);
+
passcodeLoginAction = ui->passcodeLogin->addAction(
QIcon(":/icons/green_arrow-right-line.png"), QLineEdit::TrailingPosition);
passcodeLoginAction->setEnabled(false);
diff --git a/src/lock.ui b/src/lock.ui
index b4ec96b..2ee77a7 100644
--- a/src/lock.ui
+++ b/src/lock.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>936</width>
- <height>680</height>
+ <width>946</width>
+ <height>708</height>
</rect>
</property>
<property name="sizePolicy">
@@ -689,6 +689,25 @@ border-bottom-left-radius: 4px;</string>
</layout>
</item>
<item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Maximum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="moreAppsLayout"/>
+ </item>
+ <item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
diff --git a/src/widgets/MoreApps/MoreApps.pri b/src/widgets/MoreApps/MoreApps.pri
new file mode 100644
index 0000000..fc50eea
--- /dev/null
+++ b/src/widgets/MoreApps/MoreApps.pri
@@ -0,0 +1,4 @@
+SOURCES += $$PWD/moreapps.cpp
+HEADERS += $$PWD/moreapps.h
+FORMS += $$PWD/moreapps.ui
+INCLUDEPATH += $$PWD
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) {}
diff --git a/src/widgets/MoreApps/moreapps.h b/src/widgets/MoreApps/moreapps.h
new file mode 100644
index 0000000..7b6666d
--- /dev/null
+++ b/src/widgets/MoreApps/moreapps.h
@@ -0,0 +1,272 @@
+#ifndef MOREAPPS_H
+#define MOREAPPS_H
+
+#include <QLabel>
+#include <QPair>
+#include <QWidget>
+#include <QtNetwork>
+
+namespace Ui {
+class MoreApps;
+}
+
+class AppItem {
+public:
+ AppItem();
+ AppItem(const QString &name, const QString &title, const QString &summary,
+ const QUrl &iconUrl, const QUrl &storeUrl);
+
+ QString getName() const;
+ void setName(const QString &newName);
+
+ QString getSummary() const;
+ void setSummary(const QString &newSummary);
+
+ QUrl getIconUrl() const;
+ void setIconUrl(const QUrl &newIconUrl);
+
+ QUrl getStoreUrl() const;
+ void setStoreUrl(const QUrl &newLink);
+
+ QString toString() const {
+ return QString("name: %1\ntitle: %2\nshortDescription: %3\niconUrl: "
+ "%4\nlink: %5")
+ .arg(this->getName(), this->getTitle(), this->getSummary(),
+ this->getIconUrl().toString(), this->getStoreUrl().toString());
+ }
+
+ QString getTitle() const;
+ void setTitle(const QString &newTitle);
+
+private:
+ QString name;
+ QString title;
+ QString summary;
+ QUrl iconUrl;
+ QUrl storeUrl;
+};
+
+class MoreApps : public QWidget {
+ Q_OBJECT
+
+public:
+ MoreApps(QWidget *parent = nullptr, QNetworkAccessManager *nam = nullptr,
+ const QString &publisherName = "",
+ const QUrl &remoteFilterUrl = QUrl(""), bool uIdebugMode = false,
+ bool remoteIconPreCaching = true);
+ ~MoreApps();
+
+private slots:
+ void getAppsMeta();
+
+ void applyFilter();
+
+ void addToAppFilterList(const QString &appName);
+
+ QList<AppItem> prepareAppsToShow(const QByteArray &bytes);
+
+ void showApps();
+
+ void setRemoteIcon(const QUrl &iconUrl, QLabel *lb = nullptr);
+
+ int getAppsToShowCount();
+
+ void init(QNetworkAccessManager *nam = nullptr);
+
+ void initNetworkManager(QNetworkAccessManager *nam = nullptr);
+
+ void applyRemoteFilter(const QUrl &remoteFilterUrl);
+
+ int getMaxWidth(int x, int y);
+private:
+ Ui::MoreApps *ui;
+ QString mFields;
+ QString mPublisherName;
+ QUrl mRemoteFilterUrl;
+ bool mUiDebugMode;
+ bool mRemoteIconPreCaching;
+
+ QNetworkAccessManager *mNetworkManager = nullptr;
+ QList<AppItem> mAppList;
+ QStringList mFilterList;
+ QByteArray mAppMeta;
+ QString mSnapIconBin =
+ "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABmJLR0QAnQCbAJtGTkUDAAAA"
+ "CXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH5gwSEQYxKUlO0QAAGhhJREFUeNrtnXucHGWd"
+ "r59f9SQhCUKm660ZbupZd+Xo2RUMd1nRs15WQFA5rnKT+"
+ "01AJYAY8IIieEHuIIiKCyuuCCThDuLiBUEUFVzPcdeju5496wpKd1VPgACZZLp++"
+ "0f1HOdMZiaTTE9PX77P55M/"
+ "+CRkOvXW96m3vl31viCEEEIIIYQQQoieoFqtRjoKQvQmlqbpX7v7fDN7EHguhJDrsAjRIwLI"
+ "smxLd78S2Ae4A1gFPB5F0XC5XHYdIiG6WAAADQlcAxwGjAD/"
+ "BKxw95Vm9m8hhPU6VEJ0qQAmkEAJcOB54GEzWwnc7e6VEEJdh02ILhPAJBIYpQ4MAfe7+"
+ "61m9t0oitaUy2X1BUJ0iwA2IoFR1gNPAreb2Qp3/"
+ "2m9Xh8eHBxUXyBEpwtgmhKgcYuwDvgnd18ZRdFtwG/"
+ "iOF6nwypEBwtgjASuBg6fQgJjZfA88EMzuwW4O8/"
+ "zapIkIzrEQnSgAADSNN0SmK4ExvcF3wJWRVH0APCs+"
+ "gIhOkwADQksBj4PHLEJEhhlBHgCuMPMbgUey/"
+ "N8bZIk6guE6AQBjJHAVcCRmyGBsX3Br4BbzWwV8K/"
+ "qC4ToAAEAVKvVxWY2EwmM7wt+BKwA7gKeCiGoLxCiXQXQZAmMkgOrG33BCnd/"
+ "oFQqqS8Qoh0F0JDAooYEjmqSBDboC4DbgEfdXX2BEO0kgFmWwCjrgP8N3ErxctK/"
+ "hhDUFwjRDgJokQRG+4IXgEcbMrhTfYEQbSCAFkpgbF/wNHB/oy/"
+ "4NvBMkiTqC4SYCwEAVCqVRVEUXQkc3QIJjO0L/"
+ "tDoC1aoLxBijgQwhxIY3xesAlYCv1ZfIEQLBdAGEhjtC9Y2+oJbgDvd/"
+ "Sm9jyBECwTQJhIY3xd8u9EX3G9mz2i9QyFmUQBtJoGJ+oJVZvajer3+wsDAgPoCIQHMxl/"
+ "ahhIY2xf8S6MrWAH8Sn2BkAB6SwLj+4JVjdnBk3q+"
+ "QEgAvSOB8X3Bd4Bb3f1bFM8XaPFTIQE0SQJXAMe0sQTG9gUVijcUb42i6If1ev0FPV8gJIDe"
+ "kcD4vmAVsMrdf5kkybBOGSEB9I4ExvYFj1E8X3CHmT0Zx7H6AiEB9IgExvYFzwDfpVjZ6P48"
+ "z59WXyAkgGny1FNPLSyVSpcCJ3SoBCbqC1ZEUfQI8IL2UxQSwMZnAgujKOoGCYztC/"
+ "4Pf3y+"
+ "4JchBPUFQgLogZnAZH3BCjO7w8yeKJfL2lxVSAA9IoGxfcGaRl9wc57n3yqVSqvjOFZfICSA"
+ "UYaGhhbW6/VulcDYvqAK3NPoCx4GnldfIHpeAD0kgVHWN/"
+ "qCFe6+0sz+ua+"
+ "vb92SJUskA9GbAhgjgUuAE3tAAqN9wTDwj8AtZna7mf1OfYHoSQH0qAQm6gtWuvt9pVJpqFw"
+ "uqy8QvSOAHpfAKHUgBe4Gbi2VSg+7u/"
+ "oC0RsCkAQm7AtG1zv8Rb1eXzc4OCgZiO4VgCSw8b7A3X8XQlBfILpTAJLARvuCh4Bb3P1eMx"
+ "sKIagvEN0lAElgk/"
+ "qCle7+kJk9r8VPRdcIYIwELgZOkgSm7Av+L8XGqrcAv8jzfJ0WPxUdLwBJYLP7gpXA7cC/"
+ "qy8QHS2AhgS2aNwOSALT7wueA77fmBXcC6gvEJ0pgHESOBHo0xBuUl9Qo/E+grs/"
+ "qL5AWCd+6IYERm8HJIEZ9AXuvsrMfp7n+bD6AglAEui9vmAd8L8atwi3qS+"
+ "QADpNAp8DTpYEmtYXPEyxP8I9ZpapL5AA2l4CeZ5f6O6nSAJN7wvuBW6lKBGfU18gAUgCvdk"
+ "X/"
+ "I7ifYQVwD+6+7A2S5EAdDvQu33BzWb29TiOn5zsD1er1YVAorGYMc8mSVKdrb+8awanv79/"
+ "ba1W+1Ce5zlwqk68WblYLAB2A5a6ew5cOukfNqsDu1Cs8rR34/8Vm84/"
+ "p2n630MIaySAjVAul9fWarXlhQMkgVkkavyalMa267enaXofsAdwCnAAsLibZp4tYGfgb4Ab"
+ "Zmsg6TIJDEdRtBy4mmIxTjGHhBCGQwgPufsRZrYHcDnwB4pvHcT0LtKnpWm6pQSw6RK4RhJo"
+ "DWmaWpqmL6lWq/"
+ "Mn+"
+ "v0kSUbiOP5lFEUfNLNdgLOBX2t8psVfuPs7JYDNk8DndZK17Er1BTO7r1qtvrNSqSweGhqyC"
+ "cYlj+"
+ "P498DFURTtQbFP5A8oCkYxybE1s2XVanVLCWATO4Eois6WBFrGAuANZnZTFEWP1Ov196Vpml"
+ "Sr1WiCWwMvl8tPhxC+BrwR2I/iacQ1FN84iHGzAOB/"
+ "SACbNxOQBFrLPGCnxv3+"
+ "42Z2QbVafXm1Wi1N0RN8x90PB14DXEWxkYp6glmeBUS9cOQkgTkjAnYAzjazH5vZl7Ms2ytN"
+ "00l7ghDCL9z9dGAp8FHgXyieTBTwKjM7SAKQBDoNA5YAx7j7d4E70jR9R5Zli2q1mk0ggjyE"
+ "8ESe5591993d/XjgEfUE9AHLqtXqYglAEuhUtgD2pVjQ9JE8z0/"
+ "Msqw8kQgGBgY8SZKnkyS5wczeCLyVYoWj53u4J9ipmbOAqNeO3hgJXCUJzHlPsDNwtbv/LM/"
+ "z89I0fVmWZRP2BHEcrw0hPODu73b3vYEvUCyI2ms9QVNnAVEvnnkNCZwjCbQFJeAljfv9n7j"
+ "7tWma7pam6bxJeoL1SZL83N0/AOwKfJxi85Re6gl2NrN3SACSQLf1BGXgeIrXj29P0/"
+ "SAWq22cBIR1EMIv3X3T5nZ7sB7gZ/2SE/"
+ "QB5xeqVQWSwAzlICZSQLtx0Jgf2BVnucPpml6Qpqm/"
+ "WmaTlQYehzHtRDCdWb2OuAg4I5GT9DVs4Aoit4uAcyQOI4lgfbuCXZv3O8/"
+ "BnysWq2+"
+ "JE3TyXqCF0II97r7u9z9dcCXgCG6szDsA5ZVKpVFEoAk0As9wZ8AnzCzx4Gr0jRdupGe4DEz"
+ "O8XMdgU+SbEIarf1BEtnOguQADaUwGWSQFv3BHHjfv9h4OZqtbp/lmVbTDKm9TiO/"
+ "y2KovPcfTeKV8Qfo1jlqGu6gFqttkgCaJ4EPioJdIQIFgEHmdlt7v5gmqZH12q1rbMsm+"
+ "gFJE+SJAshfLFUKr0OeCdwH7C2G2YBeZ6/TQJongTWSQIdxXyKBUeuy/P8MXc/"
+ "p1qtvrhSqUx4bvf39z8fQrgLeAfweuBvgdUd3BPMaBYgAUwtgUslgY7qCf4UON/"
+ "MfhpF0RVpmu40WU8QQlgXQvixu5/YuD34FPAfHdoTLM3z/"
+ "EAJoPkS+"
+ "Jgk0HFEwEDjfv8R4Btpmr4pTdMJ1yRMkqSeJMlvoig6t1EYngb8zw7rCeYBpw8NDS2SACQB8"
+ "ceeYDHFO/T3AA+kaXpklmVbTfQ8Qblc9jiOq/V6/"
+ "Zp6vb43cAhwfwf1BLvkeX6ABCAJiIl7gtcCX3H3nwBnZVm2bZZlG5z/"
+ "g4ODPjg4+FwIYRXwNorFSv4OeKbNe4J57n76ZE9OSgCSgCgKsx2Bz7j7z9z94jRN/"
+ "zzLsr4peoJH3P14d98d+BzwBO37AtKumzoLkAA2TwLaPLPze4JBYBnwI3e/"
+ "MU3TN0zRE4wkSfJr4MMULyCdQbFBSrtdDOZRfCOwUAKQBMT0eoItG/f79wL/"
+ "kGXZIdVqdcuJnicIIeQhhKfc/crGK8mHAd8Bhtvo37RbnudvlQBmVwLnSgJdxwJgH3e/"
+ "0cx+"
+ "7O7L0jTdZqKeIEkST5JkTQjhVnffH3gz8HXg2TboCTZpFiABSAJiw57glcDFwOPufmGapq9I"
+ "07RvktuD4RDCQ2Z2lJntCVzC3G98snue5/"
+ "tLAK2RwGWSQNf2BNsCZwKPAtenabrzFOfDSBzHvwSWNzY+"
+ "WQ78ao56gnnA6VmWLZQA1AmImfcEWwH7mNlG8xJCyOM4/"
+ "r27X2Jmu5vZkcCDc9AT7OHu+0oAuh0QM+ffzezdcRz/bLr/"
+ "Q2OhkmfjOL7JzN5CsRDqzbRu45N5wBmTvSkpAcyOBC6RBLoy/"
+ "AfHcfzjGZwfwyGE77n7eyg2PrkSqLSgJ9jT3feTAFongY9LAl3Fbxvhf7QZf9noxidmdoaZL"
+ "QXOmeWeYKOzAAlAEhAT8x9A08I/7jzJ4zh+0t0vcvc9md0NUvecqguQACQBsSG/"
+ "Aw4ZHh5+dDZ/SON5gqdDCF8zszdRbHyyCniuiT3BlLMACWB2JXCRJNB54Xf3g4eHh3+4/"
+ "fbbewvPmbUhhAeAQ9z9L4Grad7GJ3u5+1skgBZLADgPuFAS6Jzw53l+"
+ "SKvDP5YQwvokSX5OsS7BrsC5zHzjk9HnAraQAFo7mOuA8yWBjrnyH7J+/"
+ "fpHdthhB2+DcycPIfw2z/NPu/uuwAnAj2bQE7yG4pFlCWCOJPBZSaBteWI0/"
+ "HN15Z+Mxgapq0MI15vZXwFv548bn2zKZ53v7mekabqFBDA3ErhAEmjv8G+"
+ "33XZtvTBooyf4JvAuYB+KjU9qmyCCvcfPAiQASaDnwz8yMvKDdg//"
+ "+J4ghPC4mZ0KLAU+Mc2eYD5wxth1DyQASaBX+T1w6MjIyA+23XbbjlwSPI7j0Q1Sz8/"
+ "zfOwGqeunOwuQACSBngy/mR2a5/"
+ "nDnRr+sSRJ4gMDA7UQwnWlUun1FBuf3AO8MNksIMuyBRLA3EvgM5LA3Fz58zz//"
+ "sDAQNdtGjpm45ODKDY++TIbbpD6l+7+JglAEui58Lv7YXmefz9JEu/"
+ "y82t9COEnURSdbGa7U2x88ttGTzAfOD1N0wUSwBwPkiTQMv7g7ocBD3bjlX8yyuVyPY7j30R"
+ "RdK677wK8H/gZsBfwBgmgfSSgTmB2r/yHAg92+5V/ChF4kiRZvV6/Ns/"
+ "z1wJHALv06dxoDwmkaXpB4z7tHIpHN0WTrvzA4b0c/"
+ "rEMDg46xUNEt6VpepdmAG3UCbi7bgeaH/"
+ "7DzOx7Cv+E59yIBNBGJEmyXhJoGhXgiCiKvhfHscI/CRJA+0rg05LAjMJ/"
+ "eKlU+na5XFb4JYCOlMCnJIHNC7+ZvadUKn27v79f4ZcAJIEeompm74mi6AGFXwKQBHow/"
+ "KVSSeGXALpSAhcwO4tGdlP4/2HJkiUKvwTQfRKg+GbgfElgw/"
+ "ADCr8E0N00nhi8UBLYIPxH9PX1KfwSQE9J4JOSABlwtJl9S+GXAHpNAp/"
+ "rcQlkwJHufp8e8pEAJIEeDb8e75UAeloC7t5rEsiAoxR+CUDw/74i7BUJjIb/"
+ "XoVfAhC9JYEacLTCLwH0PFmWLc2ybMl4CXRxJ1ADjgLuUfglgJ6mWq1u6e5fdPcbsizrn6QT"
+ "OK+LJDAEHA3cE0JQ+CWA3mX16tUWRdHBFBtBHOju14+"
+ "XQON24KIukcCQmR0TRZHCLwGIkZGRrd39TKCvMW4Huvv1tVqtf4LbgU6XwGozO9bM7iqXy7l"
+ "GXwLo9am/"
+ "AccAO44buwPzPL9hvARCCOvNrFMlsLpx5b9T4ZcABGBmAxT7xZcmGL8DJpJAHMedKIHV7n6M"
+ "u9/"
+ "Z39+"
+ "v8EsAIsuyCDgZePEUY7gxCXyiAySw2t2PzfP8zhCCwi8BiAYvBk7ayFhtTAIXt7kEVrv7se5"
+ "+x+DgoMIvAYjGvX/"
+ "J3U8DBqc5lp0ogdXAce5+"
+ "x8DAgMIvAYgx7EjxPbhtwngekOf5DZVKpRMk8DRworvfrvBLAGIMaZr2mdmZwJLNGNMDoihq"
+ "dwk8A5wArEySROGXAMQ4dgYO3oSr/"
+ "7QlAFwMfHQOJfAMcIKZrVThJwGIcWRZNg9YDmw5w7GdUAKNx4YvmyMJjIZ/"
+ "RRzHCr8EIMbj7nsDBzZpfA+"
+ "IouiGarVaHvsbSZKMjJHAsMIvAYj2uPdfQLFD8BZNHOMDzGyuJfAMcKLCLwGIqflr4K9mYZy"
+ "nksDlsyyBZ4AToyi6VeGXAMQk1Gq1RcDZwPxZ+OttjiTw7Gj49Wy/BCCmIM/"
+ "zg4A9ZvFHtFoCaygeY1b4JQAxFVmWbQV8iOJ1X7pAAmuA97r7TfqqTwIQU1CtVs3dDwf+"
+ "vEU/ckoJAJcDH5mBBNYAJ7v7TXrIRwIQG6cMnMGGr/u2QgJ/N14CIYQRM7tiMyUwOu3/"
+ "usIvAYhpXP3N7ATgZXPw4w1460QSiON4cySwBjgF+Lqm/"
+ "RKAmE4CzbYD3jeHYzEdCUynE6g3/tzfK/"
+ "wSgJgGtVotAk4FtptrDzUkcGOapvF4CUynEwgh5CGEXyj8EoCYJu7+Moo34qwNPo4B+"
+ "wEbSGCGnYCQAMQE9/"
+ "4ldz8diNvpjgTYtyGB0IROQEgAYhL+G3B4m1z9J5LAVzcigXUaws7HdAhaT5qmfcD1wHva+"
+ "Q4FuA84KoSQTvD5+"
+ "0MIVY2mBCA2XQCvAR4AFrV7TQF808yOjOM41cjpFkDMPPzzKV74WdQBH9eAfd39q1mWBY2eB"
+ "CBmmiiz1wNv6bBZ4r7u/uXVq1fP1whKAGIzybJsC3c/B1jQYR99LfDNkZGREY2iBCA294ba/"
+ "QDgtR32sV8AznD36/SQTxfOSHUIWkO1Wl1sZt8Dduuw8J/"
+ "p7l9KkqSuUdQMQGwGQ0NDZmYHA69W+IUE0GPU6/"
+ "WtgQ8y+4t9KPxCAmizqb9RbO+1Y4d85LUNWSn8EoCYKWY2AJxGaxf7mEn4zwS+GEJQ+"
+ "CUAMRMqlUpEsbX3Szok/"
+ "GcBX1L4JQDRjIMbRS+mWB4r6oDwL3f3a0MI+"
+ "q5fAhAzJU3TCPgAMNjmH3UYONvdr2ksCCokANEEdgSOob2ftRhuXPmvVvglANEkqtVqH8Uqv"
+ "0s64Mqv8EsAopmY2U7AoW189R+meCPx8wq/BCCaSJZl84DlwJbtHn4VfkICaDLuvjfwtjYO/"
+ "zkKv5AAZoE0TRc0rv5btGn4PwJcpfALCWB2eDPwxjb8XOuAj7n7FQq/"
+ "kABmgVqttrAxvZ7fpuG/TIWfkABmiTzPDwL2aMfwA5cq/GIitCBIE8iy7EXu/"
+ "hCwczuGX9N+oRnALJGmqbn74cBftFn4zwUuU/iFBDCLuHuZ4qm/UpuF/"
+ "9IQwnqNkJAAZolKpWLAccCftlH4P67wCwmgBZjZtmb2/"
+ "jY5juuA84BLFH4hAcwyWZZFZnYqsH2bhP984CKFX0gAreFPgBOY+29S1gEXABcq/"
+ "EICaM3Vv+Tuy4DQJuH/rMIvJIAWkef5K4Ej5vjqv17hFxJA66/+fWZ2FrC1wi8kgB7D3XcD/"
+ "qYNwv8ZhV/MlD4dgumTpul8itd9F81h+D9lZp+J41jhF5oBtJjXAfvNcfg/rfALCaD19/"
+ "5bULzuu2COwn8hoPALCWCO7v33B/aZo/B/Djhf9/yi2eh14GlQqVQWR1H0XWD3uQi/"
+ "mX0yjuN1GgmhGUCLqdVqFkXRu4GlcxD+ixR+"
+ "IQHMIXmeb02xXXZfq8MPnKfwCwlgjhgaGjLgSOC/tjj8F7v7eSEEhV9IAHN49U+AZbRusY/"
+ "R8H8iSRKFX0gAc0WlUonc/STgpS0M/"
+ "yUKv2glehJwMjNG0Q7AyS2S5IiZXQ58XNN+oRnAHJNlWQS8H9imVeE3s4+"
+ "p8BMSQHvwcuBYZv85iRHgcjP7aLlcHtZhFxLA3F/"
+ "9S+5+JtDfgvBfofALCaCNcPedgENn+eo/"
+ "Gv6PxHGs8AsJoB1I03Qexeu+Wyr8QgLoPfYC3jbL4b9S4RcSQPtd/edTvO67cJbD/"
+ "2GFX7QLeg7gj7wZeONshd/MrjGzD6vwE5oBtBlZli0Ezgbmz9KV/"
+ "wtmtlzhFxJAG+Lu7wD2nK3wR1H0oXK5vFZHWrQbPb8gSJZlL3L37wOvVviFZgA9RLVaNXc/"
+ "DHjVLIT/WoVfSABtTKlU6gfOoLmv+44A15ZKpbMUfiEBtO/U3/I8Pw74syaH/4ulUums/"
+ "v5+hV9IAG3MNhRv/DXrGNQb4f+gwi8kgPa+94/c/VRghyaG/7o8z3XlFxJAu2Nm/"
+ "wU4geZ8CzIa/tMHBgZe0CklJID2vvcvUazzlzQj/O7+lZGREYVfSAAdwispVvq1ZoQ/z/"
+ "Nl22yzjcIvJIB2J03TPnc/C9i6CdP+r+R5vmxwcFDhFxJAh7AL8M4mhP9v6/"
+ "W6wi8kgA66+s+neOFn8UzDryu/kAA6j32A/WcY/"
+ "uvzPF82MDDwvE4dIQF0CFmWLaBY7GPBDML/VXc/"
+ "TeEXEkCH4e77N2YAmxv+G4H3J0mi8IuuoutfB65UKoujKPoOsMfmhN/"
+ "dbwTelyTJczpdRLfR1UuCZVlm7v4uivZ/c8L/"
+ "NYVf6BagQ8nzfCvgg5shutHwn6rwCwmgA6nVamZmRwKv2Ix7foVfSACdjLsH4HQ2bbGPOvD3"
+ "Cr+QADr86g+cBLx0U+4YGuE/"
+ "JYSg8IueoCtLwDzPdwBO3gTB5cBNZnZqHMcKv9AMoFPJsiyiWOln200I/zfM7L1xHK/"
+ "RKSE0A+hs/gw4juk945AD34ii6L3lclnhF5oBdPjVv+TuZwL90wz/"
+ "zY3wP6tTQWgG0Pm8Cjh0Glf/3MxujqLopP7+foVfaAbQ6VSr1Xnuvhx40TSv/Aq/"
+ "kAC65R9iZnsCb59G+G9R+IXoIgE0Fvs4B1g4jfCfqHt+IbprBvCmxq+pwr/"
+ "S3U9S+IXoIgHUarWFFEt9zZ8i/Kvc/YQkSZ7RkAvRRQLI8/ztwF5ThR84PkmSpzXcQvz/"
+ "dPSCIGmavgh4EFg6VfhDCAq/"
+ "EN00A3jqqacMOATYaZLw36bwC9GlAujr6+sHzmTD131Hw3+cwi9EFwqgsdTXscDLJwj/7Qq/"
+ "EN09A9gG+MC4zz8a/mMVfiGmOZPutA+cpmnk7icD248L/11mdnwcxwq/EF08A3gpxWo/"
+ "0Zjw321mx8RxPKQhFaJLZwBpmpaAZUAyNvxRFB1dLpcVfiG6fAbwCuAoiucXFH4hekUAWZb1"
+ "UazxvxXgwN15niv8QvSCANx9F+Bdjf+8292PGRgYUPiF6HYBpGk6D1hO8brv3e5+"
+ "dJIkNQ2fEL0xA9gH2A+4V+EXonm0/"
+ "ctAaZouAO6k2LXniBBCpmETondmAPsCa4EjFX4hmktbPwdQqVQWAq82s+"
+ "PiOE41XEL0ENVqdds0TZfoSAghhBBCCCGEEEIIIYQQm8F/AidYDd/"
+ "hAAzXAAAAAElFTkSuQmCC";
+
+
+};
+
+#endif // MOREAPPS_H
diff --git a/src/widgets/MoreApps/moreapps.ui b/src/widgets/MoreApps/moreapps.ui
new file mode 100644
index 0000000..cfcf8fa
--- /dev/null
+++ b/src/widgets/MoreApps/moreapps.ui
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MoreApps</class>
+ <widget class="QWidget" name="MoreApps">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>806</width>
+ <height>141</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="loadingLabel">
+ <property name="text">
+ <string>... ... ... ...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout"/>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>