components/mobileshell: Add mechanism to keep track of and stop launched apps

This commit is contained in:
Devin Lin 2022-10-12 17:00:21 -04:00
parent c2b9a06e61
commit 7385ca9dca
9 changed files with 138 additions and 17 deletions

View file

@ -22,10 +22,6 @@ MouseArea { // use mousearea to ensure clicks don't go behind
visible: false
property alias backgroundColor: background.color
Kirigami.ImageColors {
id: colorGenerator
source: icon.source
}
function open(splashIcon, title, x, y, sourceIconSize, color) {
iconParent.scale = sourceIconSize/iconParent.width;
@ -83,6 +79,11 @@ MouseArea { // use mousearea to ensure clicks don't go behind
background.state = "closed";
}
}
Kirigami.ImageColors {
id: colorGenerator
source: icon.source
}
Item {
id: backgroundParent

View file

@ -243,5 +243,12 @@ Item {
id: startupFeedback
z: 999999
anchors.fill: parent
// if the startup feedback closes, clear the shell's stored launching app
onVisibleChanged: {
if (!visible) {
MobileShell.ShellUtil.clearLaunchingApp();
}
}
}
}

View file

@ -27,7 +27,7 @@ PlasmaComponents.Label {
TapHandler {
onTapped: {
MobileShell.ShellUtil.launchApp("org.kde.kclock");
MobileShell.ShellUtil.launchApp("org.kde.kclock.desktop");
}
}
}

View file

@ -11,7 +11,6 @@ import QtQuick.Controls 2.15 as Controls
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras

View file

@ -7,12 +7,13 @@
*/
#include "shellutil.h"
#include "windowutil.h"
#include <KConfigGroup>
#include <KFileUtils>
#include <KIO/ApplicationLauncherJob>
#include <KLocalizedString>
#include <KNotification>
#include <KNotificationJobUiDelegate>
#include <QDBusPendingReply>
#include <QDateTime>
@ -24,6 +25,7 @@
ShellUtil::ShellUtil(QObject *parent)
: QObject{parent}
, m_launchingApp{nullptr}
{
m_localeConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::SimpleConfig);
m_localeConfigWatcher = KConfigWatcher::create(m_localeConfig);
@ -77,13 +79,54 @@ bool ShellUtil::isSystem24HourFormat()
return timeFormat == QStringLiteral(FORMAT24H);
}
void ShellUtil::launchApp(const QString &app)
void ShellUtil::launchApp(const QString &storageId)
{
const KService::Ptr appService = KService::serviceByDesktopName(app);
if (!appService) {
qWarning() << "Could not find" << app;
// try to activate a running window first
auto windows = WindowUtil::instance()->windowsFromStorageId(storageId);
if (!windows.empty()) {
windows[0]->requestActivate();
return;
}
auto job = new KIO::ApplicationLauncherJob(appService, this);
// now try launching the window
KService::Ptr service = KService::serviceByStorageId(storageId);
if (!service) {
qWarning() << "Could not find" << storageId;
return;
}
auto job = new KIO::ApplicationLauncherJob(service, this);
job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
job->start();
setLaunchingApp(job);
}
bool ShellUtil::isLaunchingApp()
{
return m_launchingApp != nullptr;
}
void ShellUtil::setLaunchingApp(KIO::ApplicationLauncherJob *launcherJob)
{
m_launchingApp = launcherJob;
connect(launcherJob, &KIO::ApplicationLauncherJob::result, this, [this](auto *job) {
m_launchingAppPids = m_launchingApp->pids();
});
Q_EMIT isLaunchingAppChanged();
}
void ShellUtil::cancelLaunchingApp()
{
for (auto pid : m_launchingAppPids) {
QProcess::execute("kill", {QString::number(pid)});
}
clearLaunchingApp();
}
void ShellUtil::clearLaunchingApp()
{
m_launchingApp = nullptr;
Q_EMIT isLaunchingAppChanged();
}

View file

@ -11,6 +11,7 @@
#include <QQuickItem>
#include <KConfigWatcher>
#include <KIO/ApplicationLauncherJob>
#include <KSharedConfig>
/**
@ -22,7 +23,7 @@ class ShellUtil : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isSystem24HourFormat READ isSystem24HourFormat NOTIFY isSystem24HourFormatChanged)
Q_PROPERTY(bool launchingApp READ isLaunchingApp NOTIFY isLaunchingAppChanged)
Q_PROPERTY(bool isLaunchingApp READ isLaunchingApp NOTIFY isLaunchingAppChanged)
public:
ShellUtil(QObject *parent = nullptr);
@ -52,21 +53,44 @@ public:
Q_INVOKABLE void executeCommand(const QString &command);
/**
* Launch an application by name.
* Launch an application by name. Sets the internal "launched app" state.
*
* @param app The name of the application to launch.
* @param storageId The storage id of the application to launch.
*/
Q_INVOKABLE void launchApp(const QString &app);
Q_INVOKABLE void launchApp(const QString &storageId);
/**
* Whether the system is using 24 hour format.
*/
Q_INVOKABLE bool isSystem24HourFormat();
/**
* Whether an application is being launched.
*/
Q_INVOKABLE bool isLaunchingApp();
/**
* Cancels an application launch by running `kill pid` for every associated pid of the launching app.
*/
Q_INVOKABLE void cancelLaunchingApp();
/**
* Clears the currently stored launching app.
*
* This should be called if the application window finally shows.
*/
Q_INVOKABLE void clearLaunchingApp();
Q_SIGNALS:
void isSystem24HourFormatChanged();
void isLaunchingAppChanged();
private:
void setLaunchingApp(KIO::ApplicationLauncherJob *launcherJob);
KConfigWatcher::Ptr m_localeConfigWatcher;
KSharedConfig::Ptr m_localeConfig;
KIO::ApplicationLauncherJob *m_launchingApp;
QVector<qint64> m_launchingAppPids;
};

View file

@ -75,6 +75,7 @@ void WindowUtil::initWayland()
connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, [this](KWayland::Client::PlasmaWindow *window) {
Q_EMIT windowCreated(window);
});
connect(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::windowCreated, this, &WindowUtil::windowCreatedSlot);
connect(m_windowManagement, &PlasmaWindowManagement::showingDesktopChanged, this, &WindowUtil::updateShowingDesktop);
connect(m_windowManagement, &PlasmaWindowManagement::activeWindowChanged, m_activeWindowTimer, qOverload<>(&QTimer::start));
@ -222,3 +223,34 @@ void WindowUtil::forgetActiveWindow()
m_activeWindow.clear();
Q_EMIT hasCloseableActiveWindowChanged();
}
QList<KWayland::Client::PlasmaWindow *> WindowUtil::windowsFromStorageId(const QString &storageId) const
{
if (!m_windows.contains(storageId)) {
return {};
}
return m_windows[storageId];
}
void WindowUtil::windowCreatedSlot(KWayland::Client::PlasmaWindow *window)
{
QString storageId = window->appId() + QStringLiteral(".desktop");
// ignore empty windows
if (storageId == ".desktop" || storageId == "org.kde.plasmashell.desktop") {
return;
}
if (!m_windows.contains(storageId)) {
m_windows[storageId] = {};
}
m_windows[storageId].push_back(window);
// listen for window close
connect(window, &KWayland::Client::PlasmaWindow::unmapped, this, [this, storageId]() {
m_windows.remove(storageId);
Q_EMIT windowChanged(storageId);
});
Q_EMIT windowChanged(storageId);
}

View file

@ -67,6 +67,11 @@ public:
*/
bool hasCloseableActiveWindow() const;
/**
* Get the list of windows associated to a storage id.
*/
QList<KWayland::Client::PlasmaWindow *> windowsFromStorageId(const QString &storageId) const;
/**
* Close the current active window.
*/
@ -99,11 +104,13 @@ Q_SIGNALS:
void hasCloseableActiveWindowChanged();
void activeWindowChanged();
void activeWindowIsShellChanged();
void windowChanged(QString storageId); // emitted on window open or close
private Q_SLOTS:
void updateActiveWindowIsShell();
void forgetActiveWindow();
void updateShowingDesktop(bool showing);
void windowCreatedSlot(KWayland::Client::PlasmaWindow *window);
private:
void initWayland();
@ -117,4 +124,6 @@ private:
bool m_allWindowsMinimized = true;
bool m_allWindowsMinimizedExcludingShell = true;
bool m_activeWindowIsShell = false;
QHash<QString, QList<KWayland::Client::PlasmaWindow *>> m_windows; // <storageId, window>
};

View file

@ -82,7 +82,7 @@ MobileShell.NavigationPanel {
rightAction: MobileShell.NavigationPanelAction {
id: closeAppAction
enabled: Keyboards.KWinVirtualKeyboard.visible || root.taskSwitcher.visible || MobileShell.WindowUtil.hasCloseableActiveWindow
enabled: Keyboards.KWinVirtualKeyboard.visible || root.taskSwitcher.visible || MobileShell.WindowUtil.hasCloseableActiveWindow || MobileShell.ShellUtil.isLaunchingApp
iconSource: Keyboards.KWinVirtualKeyboard.visible ? "go-down-symbolic" : "mobile-close-app"
// mobile-close-app (from plasma-frameworks) seems to have less margins than icons from breeze-icons
iconSizeFactor: Keyboards.KWinVirtualKeyboard.visible ? 1 : 0.75
@ -101,6 +101,12 @@ MobileShell.NavigationPanel {
if (root.taskSwitcher.tasksModel.activeTask !== 0) {
root.taskSwitcher.tasksModel.requestClose(root.taskSwitcher.tasksModel.activeTask);
}
MobileShell.HomeScreenControls.closeAppLaunchAnimation();
} else if (MobileShell.ShellUtil.isLaunchingApp) {
// cancel the launching of the app
MobileShell.HomeScreenControls.closeAppLaunchAnimation();
MobileShell.ShellUtil.cancelLaunchingApp();
}
}
}