Skip to content
Snippets Groups Projects
Commit 3db33c4f authored by Andreas Traczyk's avatar Andreas Traczyk
Browse files

mainapplication: add URI start option

+ Refactors some window management logic in LayoutManagar.qml.
+ Refactors app start to support both a minimized start and a start
  URI. The start URI should force visibility.
+ StartMinimized now starts the application in a closed-to-tray
  state, instead of a minimized state.
+ The close-to-tray feature now saves the previous windowed state.
+ InstanceManager handles URI transfer to secondary instances.

This commit does not implement URI handling. The original
implementation supported only calls via a user infohash as the URI
parameter. A new and flexible protocol should be defined to support
multiple Jami features.

Gitlab: #655
Change-Id: I2c47028930e4e9d7ccca94d9362545df14b98160
parent f319c954
No related branches found
No related tags found
No related merge requests found
...@@ -20,6 +20,8 @@ import QtQuick ...@@ -20,6 +20,8 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import net.jami.Adapters 1.1 import net.jami.Adapters 1.1
import net.jami.Enums 1.1
import net.jami.Constants 1.1
import "mainview/components" import "mainview/components"
...@@ -49,6 +51,73 @@ QtObject { ...@@ -49,6 +51,73 @@ QtObject {
} }
visibility = priv.windowedVisibility visibility = priv.windowedVisibility
} }
appWindow.allowVisibleWindow = true
}
// Start in a hidden state.
function startMinimized(visibilitySetting) {
// Save the loaded setting for when the app is restored.
priv.windowedVisibility = visibilitySetting
appWindow.allowVisibleWindow = false
appWindow.hide();
}
// Close to a hidden state.
function closeToTray(visibilitySetting = undefined) {
// Save the current visibility.
priv.windowedVisibility = visibility
appWindow.hide();
}
// Save the window geometry and visibility settings.
function saveWindowSettings() {
var geometry = Qt.rect(appWindow.x, appWindow.y,
appWindow.width, appWindow.height)
AppSettingsManager.setValue(Settings.WindowGeometry, geometry)
// If closed-to-tray or minimized, save the cached windowedVisibility
// value instead.
if (isHidden) {
AppSettingsManager.setValue(Settings.WindowState, priv.windowedVisibility)
} else {
AppSettingsManager.setValue(Settings.WindowState, visibility)
}
}
// Restore the window geometry and visibility settings.
function restoreWindowSettings() {
var geometry = AppSettingsManager.getValue(Settings.WindowGeometry)
// Position.
if (!isNaN(geometry.x) && !isNaN(geometry.y)) {
appWindow.x = geometry.x
appWindow.y = geometry.y
}
// Dimensions.
appWindow.width = geometry.width ?
geometry.width :
JamiTheme.mainViewPreferredWidth
appWindow.height = geometry.height ?
geometry.height :
JamiTheme.mainViewPreferredHeight
appWindow.minimumWidth = JamiTheme.mainViewMinWidth
appWindow.minimumHeight = JamiTheme.mainViewMinHeight
// State.
const visibilityStr = AppSettingsManager.getValue(Settings.WindowState)
var visibilitySetting = parseInt(visibilityStr)
// We should never restore a hidden state here. Default to normal
// windowed state in such a case. This shouldn't happen.
if (visibilitySetting === Window.Hidden) {
visibilitySetting = Window.Windowed
}
if (MainApplication.startMinimized) {
startMinimized(visibilitySetting)
} else {
visibility = visibilitySetting
}
} }
// Adds an item to the fullscreen item stack. Automatically puts // Adds an item to the fullscreen item stack. Automatically puts
......
...@@ -53,6 +53,7 @@ ApplicationWindow { ...@@ -53,6 +53,7 @@ ApplicationWindow {
} }
property bool windowSettingsLoaded: false property bool windowSettingsLoaded: false
property bool allowVisibleWindow: true
function checkLoadedSource() { function checkLoadedSource() {
var sourceString = mainApplicationLoader.source.toString() var sourceString = mainApplicationLoader.source.toString()
...@@ -83,19 +84,18 @@ ApplicationWindow { ...@@ -83,19 +84,18 @@ ApplicationWindow {
if (force || !UtilsAdapter.getAppValue(Settings.MinimizeOnClose) || if (force || !UtilsAdapter.getAppValue(Settings.MinimizeOnClose) ||
!UtilsAdapter.getAccountListSize()) { !UtilsAdapter.getAccountListSize()) {
// Save the window geometry and state before quitting. // Save the window geometry and state before quitting.
var geometry = Qt.rect(appWindow.x, appWindow.y, layoutManager.saveWindowSettings()
appWindow.width, appWindow.height)
AppSettingsManager.setValue(Settings.WindowGeometry, geometry)
AppSettingsManager.setValue(Settings.WindowState, appWindow.visibility)
Qt.quit() Qt.quit()
} else { } else {
hide() layoutManager.closeToTray()
} }
} }
title: JamiStrings.appTitle title: JamiStrings.appTitle
visible: mainApplicationLoader.status === Loader.Ready && windowSettingsLoaded visible: mainApplicationLoader.status === Loader.Ready
&& windowSettingsLoaded
&& allowVisibleWindow
// To facilitate reparenting of the callview during // To facilitate reparenting of the callview during
// fullscreen mode, we need QQuickItem based object. // fullscreen mode, we need QQuickItem based object.
...@@ -134,38 +134,21 @@ ApplicationWindow { ...@@ -134,38 +134,21 @@ ApplicationWindow {
onSourceChanged: windowSettingsLoaded = false onSourceChanged: windowSettingsLoaded = false
onLoaded: { onLoaded: {
if (UtilsAdapter.getAppValue(Settings.StartMinimized)) { if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView) {
showMinimized() // Onboarding wizard window, these settings are fixed.
// - window screen should default to the primary
// - position should default to being centered based on the
// following dimensions
// - the window will showNormal once windowSettingsLoaded is
// set to true(then forcing visible to true)
appWindow.width = JamiTheme.wizardViewMinWidth
appWindow.height = JamiTheme.wizardViewMinHeight
appWindow.minimumWidth = JamiTheme.wizardViewMinWidth
appWindow.minimumHeight = JamiTheme.wizardViewMinHeight
} else { } else {
if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView) { // Main window, load any valid app settings, and allow the
appWindow.width = JamiTheme.wizardViewMinWidth // layoutManager to handle as much as possible.
appWindow.height = JamiTheme.wizardViewMinHeight layoutManager.restoreWindowSettings()
appWindow.minimumWidth = JamiTheme.wizardViewMinWidth
appWindow.minimumHeight = JamiTheme.wizardViewMinHeight
} else {
// Main window, load settings if possible.
var geometry = AppSettingsManager.getValue(Settings.WindowGeometry)
// Position.
if (!isNaN(geometry.x) && !isNaN(geometry.y)) {
appWindow.x = geometry.x
appWindow.y = geometry.y
}
// Dimensions.
appWindow.width = geometry.width ?
geometry.width :
JamiTheme.mainViewPreferredWidth
appWindow.height = geometry.height ?
geometry.height :
JamiTheme.mainViewPreferredHeight
appWindow.minimumWidth = JamiTheme.mainViewMinWidth
appWindow.minimumHeight = JamiTheme.mainViewMinHeight
// State.
const visibilityStr = AppSettingsManager.getValue(Settings.WindowState)
appWindow.visibility = parseInt(visibilityStr)
}
} }
// This will trigger `visible = true`. // This will trigger `visible = true`.
...@@ -176,6 +159,9 @@ ApplicationWindow { ...@@ -176,6 +159,9 @@ ApplicationWindow {
UpdateManager.checkForUpdates(true) UpdateManager.checkForUpdates(true)
UpdateManager.setAutoUpdateCheck(true) UpdateManager.setAutoUpdateCheck(true)
} }
// Handle a start URI if set as start option.
MainApplication.handleUriAction();
} }
} }
......
...@@ -52,13 +52,20 @@ public: ...@@ -52,13 +52,20 @@ public:
{} {}
~Impl() = default; ~Impl() = default;
bool tryToRun() bool tryToRun(const QByteArray& startUri)
{ {
if (isAnotherRunning()) { if (isAnotherRunning()) {
// This is a secondary instance, // This is a secondary instance, connect to the primary
// connect to the primary instance to trigger a restore // instance to trigger a restore then die.
// then fail.
if (connectToLocal()) { if (connectToLocal()) {
// Okay we connected. Send the start uri if not empty.
if (startUri.size()) {
qDebug() << "Sending start URI to secondary instance." << startUri;
socket_->write(startUri);
socket_->waitForBytesWritten();
}
// Now this instance can die.
return false; return false;
} }
// If not connected, this means that the server doesn't exist // If not connected, this means that the server doesn't exist
...@@ -99,7 +106,7 @@ public: ...@@ -99,7 +106,7 @@ public:
return; return;
} }
socket_->write(reinterpret_cast<const char*>(terminateSeq_.data()), 4); socket_->write(terminateSeq_);
socket_->waitForBytesWritten(); socket_->waitForBytesWritten();
}; };
...@@ -139,10 +146,15 @@ private Q_SLOTS: ...@@ -139,10 +146,15 @@ private Q_SLOTS:
if (recievedData == terminateSeq_) { if (recievedData == terminateSeq_) {
qWarning() << "Received terminate signal."; qWarning() << "Received terminate signal.";
mainAppInstance_->quit(); mainAppInstance_->quit();
} else {
qDebug() << "Received start URI:" << recievedData;
auto startUri = QString::fromLatin1(recievedData);
mainAppInstance_->handleUriAction(startUri);
} }
}); });
// Restore primary instance // Restore primary instance
qDebug() << "Received wake-up from secondary instance.";
mainAppInstance_->restoreApp(); mainAppInstance_->restoreApp();
}; };
...@@ -193,9 +205,9 @@ InstanceManager::~InstanceManager() ...@@ -193,9 +205,9 @@ InstanceManager::~InstanceManager()
} }
bool bool
InstanceManager::tryToRun() InstanceManager::tryToRun(const QByteArray& startUri)
{ {
return pimpl_->tryToRun(); return pimpl_->tryToRun(startUri);
} }
void void
......
...@@ -32,7 +32,7 @@ public: ...@@ -32,7 +32,7 @@ public:
explicit InstanceManager(MainApplication* mainApp); explicit InstanceManager(MainApplication* mainApp);
~InstanceManager(); ~InstanceManager();
bool tryToRun(); bool tryToRun(const QByteArray& startUri);
void tryToKill(); void tryToKill();
private: private:
......
...@@ -106,9 +106,12 @@ main(int argc, char* argv[]) ...@@ -106,9 +106,12 @@ main(int argc, char* argv[])
qWarning() << "Attempting to terminate other instances."; qWarning() << "Attempting to terminate other instances.";
im.tryToKill(); im.tryToKill();
return 0; return 0;
} else if (!im.tryToRun()) { } else {
qWarning() << "Another instance is running."; auto startUri = app.getOpt(MainApplication::Option::StartUri);
return 0; if (!im.tryToRun(startUri.toByteArray())) {
qWarning() << "Another instance is running.";
return 0;
}
} }
if (!app.init()) { if (!app.init()) {
......
...@@ -241,6 +241,10 @@ MainApplication::init() ...@@ -241,6 +241,10 @@ MainApplication::init()
lrcInstance_->accountModel().autoTransferFromTrusted = allowTransferFromTrusted; lrcInstance_->accountModel().autoTransferFromTrusted = allowTransferFromTrusted;
lrcInstance_->accountModel().autoTransferSizeThreshold = acceptTransferBelow; lrcInstance_->accountModel().autoTransferSizeThreshold = acceptTransferBelow;
auto startMinimizedSetting = settingsManager_->getValue(Settings::Key::StartMinimized).toBool();
// The presence of start URI should override the startMinimized setting for this instance.
set_startMinimized(startMinimizedSetting && runOptions_[Option::StartUri].isNull());
initQmlLayer(); initQmlLayer();
settingsManager_->setValue(Settings::Key::StartMinimized, settingsManager_->setValue(Settings::Key::StartMinimized,
...@@ -257,6 +261,20 @@ MainApplication::restoreApp() ...@@ -257,6 +261,20 @@ MainApplication::restoreApp()
Q_EMIT lrcInstance_->restoreAppRequested(); Q_EMIT lrcInstance_->restoreAppRequested();
} }
void
MainApplication::handleUriAction(const QString& arg)
{
QString uri {};
if (arg.isEmpty() && !runOptions_[Option::StartUri].isNull()) {
uri = runOptions_[Option::StartUri].toString();
qDebug() << "URI action invoked by run option" << uri;
} else {
uri = arg;
qDebug() << "URI action invoked by secondary instance" << uri;
}
// TODO: implement URI protocol handling.
}
void void
MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bool logDaemon) MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bool logDaemon)
{ {
...@@ -290,6 +308,13 @@ MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bo ...@@ -290,6 +308,13 @@ MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bo
void void
MainApplication::parseArguments() MainApplication::parseArguments()
{ {
// See if the app is being started with a URI.
for (const auto& arg : QApplication::arguments()) {
if (arg.startsWith("jami:")) {
runOptions_[Option::StartUri] = arg;
}
}
QCommandLineParser parser; QCommandLineParser parser;
parser.addHelpOption(); parser.addHelpOption();
parser.addVersionOption(); parser.addVersionOption();
......
...@@ -58,7 +58,7 @@ class MainApplication : public QApplication ...@@ -58,7 +58,7 @@ class MainApplication : public QApplication
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(MainApplication) Q_DISABLE_COPY(MainApplication)
QML_RO_PROPERTY(bool, startMinimized)
public: public:
explicit MainApplication(int& argc, char** argv); explicit MainApplication(int& argc, char** argv);
~MainApplication(); ~MainApplication();
...@@ -66,6 +66,8 @@ public: ...@@ -66,6 +66,8 @@ public:
bool init(); bool init();
void restoreApp(); void restoreApp();
Q_INVOKABLE void handleUriAction(const QString& uri = {});
enum class Option { enum class Option {
StartMinimized = 0, StartMinimized = 0,
Debug, Debug,
...@@ -73,7 +75,8 @@ public: ...@@ -73,7 +75,8 @@ public:
DebugToFile, DebugToFile,
UpdateUrl, UpdateUrl,
MuteJamid, MuteJamid,
TerminationRequested TerminationRequested,
StartUri
}; };
QVariant getOpt(const Option opt) QVariant getOpt(const Option opt)
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment