From 11072b2f162ffc2f7d1b0b379bfb547f05a1d028 Mon Sep 17 00:00:00 2001 From: Stefan Brand Date: Thu, 9 Jan 2014 19:56:26 +0100 Subject: [PATCH 1/5] Changed DB Schema * DB Schema now supports setting favourites * Added Colums for future addition of HOTP Tokens * Created DB-Update Logic --- qml/lib/storage.js | 38 ++++++++++++++++++++++++++------------ qml/pages/MainView.qml | 2 -- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/qml/lib/storage.js b/qml/lib/storage.js index a616ad8..43dbad9 100644 --- a/qml/lib/storage.js +++ b/qml/lib/storage.js @@ -29,20 +29,34 @@ .import QtQuick.LocalStorage 2.0 as LS -// Get DB Connection +// Get DB Connection, Initialize or Upgrade DB function getDB() { - return LS.LocalStorage.openDatabaseSync("harbour-sailotp", "1.0", "SailOTP Config Storage", 1000000); -} + try { + var db = LS.LocalStorage.openDatabaseSync("harbour-sailotp", "", "SailOTP Config Storage", 1000000); -// Initialize Table if not exists -function initialize() { - var db = getDB(); - - db.transaction( - function(tx) { - tx.executeSql("CREATE TABLE IF NOT EXISTS OTPStorage(title TEXT, secret TEXT);"); + if (db.version == "") { + // Initialize an empty DB, Create the Table + db.changeVersion("", "2", + function(tx) { + tx.executeSql("CREATE TABLE IF NOT EXISTS OTPStorage(title TEXT, secret TEXT, type TEXT, counter INTEGER, fav INTEGER);"); + } + ); + } else if (db.version == "1.0") { + // Upgrade DB Schema to Version 2 + db.changeVersion("1.0", "2", + function(tx) { + tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN type TEXT DEFAULT 'TOTP';"); + tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN counter INTEGER DEFAULT 0;"); + tx.executeSql("ALTER TABLE OTPStorage ADD COLUMN fav INTEGER DEFAULT 0;"); + } + ); } - ) + } catch (e) { + // DB Failed to open + console.log("Could not open DB: " + e); + } + + return db; } // Get all OTPs into the list model @@ -65,7 +79,7 @@ function addOTP(title, secret) { db.transaction( function(tx) { - tx.executeSql("INSERT INTO OTPStorage VALUES(?, ?);", [title, secret]); + tx.executeSql("INSERT INTO OTPStorage VALUES(?, ?, ?, ?, ?);", [title, secret, 'TOTP', 0, 0]); } ) } diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml index e5aadb2..d6e1406 100644 --- a/qml/pages/MainView.qml +++ b/qml/pages/MainView.qml @@ -174,8 +174,6 @@ Page { VerticalScrollDecorator{} Component.onCompleted: { - // Initialize DB (create tables etc..) - DB.initialize(); // Load list of OTP-Entries refreshOTPList(); } From 40f53fc47a3d42c69fde63fe5a95704c9d3588cc Mon Sep 17 00:00:00 2001 From: Stefan Brand Date: Thu, 9 Jan 2014 22:12:55 +0100 Subject: [PATCH 2/5] Changed MainView to support favourites * Added switch to show status * Save selected favourite to LocalStorage DB --- qml/lib/storage.js | 13 +++++++++++- qml/pages/MainView.qml | 48 ++++++++++++++++++++++++++++++------------ 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/qml/lib/storage.js b/qml/lib/storage.js index 43dbad9..1f86a57 100644 --- a/qml/lib/storage.js +++ b/qml/lib/storage.js @@ -67,7 +67,7 @@ function getOTP() { function(tx) { var res = tx.executeSql("select * from OTPStorage;"); for (var i=0; i < res.rows.length; i++) { - mainPage.appendOTP(res.rows.item(i).title, res.rows.item(i).secret); + mainPage.appendOTP(res.rows.item(i).title, res.rows.item(i).secret, res.rows.item(i).type, res.rows.item(i).counter, res.rows.item(i).fav); } } ) @@ -95,6 +95,17 @@ function removeOTP(title, secret) { ) } +function setFav(title, secret) { + var db = getDB(); + + db.transaction( + function(tx) { + tx.executeSql("UPDATE OTPStorage set fav = 0"); + tx.executeSql("UPDATE OTPStorage set fav = 1 WHERE title=? and secret=?;", [title, secret]); + } + ) +} + // Change an existing OTP function changeOTP(title, secret, oldtitle, oldsecret) { var db = getDB(); diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml index d6e1406..1bdbf75 100644 --- a/qml/pages/MainView.qml +++ b/qml/pages/MainView.qml @@ -44,15 +44,17 @@ Page { property double lastUpdated: null // Add an entry to the list - function appendOTP(title, secret) { - otpListModel.append({"secret": secret, "title": title, "otp": ""}); + function appendOTP(title, secret, type, counter, fav) { + otpListModel.append({"secret": secret, "title": title, "fav": fav, "otp": ""}); } // Reload the List of OTPs from storage function refreshOTPList() { + otpList.visible = false; otpListModel.clear(); DB.getOTP(); refreshOTPValues(); + otpList.visible = true; } // Calculate new OTPs for every entry @@ -67,7 +69,6 @@ Page { if (otpListModel.get(i).otp == "" || seconds == 30 || seconds == 0 || (curDate.getTime() - lastUpdated > 2000)) { var curOTP = OTP.calcOTP(otpListModel.get(i).secret) otpListModel.setProperty(i, "otp", curOTP); - console.log("Updating Value ", i); } } @@ -79,7 +80,8 @@ Page { Timer { interval: 1000 - running: Qt.application.active // Timer only runs when App is active + // Timer only runs when app is acitive and we have entries + running: Qt.application.active && otpListModel.count repeat: true onTriggered: refreshOTPValues(); } @@ -104,6 +106,8 @@ Page { maximumValue: 29 anchors.top: parent.top anchors.topMargin: 48 + // Only show when there are enries + visible: otpListModel.count } SilicaListView { @@ -121,37 +125,55 @@ Page { hintText: "Pull down to add a OTP" } - - delegate: ListItem { id: otpListItem menu: otpContextMenu - width: otpList.width contentHeight: Theme.itemSizeMedium + width: parent.width - function remove() { + function remove() { // Show 5s countdown, then delete from DB and List remorseAction("Deleting", function() { DB.removeOTP(title, secret); otpListModel.remove(index) }) } ListView.onRemove: animateRemoval() Rectangle { + id: listRow + width: parent.width anchors.horizontalCenter: parent.horizontalCenter - Label { + Switch { + checked: model.fav + anchors.left: parent.left + onClicked: { + if (checked) DB.setFav(title, secret) + refreshOTPList(); + /*model.fav = checked ? 1 : 0; + for (var i=0; i Date: Fri, 10 Jan 2014 06:38:38 +0100 Subject: [PATCH 3/5] Changed favourite switch to icon --- qml/pages/MainView.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml index 1bdbf75..83b8fe3 100644 --- a/qml/pages/MainView.qml +++ b/qml/pages/MainView.qml @@ -142,18 +142,18 @@ Page { width: parent.width anchors.horizontalCenter: parent.horizontalCenter - Switch { - checked: model.fav + IconButton { + icon.source: fav == 1 ? "image://theme/icon-m-favorite-selected" : "image://theme/icon-m-favorite" anchors.left: parent.left onClicked: { - if (checked) DB.setFav(title, secret) - refreshOTPList(); - /*model.fav = checked ? 1 : 0; + DB.setFav(title, secret) for (var i=0; i Date: Fri, 10 Jan 2014 19:20:52 +0100 Subject: [PATCH 4/5] Finalized favourites feature * Favourite is now shown on the ActiveCover * Added possibility to "unstar" a favourite --- qml/cover/CoverPage.qml | 52 ++++++++++++++++++++++++++++++++++++----- qml/harbour-sailotp.qml | 7 ++++++ qml/lib/storage.js | 11 +++++++++ qml/pages/MainView.qml | 28 ++++++++++++++++------ 4 files changed, 85 insertions(+), 13 deletions(-) diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml index e307c99..1388668 100644 --- a/qml/cover/CoverPage.qml +++ b/qml/cover/CoverPage.qml @@ -29,11 +29,39 @@ import QtQuick 2.0 import Sailfish.Silica 1.0 +import "../lib/crypto.js" as OTP // Define the Layout of the Active Cover CoverBackground { + id: coverPage - // Show the SailOTP Logo + property double lastUpdated: 0 + + Timer { + interval: 1000 + // Timer runs only when cover is visible and favourite is set + running: !Qt.application.active && appWin.coverSecret != "" + repeat: true + onTriggered: { + // get seconds from current Date + var curDate = new Date(); + + if (lOTP.text == "" || curDate.getSeconds() == 30 || curDate.getSeconds() == 0 || (curDate.getTime() - lastUpdated > 2000)) { + appWin.coverOTP = OTP.calcOTP(appWin.coverSecret); + } + + // Change color of the OTP to red if less than 5 seconds left + if (29 - (curDate.getSeconds() % 30) < 5) { + lOTP.color = "red" + } else { + lOTP.color = Theme.highlightColor + } + + lastUpdated = curDate.getTime(); + } + } + + // Show the SailOTP Logo Image { id: logo source: "../sailotp.png" @@ -42,10 +70,22 @@ CoverBackground { anchors.topMargin: 48 } - // Show the Application Name - Label { - id: label - anchors.centerIn: parent - text: "SailOTP" + Column { + anchors.top: logo.bottom + anchors.topMargin: 48 + anchors.horizontalCenter: parent.horizontalCenter + + Label { + text: appWin.coverTitle + anchors.horizontalCenter: parent.horizontalCenter + color: Theme.secondaryColor + } + Label { + id: lOTP + text: appWin.coverOTP + anchors.horizontalCenter: parent.horizontalCenter + color: Theme.highlightColor + font.pixelSize: Theme.fontSizeExtraLarge + } } } diff --git a/qml/harbour-sailotp.qml b/qml/harbour-sailotp.qml index b8632dc..2b9c122 100644 --- a/qml/harbour-sailotp.qml +++ b/qml/harbour-sailotp.qml @@ -33,6 +33,13 @@ import "pages" ApplicationWindow { + id: appWin + + // Properties to pass values between MainPage and Cover + property string coverTitle: "SailOTP" + property string coverSecret: "" + property string coverOTP: "" + initialPage: Component { MainView { } } cover: Qt.resolvedUrl("cover/CoverPage.qml") } diff --git a/qml/lib/storage.js b/qml/lib/storage.js index 1f86a57..8dabb15 100644 --- a/qml/lib/storage.js +++ b/qml/lib/storage.js @@ -68,6 +68,7 @@ function getOTP() { var res = tx.executeSql("select * from OTPStorage;"); for (var i=0; i < res.rows.length; i++) { mainPage.appendOTP(res.rows.item(i).title, res.rows.item(i).secret, res.rows.item(i).type, res.rows.item(i).counter, res.rows.item(i).fav); + if (res.rows.item(i).fav) mainPage.setCoverOTP(res.rows.item(i).title, res.rows.item(i).secret); } } ) @@ -106,6 +107,16 @@ function setFav(title, secret) { ) } +function resetFav(title, secret) { + var db = getDB(); + + db.transaction( + function(tx) { + tx.executeSql("UPDATE OTPStorage set fav = 0"); + } + ) +} + // Change an existing OTP function changeOTP(title, secret, oldtitle, oldsecret) { var db = getDB(); diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml index 83b8fe3..b136e18 100644 --- a/qml/pages/MainView.qml +++ b/qml/pages/MainView.qml @@ -41,13 +41,20 @@ Page { } // This holds the time of the last update of the page as Unix Timestamp (in Milliseconds) - property double lastUpdated: null + property double lastUpdated: 0 // Add an entry to the list function appendOTP(title, secret, type, counter, fav) { otpListModel.append({"secret": secret, "title": title, "fav": fav, "otp": ""}); } + // Hand favorite over to the cover + function setCoverOTP(title, secret) { + appWin.coverTitle = title + appWin.coverSecret = secret + if (secret == "") appWin.coverOTP = "" + } + // Reload the List of OTPs from storage function refreshOTPList() { otpList.visible = false; @@ -146,13 +153,20 @@ Page { icon.source: fav == 1 ? "image://theme/icon-m-favorite-selected" : "image://theme/icon-m-favorite" anchors.left: parent.left onClicked: { - DB.setFav(title, secret) - for (var i=0; i Date: Fri, 10 Jan 2014 19:26:46 +0100 Subject: [PATCH 5/5] Added copy token on tap --- qml/pages/MainView.qml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qml/pages/MainView.qml b/qml/pages/MainView.qml index b136e18..a9c7787 100644 --- a/qml/pages/MainView.qml +++ b/qml/pages/MainView.qml @@ -138,11 +138,15 @@ Page { contentHeight: Theme.itemSizeMedium width: parent.width - function remove() { - // Show 5s countdown, then delete from DB and List + function remove() { + // Show 5s countdown, then delete from DB and List remorseAction("Deleting", function() { DB.removeOTP(title, secret); otpListModel.remove(index) }) } + onClicked: { + Clipboard.text = otp + } + ListView.onRemove: animateRemoval() Rectangle { id: listRow