Upload most recent update of standard plugin prior to any personal tweaks

This commit is contained in:
2026-01-31 14:07:48 -07:00
commit 89d0f2d8ed
12 changed files with 1369 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
local _ = require("gettext")
return {
name = "pinpadlock",
fullname = _("PIN Pad Lock Screen"),
description = _([[Lock the device with a PIN code]]),
version = "v1.1.0"
}

View File

@@ -0,0 +1,165 @@
--[[
Author: Lena2309 (better lock management, boot hooking, event listening, ...), yogi81 (original inspiration and main skeleton)
Inspiration and original skeleton from `https://github.com/yogi81/screenlock_koreader_plugin/tree/main`
Description: implements a screen lock mechanism for KOReader
using a PIN pad interface. Also adds a menu entry in Settings -> Screen
]]
local Dispatcher = require("dispatcher")
local PinPadDialog = require("ui/pinpaddialog")
local EventListener = require("ui/widget/eventlistener")
local _ = require("gettext")
local UIManager = require("ui/uimanager")
local MenuEntryItems = require("menu/menuentryitems")
local logger = require("logger")
-- Default settings
if G_reader_settings:hasNot("pinpadlock_pin_code") then
G_reader_settings:saveSetting("pinpadlock_pin_code", "1234")
end
if G_reader_settings:hasNot("pinpadlock_activated") then
G_reader_settings:makeFalse("pinpadlock_activated")
end
if G_reader_settings:hasNot("pinpadlock_correct_pin_message_activated") then
G_reader_settings:makeTrue("pinpadlock_correct_pin_message_activated")
end
if G_reader_settings:hasNot("pinpadlock_timeout_time") then
G_reader_settings:saveSetting("pinpadlock_timeout_time", "30")
end
if G_reader_settings:hasNot("pinpadlock_max_tries") then
G_reader_settings:saveSetting("pinpadlock_max_tries", "3")
end
if G_reader_settings:hasNot("pinpadlock_show_message") then
G_reader_settings:makeFalse("pinpadlock_show_message")
end
if G_reader_settings:hasNot("pinpadlock_message") then
G_reader_settings:saveSetting("pinpadlock_message", "Locked")
end
if G_reader_settings:hasNot("pinpadlock_message_position") then
G_reader_settings:saveSetting("pinpadlock_message_position", "top")
end
if G_reader_settings:hasNot("pinpadlock_message_alignment") then
G_reader_settings:saveSetting("pinpadlock_message_alignment", "center")
end
if G_reader_settings:hasNot("suspended_device") then
G_reader_settings:makeTrue("suspended_device")
end
local ScreenLock = EventListener:extend {
pinPadDialog = nil,
}
function ScreenLock:erasePinPadDialog()
if self.pinPadDialog then
self.pinPadDialog:close(function()
self.pinPadDialog = nil
end)
end
end
------------------------------------------------------------------------------
--- DEVICE LISTENER OVERRIDE
------------------------------------------------------------------------------
local ref_self = nil
local _original_PowerOff = UIManager.poweroff_action
UIManager.poweroff_action = function()
logger.warn("in suspend ref_self: " .. tostring(ref_self))
if ref_self then
logger.warn("before erase dialog: " .. tostring(ref_self))
ref_self:erasePinPadDialog()
logger.warn("after erase dialog: " .. tostring(ref_self))
end
UIManager:nextTick(function()
_original_PowerOff()
end)
end
------------------------------------------------------------------------------
--- REGISTER DISPATCHER ACTIONS
------------------------------------------------------------------------------
function ScreenLock:onDispatcherRegisterActions()
Dispatcher:registerAction("screenlock_pin_pad_lock_screen", {
category = "none",
event = "LockScreenPinPad",
title = _("Lock Screen (PinPad)"),
filemanager = true,
device = true,
})
end
------------------------------------------------------------------------------
--- EVENT LISTENING
------------------------------------------------------------------------------
---KOReader exit and restart
function ScreenLock:onExit()
G_reader_settings:makeTrue("suspended_device")
end
function ScreenLock:onRestart()
G_reader_settings:makeTrue("suspended_device")
end
--- Device suspension, reboot or power off
function ScreenLock:onRequestSuspend()
G_reader_settings:makeTrue("suspended_device")
end
function ScreenLock:onRequestReboot()
G_reader_settings:makeTrue("suspended_device")
end
function ScreenLock:onRequestPowerOff()
G_reader_settings:makeTrue("suspended_device")
end
------------------------------------------------------------------------------
--- INIT & WAKE-UP HANDLING
------------------------------------------------------------------------------
function ScreenLock:init()
self:onDispatcherRegisterActions()
self.ui.menu:registerToMainMenu(self)
ref_self = self
if G_reader_settings:isTrue("pinpadlock_activated") and G_reader_settings:isTrue("suspended_device") then
ref_self:lockScreen()
end
return self
end
function ScreenLock:onResume()
if G_reader_settings:isTrue("pinpadlock_activated") then
self:lockScreen()
end
end
------------------------------------------------------------------------------
--- DISPATCHER HANDLER
------------------------------------------------------------------------------
function ScreenLock:onLockScreen()
self:lockScreen()
return true
end
------------------------------------------------------------------------------
--- LOCK SCREEN
------------------------------------------------------------------------------
function ScreenLock:lockScreen()
ref_self = self
UIManager:nextTick(function()
if self.pinPadDialog then self.pinPadDialog:closeDialogs() end
self.pinPadDialog = PinPadDialog:init()
self.pinPadDialog:showPinPad()
end)
ref_self = self
end
------------------------------------------------------------------------------
--- MAIN MENU ENTRY
------------------------------------------------------------------------------
function ScreenLock:addToMainMenu(menu_items)
menu_items.pinpad = MenuEntryItems
end
return ScreenLock

