mirror of
https://github.com/monero-project/monero-gui.git
synced 2025-01-10 11:52:05 +02:00
Lock wallet on inactivity
This commit is contained in:
parent
bac833c1dd
commit
5bebf83d52
@ -38,6 +38,10 @@ filter::filter(QObject *parent) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool filter::eventFilter(QObject *obj, QEvent *ev) {
|
bool filter::eventFilter(QObject *obj, QEvent *ev) {
|
||||||
|
if(ev->type() == QEvent::KeyPress || ev->type() == QEvent::MouseButtonRelease){
|
||||||
|
emit userActivity();
|
||||||
|
}
|
||||||
|
|
||||||
switch(ev->type()) {
|
switch(ev->type()) {
|
||||||
case QEvent::KeyPress: {
|
case QEvent::KeyPress: {
|
||||||
QKeyEvent *ke = static_cast<QKeyEvent*>(ev);
|
QKeyEvent *ke = static_cast<QKeyEvent*>(ev);
|
||||||
|
1
filter.h
1
filter.h
@ -48,6 +48,7 @@ signals:
|
|||||||
void sequenceReleased(const QVariant &o, const QVariant &seq);
|
void sequenceReleased(const QVariant &o, const QVariant &seq);
|
||||||
void mousePressed(const QVariant &o, const QVariant &x, const QVariant &y);
|
void mousePressed(const QVariant &o, const QVariant &x, const QVariant &y);
|
||||||
void mouseReleased(const QVariant &o, const QVariant &x, const QVariant &y);
|
void mouseReleased(const QVariant &o, const QVariant &x, const QVariant &y);
|
||||||
|
void userActivity();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FILTER_H
|
#endif // FILTER_H
|
||||||
|
2
main.cpp
2
main.cpp
@ -324,6 +324,6 @@ int main(int argc, char *argv[])
|
|||||||
QObject::connect(eventFilter, SIGNAL(sequenceReleased(QVariant,QVariant)), rootObject, SLOT(sequenceReleased(QVariant,QVariant)));
|
QObject::connect(eventFilter, SIGNAL(sequenceReleased(QVariant,QVariant)), rootObject, SLOT(sequenceReleased(QVariant,QVariant)));
|
||||||
QObject::connect(eventFilter, SIGNAL(mousePressed(QVariant,QVariant,QVariant)), rootObject, SLOT(mousePressed(QVariant,QVariant,QVariant)));
|
QObject::connect(eventFilter, SIGNAL(mousePressed(QVariant,QVariant,QVariant)), rootObject, SLOT(mousePressed(QVariant,QVariant,QVariant)));
|
||||||
QObject::connect(eventFilter, SIGNAL(mouseReleased(QVariant,QVariant,QVariant)), rootObject, SLOT(mouseReleased(QVariant,QVariant,QVariant)));
|
QObject::connect(eventFilter, SIGNAL(mouseReleased(QVariant,QVariant,QVariant)), rootObject, SLOT(mouseReleased(QVariant,QVariant,QVariant)));
|
||||||
|
QObject::connect(eventFilter, SIGNAL(userActivity()), rootObject, SLOT(userActivity()));
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
39
main.qml
39
main.qml
@ -75,6 +75,7 @@ ApplicationWindow {
|
|||||||
property var cameraUi
|
property var cameraUi
|
||||||
property bool remoteNodeConnected: false
|
property bool remoteNodeConnected: false
|
||||||
property bool androidCloseTapped: false;
|
property bool androidCloseTapped: false;
|
||||||
|
property int userLastActive; // epoch
|
||||||
// Default daemon addresses
|
// Default daemon addresses
|
||||||
readonly property string localDaemonAddress : persistentSettings.nettype == NetworkType.MAINNET ? "localhost:18081" : persistentSettings.nettype == NetworkType.TESTNET ? "localhost:28081" : "localhost:38081"
|
readonly property string localDaemonAddress : persistentSettings.nettype == NetworkType.MAINNET ? "localhost:18081" : persistentSettings.nettype == NetworkType.TESTNET ? "localhost:28081" : "localhost:38081"
|
||||||
property string currentDaemonAddress;
|
property string currentDaemonAddress;
|
||||||
@ -219,6 +220,8 @@ ApplicationWindow {
|
|||||||
// Local daemon settings
|
// Local daemon settings
|
||||||
walletManager.setDaemonAddress(localDaemonAddress)
|
walletManager.setDaemonAddress(localDaemonAddress)
|
||||||
|
|
||||||
|
// enable user inactivity timer
|
||||||
|
userInActivityTimer.running = true;
|
||||||
|
|
||||||
// wallet already opened with wizard, we just need to initialize it
|
// wallet already opened with wizard, we just need to initialize it
|
||||||
if (typeof wizard.m_wallet !== 'undefined') {
|
if (typeof wizard.m_wallet !== 'undefined') {
|
||||||
@ -932,6 +935,8 @@ ApplicationWindow {
|
|||||||
rootItem.state = "wizard"
|
rootItem.state = "wizard"
|
||||||
// reset balance
|
// reset balance
|
||||||
leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0);
|
leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0);
|
||||||
|
// disable inactivity timer
|
||||||
|
userInActivityTimer.running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideMenu() {
|
function hideMenu() {
|
||||||
@ -1041,6 +1046,8 @@ ApplicationWindow {
|
|||||||
property int segregationHeight: 0
|
property int segregationHeight: 0
|
||||||
property int kdfRounds: 1
|
property int kdfRounds: 1
|
||||||
property bool hideBalance: false
|
property bool hideBalance: false
|
||||||
|
property bool lockOnUserInActivity: true
|
||||||
|
property int lockOnUserInActivityInterval: 10 // minutes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Information dialog
|
// Information dialog
|
||||||
@ -1696,6 +1703,12 @@ ApplicationWindow {
|
|||||||
triggeredOnStart: false
|
triggeredOnStart: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: userInActivityTimer
|
||||||
|
interval: 2000; running: false; repeat: true
|
||||||
|
onTriggered: checkInUserActivity()
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: statusMessage
|
id: statusMessage
|
||||||
z: 99
|
z: 99
|
||||||
@ -1823,6 +1836,32 @@ ApplicationWindow {
|
|||||||
leftPanel.balanceLabelText = qsTr("Balance")
|
leftPanel.balanceLabelText = qsTr("Balance")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function userActivity() {
|
||||||
|
// register user activity
|
||||||
|
var epoch = Math.floor((new Date).getTime()/1000);
|
||||||
|
appWindow.userLastActive = epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkInUserActivity() {
|
||||||
|
if(!persistentSettings.lockOnUserInActivity) return;
|
||||||
|
|
||||||
|
// prompt password after X seconds of inactivity
|
||||||
|
var epoch = Math.floor((new Date).getTime() / 1000);
|
||||||
|
var inactivity = epoch - appWindow.userLastActive;
|
||||||
|
if(inactivity < (persistentSettings.lockOnUserInActivityInterval * 60)) return;
|
||||||
|
|
||||||
|
passwordDialog.onAcceptedCallback = function() {
|
||||||
|
if(walletPassword === passwordDialog.password){
|
||||||
|
passwordDialog.close();
|
||||||
|
} else {
|
||||||
|
passwordDialog.showError(qsTr("Wrong password"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
passwordDialog.onRejectedCallback = function() { appWindow.showWizard(); }
|
||||||
|
passwordDialog.open();
|
||||||
|
}
|
||||||
|
|
||||||
// Daemon console
|
// Daemon console
|
||||||
DaemonConsole {
|
DaemonConsole {
|
||||||
id: daemonConsolePopup
|
id: daemonConsolePopup
|
||||||
|
@ -65,17 +65,16 @@ ColumnLayout {
|
|||||||
|
|
||||||
onCurrentViewChanged: {
|
onCurrentViewChanged: {
|
||||||
if (previousView) {
|
if (previousView) {
|
||||||
// if (typeof previousView.onPageClosed === "function") {
|
if (typeof previousView.onPageClosed === "function") {
|
||||||
// previousView.onPageClosed();
|
previousView.onPageClosed();
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
previousView = currentView
|
previousView = currentView
|
||||||
if (currentView) {
|
if (currentView) {
|
||||||
stackView.replace(currentView)
|
stackView.replace(currentView)
|
||||||
// Component.onCompleted is called before wallet is initilized
|
if (typeof currentView.onPageCompleted === "function") {
|
||||||
// if (typeof currentView.onPageCompleted === "function") {
|
currentView.onPageCompleted();
|
||||||
// currentView.onPageCompleted();
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,14 @@ Rectangle {
|
|||||||
height: 1400
|
height: 1400
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
function onPageCompleted() {
|
||||||
|
userInactivitySliderTimer.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPageClosed() {
|
||||||
|
userInactivitySliderTimer.running = false;
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: settingsUI
|
id: settingsUI
|
||||||
property int itemHeight: 60 * scaleRatio
|
property int itemHeight: 60 * scaleRatio
|
||||||
@ -70,6 +78,82 @@ Rectangle {
|
|||||||
text: qsTr("Hide balance") + translationManager.emptyString
|
text: qsTr("Hide balance") + translationManager.emptyString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneroComponents.CheckBox {
|
||||||
|
visible: !isMobile
|
||||||
|
id: userInActivityCheckbox
|
||||||
|
checked: persistentSettings.lockOnUserInActivity
|
||||||
|
onClicked: persistentSettings.lockOnUserInActivity = !persistentSettings.lockOnUserInActivity
|
||||||
|
text: qsTr("Lock wallet on inactivity") + translationManager.emptyString
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
visible: userInActivityCheckbox.checked
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: 6 * scaleRatio
|
||||||
|
Layout.leftMargin: 42 * scaleRatio
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
MoneroComponents.TextBlock {
|
||||||
|
font.pixelSize: 14 * scaleRatio
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: {
|
||||||
|
var val = userInactivitySlider.value;
|
||||||
|
var minutes = val > 1 ? qsTr("minutes") : qsTr("minute");
|
||||||
|
|
||||||
|
qsTr("After ") + val + " " + minutes + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider {
|
||||||
|
id: userInactivitySlider
|
||||||
|
from: 1
|
||||||
|
value: persistentSettings.lockOnUserInActivityInterval
|
||||||
|
to: 60
|
||||||
|
leftPadding: 0
|
||||||
|
stepSize: 1
|
||||||
|
snapMode: Slider.SnapAlways
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
x: parent.leftPadding
|
||||||
|
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||||
|
implicitWidth: 200 * scaleRatio
|
||||||
|
implicitHeight: 4 * scaleRatio
|
||||||
|
width: parent.availableWidth
|
||||||
|
height: implicitHeight
|
||||||
|
radius: 2
|
||||||
|
color: MoneroComponents.Style.grey
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.visualPosition * parent.width
|
||||||
|
height: parent.height
|
||||||
|
color: MoneroComponents.Style.green
|
||||||
|
radius: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width)
|
||||||
|
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||||
|
implicitWidth: 18 * scaleRatio
|
||||||
|
implicitHeight: 18 * scaleRatio
|
||||||
|
radius: 8
|
||||||
|
color: parent.pressed ? "#f0f0f0" : "#f6f6f6"
|
||||||
|
border.color: MoneroComponents.Style.grey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
// @TODO: Slider.onMoved{} is available in Qt > 5.9, use a hacky timer for now
|
||||||
|
id: userInactivitySliderTimer
|
||||||
|
interval: 1000; running: false; repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
if(persistentSettings.lockOnUserInActivityInterval != userInactivitySlider.value) {
|
||||||
|
persistentSettings.lockOnUserInActivityInterval = userInactivitySlider.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MoneroComponents.TextBlock {
|
MoneroComponents.TextBlock {
|
||||||
visible: isMobile
|
visible: isMobile
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
|
Loading…
Reference in New Issue
Block a user