local BD = require("ui/bidi") local Blitbuffer = require("ffi/blitbuffer") local ButtonDialogTitle = require("ui/widget/buttondialogtitle") local BookStatusWidget = require("ui/widget/bookstatuswidget") local BottomContainer = require("ui/widget/container/bottomcontainer") local DataStorage = require("datastorage") local Device = require("device") local DocSettings = require("docsettings") local DocumentRegistry = require("document/documentregistry") local Font = require("ui/font") local Geom = require("ui/geometry") local InfoMessage = require("ui/widget/infomessage") local ImageWidget = require("ui/widget/imagewidget") local Math = require("optmath") local ScreenSaverWidget = require("ui/widget/screensaverwidget") local TextBoxWidget = require("ui/widget/textboxwidget") local TopContainer = require("ui/widget/container/topcontainer") local UIManager = require("ui/uimanager") local lfs = require("libs/libkoreader-lfs") local logger = require("logger") local _ = require("gettext") local Screen = Device.screen local T = require("ffi/util").template local screensaver_provider = { ["jpg"] = true, ["jpeg"] = true, ["png"] = true, ["gif"] = true, ["tif"] = true, ["tiff"] = true, } local default_screensaver_message = _("Sleeping") local Screensaver = {} local function getRandomImage(dir) if string.sub(dir, string.len(dir)) ~= "/" then dir = dir .. "/" end local pics = {} local i = 0 math.randomseed(os.time()) local ok, iter, dir_obj = pcall(lfs.dir, dir) if ok then for entry in iter, dir_obj do if lfs.attributes(dir .. entry, "mode") == "file" then local extension = string.lower(string.match(entry, ".+%.([^.]+)") or "") if screensaver_provider[extension] then i = i + 1 pics[i] = entry end end end if i == 0 then return nil end else return nil end return dir .. pics[math.random(i)] end function Screensaver:chooseFolder() local buttons = {} table.insert(buttons, { { text = _("Choose screensaver directory"), callback = function() UIManager:close(self.choose_dialog) require("ui/downloadmgr"):new{ onConfirm = function(path) logger.dbg("set screensaver directory to", path) G_reader_settings:saveSetting("screensaver_dir", path) UIManager:show(InfoMessage:new{ text = T(_("Screensaver directory set to:\n%1"), BD.dirpath(path)), timeout = 3, }) end, }:chooseDir() end, } }) table.insert(buttons, { { text = _("Close"), callback = function() UIManager:close(self.choose_dialog) end, } }) local screensaver_dir = G_reader_settings:readSetting("screensaver_dir") if screensaver_dir == nil then screensaver_dir = DataStorage:getDataDir() .. "/screenshots/" end self.choose_dialog = ButtonDialogTitle:new{ title = T(_("Current screensaver image directory:\n%1"), BD.dirpath(screensaver_dir)), buttons = buttons } UIManager:show(self.choose_dialog) end function Screensaver:chooseFile(document_cover) local text = document_cover and _("Choose document cover") or _("Choose screensaver image") local buttons = {} table.insert(buttons, { { text = text, callback = function() UIManager:close(self.choose_dialog) local util = require("util") local PathChooser = require("ui/widget/pathchooser") local path_chooser = PathChooser:new{ select_directory = false, select_file = true, file_filter = function(filename) local suffix = util.getFileNameSuffix(filename) if document_cover and DocumentRegistry:hasProvider(filename) then return true elseif screensaver_provider[suffix] then return true end end, detailed_file_info = true, path = self.root_path, onConfirm = function(file_path) if document_cover then G_reader_settings:saveSetting("screensaver_document_cover", file_path) UIManager:show(InfoMessage:new{ text = T(_("Screensaver document cover set to:\n%1"), BD.filepath(file_path)), timeout = 3, }) else G_reader_settings:saveSetting("screensaver_image", file_path) UIManager:show(InfoMessage:new{ text = T(_("Screensaver image set to:\n%1"), BD.filepath(file_path)), timeout = 3, }) end end } UIManager:show(path_chooser) end, } }) table.insert(buttons, { { text = _("Close"), callback = function() UIManager:close(self.choose_dialog) end, } }) local screensaver_image = G_reader_settings:readSetting("screensaver_image") local screensaver_document_cover = G_reader_settings:readSetting("screensaver_document_cover") if screensaver_image == nil then screensaver_image = DataStorage:getDataDir() .. "/resources/koreader.png" end local title = document_cover and T(_("Current screensaver document cover:\n%1"), BD.filepath(screensaver_document_cover)) or T(_("Current screensaver image:\n%1"), BD.filepath(screensaver_image)) self.choose_dialog = ButtonDialogTitle:new{ title = title, buttons = buttons } UIManager:show(self.choose_dialog) end function Screensaver:stretchImages() return G_reader_settings:isTrue("screensaver_stretch_images") end function Screensaver:whiteBackground() return G_reader_settings:isTrue("screensaver_white_background") end function Screensaver:noBackground() return G_reader_settings:isTrue("screensaver_no_background") end function Screensaver:excluded() local lastfile = G_reader_settings:readSetting("lastfile") local exclude_ss = false -- consider it not excluded if there's no docsetting if DocSettings:hasSidecarFile(lastfile) then local doc_settings = DocSettings:open(lastfile) exclude_ss = doc_settings:readSetting("exclude_screensaver") end return exclude_ss or false end function Screensaver:setMessage() local InputDialog = require("ui/widget/inputdialog") local screensaver_message = G_reader_settings:readSetting("screensaver_message") if screensaver_message == nil then screensaver_message = default_screensaver_message end self.input_dialog = InputDialog:new{ title = "Screensaver message", description = _("Enter the message to be displayed by the screensaver. The following escape sequences can be used:\n %p percentage read\n %c current page number\n %t total number of pages\n %T title"), input = screensaver_message, buttons = { { { text = _("Cancel"), callback = function() UIManager:close(self.input_dialog) end, }, { text = _("Set message"), is_enter_default = true, callback = function() G_reader_settings:saveSetting("screensaver_message", self.input_dialog:getInputText()) UIManager:close(self.input_dialog) end, }, }, }, } UIManager:show(self.input_dialog) self.input_dialog:onShowKeyboard() end function Screensaver:show(event, fallback_message) -- These 2 (optional) parameters are to support poweroff and reboot actions -- on Kobo (see uimanager.lua) if self.left_msg then UIManager:close(self.left_msg) self.left_msg = nil end local covers_fullscreen = true -- hint for UIManager:_repaint() local overlay_message local prefix = event and event.."_" or "" -- "", "poweroff_" or "reboot_" local screensaver_type = G_reader_settings:readSetting(prefix.."screensaver_type") if prefix and not screensaver_type then -- No manually added setting for poweroff/reboot, fallback to using the -- same settings as for suspend that could be set via menus screensaver_type = G_reader_settings:readSetting("screensaver_type") prefix = "" -- And display fallback_message over the common screensaver, -- so user can distinguish between suspend (no message) and -- poweroff (overlay message) overlay_message = fallback_message end if screensaver_type == nil then screensaver_type = "message" end if screensaver_type == "disable" then return end local widget = nil local background = Blitbuffer.COLOR_BLACK if self:whiteBackground() then background = Blitbuffer.COLOR_WHITE elseif self:noBackground() then background = nil end local lastfile = G_reader_settings:readSetting("lastfile") if screensaver_type == "document_cover" then -- Set lastfile to the document of which we want to show the cover. lastfile = G_reader_settings:readSetting("screensaver_document_cover") screensaver_type = "cover" end if screensaver_type == "cover" then lastfile = lastfile ~= nil and lastfile or G_reader_settings:readSetting("lastfile") local exclude = false -- consider it not excluded if there's no docsetting if DocSettings:hasSidecarFile(lastfile) then local doc_settings = DocSettings:open(lastfile) exclude = doc_settings:readSetting("exclude_screensaver") end if exclude ~= true then if lastfile and lfs.attributes(lastfile, "mode") == "file" then local doc = DocumentRegistry:openDocument(lastfile) if doc.loadDocument then -- CreDocument doc:loadDocument(false) -- load only metadata end local image = doc:getCoverPageImage() doc:close() if image ~= nil then widget = ImageWidget:new{ image = image, image_disposable = true, height = Screen:getHeight(), width = Screen:getWidth(), scale_factor = not self:stretchImages() and 0 or nil, } else screensaver_type = "random_image" end else screensaver_type = "random_image" end else --fallback to random images if this book cover is excluded screensaver_type = "random_image" end end if screensaver_type == "bookstatus" then if lastfile and lfs.attributes(lastfile, "mode") == "file" then local doc = DocumentRegistry:openDocument(lastfile) local doc_settings = DocSettings:open(lastfile) local instance = require("apps/reader/readerui"):_getRunningInstance() if instance ~= nil then widget = BookStatusWidget:new { thumbnail = doc:getCoverPageImage(), props = doc:getProps(), document = doc, settings = doc_settings, view = instance.view, readonly = true, } else screensaver_type = "message" end doc:close() else screensaver_type = "message" end end if screensaver_type == "random_image" then local screensaver_dir = G_reader_settings:readSetting(prefix.."screensaver_dir") if screensaver_dir == nil and prefix ~= "" then screensaver_dir = G_reader_settings:readSetting("screensaver_dir") end if screensaver_dir == nil then screensaver_dir = DataStorage:getDataDir() .. "/screenshots/" end local image_file = getRandomImage(screensaver_dir) if image_file == nil then screensaver_type = "message" else widget = ImageWidget:new{ file = image_file, file_do_cache = false, alpha = true, height = Screen:getHeight(), width = Screen:getWidth(), scale_factor = not self:stretchImages() and 0 or nil, } end end if screensaver_type == "image_file" then local screensaver_image = G_reader_settings:readSetting(prefix.."screensaver_image") if screensaver_image == nil and prefix ~= "" then screensaver_image = G_reader_settings:readSetting("screensaver_image") end if screensaver_image == nil then screensaver_image = DataStorage:getDataDir() .. "/resources/koreader.png" end if lfs.attributes(screensaver_image, "mode") ~= "file" then screensaver_type = "message" else widget = ImageWidget:new{ file = screensaver_image, file_do_cache = false, alpha = true, height = Screen:getHeight(), width = Screen:getWidth(), scale_factor = not self:stretchImages() and 0 or nil, } end end if screensaver_type == "readingprogress" then if Screensaver.getReaderProgress ~= nil then widget = Screensaver.getReaderProgress() else screensaver_type = "message" end end if screensaver_type == "message" then local screensaver_message = G_reader_settings:readSetting(prefix.."screensaver_message") local message_pos = G_reader_settings:readSetting(prefix.."screensaver_message_position") if not self:whiteBackground() then background = nil -- no background filling, let book text visible covers_fullscreen = false end if screensaver_message == nil and prefix ~= "" then screensaver_message = G_reader_settings:readSetting("screensaver_message") end local fallback = fallback_message or default_screensaver_message if screensaver_message == nil then screensaver_message = fallback else screensaver_message = self:expandSpecial(screensaver_message, fallback) end if message_pos == "middle" or message_pos == nil then widget = InfoMessage:new{ text = screensaver_message, readonly = true, } else local face = Font:getFace("infofont") local container if message_pos == "bottom" then container = BottomContainer else container = TopContainer end local screen_w, screen_h = Screen:getWidth(), Screen:getHeight() widget = container:new{ dimen = Geom:new{ w = screen_w, h = screen_h, }, TextBoxWidget:new{ text = screensaver_message, face = face, width = screen_w, alignment = "center", } } end -- No overlay needed as we just displayed the message overlay_message = nil end if overlay_message then widget = self:addOverlayMessage(widget, overlay_message) end if widget then self.left_msg = ScreenSaverWidget:new{ widget = widget, background = background, covers_fullscreen = covers_fullscreen, } self.left_msg.modal = true -- Refresh whole screen for other types self.left_msg.dithered = true UIManager:show(self.left_msg, "full") end end function Screensaver:expandSpecial(message, fallback) -- Expand special character sequences in given message. Use fallback string if there is no document instance -- %p percentage read -- %c current page -- %t total pages -- %T document title local ret = message local lastfile = G_reader_settings:readSetting("lastfile") local instance = require("apps/reader/readerui"):_getRunningInstance() if lastfile and lfs.attributes(lastfile, "mode") == "file" and instance ~= nil then local doc = DocumentRegistry:openDocument(lastfile) local currentpage = instance.view.state.page ret = string.gsub(ret, "%%c", currentpage) local totalpages = doc:getPageCount() ret = string.gsub(ret, "%%t", totalpages) local percent = Math.round((currentpage * 100) / totalpages) ret = string.gsub(ret, "%%p", percent) local props = doc:getProps() ret = string.gsub(ret, "%%T", props.title) doc:close() else ret = fallback end return ret end function Screensaver:close() if self.left_msg == nil then return end local screensaver_delay = G_reader_settings:readSetting("screensaver_delay") local screensaver_delay_number = tonumber(screensaver_delay) if screensaver_delay_number then UIManager:scheduleIn(screensaver_delay_number, function() logger.dbg("close screensaver") if self.left_msg then UIManager:close(self.left_msg, "full") self.left_msg = nil end end) elseif screensaver_delay == "disable" or screensaver_delay == nil then logger.dbg("close screensaver") if self.left_msg then UIManager:close(self.left_msg, "full") self.left_msg = nil end else logger.dbg("tap to exit from screensaver") end end function Screensaver:addOverlayMessage(widget, text) local FrameContainer = require("ui/widget/container/framecontainer") local OverlapGroup = require("ui/widget/overlapgroup") local RightContainer = require("ui/widget/container/rightcontainer") local Size = require("ui/size") local TextWidget = require("ui/widget/textwidget") local face = Font:getFace("infofont") local screen_w, screen_h = Screen:getWidth(), Screen:getHeight() local textw = TextWidget:new{ text = text, face = face, } -- Don't make our message reach full screen width if textw:getWidth() > screen_w * 0.9 then -- Text too wide: use TextBoxWidget for multi lines display textw = TextBoxWidget:new{ text = text, face = face, width = math.floor(screen_w * 0.9) } end textw = FrameContainer:new{ background = Blitbuffer.COLOR_WHITE, bordersize = Size.border.default, margin = 0, textw, } textw = RightContainer:new{ dimen = { w = screen_w, h = textw:getSize().h, }, textw, } widget = OverlapGroup:new{ dimen = { h = screen_w, w = screen_h, }, widget, textw, } return widget end return Screensaver