View File

@@ -0,0 +1,135 @@
--[[
Author: Lena2309
Description: Displays items in the Screen Menu Entry to manage the plugin
]]
local PinPadMenuEntry = require("menu/pinpadmenuentry")
local _ = require("gettext")
return {
text = _("PIN Pad Lock"),
sorting_hint = "screen",
sub_item_table = {
{
text = _("Activated"),
checked_func = function()
return PinPadMenuEntry:pinPadEnabled()
end,
callback = function()
G_reader_settings:toggle("pinpadlock_activated")
end,
separator = true,
},
{
text = _("Manage PIN Code"),
keep_menu_open = true,
sub_item_table = {
{
text = _("Change PIN Code"),
callback = function()
PinPadMenuEntry:changePinCode()
end
},
{
text = _("Reset PIN Code"),
callback = function()
PinPadMenuEntry:resetPinCode()
end,
},
}
},
{
text = _("PIN pad lock message"),
separator = true,
sub_item_table = {
{
text = _("Add custom message to lock"),
checked_func = function()
return PinPadMenuEntry:showMessageEnabled()
end,
callback = function()
G_reader_settings:toggle("pinpadlock_show_message")
end,
separator = true,
},
{
text = _("Edit PIN pad lock message"),
enabled_func = function()
return PinPadMenuEntry:showMessageEnabled()
end,
keep_menu_open = true,
callback = function()
PinPadMenuEntry:setMessage()
end,
},
{
text = _("Message position"),
enabled_func = function()
return PinPadMenuEntry:showMessageEnabled()
end,
sub_item_table = {
PinPadMenuEntry:genRadioMenuItem(_("Top"), "pinpadlock_message_position", "top"),
PinPadMenuEntry:genRadioMenuItem(_("Middle"), "pinpadlock_message_position", "middle"),
PinPadMenuEntry:genRadioMenuItem(_("Bottom"), "pinpadlock_message_position", "bottom"),
},
},
{
text = _("Message alignment"),
enabled_func = function()
return PinPadMenuEntry:showMessageEnabled()
end,
sub_item_table = {
PinPadMenuEntry:genRadioMenuItem(_("Left"), "pinpadlock_message_alignment", "left"),
PinPadMenuEntry:genRadioMenuItem(_("Center"), "pinpadlock_message_alignment", "center"),
PinPadMenuEntry:genRadioMenuItem(_("Right"), "pinpadlock_message_alignment", "right"),
},
},
}
},
{
text = _("Advanced Settings"),
sub_item_table = {
{
text = _("Activate correct PIN pop-up"),
checked_func = function()
return PinPadMenuEntry:correctPinMessageEnabled()
end,
callback = function()
G_reader_settings:toggle("pinpadlock_correct_pin_message_activated")
end,
},
{
text = _("Display entered digit before hiding it"),
checked_func = function()
return PinPadMenuEntry:displayDigitEnabled()
end,
callback = function()
G_reader_settings:toggle("pinpadlock_display_digit_activated")
end,
},
{
text = _("Set Timeout time"),
sub_item_table = {
PinPadMenuEntry:genRadioMenuItem(_("10 seconds"), "pinpadlock_timeout_time", "10"),
PinPadMenuEntry:genRadioMenuItem(_("30 seconds"), "pinpadlock_timeout_time", "30"),
PinPadMenuEntry:genRadioMenuItem(_("60 seconds"), "pinpadlock_timeout_time", "60"),
},
},
{
text = _("Set max tries before timeout"),
sub_item_table = {
PinPadMenuEntry:genRadioMenuItem(_("1"), "pinpadlock_max_tries", "1"),
PinPadMenuEntry:genRadioMenuItem(_("3"), "pinpadlock_max_tries", "3"),
PinPadMenuEntry:genRadioMenuItem(_("6"), "pinpadlock_max_tries", "6"),
},
},
},
},
{
text = _("Check for updates"),
callback = function()
PinPadMenuEntry:checkForUpdates()
end,
}
}
}

View File

