From ffa7b343d73d1c4950ff5513c89d9f19384389bc Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 24 Apr 2025 21:59:46 +0600 Subject: [PATCH 01/19] Refactor code formatting and improve readability in ScreenLock module --- screenlock.koplugin/main.lua | 107 +++++++++++++++-------------------- 1 file changed, 46 insertions(+), 61 deletions(-) diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index 0be930c..58871b3 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -10,9 +10,9 @@ local ScreenLock = WidgetContainer:extend{ name = "screenlock_inputdialog_buttons", is_doc_only = false, - locked = false, -- Track locked state - password = "1234", -- Your hard-coded password - hide_content = true, -- Hide screen content before password is entered + locked = false, -- Track locked state + password = "1234", -- Your hard-coded password + hide_content = true -- Hide screen content before password is entered } ------------------------------------------------------------------------------ @@ -23,7 +23,7 @@ function ScreenLock:onDispatcherRegisterActions() category = "none", event = "LockScreenButtons", title = _("Lock Screen (InputDialog + Buttons)"), - filemanager = true, + filemanager = true }) end @@ -33,7 +33,7 @@ end function ScreenLock:init() -- 1) Register dispatcher action self:onDispatcherRegisterActions() - + -- 2) Add to main menu self.ui.menu:registerToMainMenu(self) @@ -60,59 +60,44 @@ end function ScreenLock:showPasswordPrompt() local dialog dialog = InputDialog:new{ - title = _("Enter Password"), - input = "", - maskinput = true, + title = _("Enter Password"), + input = "", + maskinput = true, text_type = "password", - hint = _("Password"), - fullscreen = self.hide_content, -- request full screen mode - use_available_height = self.hide_content, -- use available screen height even when keyboard is shown - buttons = { - { - { - text = _("Cancel"), - callback = function() - UIManager:show( - InfoMessage:new{ - text = _("You must enter the correct password!"), - timeout = 1 - } - ) - UIManager:close(dialog) - self:showPasswordPrompt() - end - }, - { - text = _("OK"), - is_enter_default = true, - callback = function() - local userInput = dialog:getInputText() - if userInput == self.password then - self.locked = false - UIManager:close(dialog) - UIManager:show( - InfoMessage:new{ - text = _("Screen unlocked."), - timeout = 1 - } - ) - else - UIManager:show( - InfoMessage:new{ - text = _("Wrong password! Try again."), - timeout = 1 - } - ) - UIManager:close(dialog) - self:showPasswordPrompt() - end - end - }, - } - }, + hint = _("Password"), + fullscreen = self.hide_content, -- request full screen mode + use_available_height = self.hide_content, -- use available screen height even when keyboard is shown + buttons = {{{ + text = _("Cancel"), + callback = function() + UIManager:show(InfoMessage:new{ + text = _("You must enter the correct password!"), + timeout = 1 + }) + UIManager:close(dialog) + self:showPasswordPrompt() + end + }, { + text = _("OK"), + is_enter_default = true, + callback = function() + local userInput = dialog:getInputText() + if userInput == self.password then + self.locked = false + UIManager:close(dialog) + else + UIManager:show(InfoMessage:new{ + text = _("Wrong password! Try again."), + timeout = 1 + }) + UIManager:close(dialog) + self:showPasswordPrompt() + end + end + }}} } UIManager:show(dialog) - dialog:onShowKeyboard() -- Immediately open the on-screen keyboard + dialog:onShowKeyboard() -- Immediately open the on-screen keyboard end ------------------------------------------------------------------------------ @@ -127,12 +112,12 @@ end -- MAIN MENU ENTRY ------------------------------------------------------------------------------ function ScreenLock:addToMainMenu(menu_items) - menu_items.screenlock_inputdialog_buttons = { - text = _("Lock Screen"), - callback = function() - self:lockScreen() - end - } + menu_items.screenlock_inputdialog_buttons = { + text = _("Lock Screen"), + callback = function() + self:lockScreen() + end + } end return ScreenLock From 335063d09a8075dcff5c3eade7a206c750fe2c41 Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 24 Apr 2025 22:00:19 +0600 Subject: [PATCH 02/19] Remove hide_content feature description from README --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 0e92ba3..b91e0e6 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,6 @@ This is a small plugin to enable locking the screen of the KOReader. The plugin will automatically activate on resume from suspend. There is also a screenlock menu entry added. -### Hide Content Feature -Change `hide_content` bool to `true` to enlarge the input box and hide the screen until the password is entered correctly. This stops unauthorized users from seeing what books you're reading even if they turn on the device. Set bool to `false` to return to the small password box. - > [!CAUTION] > This plugin is made for basic protection, not security — it may not protect your device from an experienced attacker. > From 9234b53f441048df97157812631bb66d982f9d76 Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 24 Apr 2025 22:05:20 +0600 Subject: [PATCH 03/19] Update README to clarify plugin functionality and improve formatting --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b91e0e6..73611d8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ -# screenlock_koreader_plugin -This is a small plugin to enable locking the screen of the KOReader. +# KOReader Plugin: ScreenLock +This plugin lets you lock your screen with a password, either triggered from the menu or automatically upon device wake-up. ## Setup 1. Put `screenlock.koplugin` into the `kodreader/plugins` directory. -2. Change the hardcoded password at the top of the main.lua from 1234 to something else. +2. Change the hardcoded password at the top of the `main.lua` from `1234` to something else. +## Note The plugin will automatically activate on resume from suspend. There is also a screenlock menu entry added. > [!CAUTION] From 3e5d5ae6e576f0c13df721bfa439249758cf00fa Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 24 Apr 2025 22:06:28 +0600 Subject: [PATCH 04/19] Update README to enhance cautionary notes regarding plugin security --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 73611d8..f6f8f42 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # KOReader Plugin: ScreenLock This plugin lets you lock your screen with a password, either triggered from the menu or automatically upon device wake-up. +> +> [!CAUTION] +> This plugin is made for basic protection, not security — it may not protect your device from an experienced attacker. +> +> Always keep your device out of the hands of real threats. +> + ## Setup 1. Put `screenlock.koplugin` into the `kodreader/plugins` directory. 2. Change the hardcoded password at the top of the `main.lua` from `1234` to something else. ## Note The plugin will automatically activate on resume from suspend. There is also a screenlock menu entry added. - -> [!CAUTION] -> This plugin is made for basic protection, not security — it may not protect your device from an experienced attacker. -> -> Always keep your device out of the hands of real threats. \ No newline at end of file From 3fd9af2002d50229dbf90d9bb0126150c4dc0faf Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 24 Apr 2025 22:08:02 +0600 Subject: [PATCH 05/19] Consolidate cautionary notes in README for clarity and emphasis on security limitations --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f6f8f42..9aba86f 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,8 @@ This plugin lets you lock your screen with a password, either triggered from the menu or automatically upon device wake-up. > -> [!CAUTION] -> This plugin is made for basic protection, not security — it may not protect your device from an experienced attacker. -> -> Always keep your device out of the hands of real threats. +> # [!CAUTION] +> **This plugin is made for basic protection, not security — it may not protect your device from an experienced attacker. Always keep your device out of the hands of real threats.** > ## Setup From e8a453233dd99f6dce46b93d535a68f0fce8df99 Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 24 Apr 2025 22:08:42 +0600 Subject: [PATCH 06/19] Fix formatting of cautionary note in README for improved clarity --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9aba86f..685b566 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This plugin lets you lock your screen with a password, either triggered from the menu or automatically upon device wake-up. > -> # [!CAUTION] +> [!CAUTION] > **This plugin is made for basic protection, not security — it may not protect your device from an experienced attacker. Always keep your device out of the hands of real threats.** > From e312d15f6afd4cadf089d79a0e6f5a88360d6bf3 Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 24 Apr 2025 23:51:30 +0600 Subject: [PATCH 07/19] Refactor onResume method for safe device wake-up handling and improve password prompt button logic --- screenlock.koplugin/main.lua | 53 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index 58871b3..ed947a3 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -37,8 +37,10 @@ function ScreenLock:init() -- 2) Add to main menu self.ui.menu:registerToMainMenu(self) - -- 3) Override onResume to handle device wake-up - function self:onResume() + -- 3) Safe onResume override to handle device wake-up + local originalResume = self.onResume + self.onResume = function(self) + if originalResume then originalResume(self) end if not self.locked then self:lockScreen() end @@ -67,34 +69,37 @@ function ScreenLock:showPasswordPrompt() hint = _("Password"), fullscreen = self.hide_content, -- request full screen mode use_available_height = self.hide_content, -- use available screen height even when keyboard is shown - buttons = {{{ - text = _("Cancel"), - callback = function() - UIManager:show(InfoMessage:new{ - text = _("You must enter the correct password!"), - timeout = 1 - }) - UIManager:close(dialog) - self:showPasswordPrompt() - end - }, { - text = _("OK"), - is_enter_default = true, - callback = function() - local userInput = dialog:getInputText() - if userInput == self.password then - self.locked = false - UIManager:close(dialog) - else + buttons = {{ + { + text = _("Cancel"), + callback = function() UIManager:show(InfoMessage:new{ - text = _("Wrong password! Try again."), + text = _("You must enter the correct password!"), timeout = 1 }) UIManager:close(dialog) self:showPasswordPrompt() end - end - }}} + }, + { + text = _("OK"), + is_enter_default = true, + callback = function() + local userInput = dialog:getInputText() + if userInput == self.password then + self.locked = false + UIManager:close(dialog) + else + UIManager:show(InfoMessage:new{ + text = _("Wrong password! Try again."), + timeout = 1 + }) + UIManager:close(dialog) + self:showPasswordPrompt() + end + end + } + }} } UIManager:show(dialog) dialog:onShowKeyboard() -- Immediately open the on-screen keyboard From 6393d62be28aec3be1d29e9ac6bff6487674eb7b Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Fri, 25 Apr 2025 00:04:06 +0600 Subject: [PATCH 08/19] Fix formatting of menu entry in README for consistency --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 685b566..91dd535 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ This plugin lets you lock your screen with a password, either triggered from the 2. Change the hardcoded password at the top of the `main.lua` from `1234` to something else. ## Note -The plugin will automatically activate on resume from suspend. There is also a screenlock menu entry added. +The plugin will automatically activate on resume from suspend. There is also a **Screen Lock** menu entry. From 2e477f946125d4d89dab42cba523f71852e2eb65 Mon Sep 17 00:00:00 2001 From: lnx00 <34384416+lnx00@users.noreply.github.com> Date: Wed, 7 May 2025 01:05:23 +0200 Subject: [PATCH 09/19] Fill background with color instead of using fullscreen dialog --- .luarc.json | 9 ++++ screenlock.koplugin/main.lua | 85 ++++++++++++++++++++++-------------- 2 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 .luarc.json diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..f8b8208 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json", + "workspace.library": [ + "~/koreader-base/ffi", + "~/koreader/frontend" + ], + "runtime.version": "Lua 5.1", + "hint.enable": false +} \ No newline at end of file diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index ed947a3..cc7f646 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -1,3 +1,6 @@ +local Blitbuffer = require("ffi/blitbuffer") + +local Device = require("device") local Dispatcher = require("dispatcher") local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") @@ -6,18 +9,34 @@ local InputDialog = require("ui/widget/inputdialog") local InfoMessage = require("ui/widget/infomessage") local _ = require("gettext") -local ScreenLock = WidgetContainer:extend{ +local Screen = Device.screen + +--[[ Fullscreen Overlay Widget ]] + +local FullscreenOverlay = WidgetContainer:extend {} + +function FullscreenOverlay:init() + self.covers_fullscreen = true + self.dimen = Screen:getSize() +end + +function FullscreenOverlay:paintTo(bb, x, y) + bb:fill(Blitbuffer.COLOR_WHITE) +end + +--[[ Screen Lock Widget ]] + +local ScreenLock = WidgetContainer:extend { name = "screenlock_inputdialog_buttons", is_doc_only = false, + background_widget = nil, - locked = false, -- Track locked state - password = "1234", -- Your hard-coded password + locked = false, -- Track locked state + password = "1234", -- Your hard-coded password hide_content = true -- Hide screen content before password is entered } ------------------------------------------------------------------------------- --- REGISTER DISPATCHER ACTIONS ------------------------------------------------------------------------------- +-- Register dispatcher actions function ScreenLock:onDispatcherRegisterActions() Dispatcher:registerAction("screenlock_inputdialog_buttons_lock_screen", { category = "none", @@ -27,17 +46,18 @@ function ScreenLock:onDispatcherRegisterActions() }) end ------------------------------------------------------------------------------- --- INIT (including wake-up handling via onResume) ------------------------------------------------------------------------------- +-- Initialize widget and register wakeup-handler function ScreenLock:init() - -- 1) Register dispatcher action + -- Initialize fullscreen widget + self.background_widget = FullscreenOverlay:new() + + -- Register dispatcher action self:onDispatcherRegisterActions() - -- 2) Add to main menu + -- Add to main menu self.ui.menu:registerToMainMenu(self) - -- 3) Safe onResume override to handle device wake-up + -- Safe onResume override to handle device wake-up local originalResume = self.onResume self.onResume = function(self) if originalResume then originalResume(self) end @@ -47,36 +67,37 @@ function ScreenLock:init() end end ------------------------------------------------------------------------------- --- LOCK SCREEN ------------------------------------------------------------------------------- +-- Lock the screen now function ScreenLock:lockScreen() self.locked = true + + -- Show fullscreen overlay + if self.hide_content then + UIManager:show(self.background_widget) + end + + -- Show passowrd prompt self:showPasswordPrompt() end ------------------------------------------------------------------------------- --- SHOW PASSWORD PROMPT (USING BUTTONS ARRAY) --- "Cancel" button reopens the prompt, preventing escape ------------------------------------------------------------------------------- +-- Shows the password prompt and prevents escaping function ScreenLock:showPasswordPrompt() local dialog - dialog = InputDialog:new{ + dialog = InputDialog:new { title = _("Enter Password"), input = "", maskinput = true, text_type = "password", hint = _("Password"), - fullscreen = self.hide_content, -- request full screen mode - use_available_height = self.hide_content, -- use available screen height even when keyboard is shown - buttons = {{ + buttons = { { { text = _("Cancel"), callback = function() - UIManager:show(InfoMessage:new{ + UIManager:show(InfoMessage:new { text = _("You must enter the correct password!"), timeout = 1 }) + UIManager:close(dialog) self:showPasswordPrompt() end @@ -88,34 +109,34 @@ function ScreenLock:showPasswordPrompt() local userInput = dialog:getInputText() if userInput == self.password then self.locked = false + UIManager:close(dialog) + UIManager:close(self.background_widget, "full") else - UIManager:show(InfoMessage:new{ + UIManager:show(InfoMessage:new { text = _("Wrong password! Try again."), timeout = 1 }) + UIManager:close(dialog) self:showPasswordPrompt() end end } - }} + } } } + UIManager:show(dialog) dialog:onShowKeyboard() -- Immediately open the on-screen keyboard end ------------------------------------------------------------------------------- --- DISPATCHER HANDLER ------------------------------------------------------------------------------- +-- Dispatch handler function ScreenLock:onLockScreenButtons() self:lockScreen() return true end ------------------------------------------------------------------------------- --- MAIN MENU ENTRY ------------------------------------------------------------------------------- +-- Register main menu entry function ScreenLock:addToMainMenu(menu_items) menu_items.screenlock_inputdialog_buttons = { text = _("Lock Screen"), From ae0fdc5c95ab8ba3b1662c20fb8d34b79818314f Mon Sep 17 00:00:00 2001 From: lnx00 <34384416+lnx00@users.noreply.github.com> Date: Wed, 7 May 2025 01:34:11 +0200 Subject: [PATCH 10/19] Added option to change password --- .luarc.json | 2 +- screenlock.koplugin/_meta.lua | 3 +- screenlock.koplugin/main.lua | 95 ++++++++++++++++++++++++++++++++--- 3 files changed, 91 insertions(+), 9 deletions(-) diff --git a/.luarc.json b/.luarc.json index f8b8208..23c9713 100644 --- a/.luarc.json +++ b/.luarc.json @@ -4,6 +4,6 @@ "~/koreader-base/ffi", "~/koreader/frontend" ], - "runtime.version": "Lua 5.1", + "runtime.version": "LuaJIT", "hint.enable": false } \ No newline at end of file diff --git a/screenlock.koplugin/_meta.lua b/screenlock.koplugin/_meta.lua index 619aea4..3866bb7 100644 --- a/screenlock.koplugin/_meta.lua +++ b/screenlock.koplugin/_meta.lua @@ -3,7 +3,6 @@ local _ = require("gettext") return { name = "screenlock", fullname = _("ScreenLock"), - description = _([[This plugin lets you lock your screen with a password, + description = _([[This plugin lets you lock your screen with a password, either triggered from the menu or automatically upon device wake-up.]]), } - diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index cc7f646..02f2b9a 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -61,6 +61,7 @@ function ScreenLock:init() local originalResume = self.onResume self.onResume = function(self) if originalResume then originalResume(self) end + if not self.locked then self:lockScreen() end @@ -86,9 +87,7 @@ function ScreenLock:showPasswordPrompt() dialog = InputDialog:new { title = _("Enter Password"), input = "", - maskinput = true, text_type = "password", - hint = _("Password"), buttons = { { { text = _("Cancel"), @@ -136,13 +135,97 @@ function ScreenLock:onLockScreenButtons() return true end +function ScreenLock:changePassword() + -- Ask for old password + local old_dialog + old_dialog = InputDialog:new { + title = _("Enter old password"), + input = "", + text_type = "password", + buttons = { { + { + text = _("Cancel"), + callback = function() + UIManager:close(old_dialog) + end + }, + { + text = _("OK"), + is_enter_default = true, + callback = function() + local old_password_input = old_dialog:getInputText() + if old_password_input == self.password then + UIManager:close(old_dialog) + + -- Ask for new password + local new_dialog + new_dialog = InputDialog:new { + title = _("Enter new password"), + input = "", + text_type = "password", + buttons = { { + { + text = _("Cancel"), + callback = function() + UIManager:close(new_dialog) + end + }, + { + text = _("OK"), + is_enter_default = true, + callback = function() + local new_password_input = new_dialog:getInputText() + self.password = new_password_input + + UIManager:show(InfoMessage:new { + text = _("Password changed!"), + timeout = 1 + }) + + UIManager:close(new_dialog) + end + } + } } + } + + UIManager:show(new_dialog) + new_dialog:onShowKeyboard() + else + UIManager:show(InfoMessage:new { + text = _("Wrong password! Try again."), + timeout = 1 + }) + + UIManager:close(old_dialog) + self:changePassword() + end + end + } + } } + } + + UIManager:show(old_dialog) + old_dialog:onShowKeyboard() +end + -- Register main menu entry function ScreenLock:addToMainMenu(menu_items) menu_items.screenlock_inputdialog_buttons = { - text = _("Lock Screen"), - callback = function() - self:lockScreen() - end + text = _("Screenlock"), + sub_item_table = { + { + text = _("Lock now"), + callback = function() + self:lockScreen() + end, + }, + { + text = _("Change password"), + callback = function() + self:changePassword() + end, + } + } } end From 00c6de2d551f35b9a9ac422dc0942d43041dbde6 Mon Sep 17 00:00:00 2001 From: lnx00 <34384416+lnx00@users.noreply.github.com> Date: Wed, 7 May 2025 01:46:46 +0200 Subject: [PATCH 11/19] Store password in settings file --- screenlock.koplugin/main.lua | 45 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index 02f2b9a..f86a7a6 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -1,13 +1,15 @@ local Blitbuffer = require("ffi/blitbuffer") +local _ = require("gettext") local Device = require("device") local Dispatcher = require("dispatcher") +local DataStorage = require("datastorage") +local LuaSettings = require("luasettings") + local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") - local InputDialog = require("ui/widget/inputdialog") local InfoMessage = require("ui/widget/infomessage") -local _ = require("gettext") local Screen = Device.screen @@ -29,13 +31,36 @@ end local ScreenLock = WidgetContainer:extend { name = "screenlock_inputdialog_buttons", is_doc_only = false, + + locked = false, background_widget = nil, + settings_file = DataStorage:getSettingsDir() .. "/screen_lock.lua", + settings = nil, - locked = false, -- Track locked state password = "1234", -- Your hard-coded password hide_content = true -- Hide screen content before password is entered } +function ScreenLock:loadSettings() + if self.settings then + return + end + + self.settings = LuaSettings:open(self.settings_file) + + self.password = self.settings:readSetting("password") or "1234" + self.hide_content = self.settings:readSetting("hide_content") == true +end + +function ScreenLock:onFlushSettings() + if self.settings then + self.settings:saveSetting("password", self.password) + self.settings:saveSetting("hide_content", self.hide_content) + + self.settings:flush() + end +end + -- Register dispatcher actions function ScreenLock:onDispatcherRegisterActions() Dispatcher:registerAction("screenlock_inputdialog_buttons_lock_screen", { @@ -48,6 +73,8 @@ end -- Initialize widget and register wakeup-handler function ScreenLock:init() + self:loadSettings() + -- Initialize fullscreen widget self.background_widget = FullscreenOverlay:new() @@ -218,13 +245,23 @@ function ScreenLock:addToMainMenu(menu_items) callback = function() self:lockScreen() end, + separator = true, }, { text = _("Change password"), callback = function() self:changePassword() end, - } + }, + { + text = _("Hide screen content"), + checked_func = function() + return self.hide_content + end, + callback = function() + self.hide_content = not self.hide_content + end, + }, } } end From 58ac7bf19d7b2b3c52d65071b8a8f06f75cc7e1f Mon Sep 17 00:00:00 2001 From: lnx00 <34384416+lnx00@users.noreply.github.com> Date: Wed, 7 May 2025 02:24:03 +0200 Subject: [PATCH 12/19] Hash the password instead of storing it in plain text --- README.md | 3 ++- screenlock.koplugin/main.lua | 27 ++++++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 91dd535..c89c338 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ This plugin lets you lock your screen with a password, either triggered from the ## Setup 1. Put `screenlock.koplugin` into the `kodreader/plugins` directory. -2. Change the hardcoded password at the top of the `main.lua` from `1234` to something else. +2. In KOReader, open the menu and select "Screenlock". +3. Press "Change password" to set your password. The default password is **1234**. ## Note The plugin will automatically activate on resume from suspend. There is also a **Screen Lock** menu entry. diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index f86a7a6..77f75c0 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -1,4 +1,5 @@ local Blitbuffer = require("ffi/blitbuffer") +local sha2 = require("ffi/sha2") local _ = require("gettext") local Device = require("device") @@ -12,6 +13,7 @@ local InputDialog = require("ui/widget/inputdialog") local InfoMessage = require("ui/widget/infomessage") local Screen = Device.screen +local DefaultPassword = sha2.sha256("1234") --[[ Fullscreen Overlay Widget ]] @@ -37,8 +39,8 @@ local ScreenLock = WidgetContainer:extend { settings_file = DataStorage:getSettingsDir() .. "/screen_lock.lua", settings = nil, - password = "1234", -- Your hard-coded password - hide_content = true -- Hide screen content before password is entered + password_hash = DefaultPassword, + hide_content = true } function ScreenLock:loadSettings() @@ -48,13 +50,13 @@ function ScreenLock:loadSettings() self.settings = LuaSettings:open(self.settings_file) - self.password = self.settings:readSetting("password") or "1234" + self.password_hash = self.settings:readSetting("password_hash") or DefaultPassword self.hide_content = self.settings:readSetting("hide_content") == true end function ScreenLock:onFlushSettings() if self.settings then - self.settings:saveSetting("password", self.password) + self.settings:saveSetting("password_hash", self.password_hash) self.settings:saveSetting("hide_content", self.hide_content) self.settings:flush() @@ -132,8 +134,10 @@ function ScreenLock:showPasswordPrompt() text = _("OK"), is_enter_default = true, callback = function() - local userInput = dialog:getInputText() - if userInput == self.password then + local password_input = dialog:getInputText() + local password_hash = sha2.sha256(password_input) + + if password_hash == self.password_hash then self.locked = false UIManager:close(dialog) @@ -156,12 +160,13 @@ function ScreenLock:showPasswordPrompt() dialog:onShowKeyboard() -- Immediately open the on-screen keyboard end --- Dispatch handler +-- Dispatch handler when screen is locked function ScreenLock:onLockScreenButtons() self:lockScreen() return true end +-- Shows a dialog to change the password function ScreenLock:changePassword() -- Ask for old password local old_dialog @@ -181,7 +186,9 @@ function ScreenLock:changePassword() is_enter_default = true, callback = function() local old_password_input = old_dialog:getInputText() - if old_password_input == self.password then + local old_pasword_hash = sha2.sha256(old_password_input) + + if old_pasword_hash == self.password_hash then UIManager:close(old_dialog) -- Ask for new password @@ -202,7 +209,9 @@ function ScreenLock:changePassword() is_enter_default = true, callback = function() local new_password_input = new_dialog:getInputText() - self.password = new_password_input + local new_password_hash = sha2.sha256(new_password_input) + + self.password_hash = new_password_hash UIManager:show(InfoMessage:new { text = _("Password changed!"), From 7e4da83ce17b6fecfe406dc02948fc0951a14441 Mon Sep 17 00:00:00 2001 From: lnx00 <34384416+lnx00@users.noreply.github.com> Date: Wed, 7 May 2025 02:37:35 +0200 Subject: [PATCH 13/19] Added preview image to README --- .github/assets/preview.png | Bin 0 -> 30704 bytes README.md | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 .github/assets/preview.png diff --git a/.github/assets/preview.png b/.github/assets/preview.png new file mode 100644 index 0000000000000000000000000000000000000000..b08a68eea6cbb42aa9bdf5b0c79803c7144d933f GIT binary patch literal 30704 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 WTkTWKKFEOk>ad}iL6QFPi+=}$pEhIw literal 0 HcmV?d00001 diff --git a/README.md b/README.md index c89c338..f500c2f 100644 --- a/README.md +++ b/README.md @@ -11,5 +11,9 @@ This plugin lets you lock your screen with a password, either triggered from the 2. In KOReader, open the menu and select "Screenlock". 3. Press "Change password" to set your password. The default password is **1234**. +## Preview + + + ## Note The plugin will automatically activate on resume from suspend. There is also a **Screen Lock** menu entry. From 62b3766920279e0311cade4aebf81a7bdee3f1e1 Mon Sep 17 00:00:00 2001 From: lizilizzhh Date: Sat, 9 Aug 2025 16:46:55 +0800 Subject: [PATCH 14/19] Added a numeric only keyboard --- screenlock.koplugin/main.lua | 23 +++++++++++ .../keyboardlayouts/password_keyboard.lua | 38 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index 77f75c0..1e46402 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -11,6 +11,7 @@ local UIManager = require("ui/uimanager") local WidgetContainer = require("ui/widget/container/widgetcontainer") local InputDialog = require("ui/widget/inputdialog") local InfoMessage = require("ui/widget/infomessage") +local VirtualKeyboard = require("ui/widget/virtualkeyboard") local Screen = Device.screen local DefaultPassword = sha2.sha256("1234") @@ -113,6 +114,7 @@ end -- Shows the password prompt and prevents escaping function ScreenLock:showPasswordPrompt() local dialog + self:addKeyboard() dialog = InputDialog:new { title = _("Enter Password"), input = "", @@ -142,6 +144,7 @@ function ScreenLock:showPasswordPrompt() UIManager:close(dialog) UIManager:close(self.background_widget, "full") + self:restoreKeyboard() else UIManager:show(InfoMessage:new { text = _("Wrong password! Try again."), @@ -149,6 +152,7 @@ function ScreenLock:showPasswordPrompt() }) UIManager:close(dialog) + self:restoreKeyboard() self:showPasswordPrompt() end end @@ -168,6 +172,7 @@ end -- Shows a dialog to change the password function ScreenLock:changePassword() + self:addKeyboard() -- Ask for old password local old_dialog old_dialog = InputDialog:new { @@ -179,6 +184,7 @@ function ScreenLock:changePassword() text = _("Cancel"), callback = function() UIManager:close(old_dialog) + self:restoreKeyboard() end }, { @@ -202,6 +208,7 @@ function ScreenLock:changePassword() text = _("Cancel"), callback = function() UIManager:close(new_dialog) + self:restoreKeyboard() end }, { @@ -219,6 +226,7 @@ function ScreenLock:changePassword() }) UIManager:close(new_dialog) + self:restoreKeyboard() end } } } @@ -233,6 +241,7 @@ function ScreenLock:changePassword() }) UIManager:close(old_dialog) + self:restoreKeyboard() self:changePassword() end end @@ -275,4 +284,18 @@ function ScreenLock:addToMainMenu(menu_items) } end +function ScreenLock:addKeyboard() + VirtualKeyboard.lang_to_keyboard_layout[_ "ScreenLockPassword"] = "password_keyboard" + VirtualKeyboard.layout_file = "password_keyboard" + self.original_keyboard_layout = G_reader_settings:readSetting("keyboard_layout") + G_reader_settings:saveSetting("keyboard_layout", "ScreenLockPassword") +end + +function ScreenLock:restoreKeyboard() + VirtualKeyboard.lang_to_keyboard_layout[_ "ScreenLockPassword"] = nil + VirtualKeyboard.layout_file = nil + + G_reader_settings:saveSetting("keyboard_layout", self.original_keyboard_layout) +end + return ScreenLock diff --git a/screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua b/screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua new file mode 100644 index 0000000..bf8e2a8 --- /dev/null +++ b/screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua @@ -0,0 +1,38 @@ +--[[ + Keyboard layout for number password +]] + +return { + min_layer = 1, + max_layer = 1, + shiftmode_keys = {}, + symbolmode_keys = {}, + utf8mode_keys = {}, + keys = { + { + { "7" }, + { "8" }, + { "9" }, + }, + { + { "4" }, + { "5" }, + { "6" }, + }, + { + { "1" }, + { "2" }, + { "3" }, + }, + { + { label = "", width = 1.0, bold = false }, --delete + { "0" }, + { + label = "⮠", + "\n", + width = 1.0, + bold = true + }, + }, + }, +} From 90c623956d76daab04038032fde76f35af719492 Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Fri, 15 Aug 2025 23:44:39 +0600 Subject: [PATCH 15/19] changed preview image --- .../{preview.png => preview-password.png} | Bin .github/assets/preview-pin.png | Bin 0 -> 18722 bytes README.md | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) rename .github/assets/{preview.png => preview-password.png} (100%) create mode 100644 .github/assets/preview-pin.png diff --git a/.github/assets/preview.png b/.github/assets/preview-password.png similarity index 100% rename from .github/assets/preview.png rename to .github/assets/preview-password.png diff --git a/.github/assets/preview-pin.png b/.github/assets/preview-pin.png new file mode 100644 index 0000000000000000000000000000000000000000..466191daf6210550bd2f5a60e7dfa085c3e1b72b GIT binary patch literal 18722 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index f500c2f..c2613cd 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ This plugin lets you lock your screen with a password, either triggered from the ## Preview - + ## Note The plugin will automatically activate on resume from suspend. There is also a **Screen Lock** menu entry. From 45b30bc4fb5b82343e45116d6097ffc677d07ca3 Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 1 Jan 2026 18:38:20 +0600 Subject: [PATCH 16/19] change button layout to t9 --- .../ui/data/keyboardlayouts/password_keyboard.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua b/screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua index bf8e2a8..8feeb7b 100644 --- a/screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua +++ b/screenlock.koplugin/ui/data/keyboardlayouts/password_keyboard.lua @@ -10,9 +10,9 @@ return { utf8mode_keys = {}, keys = { { - { "7" }, - { "8" }, - { "9" }, + { "1" }, + { "2" }, + { "3" }, }, { { "4" }, @@ -20,9 +20,9 @@ return { { "6" }, }, { - { "1" }, - { "2" }, - { "3" }, + { "7" }, + { "8" }, + { "9" }, }, { { label = "", width = 1.0, bold = false }, --delete From 45b37031795b56f347cbf203ccbfaf63ec28228b Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 1 Jan 2026 18:44:43 +0600 Subject: [PATCH 17/19] fix typo --- screenlock.koplugin/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index 1e46402..d10bf84 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -192,9 +192,9 @@ function ScreenLock:changePassword() is_enter_default = true, callback = function() local old_password_input = old_dialog:getInputText() - local old_pasword_hash = sha2.sha256(old_password_input) + local old_password_hash = sha2.sha256(old_password_input) - if old_pasword_hash == self.password_hash then + if old_password_hash == self.password_hash then UIManager:close(old_dialog) -- Ask for new password From c0040081f19c272798f49172411e8433fa26858e Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 1 Jan 2026 19:33:00 +0600 Subject: [PATCH 18/19] cancel on lockscreen put device to sleep --- screenlock.koplugin/main.lua | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/screenlock.koplugin/main.lua b/screenlock.koplugin/main.lua index d10bf84..f0cd29e 100644 --- a/screenlock.koplugin/main.lua +++ b/screenlock.koplugin/main.lua @@ -123,13 +123,8 @@ function ScreenLock:showPasswordPrompt() { text = _("Cancel"), callback = function() - UIManager:show(InfoMessage:new { - text = _("You must enter the correct password!"), - timeout = 1 - }) - - UIManager:close(dialog) - self:showPasswordPrompt() + -- Sleep the device + UIManager:suspend() end }, { @@ -261,7 +256,8 @@ function ScreenLock:addToMainMenu(menu_items) { text = _("Lock now"), callback = function() - self:lockScreen() + -- Sleep the device + UIManager:suspend() end, separator = true, }, From 862e7ca186e46538ae6a017553bdd6504e08098e Mon Sep 17 00:00:00 2001 From: Fahim Ahmed Date: Thu, 1 Jan 2026 19:55:04 +0600 Subject: [PATCH 19/19] add new info --- README.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c2613cd..e95ff4d 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,61 @@ # KOReader Plugin: ScreenLock -This plugin lets you lock your screen with a password, either triggered from the menu or automatically upon device wake-up. -> +This plugin adds a simple pin-based lock screen to KOReader. + > [!CAUTION] -> **This plugin is made for basic protection, not security — it may not protect your device from an experienced attacker. Always keep your device out of the hands of real threats.** -> +> This plugin provides **basic protection only**. +> It is **not a security solution** and will not stop a determined or technically skilled attacker. +> Do not rely on it to protect sensitive data. + +## Features + +- Lock KOReader with a password +- Automatically locks after device suspend / resume +- Optional blank (white) screen to hide document contents +- On-screen password keyboard +- Menu actions for locking and password management + +## Installation + +1. Copy the `screenlock.koplugin` folder into: + ``` + koreader/plugins/ + ``` +2. Start (or restart) KOReader. +3. Open the KOReader menu and select **ScreenLock**. + +## Usage + +### Lock the screen + +- Open the KOReader menu +- Go to **ScreenLock** +- Select **Lock now** -## Setup -1. Put `screenlock.koplugin` into the `kodreader/plugins` directory. -2. In KOReader, open the menu and select "Screenlock". -3. Press "Change password" to set your password. The default password is **1234**. +> or + +- Press power/lock button + +### Change the password + +- Open **ScreenLock** in the menu +- Select **Change password** +- Enter your current password, then set a new one + +> **Default password:** `1234` + +## Behavior Notes + +- The lock screen **automatically activates when the device resumes from sleep** +- Pressing **Cancel** on the password prompt puts the device back to sleep +- If enabled, screen contents are hidden while locked ## Preview -## Note -The plugin will automatically activate on resume from suspend. There is also a **Screen Lock** menu entry. +## Limitations + +- This plugin does **not encrypt files or memory** +- It only protects access within KOReader +- Removing the plugin or editing settings files can bypass the lock