From b6f951e52351e3827ae1ff77dea26ae66fefd8c3 Mon Sep 17 00:00:00 2001 From: poire-z Date: Mon, 11 Jul 2022 13:50:28 +0200 Subject: [PATCH] Screensaver: add option to require a gesture after resume (#9316) --- frontend/dispatcher.lua | 2 + frontend/ui/elements/screensaver_menu.lua | 9 ++ frontend/ui/screensaver.lua | 5 ++ frontend/ui/widget/infomessage.lua | 33 +++---- frontend/ui/widget/screensaverwidget.lua | 101 ++++++++++++++++++++-- 5 files changed, 127 insertions(+), 23 deletions(-) diff --git a/frontend/dispatcher.lua b/frontend/dispatcher.lua index 937735625..c448e1c04 100644 --- a/frontend/dispatcher.lua +++ b/frontend/dispatcher.lua @@ -73,6 +73,7 @@ local settingsList = { toggle_wifi = {category="none", event="ToggleWifi", title=_("Toggle Wi-Fi"), device=true, condition=Device:hasWifiToggle()}, toggle_fullscreen = {category="none", event="ToggleFullscreen", title=_("Toggle Fullscreen"), device=true, condition=not Device:isAlwaysFullscreen()}, show_network_info = {category="none", event="ShowNetworkInfo", title=_("Show network info"), device=true, separator=true}, + exit_screensaver = {category="none", event="ExitScreensaver", title=_("Exit screensaver"), device=true}, suspend = {category="none", event="SuspendEvent", title=_("Suspend"), device=true}, exit = {category="none", event="Exit", title=_("Exit KOReader"), device=true}, restart = {category="none", event="Restart", title=_("Restart KOReader"), device=true, condition=Device:canRestart()}, @@ -221,6 +222,7 @@ local dispatcher_menu_order = { "show_menu", "screenshot", + "exit_screensaver", "suspend", "exit", "restart", diff --git a/frontend/ui/elements/screensaver_menu.lua b/frontend/ui/elements/screensaver_menu.lua index fb36e3ca1..e8a87ab22 100644 --- a/frontend/ui/elements/screensaver_menu.lua +++ b/frontend/ui/elements/screensaver_menu.lua @@ -295,6 +295,15 @@ return { G_reader_settings:saveSetting("screensaver_delay", "tap") end }, + { + text = _("Until 'Exit screensaver' gesture"), + checked_func = function() + return G_reader_settings:readSetting("screensaver_delay") == "gesture" + end, + callback = function() + G_reader_settings:saveSetting("screensaver_delay", "gesture") + end + }, }, }, }, diff --git a/frontend/ui/screensaver.lua b/frontend/ui/screensaver.lua index cee934237..3012e3c62 100644 --- a/frontend/ui/screensaver.lua +++ b/frontend/ui/screensaver.lua @@ -690,6 +690,7 @@ function Screensaver:show() message_widget = InfoMessage:new{ text = screensaver_message, readonly = true, + dismissable = false, } else local face = Font:getFace("infofont") @@ -777,6 +778,10 @@ function Screensaver:close() self.delayed_close = true elseif screensaver_delay == "disable" then self:close_widget() + elseif screensaver_delay == "gesture" then + if self.screensaver_widget then + self.screensaver_widget:showWaitForGestureMessage() + end else logger.dbg("tap to exit from screensaver") end diff --git a/frontend/ui/widget/infomessage.lua b/frontend/ui/widget/infomessage.lua index 5c5888a78..e307ed95b 100644 --- a/frontend/ui/widget/infomessage.lua +++ b/frontend/ui/widget/infomessage.lua @@ -60,6 +60,7 @@ local InfoMessage = InputContainer:new{ show_icon = true, icon = "notice-info", alpha = nil, -- if image or icon have an alpha channel (default to true for icons, false for images + dismissable = true, dismiss_callback = nil, -- Passed to TextBoxWidget alignment = "left", @@ -76,23 +77,25 @@ local InfoMessage = InputContainer:new{ } function InfoMessage:init() - if Device:hasKeys() then - self.key_events = { - AnyKeyPressed = { { Input.group.Any }, - seqtext = "any key", doc = "close dialog" } - } - 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(), + if self.dismissable then + if Device:hasKeys() then + self.key_events = { + AnyKeyPressed = { { Input.group.Any }, + seqtext = "any key", doc = "close dialog" } + } + 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 image_widget diff --git a/frontend/ui/widget/screensaverwidget.lua b/frontend/ui/widget/screensaverwidget.lua index 9079229a6..0d55a2ef7 100644 --- a/frontend/ui/widget/screensaverwidget.lua +++ b/frontend/ui/widget/screensaverwidget.lua @@ -3,8 +3,11 @@ local Event = require("ui/event") local Geom = require("ui/geometry") local GestureRange = require("ui/gesturerange") local FrameContainer = require("ui/widget/container/framecontainer") +local InfoMessage = require("ui/widget/infomessage") local InputContainer = require("ui/widget/container/inputcontainer") local UIManager = require("ui/uimanager") +local util = require("util") +local _ = require("gettext") local Screen = Device.screen local ScreenSaverWidget = InputContainer:new{ @@ -20,18 +23,100 @@ function ScreenSaverWidget:init() } end if Device:isTouchDevice() then - local range = Geom:new{ - x = 0, y = 0, - w = Screen:getWidth(), - h = Screen:getHeight(), - } - self.ges_events = { - Tap = { GestureRange:new{ ges = "tap", range = range } }, - } + self.ges_events = {} + if G_reader_settings:readSetting("screensaver_delay") == "gesture" then + self:setupGestureEvents() + end + if not self.has_exit_screensaver_gesture then + -- Exit with gesture not enabled, or no configured gesture found: allow exiting with tap + local range = Geom:new{ + x = 0, y = 0, + w = Screen:getWidth(), + h = Screen:getHeight(), + } + self.ges_events["Tap"] = { GestureRange:new{ ges = "tap", range = range } } + end end self:update() end +function ScreenSaverWidget:setupGestureEvents() + -- The configured gesture(s) won't trigger, because this widget is at top + -- of the UI stack and will prevent ReaderUI/Filemanager from getting + -- and handling any configured gesture event. + -- We need to find all the ones configured for the "exit_screensaver" action, + -- and clone them so they are handled by this widget. + local ReaderUI = require("apps/reader/readerui") + local ui = ReaderUI:_getRunningInstance() + if not ui then + local FileManager = require("apps/filemanager/filemanager") + ui = FileManager.instance + end + if ui and ui.gestures and ui.gestures.gestures then + local multiswipe_already_met = false + for gesture, actions in pairs(ui.gestures.gestures) do + if util.stringStartsWith(gesture, "multiswipe") then + -- All multiswipes are handled by the single handler for "multiswipe" + -- We only need to clone one of them + gesture = "multiswipe" + end + if actions["exit_screensaver"] and (gesture ~= "multiswipe" or not multiswipe_already_met) then + if gesture == "multiswipe" then + multiswipe_already_met = true + end + -- Clone the gesture found in our self.ges_events + local ui_gesture = ui._zones[gesture] + if ui_gesture and ui_gesture.handler then + -- We can reuse its GestureRange object + self.ges_events[gesture] = { ui_gesture.gs_range } + -- For each of them, we need a distinct event and its handler. + -- This handler will call the original handler (created by gestures.koplugin) + -- which, after some checks (like swipe distance and direction, and multiswipe + -- directions), will emit normally the configured real ExitScreensaver event, + -- that this widget (being at top of the UI stack) will get and that + -- onExitScreensaver() will handle. + local event_name = "TriggerExitScreensaver_" .. gesture + self.ges_events[gesture].event = event_name + self["on"..event_name] = function(this, args, ev) + ui_gesture.handler(ev) + return true + end + end + end + end + end + if next(self.ges_events) then -- we found a gesture configured + self.has_exit_screensaver_gesture = true + -- Override handleEvent(), so we can stop any event from propagating to widgets + -- below this one (we may get some from other multiswipe as the handler does + -- not filter the one we are insterested with, but also when multiple actions + -- are assigned to a single gesture). + self.handleEvent = function(this, event) + InputContainer.handleEvent(this, event) + return true + end + self.key_events = nil -- also disable exit with keys + end +end + +function ScreenSaverWidget:showWaitForGestureMessage(msg) + -- We just paint an InfoMessage on screen directly: we don't want + -- another widget that we would need to prevent catching events + local infomsg = InfoMessage:new{ + text = self.has_exit_screensaver_gesture + and _("Waiting for specific gesture to exit screensaver.") + or _("No exit screensaver gesture configured. Tap to exit.") + } + infomsg:paintTo(Screen.bb, 0, 0) + infomsg:onShow() -- get the screen refreshed + infomsg:free() +end + +function ScreenSaverWidget:onExitScreensaver() + self:onClose() + return true +end + function ScreenSaverWidget:update() self.height = Screen:getHeight() self.width = Screen:getWidth()