@@ -0,0 +1,187 @@
--[[
Author: Lena2309
Description: Execute actions when tapping on a menu entry item
]]
local ConfirmBox = require("ui/widget/confirmbox")
local InfoMessage = require("ui/widget/infomessage")
local InputDialog = require("ui/widget/inputdialog")
local PinPadDialog = require("ui/pinpaddialog")
local UIManager = require("ui/uimanager")
local _ = require("gettext")
local http = require("socket.http")
local ltn12 = require("ltn12")
local json = require("dkjson")
local PinPadMenuEntry = {}
function PinPadMenuEntry:setMessage()
local lock_message = G_reader_settings:readSetting("pinpadlock_message")
local input_dialog
input_dialog = InputDialog:new {
title = _("PIN pad lock message"),
description = _("Enter a custom message to be displayed on the PIN pad lock."),
input = lock_message,
buttons = {
{
{
text = _("Cancel"),
id = "close",
callback = function()
UIManager:close(input_dialog)
end,
},
{
text = _("Set message"),
is_enter_default = true,
callback = function()
G_reader_settings:saveSetting("pinpadlock_message", input_dialog:getInputText())
UIManager:close(input_dialog)
end,
},
},
},
}
UIManager:show(input_dialog)
input_dialog:onShowKeyboard()
end
function PinPadMenuEntry:showMessageEnabled()
return G_reader_settings:isTrue("pinpadlock_show_message")
end
function PinPadMenuEntry:pinPadEnabled()
return G_reader_settings:isTrue("pinpadlock_activated")
end
function PinPadMenuEntry:correctPinMessageEnabled()
return G_reader_settings:isTrue("pinpadlock_correct_pin_message_activated")
end
function PinPadMenuEntry:displayDigitEnabled()
return G_reader_settings:isTrue("pinpadlock_display_digit_activated")
end
function PinPadMenuEntry:genRadioMenuItem(text, setting, value)
return {
text = text,
checked_func = function()
return G_reader_settings:readSetting(setting) == value
end,
callback = function()
G_reader_settings:saveSetting(setting, value)
end,
radio = true,
}
end
function PinPadMenuEntry:changePinCode()
local confirmCurrentCode = PinPadDialog:new { stage = "confirm_current_code" }
confirmCurrentCode:showPinPad()
end
function PinPadMenuEntry:resetPinCode()
local confirmBox
confirmBox = ConfirmBox:new {
text = _("Do you really want to reset the PIN code to \"1234\" ?"),
ok_text = _("Yes"),
ok_callback = function()
G_reader_settings:saveSetting("pinpadlock_pin_code", "1234")
UIManager:close(confirmBox)
UIManager:show(InfoMessage:new { text = _("PIN Code resetted successfully."), timeout = 1 })
end,
cancel_callback = function()
UIManager:close(confirmBox)
end
}
UIManager:show(confirmBox)
end
-- Parse a version string like "v1.2.3"
local function parseVersion(version)
local major, minor, patch = version:match("^v(%d+)%.(%d+)%.(%d+)$")
return {
major = tonumber(major) or 0,
minor = tonumber(minor) or 0,
patch = tonumber(patch) or 0
}
end
-- Returns -1 if a < b, 0 if equal, 1 if a > b
local function compareVersions(local_version, remote_version)
if local_version.major ~= remote_version.major then
if local_version.major > remote_version.major then
return 1
else
return -1
end
elseif local_version.minor ~= remote_version.minor then
if local_version.minor > remote_version.minor then
return 1
else
return -1
end
elseif local_version.patch ~= remote_version.patch then
if local_version.patch > remote_version.patch then
return 1
else
return -1
end
end
return 0
end
function PinPadMenuEntry:checkForUpdates()
local meta = dofile("plugins/pinpadlockscreen.koplugin/_meta.lua")
local local_version = meta.version or "unknown"
local response_body = {}
local ok, status = http.request {
url = "https://api.github.com/repos/Lena2309/pinpad_screenlock_plugin/releases/latest",
sink = ltn12.sink.table(response_body),
redirect = true
}
if not ok or status ~= 200 then
UIManager:show(InfoMessage:new {
text = _("Unable to check for updates. Make sure your device is connected to the Internet."),
timeout = 3,
})
return
end
local body = table.concat(response_body)
local data, pos, err = json.decode(body, 1, nil)
if not data or not data.tag_name then
UIManager:show(InfoMessage:new {
text = _("Error parsing GitHub release info."),
timeout = 3,
})
return
end
local remote_version = data.tag_name
local cmp = compareVersions(parseVersion(local_version), parseVersion(remote_version))
if cmp < 0 then
UIManager:show(InfoMessage:new {
text = _("New version available: ") .. remote_version ..
_("\nYou are running: ") .. local_version,
timeout = 5,
})
else
if cmp > 0 then
UIManager:show(InfoMessage:new {
text = _("Youre ahead of the official release.\nLatest official version: " .. remote_version),
timeout = 5,
})
else
UIManager:show(InfoMessage:new {
text = _("You are up to date!"),
timeout = 3,
})
end
end
end
return PinPadMenuEntry

View File

