Commit e7bcb944 authored by Martin Straka's avatar Martin Straka
Browse files

Merge branch 'inactivity-lock' into 'develop'

Inactivity Lock

See merge request !36
parents 3c233026 0f87011a
......@@ -89,6 +89,7 @@ SOURCES += \
src/io/filesystem.cpp \
src/io/sqlite/db.cpp \
src/io/sqlite/table.cpp \
src/locker.cpp \
src/log/log.cpp \
src/main.cpp \
src/messages.cpp \
......@@ -118,6 +119,7 @@ HEADERS += \
src/io/filesystem.h \
src/io/sqlite/db.h \
src/io/sqlite/table.h \
src/locker.h \
src/log/log.h \
src/messages.h \
src/models/accountmodel.h \
......
......@@ -106,7 +106,7 @@ ApplicationWindow {
id: mainPage
anchors.fill: parent
color: "transparent"
visible: false
visible: true
StackView {
id: pageView
anchors.fill: parent
......@@ -220,7 +220,12 @@ ApplicationWindow {
}
onEditingFinished: {
verifyPin()
// This function is called repeatedly when switching
// windows. The condition should reduce PIN verification
// calls.
if (pinCodeInput.text.length > 0) {
verifyPin()
}
}
}
Text {
......@@ -248,30 +253,23 @@ ApplicationWindow {
versionLabel.text = qsTr("Version") + ": " + version
}
}
Connections {
target: settings
onShowPinScreen: {
if (show) {
mainPage.visible = false
pinScreen.visible = true
} else {
pinScreen.visible = false
mainPage.visible = true
}
}
}
Connections {
target: settings
onSendPinReply: {
if (success) {
Qt.inputMethod.hide()
pinScreen.visible = false
mainPage.visible = true
pinCodeInput.text = ""
} else {
wrongPin.visible = true
pinCodeInput.text = ""
pinScreen.visible = false
}
wrongPin.visible = !success
pinCodeInput.text = ""
}
}
Connections {
target: locker
onLockApp: {
mainPage.visible = false
pinScreen.visible = true
}
}
}
......
......@@ -263,7 +263,8 @@ Component {
MouseArea {
anchors.fill: parent
onClicked: {
files.openAttachnemt(gUserName, gMsgId, rFileId)
locker.ignoreNextSuspension()
files.openAttachment(gUserName, gMsgId, rFileId)
}
}
Rectangle {
......
This diff is collapsed.
......@@ -200,7 +200,7 @@ void Files::fillFileList(const QString &userName, qint64 msgId)
/*
* Slot: Open attachment in default application.
*/
void Files::openAttachnemt(const QString &userName, qint64 msgId, int fileId)
void Files::openAttachment(const QString &userName, qint64 msgId, int fileId)
/* ========================================================================= */
{
qDebug("%s()", __func__);
......
......@@ -45,7 +45,7 @@ public:
/*!
* @brief Open attachment in default application.
*/
Q_INVOKABLE void openAttachnemt(const QString &userName, qint64 msgId,
Q_INVOKABLE void openAttachment(const QString &userName, qint64 msgId,
int fileId);
/*!
......
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#include <QEvent>
#include "src/locker.h"
#include "src/settings.h"
/*
* Application behaviour on supported platforms:
*
* Android:
* on close (not terminate): Active -(immediately)-> Inactive -(short time)-> Suspended
* on attachment open: Active -(immediately)-> Inactive -(short time)-> Suspended
*
* iOS:
* on close (not terminate): Active -(immediately)-> Inactive -(short time)-> Suspended
* on attachment open: remains Active
*
* Don't know how to achieve hidden state.
*/
Locker::Locker(QObject *parent)
: QObject(parent),
m_inactivityInterval(0),
m_inactivityTimer(),
m_ignoreImmediateSuspension(false)
{
/* Don't start time yet. */
connect(&m_inactivityTimer, SIGNAL(timeout()),
this, SLOT(inactivityTimeOut()));
}
void Locker::ignoreNextSuspension(void)
{
m_ignoreImmediateSuspension = true;
}
void Locker::setInactivityInterval(int secs)
{
m_inactivityTimer.stop();
if (secs > 0) {
m_inactivityInterval = secs;
m_inactivityTimer.setInterval(m_inactivityInterval * 1000);
m_inactivityTimer.start();
} else {
m_inactivityInterval = 0;
}
}
void Locker::processNewState(Qt::ApplicationState state)
{
switch (state) {
case Qt::ApplicationSuspended:
case Qt::ApplicationHidden:
if (!m_ignoreImmediateSuspension) {
inactivityTimeOut();
} else {
/* Was ignored once. */
m_ignoreImmediateSuspension = false;
}
return;
break;
case Qt::ApplicationInactive:
case Qt::ApplicationActive:
/* Let the timer handle the locking. */
break;
default:
Q_ASSERT(0);
return;
break;
}
}
void Locker::inactivityTimeOut(void)
{
/* Inactivity timer timed out and PIN value is set and recovered. */
if (!globSet._pinVal.isEmpty()) {
emit lockApp();
}
}
bool Locker::eventFilter(QObject *watched, QEvent *event)
{
switch (event->type()) {
case QEvent::MouseMove:
case QEvent::KeyPress:
case QEvent::TouchBegin:
if (m_inactivityInterval > 0) {
m_inactivityTimer.stop(); /* Reset the timer. */
m_inactivityTimer.start();
}
break;
default:
/* Do nothing. */
break;
}
/* Standard event handling. */
return QObject::eventFilter(watched, event);
}
/*
* Copyright (C) 2014-2017 CZ.NIC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations including
* the two.
*/
#ifndef _LOCKER_H_
#define _LOCKER_H_
#include <QObject>
#include <QTimer>
/*!
* @brief Application locker class.
*/
class Locker : public QObject {
Q_OBJECT
public:
/*!
* @brief Constructor.
*
* @param[in] parent Parent object.
*/
Locker(QObject *parent = Q_NULLPTR);
/*!
* @brief Sets internal flag. Following immediate application
* suspension call will be ignored.
*
* @note This is useful when the control is handed over to another
* application (e.g. when opening an attachment in associated
* application).
*/
Q_INVOKABLE
void ignoreNextSuspension(void);
/*!
* @brief Set inactivity locking interval in seconds.
*
* @param[in] secs Interval length in seconds.
*/
Q_INVOKABLE
void setInactivityInterval(int secs);
public slots:
/*!
* @brief Processes supplied application state. Calls
* inactivityTimeOut() if suspension state achieved which should
* not be ignored.
*/
void processNewState(Qt::ApplicationState state);
private slots:
/*!
* @brief Emits lockApp signal if PIN is set.
*/
void inactivityTimeOut(void);
signals:
/*!
* @brief Signal emitted when the application should be locked.
*/
void lockApp(void);
protected:
/*!
* @brief Watches for MouseMove, KeyPress and TouchBegin events. Resets
* inactivity timer on those events.
*
* @param[in] watched Watched object.
* @param[in] event Incoming event.
* @return Returns whatever the parent class would return.
*/
virtual
bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE;
private:
int m_inactivityInterval; /*!<
* Interval length in seconds, value less or
* equal to 0 is off.
*/
QTimer m_inactivityTimer; /*!< Inactivity timer. */
bool m_ignoreImmediateSuspension; /*!<
* Set true if next suspension should
* be ignored.
*/
};
#endif /* _LOCKER_H_ */
......@@ -32,6 +32,7 @@
#include "src/accounts.h"
#include "src/common.h"
#include "src/files.h"
#include "src/locker.h"
#include "src/net/isds_wrapper.h"
#include "src/models/accountmodel.h"
#include "src/models/filemodel.h"
......@@ -134,6 +135,13 @@ int main(int argc, char *argv[])
QObject::connect(&accounts, SIGNAL(removeIsdsCtx(QString)),
&isds, SLOT(removeIsdsCtx(QString)));
/* Register application state changes. */
class Locker locker;
QObject::connect(&app,
SIGNAL(applicationStateChanged(Qt::ApplicationState)),
&locker, SLOT(processNewState(Qt::ApplicationState)));
app.installEventFilter(&locker);
/* Create and init account model. */
globAccountsModelPtr = new (std::nothrow) AccountListModel;
if (0 == globAccountsModelPtr) {
......@@ -162,6 +170,7 @@ int main(int argc, char *argv[])
ctx->setContextProperty("accounts", &accounts);
ctx->setContextProperty("files", &files);
ctx->setContextProperty("settings", &settings);
ctx->setContextProperty("locker", &locker);
/* register and set models in QML */
ctx->setContextProperty(globAccountsModelPtr->objectName(),
......@@ -235,11 +244,21 @@ int main(int argc, char *argv[])
files.deleteExpiredFilesFromDbs(globSet.fileLifeTimeInDays);
}
/* Inactivity locking is disabled when equal to 0. */
if (globSet.pinInactTimeoutInSecs > 0) {
locker.setInactivityInterval(globSet.pinInactTimeoutInSecs);
}
/* Load counters. */
Accounts::loadModelCounters();
/* show PIN screen if needed */
emit settings.showPinScreen(!globSet.pinCode.isEmpty());
/*
* Show PIN screen if needed. Encoded PIN is checked because it hasn't
* been decoded yet.
*/
if (!globSet.pinCode.isEmpty()) {
emit locker.lockApp();
}
/* Run app main event loop */
int ret = app.exec();
......
......@@ -45,6 +45,7 @@ Settings::Settings(void)
pinAlg(),
pinSalt(),
pinCode(),
pinInactTimeoutInSecs(DEFAULT_PIN_INACT_TIMEOUT),
dbsLocation(""),
m_lastUpdate()
{
......@@ -80,6 +81,12 @@ void Settings::saveToSettings(QSettings &settings) const
Q_ASSERT(!pinCode.isEmpty());
settings.setValue(SETTINGS_PIN_CODE,
QString::fromUtf8(pinCode.toBase64()));
if (pinInactTimeoutInSecs > 0) {
/* Only when some value is used. */
settings.setValue(SETTINGS_PIN_INACT_TIMEOUT_IN_SECS,
pinInactTimeoutInSecs);
}
}
settings.setValue(SETTINGS_LAST_UPDATE, m_lastUpdate);
......@@ -125,6 +132,10 @@ void Settings::loadFromSettings(const QSettings &settings)
SETTINGS_GLOBAL_GROUP "/" SETTINGS_PIN_CODE,
QString()).toString().toUtf8());
pinInactTimeoutInSecs = settings.value(
SETTINGS_GLOBAL_GROUP "/" SETTINGS_PIN_INACT_TIMEOUT_IN_SECS,
DEFAULT_PIN_INACT_TIMEOUT).toInt();
setLastUpdate(settings.value(
SETTINGS_GLOBAL_GROUP "/" SETTINGS_LAST_UPDATE).toString());
......
......@@ -37,6 +37,7 @@
* attachment files stored in local database (in days)*/
#define DEFAULT_MSG_LIFETIME 90
#define DEFAULT_FILE_LIFETIME 10
#define DEFAULT_PIN_INACT_TIMEOUT 0
/* Names of setting items in GLOBALS group in the datovka.conf file */
#define SETTINGS_GLOBAL_GROUP "GLOBALS"
#define SETTINGS_ONLY_NEW_MSGS "only_new_msgs"
......@@ -47,6 +48,7 @@
#define SETTINGS_PIN_ALG "pin_alg"
#define SETTINGS_PIN_SALT "pin_salt"
#define SETTINGS_PIN_CODE "pin_code"
#define SETTINGS_PIN_INACT_TIMEOUT_IN_SECS "pin_inact_timeout"
#define SETTINGS_LAST_UPDATE "last_update"
#define SETTINGS_LANGUAGE "language"
#define SETTINGS_FONTSIZE "font_size"
......@@ -124,6 +126,7 @@ public:
QString pinAlg; /*!< PIN algorithm identifier. */
QByteArray pinSalt; /*!< Salt value used to generate PIN hash. */
QByteArray pinCode; /*!< Hashed PIN code. */
int pinInactTimeoutInSecs; /*!< Inactivity locking interval. */
QString dbsLocation;
private:
......
......@@ -113,19 +113,11 @@ void GlobalSettingsQmlWrapper::loadSettings(const QString &section)
}
}
/* ========================================================================= */
/*
* Slot: Load current Pin settings from global structure.
*/
void GlobalSettingsQmlWrapper::loadPinSettings(void)
/* ========================================================================= */
QString GlobalSettingsQmlWrapper::pinValue(void) const
{
/* Send PIN data to QML */
emit sendPinData(globSet._pinVal);
return globSet._pinVal;
}
/* ========================================================================= */
/*
* Slot: Set and save general settings from QML to global structure.
......@@ -308,3 +300,13 @@ QString GlobalSettingsQmlWrapper::changeDbPath(const QString &currentLocation,
return newLocation;
}
int GlobalSettingsQmlWrapper::inactivityInterval(void) const
{
return globSet.pinInactTimeoutInSecs;
}
void GlobalSettingsQmlWrapper::setInactivityInterval(int secs)
{
globSet.pinInactTimeoutInSecs = (secs > 0) ? secs : 0;
}
......@@ -36,7 +36,6 @@ class GlobalSettingsQmlWrapper : public QObject {
Q_OBJECT
public:
/*!
* @brief Constructor.
*/
......@@ -63,10 +62,10 @@ public:
void loadSettings(const QString &section);
/*!
* @brief Load current PIN settings.
* @brief Return current PIN value.
*/
Q_INVOKABLE
void loadPinSettings(void);
QString pinValue(void) const;
/*!
* @brief Save general settings from QML.
......@@ -141,6 +140,22 @@ public:
QString changeDbPath(const QString &currentLocation,
bool setDefaultLocation);
/*!
* @brief Get inactivity timeout length.
*
* @return Inactivity interval in seconds.
*/
Q_INVOKABLE
int inactivityInterval(void) const;
/*!
* @brief Set inactivity timeout length.
*
* @param[in] secs Inactivity interval length in seconds.
*/
Q_INVOKABLE
void setInactivityInterval(int secs);
signals:
/*!
......@@ -180,13 +195,6 @@ signals:
int fileLifeDays, QString dbPath, bool showChangeDbLocation,
bool showSetDefaulButton);
/*!
* @brief Send current PIN settings to QML.
*
* @param[in] currentPinVal - PIN data string for QML.
*/
void sendPinData(QString currentPinVal);
/*!
* @brief Send PIN verification result to QML.
*
......@@ -194,13 +202,6 @@ signals:
*/
void sendPinReply(bool success);
/*!
* @brief Show PIN screen to QML.
*
* @param[in] show - Show PIN page in the QML.
*/
void showPinScreen(bool show);
/*!
* @brief Set new statusbar text and active busy indicator to QML.
*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment