2020-01-04 00:18:51 +00:00
local BD = require ( " ui/bidi " )
2017-12-17 17:27:24 +00:00
local Blitbuffer = require ( " ffi/blitbuffer " )
local ButtonDialogTitle = require ( " ui/widget/buttondialogtitle " )
local BookStatusWidget = require ( " ui/widget/bookstatuswidget " )
2020-02-22 08:07:28 +00:00
local BottomContainer = require ( " ui/widget/container/bottomcontainer " )
2016-04-18 05:30:46 +00:00
local Device = require ( " device " )
2014-09-17 10:24:33 +00:00
local DocSettings = require ( " docsettings " )
2017-09-05 07:01:46 +00:00
local DocumentRegistry = require ( " document/documentregistry " )
2020-02-22 08:07:28 +00:00
local Font = require ( " ui/font " )
local Geom = require ( " ui/geometry " )
2017-12-17 17:27:24 +00:00
local InfoMessage = require ( " ui/widget/infomessage " )
local ImageWidget = require ( " ui/widget/imagewidget " )
2019-07-19 03:52:12 +00:00
local Math = require ( " optmath " )
2020-06-06 17:23:16 +00:00
local OverlapGroup = require ( " ui/widget/overlapgroup " )
2017-12-17 17:27:24 +00:00
local ScreenSaverWidget = require ( " ui/widget/screensaverwidget " )
2022-01-02 22:13:19 +00:00
local SpinWidget = require ( " ui/widget/spinwidget " )
2020-02-22 08:07:28 +00:00
local TextBoxWidget = require ( " ui/widget/textboxwidget " )
local TopContainer = require ( " ui/widget/container/topcontainer " )
2017-09-05 07:01:46 +00:00
local UIManager = require ( " ui/uimanager " )
2022-10-08 21:56:37 +00:00
local VerticalGroup = require ( " ui/widget/verticalgroup " )
local VerticalSpan = require ( " ui/widget/verticalspan " )
2022-06-11 17:30:16 +00:00
local ffiUtil = require ( " ffi/util " )
2017-12-17 17:27:24 +00:00
local lfs = require ( " libs/libkoreader-lfs " )
2016-12-29 08:10:38 +00:00
local logger = require ( " logger " )
2022-06-11 17:30:16 +00:00
local util = require ( " util " )
2017-12-17 17:27:24 +00:00
local _ = require ( " gettext " )
2017-09-05 07:01:46 +00:00
local Screen = Device.screen
2022-06-11 17:30:16 +00:00
local T = ffiUtil.template
2014-08-22 07:05:00 +00:00
2021-04-04 01:28:45 +00:00
-- 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
2021-04-19 00:20:46 +00:00
if G_reader_settings : hasNot ( " screensaver_hide_fallback_msg " ) then
G_reader_settings : makeFalse ( " screensaver_hide_fallback_msg " )
end
2021-03-06 21:44:18 +00:00
local Screensaver = {
screensaver_provider = {
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
gif = true ,
2021-03-06 21:44:18 +00:00
jpg = true ,
jpeg = true ,
png = true ,
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
svg = true ,
2021-03-06 21:44:18 +00:00
tif = true ,
tiff = true ,
Clarify our OOP semantics across the codebase (#9586)
Basically:
* Use `extend` for class definitions
* Use `new` for object instantiations
That includes some minor code cleanups along the way:
* Updated `Widget`'s docs to make the semantics clearer.
* Removed `should_restrict_JIT` (it's been dead code since https://github.com/koreader/android-luajit-launcher/pull/283)
* Minor refactoring of LuaSettings/LuaData/LuaDefaults/DocSettings to behave (mostly, they are instantiated via `open` instead of `new`) like everything else and handle inheritance properly (i.e., DocSettings is now a proper LuaSettings subclass).
* Default to `WidgetContainer` instead of `InputContainer` for stuff that doesn't actually setup key/gesture events.
* Ditto for explicit `*Listener` only classes, make sure they're based on `EventListener` instead of something uselessly fancier.
* Unless absolutely necessary, do not store references in class objects, ever; only values. Instead, always store references in instances, to avoid both sneaky inheritance issues, and sneaky GC pinning of stale references.
* ReaderUI: Fix one such issue with its `active_widgets` array, with critical implications, as it essentially pinned *all* of ReaderUI's modules, including their reference to the `Document` instance (i.e., that was a big-ass leak).
* Terminal: Make sure the shell is killed on plugin teardown.
* InputText: Fix Home/End/Del physical keys to behave sensibly.
* InputContainer/WidgetContainer: If necessary, compute self.dimen at paintTo time (previously, only InputContainers did, which might have had something to do with random widgets unconcerned about input using it as a baseclass instead of WidgetContainer...).
* OverlapGroup: Compute self.dimen at *init* time, because for some reason it needs to do that, but do it directly in OverlapGroup instead of going through a weird WidgetContainer method that it was the sole user of.
* ReaderCropping: Under no circumstances should a Document instance member (here, self.bbox) risk being `nil`ed!
* Kobo: Minor code cleanups.
2022-10-06 00:14:48 +00:00
webp = true ,
2021-03-06 21:44:18 +00:00
} ,
default_screensaver_message = _ ( " Sleeping " ) ,
2022-10-08 21:27:01 +00:00
-- 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 ,
2018-09-30 18:29:43 +00:00
}
2016-04-15 00:42:54 +00:00
2022-07-02 18:20:35 +00:00
-- 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
2021-03-06 21:44:18 +00:00
function Screensaver : _getRandomImage ( dir )
if not dir then
return nil
end
2016-04-15 00:42:54 +00:00
if string.sub ( dir , string.len ( dir ) ) ~= " / " then
dir = dir .. " / "
end
2014-08-22 07:05:00 +00:00
local pics = { }
local i = 0
2014-08-26 10:10:26 +00:00
math.randomseed ( os.time ( ) )
2018-06-16 21:05:20 +00:00
local ok , iter , dir_obj = pcall ( lfs.dir , dir )
if ok then
2022-06-11 17:30:16 +00:00
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 " " )
2021-03-06 21:44:18 +00:00
if self.screensaver_provider [ extension ] then
2018-06-16 21:05:20 +00:00
i = i + 1
2022-06-11 17:30:16 +00:00
pics [ i ] = f
2018-06-16 21:05:20 +00:00
end
2014-08-22 07:05:00 +00:00
end
end
2018-06-16 21:05:20 +00:00
if i == 0 then
return nil
end
else
2016-11-17 13:12:31 +00:00
return nil
end
2022-06-12 18:24:52 +00:00
2017-12-17 17:27:24 +00:00
return dir .. pics [ math.random ( i ) ]
2016-04-15 20:21:39 +00:00
end
2021-06-29 23:45:34 +00:00
-- 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 = util.secondsToClockDuration ( user_duration_format , pages * average_time_per_page , true )
end
return sec
end
function Screensaver : expandSpecial ( message , fallback )
2021-03-06 21:44:18 +00:00
-- 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
2021-06-29 23:45:34 +00:00
-- %h time left in chapter
-- %H time left in document
2021-03-06 21:44:18 +00:00
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 " )
2021-06-29 23:45:34 +00:00
local time_left_chapter = _ ( " N/A " )
local time_left_document = _ ( " N/A " )
2021-03-06 21:44:18 +00:00
2021-03-07 22:08:23 +00:00
local ReaderUI = require ( " apps/reader/readerui " )
local ui = ReaderUI : _getRunningInstance ( )
2021-05-18 16:20:34 +00:00
if ui and ui.document then
2021-03-07 22:08:23 +00:00
-- If we have a ReaderUI instance, use it.
local doc = ui.document
currentpage = ui.view . state.page or currentpage
totalpages = doc : getPageCount ( ) or totalpages
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
2021-06-29 23:45:34 +00:00
time_left_chapter = self : _calcAverageTimeForPages ( ui.toc : getChapterPagesLeft ( currentpage ) or doc : getTotalPagesLeft ( currentpage ) )
time_left_document = self : _calcAverageTimeForPages ( doc : getTotalPagesLeft ( currentpage ) )
2021-03-07 22:08:23 +00:00
elseif DocSettings : hasSidecarFile ( lastfile ) then
2021-03-06 21:44:18 +00:00
-- 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
2021-06-29 23:45:34 +00:00
-- Unable to set time_left_chapter and time_left_document without ReaderUI, so leave N/A
2021-03-06 21:44:18 +00:00
end
2021-06-29 23:45:34 +00:00
local replace = {
[ " %c " ] = currentpage ,
[ " %t " ] = totalpages ,
[ " %p " ] = percent ,
[ " %T " ] = title ,
[ " %A " ] = authors ,
[ " %S " ] = series ,
[ " %h " ] = time_left_chapter ,
[ " %H " ] = time_left_document ,
}
ret = ret : gsub ( " (%%%a) " , replace )
2021-03-06 21:44:18 +00:00
return ret
end
2022-10-08 21:56:37 +00:00
local function addOverlayMessage ( widget , widget_height , text )
2021-03-06 21:44:18 +00:00
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 ,
}
2022-10-08 21:56:37 +00:00
-- 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
2021-03-06 21:44:18 +00:00
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
2017-12-17 17:27:24 +00:00
function Screensaver : chooseFolder ( )
local buttons = { }
2022-09-27 23:10:50 +00:00
local choose_dialog
2017-12-17 17:27:24 +00:00
table.insert ( buttons , {
{
2021-02-22 17:44:16 +00:00
text = _ ( " Choose screensaver folder " ) ,
2017-12-17 17:27:24 +00:00
callback = function ( )
2022-09-27 23:10:50 +00:00
UIManager : close ( choose_dialog )
2017-12-17 17:27:24 +00:00
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 {
2021-03-28 11:35:56 +00:00
text = T ( _ ( " Screensaver folder set to: \n %1 " ) , BD.dirpath ( path ) ) ,
2017-12-17 17:27:24 +00:00
timeout = 3 ,
} )
end ,
} : chooseDir ( )
end ,
}
} )
table.insert ( buttons , {
{
text = _ ( " Close " ) ,
callback = function ( )
2022-09-27 23:10:50 +00:00
UIManager : close ( choose_dialog )
2017-12-17 17:27:24 +00:00
end ,
}
} )
local screensaver_dir = G_reader_settings : readSetting ( " screensaver_dir " )
2022-06-12 18:24:52 +00:00
or _ ( " N/A " )
2022-09-27 23:10:50 +00:00
choose_dialog = ButtonDialogTitle : new {
2021-02-22 17:44:16 +00:00
title = T ( _ ( " Current screensaver image folder: \n %1 " ) , BD.dirpath ( screensaver_dir ) ) ,
2017-12-17 17:27:24 +00:00
buttons = buttons
}
2022-09-27 23:10:50 +00:00
UIManager : show ( choose_dialog )
2016-08-01 01:26:26 +00:00
end
2019-09-16 13:27:49 +00:00
function Screensaver : chooseFile ( document_cover )
local text = document_cover and _ ( " Choose document cover " ) or _ ( " Choose screensaver image " )
2018-07-23 08:37:24 +00:00
local buttons = { }
2022-09-27 23:10:50 +00:00
local choose_dialog
2018-07-23 08:37:24 +00:00
table.insert ( buttons , {
{
2019-09-16 13:27:49 +00:00
text = text ,
2018-07-23 08:37:24 +00:00
callback = function ( )
2022-09-27 23:10:50 +00:00
UIManager : close ( choose_dialog )
2019-03-15 13:14:53 +00:00
local PathChooser = require ( " ui/widget/pathchooser " )
local path_chooser = PathChooser : new {
select_directory = false ,
2018-07-23 08:37:24 +00:00
file_filter = function ( filename )
local suffix = util.getFileNameSuffix ( filename )
2019-09-16 13:27:49 +00:00
if document_cover and DocumentRegistry : hasProvider ( filename ) then
return true
2021-03-06 21:44:18 +00:00
elseif self.screensaver_provider [ suffix ] then
2018-07-23 08:37:24 +00:00
return true
end
end ,
2019-03-15 13:14:53 +00:00
path = self.root_path ,
onConfirm = function ( file_path )
2019-09-16 13:27:49 +00:00
if document_cover then
G_reader_settings : saveSetting ( " screensaver_document_cover " , file_path )
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " Screensaver document cover set to: \n %1 " ) , BD.filepath ( file_path ) ) ,
2019-09-16 13:27:49 +00:00
timeout = 3 ,
} )
else
G_reader_settings : saveSetting ( " screensaver_image " , file_path )
UIManager : show ( InfoMessage : new {
2020-01-04 00:18:51 +00:00
text = T ( _ ( " Screensaver image set to: \n %1 " ) , BD.filepath ( file_path ) ) ,
2019-09-16 13:27:49 +00:00
timeout = 3 ,
} )
end
2019-03-15 13:14:53 +00:00
end
2018-07-23 08:37:24 +00:00
}
2019-03-15 13:14:53 +00:00
UIManager : show ( path_chooser )
2018-07-23 08:37:24 +00:00
end ,
}
} )
table.insert ( buttons , {
{
text = _ ( " Close " ) ,
callback = function ( )
2022-09-27 23:10:50 +00:00
UIManager : close ( choose_dialog )
2018-07-23 08:37:24 +00:00
end ,
}
} )
local screensaver_image = G_reader_settings : readSetting ( " screensaver_image " )
2022-06-12 18:24:52 +00:00
or _ ( " N/A " )
2019-09-16 13:27:49 +00:00
local screensaver_document_cover = G_reader_settings : readSetting ( " screensaver_document_cover " )
2022-06-12 18:24:52 +00:00
or _ ( " N/A " )
2020-01-04 00:18:51 +00:00
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 ) )
2022-09-27 23:10:50 +00:00
choose_dialog = ButtonDialogTitle : new {
2019-09-16 13:27:49 +00:00
title = title ,
2018-07-23 08:37:24 +00:00
buttons = buttons
}
2022-09-27 23:10:50 +00:00
UIManager : show ( choose_dialog )
2018-07-23 08:37:24 +00:00
end
2021-03-06 21:44:18 +00:00
function Screensaver : isExcluded ( )
2021-03-07 22:08:23 +00:00
local ReaderUI = require ( " apps/reader/readerui " )
local ui = ReaderUI : _getRunningInstance ( )
2021-05-18 16:20:34 +00:00
if ui and ui.doc_settings then
2021-03-07 22:08:23 +00:00
local doc_settings = ui.doc_settings
2021-03-06 21:44:18 +00:00
return doc_settings : isTrue ( " exclude_screensaver " )
else
2021-03-07 22:08:23 +00:00
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
2017-12-17 17:27:24 +00:00
end
end
function Screensaver : setMessage ( )
local InputDialog = require ( " ui/widget/inputdialog " )
local screensaver_message = G_reader_settings : readSetting ( " screensaver_message " )
2021-03-06 21:44:18 +00:00
or self.default_screensaver_message
2022-09-27 23:10:50 +00:00
local input_dialog
input_dialog = InputDialog : new {
2017-12-17 17:27:24 +00:00
title = " Screensaver message " ,
2021-06-29 23:45:34 +00:00
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 " ) ,
2017-12-17 17:27:24 +00:00
input = screensaver_message ,
buttons = {
{
{
text = _ ( " Cancel " ) ,
2022-03-04 20:20:00 +00:00
id = " close " ,
2017-12-17 17:27:24 +00:00
callback = function ( )
2022-09-27 23:10:50 +00:00
UIManager : close ( input_dialog )
2017-12-17 17:27:24 +00:00
end ,
} ,
{
text = _ ( " Set message " ) ,
is_enter_default = true ,
callback = function ( )
2022-09-27 23:10:50 +00:00
G_reader_settings : saveSetting ( " screensaver_message " , input_dialog : getInputText ( ) )
UIManager : close ( input_dialog )
2017-12-17 17:27:24 +00:00
end ,
} ,
} ,
} ,
}
2022-09-27 23:10:50 +00:00
UIManager : show ( input_dialog )
input_dialog : onShowKeyboard ( )
2014-08-22 07:05:00 +00:00
end
2022-01-02 22:13:19 +00:00
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 ,
2022-05-23 22:25:50 +00:00
default_value = 8 ,
unit = " % " ,
2022-01-02 22:13:19 +00:00
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
2021-03-06 21:44:18 +00:00
-- 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
2020-06-06 17:23:16 +00:00
2021-03-06 21:44:18 +00:00
function Screensaver : modeExpectsPortrait ( )
return self.screensaver_type ~= " message "
and self.screensaver_type ~= " disable "
and self.screensaver_type ~= " readingprogress "
and self.screensaver_type ~= " bookstatus "
end
2020-06-06 17:23:16 +00:00
2021-03-06 21:44:18 +00:00
function Screensaver : modeIsImage ( )
return self.screensaver_type == " cover "
or self.screensaver_type == " random_image "
or self.screensaver_type == " image_file "
end
2020-06-06 17:23:16 +00:00
2021-03-06 21:44:18 +00:00
function Screensaver : withBackground ( )
return self.screensaver_background ~= " none "
end
2020-06-06 17:23:16 +00:00
2022-10-08 21:27:01 +00:00
function Screensaver : setup ( event , event_message )
2021-04-04 01:28:45 +00:00
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 " )
2021-01-10 00:51:32 +00:00
2021-03-06 21:44:18 +00:00
-- 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_"
2022-10-08 21:27:01 +00:00
self.event_message = event_message
2021-03-06 21:44:18 +00:00
if G_reader_settings : has ( self.prefix .. " screensaver_type " ) then
self.screensaver_type = G_reader_settings : readSetting ( self.prefix .. " screensaver_type " )
else
2021-04-19 00:20:46 +00:00
if event and G_reader_settings : isFalse ( " screensaver_hide_fallback_msg " ) then
2022-10-08 21:27:01 +00:00
-- Display the provided event_message over the screensaver,
2021-03-06 21:44:18 +00:00
-- so the user can distinguish between suspend (no overlay),
-- and reboot/poweroff (overlaid message).
2022-10-08 21:27:01 +00:00
self.overlay_message = self.event_message
2021-01-10 00:51:32 +00:00
end
2019-03-14 19:58:45 +00:00
end
2020-06-06 17:23:16 +00:00
2021-03-06 21:44:18 +00:00
-- Check lastfile and setup the requested mode's resources, or a fallback mode if the required resources are unavailable.
2021-03-07 22:08:23 +00:00
local ReaderUI = require ( " apps/reader/readerui " )
local ui = ReaderUI : _getRunningInstance ( )
2022-09-27 23:10:50 +00:00
local lastfile = G_reader_settings : readSetting ( " lastfile " )
2021-03-06 21:44:18 +00:00
if self.screensaver_type == " document_cover " then
2019-09-16 13:27:49 +00:00
-- Set lastfile to the document of which we want to show the cover.
2022-09-27 23:10:50 +00:00
lastfile = G_reader_settings : readSetting ( " screensaver_document_cover " )
2021-03-06 21:44:18 +00:00
self.screensaver_type = " cover "
2019-09-16 13:27:49 +00:00
end
2021-03-06 21:44:18 +00:00
if self.screensaver_type == " cover " then
2022-09-27 23:10:50 +00:00
lastfile = lastfile ~= nil and lastfile or G_reader_settings : readSetting ( " lastfile " )
2021-03-06 21:44:18 +00:00
local excluded
2022-09-27 23:10:50 +00:00
if DocSettings : hasSidecarFile ( lastfile ) then
2021-03-07 22:08:23 +00:00
local doc_settings
2021-05-18 16:20:34 +00:00
if ui and ui.doc_settings then
2021-03-07 22:08:23 +00:00
doc_settings = ui.doc_settings
else
2022-09-27 23:10:50 +00:00
doc_settings = DocSettings : open ( lastfile )
2021-03-07 22:08:23 +00:00
end
2021-03-06 21:44:18 +00:00
excluded = doc_settings : isTrue ( " exclude_screensaver " )
else
-- No DocSetting, not excluded
excluded = false
2014-09-17 10:24:33 +00:00
end
2021-03-06 21:44:18 +00:00
if not excluded then
2022-09-27 23:10:50 +00:00
if lastfile and lfs.attributes ( lastfile , " mode " ) == " file " then
2021-05-18 16:20:34 +00:00
if ui and ui.document then
2021-03-07 22:08:23 +00:00
local doc = ui.document
self.image = doc : getCoverPageImage ( )
else
2022-09-27 23:10:50 +00:00
local doc = DocumentRegistry : openDocument ( lastfile )
2021-03-07 22:08:23 +00:00
if doc.loadDocument then -- CreDocument
doc : loadDocument ( false ) -- load only metadata
end
self.image = doc : getCoverPageImage ( )
doc : close ( )
2018-11-20 20:07:59 +00:00
end
2021-03-06 21:44:18 +00:00
if self.image == nil then
self.screensaver_type = " random_image "
2016-11-16 10:49:34 +00:00
end
2017-12-17 17:27:24 +00:00
else
2021-03-06 21:44:18 +00:00
self.screensaver_type = " random_image "
2014-08-26 10:10:26 +00:00
end
2021-03-06 21:44:18 +00:00
else
-- Fallback to random images if this book cover is excluded
self.screensaver_type = " random_image "
2014-08-26 10:10:26 +00:00
end
end
2021-03-06 21:44:18 +00:00
if self.screensaver_type == " bookstatus " then
2022-09-27 23:10:50 +00:00
if lastfile and lfs.attributes ( lastfile , " mode " ) == " file " then
2021-03-07 22:08:23 +00:00
if not ui then
2021-03-06 21:44:18 +00:00
self.screensaver_type = " disable "
self.show_message = true
2017-12-17 17:27:24 +00:00
end
else
2021-03-06 21:44:18 +00:00
self.screensaver_type = " disable "
self.show_message = true
2017-04-08 21:21:03 +00:00
end
2017-12-17 17:27:24 +00:00
end
2021-03-06 21:44:18 +00:00
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
2017-12-17 17:27:24 +00:00
end
end
2021-03-06 21:44:18 +00:00
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
2018-07-23 08:37:24 +00:00
end
end
2021-03-06 21:44:18 +00:00
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
2017-12-17 17:27:24 +00:00
end
end
2020-06-06 17:23:16 +00:00
2021-04-04 01:28:45 +00:00
-- 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
2021-03-06 21:44:18 +00:00
end
end
function Screensaver : show ( )
if self.screensaver_widget then
UIManager : close ( self.screensaver_widget )
self.screensaver_widget = nil
end
-- 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
2022-04-02 16:21:40 +00:00
-- 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
2021-03-06 21:44:18 +00:00
-- 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 ( ) ,
2021-07-07 17:07:36 +00:00
height = Screen : getHeight ( ) ,
2021-04-04 01:28:45 +00:00
scale_factor = G_reader_settings : isFalse ( " screensaver_stretch_images " ) and 0 or nil ,
2022-01-02 22:13:19 +00:00
stretch_limit_percentage = G_reader_settings : readSetting ( " screensaver_stretch_limit_percentage " ) ,
2021-03-06 21:44:18 +00:00
}
elseif self.screensaver_type == " bookstatus " then
2021-03-07 22:08:23 +00:00
local ReaderUI = require ( " apps/reader/readerui " )
local ui = ReaderUI : _getRunningInstance ( )
local doc = ui.document
local doc_settings = ui.doc_settings
2021-03-06 21:44:18 +00:00
widget = BookStatusWidget : new {
thumbnail = doc : getCoverPageImage ( ) ,
props = doc : getProps ( ) ,
document = doc ,
settings = doc_settings ,
2021-10-17 14:52:34 +00:00
ui = ui ,
2021-03-06 21:44:18 +00:00
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 ( ) ,
2021-07-07 17:07:36 +00:00
height = Screen : getHeight ( ) ,
2021-04-04 01:28:45 +00:00
scale_factor = G_reader_settings : isFalse ( " screensaver_stretch_images " ) and 0 or nil ,
2022-01-02 22:13:19 +00:00
stretch_limit_percentage = G_reader_settings : readSetting ( " screensaver_stretch_limit_percentage " ) ,
2021-03-06 21:44:18 +00:00
}
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
2022-10-08 21:56:37 +00:00
local message_height
2021-03-06 21:44:18 +00:00
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
2022-10-08 21:27:01 +00:00
-- 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
2021-03-06 21:44:18 +00:00
end
2017-12-17 17:27:24 +00:00
end
2021-03-06 21:44:18 +00:00
-- NOTE: Only attempt to expand if there are special characters in the message.
if screensaver_message : find ( " %% " ) then
2022-10-08 21:27:01 +00:00
screensaver_message = self : expandSpecial ( screensaver_message , self.event_message or self.default_screensaver_message )
2018-07-23 08:37:24 +00:00
end
2019-07-18 12:11:21 +00:00
2021-03-06 21:44:18 +00:00
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 " )
2019-07-18 12:11:21 +00:00
else
2021-04-04 01:28:45 +00:00
message_pos = G_reader_settings : readSetting ( " screensaver_message_position " )
2017-12-17 17:27:24 +00:00
end
2019-07-18 12:11:21 +00:00
2021-03-06 21:44:18 +00:00
-- 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
2020-06-06 17:23:16 +00:00
local message_widget
2021-03-06 21:44:18 +00:00
if message_pos == " middle " then
2020-06-06 17:23:16 +00:00
message_widget = InfoMessage : new {
2020-02-22 08:07:28 +00:00
text = screensaver_message ,
readonly = true ,
2022-07-11 11:50:28 +00:00
dismissable = false ,
2020-02-22 08:07:28 +00:00
}
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 ( )
2020-06-06 17:23:16 +00:00
message_widget = container : new {
2020-02-22 08:07:28 +00:00
dimen = Geom : new {
w = screen_w ,
h = screen_h ,
} ,
TextBoxWidget : new {
text = screensaver_message ,
face = face ,
width = screen_w ,
alignment = " center " ,
}
}
2022-10-08 21:56:37 +00:00
-- Forward the height of the top message to the overlay widget
if message_pos == " top " then
message_height = message_widget [ 1 ] : getSize ( ) . h
end
2020-02-22 08:07:28 +00:00
end
2020-06-06 17:23:16 +00:00
2021-03-06 21:44:18 +00:00
-- Check if message_widget should be overlaid on another widget
2020-06-06 17:23:16 +00:00
if message_widget then
2021-03-06 21:44:18 +00:00
if widget then -- We have a Screensaver widget
-- Show message_widget on top of previously created widget
2020-06-06 17:23:16 +00:00
widget = OverlapGroup : new {
dimen = {
2021-07-07 17:07:36 +00:00
w = Screen : getWidth ( ) ,
h = Screen : getHeight ( ) ,
2020-06-06 17:23:16 +00:00
} ,
widget ,
message_widget ,
}
else
2021-03-06 21:44:18 +00:00
-- No prevously created widget, so just show message widget
2020-06-06 17:23:16 +00:00
widget = message_widget
end
end
2018-01-21 18:44:12 +00:00
end
2021-03-06 21:44:18 +00:00
if self.overlay_message then
2022-10-08 21:56:37 +00:00
widget = addOverlayMessage ( widget , message_height , self.overlay_message )
2017-12-17 17:27:24 +00:00
end
if widget then
2021-03-06 21:44:18 +00:00
self.screensaver_widget = ScreenSaverWidget : new {
2017-12-17 17:27:24 +00:00
widget = widget ,
background = background ,
2018-03-17 22:02:32 +00:00
covers_fullscreen = covers_fullscreen ,
2017-12-17 17:27:24 +00:00
}
2021-03-06 21:44:18 +00:00
self.screensaver_widget . modal = true
self.screensaver_widget . dithered = true
2022-09-27 23:10:50 +00:00
-- 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
2021-03-06 21:44:18 +00:00
UIManager : show ( self.screensaver_widget , " full " )
2014-08-26 10:10:26 +00:00
end
end
2022-04-02 16:21:40 +00:00
function Screensaver : close_widget ( )
if self.screensaver_widget then
UIManager : close ( self.screensaver_widget )
end
end
2021-03-06 21:44:18 +00:00
function Screensaver : close ( )
if self.screensaver_widget == nil then
return
2019-07-18 12:11:21 +00:00
end
2017-12-17 17:27:24 +00:00
local screensaver_delay = G_reader_settings : readSetting ( " screensaver_delay " )
local screensaver_delay_number = tonumber ( screensaver_delay )
if screensaver_delay_number then
2022-04-02 16:21:40 +00:00
UIManager : scheduleIn ( screensaver_delay_number , self.close_widget , self )
self.delayed_close = true
2021-04-04 01:28:45 +00:00
elseif screensaver_delay == " disable " then
2022-04-02 16:21:40 +00:00
self : close_widget ( )
2022-08-11 14:45:49 +00:00
-- 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
2022-07-11 11:50:28 +00:00
elseif screensaver_delay == " gesture " then
if self.screensaver_widget then
self.screensaver_widget : showWaitForGestureMessage ( )
end
2017-12-17 17:27:24 +00:00
else
logger.dbg ( " tap to exit from screensaver " )
2014-08-26 10:10:26 +00:00
end
end
2022-09-27 23:10:50 +00:00
function Screensaver : cleanup ( )
self.show_message = nil
self.screensaver_type = nil
self.prefix = nil
2022-10-08 21:27:01 +00:00
self.event_message = nil
2022-09-27 23:10:50 +00:00
self.overlay_message = nil
self.screensaver_background = nil
self.image = nil
self.image_file = nil
self.delayed_close = nil
self.screensaver_widget = nil
end
2014-08-26 10:10:26 +00:00
return Screensaver