@@ -0,0 +1,18 @@
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<g transform="translate(0, 512) scale(0.1, -0.1)"
fill="#000000" stroke="none">
<path d="M2390 5110 c-157 -22 -329 -75 -461 -140 -95 -48 -234 -147 -319
-227 -203 -191 -334 -415 -407 -697 -27 -101 -27 -102 -30 -573 l-4 -473 -123
0 c-201 0 -285 -41 -348 -167 l-33 -68 0 -1260 c0 -1052 2 -1267 14 -1300 32
-93 104 -163 197 -191 73 -22 3296 -21 3371 1 78 23 136 74 175 152 l33 68 0
1265 0 1265 -33 68 c-63 127 -147 167 -349 167 l-123 0 0 428 c0 461 -5 523
-55 697 -86 295 -296 581 -557 756 -162 107 -310 170 -496 209 -111 24 -350
34 -452 20z m405 -519 c317 -91 554 -334 637 -653 20 -77 21 -113 25 -510 l5
-428 -902 0 -902 0 5 428 c4 397 5 433 25 510 87 335 351 591 687 666 99 22
324 15 420 -13z m-114 -2497 c151 -48 252 -192 253 -361 0 -74 -4 -88 -37
-157 -25 -50 -53 -89 -86 -118 l-49 -43 54 -250 c29 -137 53 -258 54 -267 0
-17 -21 -18 -310 -18 -289 0 -310 1 -309 18 0 9 24 130 53 267 l53 250 -48 43
c-94 83 -143 229 -119 349 47 228 269 357 491 287z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,235 @@
--[[
Author: Lena2309
Description: customizes KOReader's existing ButtonDialog class solely
for aesthetic purposes (icon widget, vertical layout...),
with the primary modification in the `init` function.
]]
local Blitbuffer = require("ffi/blitbuffer")
local ButtonDialog = require("ui/widget/buttondialog")
local ButtonTable = require("ui/widget/buttontable")
local CenterContainer = require("ui/widget/container/centercontainer")
local Device = require("device")
local FrameContainer = require("ui/widget/container/framecontainer")
local Geom = require("ui/geometry")
local GestureRange = require("ui/gesturerange")
local ImageWidget = require("ui/widget/imagewidget")
local LineWidget = require("ui/widget/linewidget")
local MovableContainer = require("ui/widget/container/movablecontainer")
local ScrollTextWidget = require("ui/widget/scrolltextwidget")
local Size = require("ui/size")
local TextBoxWidget = require("ui/widget/textboxwidget")
local UIManager = require("ui/uimanager")
local VerticalGroup = require("ui/widget/verticalgroup")
local VerticalSpan = require("ui/widget/verticalspan")
local Screen = Device.screen
local util = require("util")
local DGENERIC_ICON_SIZE = G_defaults:readSetting("DGENERIC_ICON_SIZE")
local PinpadButtonDialog = ButtonDialog:extend {
override_show_message = false, -- will prevent showing custom lock message even if enabled (specific for changing PIN Code)
}
function PinpadButtonDialog:init()
if not self.width_factor then
self.width_factor = 2 / 3
end
self.width = math.floor(math.min(Screen:getWidth(), Screen:getHeight()) * self.width_factor)
if self.dismissable then
if Device:hasKeys() then
local back_group = util.tableDeepCopy(Device.input.group.Back)
if Device:hasFewKeys() then
table.insert(back_group, "Left")
self.key_events.Close = { { back_group } }
else
table.insert(back_group, "Menu")
self.key_events.Close = { { back_group } }
end
end
if Device:isTouchDevice() then
self.ges_events.TapClose = {
GestureRange:new {
ges = "tap",
range = Geom:new {
x = 0, y = 0,
w = Screen:getWidth(),
h = Screen:getHeight(),
}
}
}
end
end
local title_face
if self.use_info_style then
title_face = self.info_face
else
title_face = self.title_face
end
local currentFileSource = debug.getinfo(1, "S").source
local plugin_dir
if currentFileSource:find("^@") then
plugin_dir = currentFileSource:gsub("^@(.*)/[^/]*", "%1")
end
self.title_widget = TextBoxWidget:new {
text = self.title,
face = title_face,
width = self.width,
alignment = self.title_align,
}
local aesthetic_space = VerticalSpan:new { width = Size.margin.default + Size.padding.default }
local text_pin_content = VerticalGroup:new {
align = "center",
ImageWidget:new {
file = plugin_dir .. "/icons/lock.svg",
alpha = true,
width = Screen:scaleBySize(DGENERIC_ICON_SIZE) * 1.25,
height = Screen:scaleBySize(DGENERIC_ICON_SIZE) * 1.25,
scale_factor = 0,
original_in_nightmode = false,
},
aesthetic_space,
VerticalGroup:new {
align = "center",
self.title_widget
},
}
self.buttontable = ButtonTable:new {
buttons = self.buttons,
width = text_pin_content:getSize().w,
shrink_unneeded_width = self.shrink_unneeded_width,
shrink_min_width = self.shrink_min_width,
show_parent = self,
}
local separator = LineWidget:new {
background = Blitbuffer.COLOR_GRAY,
dimen = Geom:new {
w = text_pin_content:getSize().w,
h = Size.line.medium,
},
}
local content
if G_reader_settings:isTrue("pinpadlock_show_message") and not self.override_show_message then
local lock_message = G_reader_settings:readSetting("pinpadlock_message")
lock_message = lock_message:gsub("\\n", "\n") -- enabling jumping lines in the message
local lock_message_alignment = G_reader_settings:readSetting("pinpadlock_message_alignment")
local lock_message_position = G_reader_settings:readSetting("pinpadlock_message_position")
local lock_message_widget = TextBoxWidget:new {
text = lock_message,
face = title_face,
width = self.width,
alignment = lock_message_alignment,
}
-- If the custom message ends up being too tall and makes the Pin Pad taller than the screen,
-- wrap it inside a ScrollableContainer
local max_height = self.buttontable:getSize().h + 3 * text_pin_content:getSize().h + Size.line.medium
local height = self.buttontable:getSize().h + text_pin_content:getSize().h + Size.line.medium +
lock_message_widget:getSize().h
if height > max_height then
local scroll_height = 3 * text_pin_content:getSize().h
lock_message_widget = ScrollTextWidget:new {
text = lock_message,
face = title_face,
width = self.width,
alignment = lock_message_alignment,
dialog = self,
height = scroll_height,
}
end
if lock_message_position == "top" then
content = VerticalGroup:new {
align = "center",
aesthetic_space,
lock_message_widget,
aesthetic_space,
separator,
aesthetic_space,
text_pin_content,
aesthetic_space,
separator,
self.buttontable,
}
elseif lock_message_position == "middle" then
content = VerticalGroup:new {
align = "center",
aesthetic_space,
text_pin_content,
aesthetic_space,
separator,
aesthetic_space,
lock_message_widget,
aesthetic_space,
separator,
self.buttontable,
}
else
content = VerticalGroup:new {
align = "center",
aesthetic_space,
text_pin_content,
aesthetic_space,
separator,
self.buttontable,
separator,
aesthetic_space,
lock_message_widget,
aesthetic_space,
}
end
else
content = VerticalGroup:new {
align = "center",
aesthetic_space,
text_pin_content,
aesthetic_space,
separator,
self.buttontable,
}
end
self.movable = MovableContainer:new {
alpha = self.alpha,
anchor = self.anchor,
FrameContainer:new {
background = Blitbuffer.COLOR_WHITE,
bordersize = Size.border.window,
radius = Size.radius.window,
padding = Size.padding.default,
padding_top = 0,
padding_bottom = 0,
content,
},
unmovable = true,
}
self.layout = self.buttontable.layout
self.buttontable.layout = nil
self[1] = CenterContainer:new {
dimen = Screen:getSize(),
self.movable,
}
end
function PinpadButtonDialog:updateTitle(text)
if self.title_widget then
self.title = text
self.title_widget:setText(text)
UIManager:setDirty(self, "ui")
end
end
return PinpadButtonDialog

