mirror of
https://github.com/koreader/koreader.git
synced 2025-08-10 00:52:38 +00:00
If there are hidden flows, this calculates the current page, total pages, percent, time left in chapter, and time left in the magic variables used in screensaver message only for the current flow. If there are no hidden flows, the behavior is the same as before.
870 lines
33 KiB
Lua
870 lines
33 KiB
Lua
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 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 OverlapGroup = require("ui/widget/overlapgroup")
|
|
local ScreenSaverWidget = require("ui/widget/screensaverwidget")
|
|
local SpinWidget = require("ui/widget/spinwidget")
|
|
local TextBoxWidget = require("ui/widget/textboxwidget")
|
|
local TopContainer = require("ui/widget/container/topcontainer")
|
|
local UIManager = require("ui/uimanager")
|
|
local VerticalGroup = require("ui/widget/verticalgroup")
|
|
local VerticalSpan = require("ui/widget/verticalspan")
|
|
local datetime = require("datetime")
|
|
local ffiUtil = require("ffi/util")
|
|
local lfs = require("libs/libkoreader-lfs")
|
|
local logger = require("logger")
|
|
local util = require("util")
|
|
local _ = require("gettext")
|
|
local Screen = Device.screen
|
|
local T = ffiUtil.template
|
|
|
|
-- Default settings
|
|
if G_reader_settings:hasNot("screensaver_show_message") then
|
|
G_reader_settings:makeFalse("screensaver_show_message")
|
|
end
|
|
if G_reader_settings:hasNot("screensaver_type") then
|
|
G_reader_settings:saveSetting("screensaver_type", "disable")
|
|
G_reader_settings:makeTrue("screensaver_show_message")
|
|
end
|
|
if G_reader_settings:hasNot("screensaver_img_background") then
|
|
G_reader_settings:saveSetting("screensaver_img_background", "black")
|
|
end
|
|
if G_reader_settings:hasNot("screensaver_msg_background") then
|
|
G_reader_settings:saveSetting("screensaver_msg_background", "none")
|
|
end
|
|
if G_reader_settings:hasNot("screensaver_message_position") then
|
|
G_reader_settings:saveSetting("screensaver_message_position", "middle")
|
|
end
|
|
if G_reader_settings:hasNot("screensaver_stretch_images") then
|
|
G_reader_settings:makeFalse("screensaver_stretch_images")
|
|
end
|
|
if G_reader_settings:hasNot("screensaver_delay") then
|
|
G_reader_settings:saveSetting("screensaver_delay", "disable")
|
|
end
|
|
if G_reader_settings:hasNot("screensaver_hide_fallback_msg") then
|
|
G_reader_settings:makeFalse("screensaver_hide_fallback_msg")
|
|
end
|
|
|
|
local Screensaver = {
|
|
screensaver_provider = {
|
|
gif = true,
|
|
jpg = true,
|
|
jpeg = true,
|
|
png = true,
|
|
svg = true,
|
|
tif = true,
|
|
tiff = true,
|
|
webp = true,
|
|
},
|
|
default_screensaver_message = _("Sleeping"),
|
|
|
|
-- State values
|
|
show_message = nil,
|
|
screensaver_type = nil,
|
|
prefix = nil,
|
|
event_message = nil,
|
|
overlay_message = nil,
|
|
screensaver_background = nil,
|
|
image = nil,
|
|
image_file = nil,
|
|
delayed_close = nil,
|
|
screensaver_widget = nil,
|
|
}
|
|
|
|
-- Remind emulator users that Power is bound to F2
|
|
if Device:isEmulator() then
|
|
Screensaver.default_screensaver_message = Screensaver.default_screensaver_message .. "\n" .. _("(Press F2 to resume)")
|
|
end
|
|
|
|
function Screensaver:_getRandomImage(dir)
|
|
if not dir then
|
|
return nil
|
|
end
|
|
|
|
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 f in iter, dir_obj do
|
|
-- Always ignore macOS resource forks, too.
|
|
if lfs.attributes(dir .. f, "mode") == "file" and not util.stringStartsWith(f, "._") then
|
|
local extension = string.lower(string.match(f, ".+%.([^.]+)") or "")
|
|
if self.screensaver_provider[extension] then
|
|
i = i + 1
|
|
pics[i] = f
|
|
end
|
|
end
|
|
end
|
|
if i == 0 then
|
|
return nil
|
|
end
|
|
else
|
|
return nil
|
|
end
|
|
|
|
return dir .. pics[math.random(i)]
|
|
end
|
|
|
|
-- This is implemented by the Statistics plugin
|
|
function Screensaver:getAvgTimePerPage()
|
|
return
|
|
end
|
|
|
|
function Screensaver:_calcAverageTimeForPages(pages)
|
|
local sec = _("N/A")
|
|
local average_time_per_page = self:getAvgTimePerPage()
|
|
|
|
-- Compare average_time_per_page against itself to make sure it's not nan
|
|
if average_time_per_page and average_time_per_page == average_time_per_page and pages then
|
|
local user_duration_format = G_reader_settings:readSetting("duration_format", "classic")
|
|
sec = datetime.secondsToClockDuration(user_duration_format, pages * average_time_per_page, true)
|
|
end
|
|
return sec
|
|
end
|
|
|
|
function Screensaver:expandSpecial(message, fallback)
|
|
-- Expand special character sequences in given message.
|
|
-- %p percentage read
|
|
-- %c current page
|
|
-- %t total pages
|
|
-- %T document title
|
|
-- %A document authors
|
|
-- %S document series
|
|
-- %h time left in chapter
|
|
-- %H time left in document
|
|
-- %b battery level
|
|
|
|
if G_reader_settings:hasNot("lastfile") then
|
|
return fallback
|
|
end
|
|
|
|
local ret = message
|
|
local lastfile = G_reader_settings:readSetting("lastfile")
|
|
|
|
local totalpages = 0
|
|
local percent = 0
|
|
local currentpage = 0
|
|
local title = _("N/A")
|
|
local authors = _("N/A")
|
|
local series = _("N/A")
|
|
local time_left_chapter = _("N/A")
|
|
local time_left_document = _("N/A")
|
|
local batt_lvl = _("N/A")
|
|
|
|
local ReaderUI = require("apps/reader/readerui")
|
|
local ui = ReaderUI:_getRunningInstance()
|
|
if ui and ui.document then
|
|
-- If we have a ReaderUI instance, use it.
|
|
local doc = ui.document
|
|
if doc:hasHiddenFlows() then
|
|
local currentpageAll = ui.view.state.page or currentpage
|
|
currentpage = doc:getPageNumberInFlow(ui.view.state.page or currentpageAll)
|
|
totalpages = doc:getTotalPagesInFlow(doc:getPageFlow(currentpageAll))
|
|
time_left_chapter = self:_calcAverageTimeForPages(ui.toc:getChapterPagesLeft(currentpageAll) or (totalpages - currentpage))
|
|
time_left_document = self:_calcAverageTimeForPages(totalpages - currentpage)
|
|
else
|
|
currentpage = ui.view.state.page or currentpage
|
|
totalpages = doc:getPageCount() or totalpages
|
|
time_left_chapter = self:_calcAverageTimeForPages(ui.toc:getChapterPagesLeft(currentpage) or doc:getTotalPagesLeft(currentpage))
|
|
time_left_document = self:_calcAverageTimeForPages(doc:getTotalPagesLeft(currentpage))
|
|
end
|
|
percent = Math.round((currentpage * 100) / totalpages)
|
|
local props = doc:getProps()
|
|
if props then
|
|
title = props.title and props.title ~= "" and props.title or title
|
|
authors = props.authors and props.authors ~= "" and props.authors or authors
|
|
series = props.series and props.series ~= "" and props.series or series
|
|
end
|
|
elseif DocSettings:hasSidecarFile(lastfile) then
|
|
-- If there's no ReaderUI instance, but the file has sidecar data, use that
|
|
local docinfo = DocSettings:open(lastfile)
|
|
totalpages = docinfo.data.doc_pages or totalpages
|
|
percent = docinfo.data.percent_finished or percent
|
|
currentpage = Math.round(percent * totalpages)
|
|
percent = Math.round(percent * 100)
|
|
if docinfo.data.doc_props then
|
|
title = docinfo.data.doc_props.title and docinfo.data.doc_props.title ~= "" and docinfo.data.doc_props.title or title
|
|
authors = docinfo.data.doc_props.authors and docinfo.data.doc_props.authors ~= "" and docinfo.data.doc_props.authors or authors
|
|
series = docinfo.data.doc_props.series and docinfo.data.doc_props.series ~= "" and docinfo.data.doc_props.series or series
|
|
end
|
|
-- Unable to set time_left_chapter and time_left_document without ReaderUI, so leave N/A
|
|
end
|
|
if Device:hasBattery() then
|
|
local powerd = Device:getPowerDevice()
|
|
if Device:hasAuxBattery() and powerd:isAuxBatteryConnected() then
|
|
batt_lvl = powerd:getCapacity() + powerd:getAuxCapacity()
|
|
else
|
|
batt_lvl = powerd:getCapacity()
|
|
end
|
|
end
|
|
|
|
local replace = {
|
|
["%c"] = currentpage,
|
|
["%t"] = totalpages,
|
|
["%p"] = percent,
|
|
["%T"] = title,
|
|
["%A"] = authors,
|
|
["%S"] = series,
|
|
["%h"] = time_left_chapter,
|
|
["%H"] = time_left_document,
|
|
["%b"] = batt_lvl,
|
|
}
|
|
ret = ret:gsub("(%%%a)", replace)
|
|
|
|
return ret
|
|
end
|
|
|
|
local function addOverlayMessage(widget, widget_height, text)
|
|
local FrameContainer = require("ui/widget/container/framecontainer")
|
|
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,
|
|
}
|
|
-- If our host widget is already at the top, we'll position ourselves below it.
|
|
if widget_height then
|
|
textw = VerticalGroup:new{
|
|
VerticalSpan:new{
|
|
width = widget_height,
|
|
},
|
|
textw,
|
|
}
|
|
end
|
|
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
|
|
|
|
function Screensaver:chooseFolder()
|
|
local buttons = {}
|
|
local choose_dialog
|
|
table.insert(buttons, {
|
|
{
|
|
text = _("Choose screensaver folder"),
|
|
callback = function()
|
|
UIManager:close(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 folder set to:\n%1"), BD.dirpath(path)),
|
|
timeout = 3,
|
|
})
|
|
end,
|
|
}:chooseDir()
|
|
end,
|
|
}
|
|
})
|
|
table.insert(buttons, {
|
|
{
|
|
text = _("Close"),
|
|
callback = function()
|
|
UIManager:close(choose_dialog)
|
|
end,
|
|
}
|
|
})
|
|
local screensaver_dir = G_reader_settings:readSetting("screensaver_dir")
|
|
or _("N/A")
|
|
choose_dialog = ButtonDialogTitle:new{
|
|
title = T(_("Current screensaver image folder:\n%1"), BD.dirpath(screensaver_dir)),
|
|
buttons = buttons
|
|
}
|
|
UIManager:show(choose_dialog)
|
|
end
|
|
|
|
function Screensaver:chooseFile(document_cover)
|
|
local text = document_cover and _("Choose document cover") or _("Choose screensaver image")
|
|
local buttons = {}
|
|
local choose_dialog
|
|
table.insert(buttons, {
|
|
{
|
|
text = text,
|
|
callback = function()
|
|
UIManager:close(choose_dialog)
|
|
local PathChooser = require("ui/widget/pathchooser")
|
|
local path_chooser = PathChooser:new{
|
|
select_directory = false,
|
|
file_filter = function(filename)
|
|
local suffix = util.getFileNameSuffix(filename)
|
|
if document_cover and DocumentRegistry:hasProvider(filename) then
|
|
return true
|
|
elseif self.screensaver_provider[suffix] then
|
|
return true
|
|
end
|
|
end,
|
|
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(choose_dialog)
|
|
end,
|
|
}
|
|
})
|
|
local screensaver_image = G_reader_settings:readSetting("screensaver_image")
|
|
or _("N/A")
|
|
local screensaver_document_cover = G_reader_settings:readSetting("screensaver_document_cover")
|
|
or _("N/A")
|
|
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))
|
|
choose_dialog = ButtonDialogTitle:new{
|
|
title = title,
|
|
buttons = buttons
|
|
}
|
|
UIManager:show(choose_dialog)
|
|
end
|
|
|
|
function Screensaver:isExcluded()
|
|
local ReaderUI = require("apps/reader/readerui")
|
|
local ui = ReaderUI:_getRunningInstance()
|
|
if ui and ui.doc_settings then
|
|
local doc_settings = ui.doc_settings
|
|
return doc_settings:isTrue("exclude_screensaver")
|
|
else
|
|
if G_reader_settings:hasNot("lastfile") then
|
|
return false
|
|
end
|
|
|
|
local lastfile = G_reader_settings:readSetting("lastfile")
|
|
if DocSettings:hasSidecarFile(lastfile) then
|
|
local doc_settings = DocSettings:open(lastfile)
|
|
return doc_settings:isTrue("exclude_screensaver")
|
|
else
|
|
-- No DocSetting, not excluded
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
function Screensaver:setMessage()
|
|
local InputDialog = require("ui/widget/inputdialog")
|
|
local screensaver_message = G_reader_settings:readSetting("screensaver_message")
|
|
or self.default_screensaver_message
|
|
local input_dialog
|
|
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\n %A authors\n %S series\n %h time left in chapter\n %H time left in document\n %b battery level"),
|
|
input = screensaver_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("screensaver_message", input_dialog:getInputText())
|
|
UIManager:close(input_dialog)
|
|
end,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
UIManager:show(input_dialog)
|
|
input_dialog:onShowKeyboard()
|
|
end
|
|
|
|
function Screensaver:setStretchLimit(touchmenu_instance)
|
|
UIManager:show(SpinWidget:new{
|
|
value = G_reader_settings:readSetting("screensaver_stretch_limit_percentage", 8),
|
|
value_min = 0,
|
|
value_max = 25,
|
|
default_value = 8,
|
|
unit = "%",
|
|
title_text = _("Set maximum stretch limit"),
|
|
ok_text = _("Set"),
|
|
ok_always_enabled = true,
|
|
callback = function(spin)
|
|
G_reader_settings:saveSetting("screensaver_stretch_limit_percentage", spin.value)
|
|
G_reader_settings:makeTrue("screensaver_stretch_images")
|
|
if touchmenu_instance then touchmenu_instance:updateItems() end
|
|
end,
|
|
extra_text = _("Disable stretch"),
|
|
extra_callback = function()
|
|
G_reader_settings:makeFalse("screensaver_stretch_images")
|
|
if touchmenu_instance then touchmenu_instance:updateItems() end
|
|
end,
|
|
option_text = _("Full stretch"),
|
|
option_callback = function()
|
|
G_reader_settings:makeTrue("screensaver_stretch_images")
|
|
G_reader_settings:delSetting("screensaver_stretch_limit_percentage")
|
|
if touchmenu_instance then touchmenu_instance:updateItems() end
|
|
end,
|
|
})
|
|
end
|
|
|
|
-- When called after setup(), may not match the saved settings, because it accounts for fallbacks that might have kicked in.
|
|
function Screensaver:getMode()
|
|
return self.screensaver_type
|
|
end
|
|
|
|
function Screensaver:modeExpectsPortrait()
|
|
return self.screensaver_type ~= "message"
|
|
and self.screensaver_type ~= "disable"
|
|
and self.screensaver_type ~= "readingprogress"
|
|
and self.screensaver_type ~= "bookstatus"
|
|
end
|
|
|
|
function Screensaver:modeIsImage()
|
|
return self.screensaver_type == "cover"
|
|
or self.screensaver_type == "random_image"
|
|
or self.screensaver_type == "image_file"
|
|
end
|
|
|
|
function Screensaver:withBackground()
|
|
return self.screensaver_background ~= "none"
|
|
end
|
|
|
|
function Screensaver:setup(event, event_message)
|
|
self.show_message = G_reader_settings:isTrue("screensaver_show_message")
|
|
self.screensaver_type = G_reader_settings:readSetting("screensaver_type")
|
|
local screensaver_img_background = G_reader_settings:readSetting("screensaver_img_background")
|
|
local screensaver_msg_background = G_reader_settings:readSetting("screensaver_msg_background")
|
|
|
|
-- These 2 (optional) parameters are to support poweroff and reboot actions on Kobo (c.f., UIManager)
|
|
self.prefix = event and event .. "_" or "" -- "", "poweroff_" or "reboot_"
|
|
self.event_message = event_message
|
|
if G_reader_settings:has(self.prefix .. "screensaver_type") then
|
|
self.screensaver_type = G_reader_settings:readSetting(self.prefix .. "screensaver_type")
|
|
else
|
|
if event and G_reader_settings:isFalse("screensaver_hide_fallback_msg") then
|
|
-- Display the provided event_message over the screensaver,
|
|
-- so the user can distinguish between suspend (no overlay),
|
|
-- and reboot/poweroff (overlaid message).
|
|
self.overlay_message = self.event_message
|
|
end
|
|
end
|
|
|
|
-- Check lastfile and setup the requested mode's resources, or a fallback mode if the required resources are unavailable.
|
|
local ReaderUI = require("apps/reader/readerui")
|
|
local ui = ReaderUI:_getRunningInstance()
|
|
local lastfile = G_reader_settings:readSetting("lastfile")
|
|
if self.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")
|
|
self.screensaver_type = "cover"
|
|
end
|
|
if self.screensaver_type == "cover" then
|
|
lastfile = lastfile ~= nil and lastfile or G_reader_settings:readSetting("lastfile")
|
|
local excluded
|
|
if DocSettings:hasSidecarFile(lastfile) then
|
|
local doc_settings
|
|
if ui and ui.doc_settings then
|
|
doc_settings = ui.doc_settings
|
|
else
|
|
doc_settings = DocSettings:open(lastfile)
|
|
end
|
|
excluded = doc_settings:isTrue("exclude_screensaver")
|
|
else
|
|
-- No DocSetting, not excluded
|
|
excluded = false
|
|
end
|
|
if not excluded then
|
|
if lastfile and lfs.attributes(lastfile, "mode") == "file" then
|
|
if ui and ui.document then
|
|
local doc = ui.document
|
|
self.image = doc:getCoverPageImage()
|
|
else
|
|
local doc = DocumentRegistry:openDocument(lastfile)
|
|
if doc.loadDocument then -- CreDocument
|
|
doc:loadDocument(false) -- load only metadata
|
|
end
|
|
self.image = doc:getCoverPageImage()
|
|
doc:close()
|
|
end
|
|
if self.image == nil then
|
|
self.screensaver_type = "random_image"
|
|
end
|
|
else
|
|
self.screensaver_type = "random_image"
|
|
end
|
|
else
|
|
-- Fallback to random images if this book cover is excluded
|
|
self.screensaver_type = "random_image"
|
|
end
|
|
end
|
|
if self.screensaver_type == "bookstatus" then
|
|
if lastfile and lfs.attributes(lastfile, "mode") == "file" then
|
|
if not ui then
|
|
self.screensaver_type = "disable"
|
|
self.show_message = true
|
|
end
|
|
else
|
|
self.screensaver_type = "disable"
|
|
self.show_message = true
|
|
end
|
|
end
|
|
if self.screensaver_type == "random_image" then
|
|
local screensaver_dir = G_reader_settings:readSetting(self.prefix .. "screensaver_dir")
|
|
or G_reader_settings:readSetting("screensaver_dir")
|
|
self.image_file = self:_getRandomImage(screensaver_dir)
|
|
if self.image_file == nil then
|
|
self.screensaver_type = "disable"
|
|
self.show_message = true
|
|
end
|
|
end
|
|
if self.screensaver_type == "image_file" then
|
|
self.image_file = G_reader_settings:readSetting(self.prefix .. "screensaver_image")
|
|
or G_reader_settings:readSetting("screensaver_image")
|
|
if self.image_file == nil or lfs.attributes(self.image_file, "mode") ~= "file" then
|
|
self.screensaver_type = "disable"
|
|
self.show_message = true
|
|
end
|
|
end
|
|
if self.screensaver_type == "readingprogress" then
|
|
-- This is implemented by the Statistics plugin
|
|
if Screensaver.getReaderProgress == nil then
|
|
self.screensaver_type = "disable"
|
|
self.show_message = true
|
|
end
|
|
end
|
|
|
|
-- Use the right background setting depending on the effective mode, now that fallbacks have kicked in.
|
|
if self:modeIsImage() then
|
|
self.screensaver_background = screensaver_img_background
|
|
else
|
|
self.screensaver_background = screensaver_msg_background
|
|
end
|
|
end
|
|
|
|
function Screensaver:show()
|
|
-- This should never happen...
|
|
if self.screensaver_widget then
|
|
UIManager:close(self.screensaver_widget)
|
|
self.screensaver_widget = nil
|
|
end
|
|
-- Notify Device methods that we're in screen saver mode, so they know whether to suspend or resume on Power events.
|
|
Device.screen_saver_mode = true
|
|
|
|
-- In as-is mode with no message and no overlay, we've got nothing to show :)
|
|
if self.screensaver_type == "disable" and not self.show_message and not self.overlay_message then
|
|
return
|
|
end
|
|
|
|
-- We mostly always suspend in Portrait/Inverted Portrait mode...
|
|
-- ... except when we just show an InfoMessage or when the screensaver
|
|
-- is disabled, as it plays badly with Landscape mode (c.f., #4098 and #5290).
|
|
-- We also exclude full-screen widgets that work fine in Landscape mode,
|
|
-- like ReadingProgress and BookStatus (c.f., #5724)
|
|
if self:modeExpectsPortrait() then
|
|
Device.orig_rotation_mode = Screen:getRotationMode()
|
|
-- Leave Portrait & Inverted Portrait alone, that works just fine.
|
|
if bit.band(Device.orig_rotation_mode, 1) == 1 then
|
|
-- i.e., only switch to Portrait if we're currently in *any* Landscape orientation (odd number)
|
|
Screen:setRotationMode(Screen.ORIENTATION_PORTRAIT)
|
|
else
|
|
Device.orig_rotation_mode = nil
|
|
end
|
|
|
|
-- On eInk, if we're using a screensaver mode that shows an image,
|
|
-- flash the screen to white first, to eliminate ghosting.
|
|
if Device:hasEinkScreen() and self:modeIsImage() then
|
|
if self:withBackground() then
|
|
Screen:clear()
|
|
end
|
|
Screen:refreshFull()
|
|
|
|
-- On Kobo, on sunxi SoCs with a recent kernel, wait a tiny bit more to avoid weird refresh glitches...
|
|
if Device:isKobo() and Device:isSunxi() then
|
|
ffiUtil.usleep(150 * 1000)
|
|
end
|
|
end
|
|
else
|
|
-- nil it, in case user switched ScreenSaver modes during our lifetime.
|
|
Device.orig_rotation_mode = nil
|
|
end
|
|
|
|
-- Build the main widget for the effective mode, all the sanity checks were handled in setup
|
|
local widget = nil
|
|
if self.screensaver_type == "cover" then
|
|
widget = ImageWidget:new{
|
|
image = self.image,
|
|
image_disposable = true,
|
|
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"),
|
|
}
|
|
elseif self.screensaver_type == "bookstatus" then
|
|
local ReaderUI = require("apps/reader/readerui")
|
|
local ui = ReaderUI:_getRunningInstance()
|
|
local doc = ui.document
|
|
local doc_settings = ui.doc_settings
|
|
widget = BookStatusWidget:new{
|
|
thumbnail = doc:getCoverPageImage(),
|
|
props = doc:getProps(),
|
|
document = doc,
|
|
settings = doc_settings,
|
|
ui = ui,
|
|
readonly = true,
|
|
}
|
|
elseif self.screensaver_type == "random_image" or self.screensaver_type == "image_file" then
|
|
widget = ImageWidget:new{
|
|
file = self.image_file,
|
|
file_do_cache = false,
|
|
alpha = true,
|
|
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"),
|
|
}
|
|
elseif self.screensaver_type == "readingprogress" then
|
|
widget = Screensaver.getReaderProgress()
|
|
end
|
|
|
|
-- Assume that we'll be covering the full-screen by default (either because of a widget, or a background fill).
|
|
local covers_fullscreen = true
|
|
-- Speaking of, set that background fill up...
|
|
local background
|
|
if self.screensaver_background == "black" then
|
|
background = Blitbuffer.COLOR_BLACK
|
|
elseif self.screensaver_background == "white" then
|
|
background = Blitbuffer.COLOR_WHITE
|
|
elseif self.screensaver_background == "none" then
|
|
background = nil
|
|
end
|
|
|
|
local message_height
|
|
if self.show_message then
|
|
-- Handle user settings & fallbacks, with that prefix mess on top...
|
|
local screensaver_message
|
|
if G_reader_settings:has(self.prefix .. "screensaver_message") then
|
|
screensaver_message = G_reader_settings:readSetting(self.prefix .. "screensaver_message")
|
|
else
|
|
if G_reader_settings:has("screensaver_message") then
|
|
screensaver_message = G_reader_settings:readSetting("screensaver_message")
|
|
else
|
|
-- In the absence of a custom message, use the event message if any, barring that, use the default message.
|
|
if self.event_message then
|
|
screensaver_message = self.event_message
|
|
-- The overlay is only ever populated with the event message, and we only want to show it once ;).
|
|
self.overlay_message = nil
|
|
else
|
|
screensaver_message = self.default_screensaver_message
|
|
end
|
|
end
|
|
end
|
|
-- NOTE: Only attempt to expand if there are special characters in the message.
|
|
if screensaver_message:find("%%") then
|
|
screensaver_message = self:expandSpecial(screensaver_message, self.event_message or self.default_screensaver_message)
|
|
end
|
|
|
|
local message_pos
|
|
if G_reader_settings:has(self.prefix .. "screensaver_message_position") then
|
|
message_pos = G_reader_settings:readSetting(self.prefix .. "screensaver_message_position")
|
|
else
|
|
message_pos = G_reader_settings:readSetting("screensaver_message_position")
|
|
end
|
|
|
|
-- The only case where we *won't* cover the full-screen is when we only display a message and no background.
|
|
if widget == nil and self.screensaver_background == "none" then
|
|
covers_fullscreen = false
|
|
end
|
|
|
|
local message_widget
|
|
if message_pos == "middle" then
|
|
message_widget = InfoMessage:new{
|
|
text = screensaver_message,
|
|
readonly = true,
|
|
dismissable = false,
|
|
}
|
|
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()
|
|
message_widget = container:new{
|
|
dimen = Geom:new{
|
|
w = screen_w,
|
|
h = screen_h,
|
|
},
|
|
TextBoxWidget:new{
|
|
text = screensaver_message,
|
|
face = face,
|
|
width = screen_w,
|
|
alignment = "center",
|
|
}
|
|
}
|
|
|
|
-- Forward the height of the top message to the overlay widget
|
|
if message_pos == "top" then
|
|
message_height = message_widget[1]:getSize().h
|
|
end
|
|
end
|
|
|
|
-- Check if message_widget should be overlaid on another widget
|
|
if message_widget then
|
|
if widget then -- We have a Screensaver widget
|
|
-- Show message_widget on top of previously created widget
|
|
widget = OverlapGroup:new{
|
|
dimen = {
|
|
w = Screen:getWidth(),
|
|
h = Screen:getHeight(),
|
|
},
|
|
widget,
|
|
message_widget,
|
|
}
|
|
else
|
|
-- No prevously created widget, so just show message widget
|
|
widget = message_widget
|
|
end
|
|
end
|
|
end
|
|
|
|
if self.overlay_message then
|
|
widget = addOverlayMessage(widget, message_height, self.overlay_message)
|
|
end
|
|
|
|
if widget then
|
|
self.screensaver_widget = ScreenSaverWidget:new{
|
|
widget = widget,
|
|
background = background,
|
|
covers_fullscreen = covers_fullscreen,
|
|
}
|
|
self.screensaver_widget.modal = true
|
|
self.screensaver_widget.dithered = true
|
|
|
|
-- NOTE: ScreenSaver itself is not a widget, so make sure we cleanup behind us...
|
|
self.screensaver_widget.onCloseWidget = function(this)
|
|
-- this is self.screensaver_widget (i.e., an object instantiated from ScreenSaverWidget)
|
|
local super = getmetatable(this)
|
|
-- super is the class object of self.screensaver_widget (i.e., ScreenSaverWidget)
|
|
if super.onCloseWidget then
|
|
super.onCloseWidget(this)
|
|
end
|
|
-- self is ScreenSaver (upvalue)
|
|
self:cleanup()
|
|
end
|
|
|
|
UIManager:show(self.screensaver_widget, "full")
|
|
end
|
|
end
|
|
|
|
function Screensaver:close_widget()
|
|
if self.screensaver_widget then
|
|
UIManager:close(self.screensaver_widget)
|
|
end
|
|
end
|
|
|
|
function Screensaver:close()
|
|
if self.screensaver_widget == nil then
|
|
-- When we *do* have a widget, this is handled by ScreenSaverWidget:onCloseWidget ;).
|
|
Device.screen_saver_mode = false
|
|
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, self.close_widget, self)
|
|
self.delayed_close = true
|
|
elseif screensaver_delay == "disable" then
|
|
self:close_widget()
|
|
-- NOTE: Notify platforms that race with the native system (e.g., Kindle or needsScreenRefreshAfterResume)
|
|
-- that we've actually closed the widget *right now*.
|
|
return true
|
|
elseif screensaver_delay == "gesture" then
|
|
if self.screensaver_widget then
|
|
self.screensaver_widget:showWaitForGestureMessage()
|
|
end
|
|
else
|
|
logger.dbg("tap to exit from screensaver")
|
|
end
|
|
end
|
|
|
|
function Screensaver:cleanup()
|
|
self.show_message = nil
|
|
self.screensaver_type = nil
|
|
self.prefix = nil
|
|
self.event_message = nil
|
|
self.overlay_message = nil
|
|
self.screensaver_background = nil
|
|
|
|
self.image = nil
|
|
self.image_file = nil
|
|
|
|
self.delayed_close = nil
|
|
self.screensaver_widget = nil
|
|
end
|
|
|
|
return Screensaver
|