View File

@@ -0,0 +1,484 @@
--[[
Author: Lena2309
Description: represents a PIN pad interface and its behavior within KOReader.
It handles user input, PIN validation, and UI interactions.
]]
local Blitbuffer = require("ffi/blitbuffer")
local BookStatusWidget = require("ui/widget/bookstatuswidget")
local Button = require("ui/widget/button")
local Device = require("device")
local FrameContainer = require("ui/widget/container/framecontainer")
local InfoMessage = require("ui/widget/infomessage")
local ImageWidget = require("ui/widget/imagewidget")
local PinPadButtonDialog = require("ui/pinpadbuttondialog")
local RenderImage = require("ui/renderimage")
local Screensaver = require("ui/screensaver")
local ScreenSaverWidget = require("ui/widget/screensaverwidget")
local UIManager = require("ui/uimanager")
local ffiUtil = require("ffi/util")
local util = require("util")
local _ = require("gettext")
local Screen = Device.screen
local ScreensaverUtil = require("util/screensaverutil")
local DGENERIC_ICON_SIZE = G_defaults:readSetting("DGENERIC_ICON_SIZE")
local LOCKED_TEXT = _("Enter your PIN")
local CONFIRM_CURRENT_CODE_TEXT = _("Enter the current PIN Code")
local NEW_CODE_TEXT = _("Enter the new PIN Code")
local MAX_TRIES_LIMIT = tonumber(G_reader_settings:readSetting("pinpadlock_max_tries"))
local TIMEOUT_TIME = tonumber(G_reader_settings:readSetting("pinpadlock_timeout_time"))
if G_reader_settings:hasNot("current_tries_number") then
G_reader_settings:saveSetting("current_tries_number", 0)
end
if G_reader_settings:hasNot("block_start_time") then
G_reader_settings:saveSetting("block_start_time", 0)
end
local PinPadDialog = FrameContainer:extend {
stage = "locked", -- "confirm_current_code" or "enter_new_code"
icon_size = Screen:scaleBySize(DGENERIC_ICON_SIZE) * 1.25,
}
function PinPadDialog:init()
self:setDialogText()
self.pin = ""
self.buttons = self:initializeButtons()
return self
end
function PinPadDialog:setDialogText()
if self.stage == "locked" then
self.dialog_text = LOCKED_TEXT
elseif self.stage == "confirm_current_code" then
self.dialog_text = CONFIRM_CURRENT_CODE_TEXT
else
self.dialog_text = NEW_CODE_TEXT
end
end
function PinPadDialog:createButton(text, callback, hold_callback)
local button = Button:new {
text = text,
callback = callback,
hold_callback = hold_callback,
}
return button
end
function PinPadDialog:initializeButtons()
local ref_self = self
local buttons = {
{
self:createButton("1", function() ref_self:onAppendToPin("1") end),
self:createButton("2", function() ref_self:onAppendToPin("2") end),
self:createButton("3", function() ref_self:onAppendToPin("3") end),
},
{
self:createButton("4", function() ref_self:onAppendToPin("4") end),
self:createButton("5", function() ref_self:onAppendToPin("5") end),
self:createButton("6", function() ref_self:onAppendToPin("6") end),
},
{
self:createButton("7", function() ref_self:onAppendToPin("7") end),
self:createButton("8", function() ref_self:onAppendToPin("8") end),
self:createButton("9", function() ref_self:onAppendToPin("9") end),
},
{
self:createButton("Cancel", function() ref_self:onDelete() end, function() ref_self:onCancel() end),
self:createButton("0", function() ref_self:onAppendToPin("0") end),
self:createButton("OK", function() ref_self:onOk() end),
}
}
return buttons
end
function PinPadDialog:initializeScreensaverBackground()
if Screensaver.screensaver_type == "disable" and not Screensaver.show_message and not Screensaver.overlay_message then
return
end
local rotation_mode = Screen:getRotationMode()
if Screensaver:modeExpectsPortrait() then
Device.orig_rotation_mode = rotation_mode
if bit.band(Device.orig_rotation_mode, 1) == 1 then
Screen:setRotationMode(Screen.DEVICE_ROTATED_UPRIGHT)
else
Device.orig_rotation_mode = nil
end
if Device:hasEinkScreen() and Screensaver:modeIsImage() then
if Screensaver:withBackground() then
Screen:clear()
end
Screen:refreshFull(0, 0, Screen:getWidth(), Screen:getHeight())
if Device:isKobo() and Device:isSunxi() then
ffiUtil.usleep(150 * 1000)
end
end
else
Device.orig_rotation_mode = nil
end
local widget = nil
if Screensaver.screensaver_type == "cover" or Screensaver.screensaver_type == "random_image" then
local widget_settings = {
width = Screen:getWidth(),
height = Screen:getHeight(),
scale_factor = G_reader_settings:isFalse("screensaver_stretch_images") and 0 or nil,
stretch_limit_percentage = G_reader_settings:readSetting("screensaver_stretch_limit_percentage"),
}
if Screensaver.image then
widget_settings.image = Screensaver.image
widget_settings.image_disposable = true
elseif Screensaver.image_file then
if G_reader_settings:isTrue("screensaver_rotate_auto_for_best_fit") then
if util.getFileNameSuffix(Screensaver.image_file) == "svg" then
widget_settings.image = RenderImage:renderSVGImageFile(Screensaver.image_file, nil, nil, 1)
else
widget_settings.image = RenderImage:renderImageFile(Screensaver.image_file, false, nil, nil)
end
if not widget_settings.image then
widget_settings.image = RenderImage:renderCheckerboard(Screen:getWidth(), Screen:getHeight(),
Screen.bb:getType())
end
widget_settings.image_disposable = true
else
widget_settings.file = Screensaver.image_file
widget_settings.file_do_cache = false
end
widget_settings.alpha = true
end
if G_reader_settings:isTrue("screensaver_rotate_auto_for_best_fit") then
local angle = rotation_mode == 3 and 180 or 0
if (widget_settings.image:getWidth() < widget_settings.image:getHeight()) ~= (widget_settings.width < widget_settings.height) then
angle = angle + (G_reader_settings:isTrue("imageviewer_rotation_landscape_invert") and -90 or 90)
end
widget_settings.rotation_angle = angle
end
widget = ImageWidget:new(widget_settings)
elseif Screensaver.screensaver_type == "bookstatus" then
local ReaderUI = require("apps/reader/readerui")
widget = BookStatusWidget:new {
ui = ReaderUI.instance,
readonly = true,
}
elseif Screensaver.screensaver_type == "readingprogress" then
widget = Screensaver.getReaderProgress()
end
local covers_fullscreen = true
local background
if Screensaver.screensaver_background == "white" then
background = Blitbuffer.COLOR_WHITE
elseif Screensaver.screensaver_background == "none" then
background = nil
else
background = Blitbuffer.COLOR_BLACK
end
UIManager:setIgnoreTouchInput(false)
if widget then
self.screensaver_widget = ScreenSaverWidget:new {
widget = widget,
background = background,
covers_fullscreen = covers_fullscreen,
}
self.screensaver_widget.dithered = true
else
local currentFileSource = debug.getinfo(1, "S").source
local plugin_dir
if currentFileSource:find("^@") then
plugin_dir = currentFileSource:gsub("^@(.*)/[^/]*", "%1")
end
-- dummy widget
widget = ImageWidget:new {
file = plugin_dir .. "/icons/lock.svg",
alpha = true,
width = self.icon_size,
height = self.icon_size,
scale_factor = 0,
original_in_nightmode = false,
}
self.screensaver_widget = ScreenSaverWidget:new {
widget = widget,
background = Blitbuffer.COLOR_BLACK,
covers_fullscreen = covers_fullscreen,
}
self.screensaver_widget.dithered = true
end
end
function PinPadDialog:isBlocked()
local block_start = G_reader_settings:readSetting("block_start_time") or 0
if block_start == 0 then
if self.blocking_dialog then
UIManager:close(self.blocking_dialog, "ui")
self.blocking_dialog = nil
end
return false
end
local elapsed = os.time() - block_start
if elapsed < 0 then
G_reader_settings:saveSetting("block_start_time", 0)
G_reader_settings:saveSetting("current_tries_number", 0)
if self.blocking_dialog then
UIManager:close(self.blocking_dialog, "ui")
self.blocking_dialog = nil
end
return false
end
if elapsed >= TIMEOUT_TIME then
if self.blocking_dialog then
UIManager:close(self.blocking_dialog, "ui")
self.blocking_dialog = nil
end
return false
else
self.remaining_block_time = TIMEOUT_TIME - elapsed
return true
end
end
function PinPadDialog:showPinPad()
local screensaver_visible = UIManager:isWidgetShown(Screensaver.screensaver_widget)
if screensaver_visible then
ScreensaverUtil.freeze()
self.reused_screensaver = true
else
Screensaver:setup()
self:initializeScreensaverBackground()
if self.screensaver_widget then
UIManager:show(self.screensaver_widget)
end
self.reused_screensaver = false
end
local currentFileSource = debug.getinfo(1, "S").source
local plugin_dir
if currentFileSource:find("^@") then
plugin_dir = currentFileSource:gsub("^@(.*)/[^/]*", "%1")
end
if self.stage == "locked" then
self.dialog = PinPadButtonDialog:new {
title = self.dialog_text,
title_align = "center",
use_info_style = false,
buttons = self.buttons,
dismissable = false,
ImageWidget:new {
file = plugin_dir .. "/icons/lock.svg",
alpha = true,
width = self.icon_size,
height = self.icon_size,
scale_factor = 0,
original_in_nightmode = false,
}
}
else
self.dialog = PinPadButtonDialog:new {
title = self.dialog_text,
title_align = "center",
use_info_style = false,
buttons = self.buttons,
dismissable = true,
override_show_message = true,
ImageWidget:new {
file = plugin_dir .. "/icons/lock.svg",
alpha = true,
width = self.icon_size,
height = self.icon_size,
scale_factor = 0,
original_in_nightmode = false,
}
}
end
UIManager:show(self.dialog, "ui")
UIManager:nextTick(function()
for i = 1, #UIManager._window_stack do
if UIManager._window_stack[i].widget == self.dialog then
local dialog_entry = table.remove(UIManager._window_stack, i)
table.insert(UIManager._window_stack, dialog_entry)
UIManager:setDirty(self.dialog, "ui")
break
end
end
end)
if self.stage == "locked" and self:isBlocked() then
self:showBlockingDialog(self.remaining_block_time)
end
end
function PinPadDialog:showBlockingDialog(remaining_time)
self.blocking_dialog = InfoMessage:new {
text = _("Too many failed attempts. Wait " .. remaining_time .. " seconds."),
timeout = remaining_time,
dismissable = false,
}
UIManager:show(self.blocking_dialog, "ui")
UIManager:nextTick(function()
if not self.blocking_dialog then return end
for i = 1, #UIManager._window_stack do
if UIManager._window_stack[i].widget == self.blocking_dialog then
local blocking_entry = table.remove(UIManager._window_stack, i)
table.insert(UIManager._window_stack, blocking_entry)
UIManager:setDirty(self.blocking_dialog, "ui")
break
end
end
end)
end
function PinPadDialog:reset()
self.pin = ""
self:setDialogText()
end
function PinPadDialog:closeDialogs()
if self.digit_display_timer then
self.digit_display_timer:stop()
self.digit_display_timer = nil
end
if self.blocking_dialog then
UIManager:close(self.blocking_dialog, "ui")
end
if self.dialog then
UIManager:close(self.dialog, "ui")
end
end
function PinPadDialog:close(callback_function)
self:reset()
if not callback_function then
self:closeDialogs()
end
if self.reused_screensaver then
ScreensaverUtil.forceClose(function()
if callback_function then
self:closeDialogs()
callback_function()
end
end)
else
if self.screensaver_widget then
ScreensaverUtil.forceClose(function()
if callback_function then
self:closeDialogs()
callback_function()
end
end)
UIManager:close(self.screensaver_widget)
self.screensaver_widget = nil
end
end
end
function PinPadDialog:onAppendToPin(digit)
if self.digit_display_timer then
self.digit_display_timer:stop()
self.digit_display_timer = nil
end
if self.dialog_text == LOCKED_TEXT or self.dialog_text == CONFIRM_CURRENT_CODE_TEXT or self.dialog_text == NEW_CODE_TEXT then
self.dialog_text = ""
end
self.pin = self.pin .. digit
if G_reader_settings:isTrue("pinpadlock_display_digit_activated") then
local temp_display_text = self.dialog_text .. digit
self.dialog:updateTitle(temp_display_text)
self.dialog_text = self.dialog_text .. "*"
self.digit_display_timer = UIManager:scheduleIn(1, function()
self.dialog:updateTitle(self.dialog_text)
self.digit_display_timer = nil
end)
else
self.dialog_text = self.dialog_text .. "*"
self.dialog:updateTitle(self.dialog_text)
end
end
function PinPadDialog:onOk()
if self.stage == "locked" then
if self.pin == G_reader_settings:readSetting("pinpadlock_pin_code") then
self:close()
if G_reader_settings:isTrue("pinpadlock_correct_pin_message_activated") then
UIManager:show(InfoMessage:new { text = _("Correct PIN, have fun !"), timeout = 2 })
end
G_reader_settings:saveSetting("current_tries_number", 0)
G_reader_settings:saveSetting("block_start_time", 0)
G_reader_settings:makeFalse("suspended_device")
else
if self.pin == "" then
return
end
local current_tries = G_reader_settings:readSetting("current_tries_number")
if (current_tries + 1) >= MAX_TRIES_LIMIT then
G_reader_settings:saveSetting("block_start_time", os.time())
self:showBlockingDialog(TIMEOUT_TIME)
else
G_reader_settings:saveSetting("current_tries_number", current_tries + 1)
UIManager:show(InfoMessage:new { text = _("Wrong PIN, try again."), timeout = 2 })
end
self:onCancel()
end
elseif self.stage == "confirm_current_code" then
if self.pin == G_reader_settings:readSetting("pinpadlock_pin_code") then
self:close()
UIManager:show(InfoMessage:new { text = _("Correct PIN, you may change your PIN Code !"), timeout = 2 })
local enterNewCode = PinPadDialog:new { stage = "enter_new_code" }
enterNewCode:showPinPad()
else
UIManager:show(InfoMessage:new { text = _("Wrong PIN, try again."), timeout = 2 })
self:onCancel()
end
else
G_reader_settings:saveSetting("pinpadlock_pin_code", self.pin)
self:close()
UIManager:show(InfoMessage:new { text = _("PIN Code changed successfully !"), timeout = 2 })
end
end
function PinPadDialog:onDelete()
if self.pin == "" then
return
elseif #self.pin == 1 then
self.pin = ""
else
self.pin = self.pin:sub(1, #self.pin - 1)
end
if #self.dialog_text == 1 then
self:setDialogText()
elseif not (self.dialog_text == LOCKED_TEXT or self.dialog_text == CONFIRM_CURRENT_CODE_TEXT or self.dialog_text == NEW_CODE_TEXT) then
self.dialog_text = self.dialog_text:sub(1, #self.dialog_text - 1)
end
self.dialog:updateTitle(self.dialog_text)
end
function PinPadDialog:onCancel()
if self.pin == "" then
return
end
self:reset()
self.dialog:updateTitle(self.dialog_text)
end
return PinPadDialog

View File

@@ -0,0 +1,53 @@
--[[
Author: Lena2309, oleasteo (original inspiration)
Inspiration and original skeleton from `https://github.com/oleasteo/koreader-screenlockpin`
Description: Utility to prevent screensaver from closing and manage z-order
]]
local Device = require("device")
local Screensaver = require("ui/screensaver")
local ScreensaverUtil = {}
local _original_setup = nil
local _original_close = nil
local _frozen = false
-- Prevent screensaver from closing
function ScreensaverUtil.freeze()
if _frozen then return end
Device.screen_saver_lock = false
Device.screen_saver_mode = false
if not _original_setup then
_original_setup = Screensaver.setup
end
_original_close = Screensaver.close
Screensaver.setup = function() end
Screensaver.close = function() end
_frozen = true
end
-- Restore screensaver normal behavior
function ScreensaverUtil.unfreeze(callback)
if callback and _original_setup then
Screensaver.setup = function()
callback()
_original_setup(Screensaver)
end
elseif _original_setup then
Screensaver.setup = _original_setup
_original_setup = nil
end
if _original_close then
Screensaver.close = _original_close
_original_close = nil
end
_frozen = false
end
function ScreensaverUtil.forceClose(callback)
ScreensaverUtil.unfreeze(callback)
Screensaver:close_widget()
end
return ScreensaverUtil