Files
koreader/plugins/autostandby.koplugin/main.lua
NiLuJe fadee1f5dc 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 02:14:48 +02:00

170 lines
6.7 KiB
Lua

local Device = require("device")
if not Device:isPocketBook() --[[and not Device:isKobo()]] then
return { disabled = true }
end
local PowerD = Device:getPowerDevice()
local DataStorage = require("datastorage")
local LuaSettings = require("luasettings")
local UIManager = require("ui/uimanager")
local WidgetContainer = require("ui/widget/container/widgetcontainer")
local SpinWidget = require("ui/widget/spinwidget")
local logger = require("logger")
local _ = require("gettext")
local AutoStandby = WidgetContainer:extend{
is_doc_only = false,
name = "autostandby",
-- static for all plugin instances
settings = LuaSettings:open(DataStorage:getSettingsDir() .. "/autostandby.lua"),
delay = 0,
lastInput = 0,
preventing = false,
}
function AutoStandby:init()
logger.dbg("AutoStandby:init() instance=", tostring(self))
if not self.settings:has("filter") then
logger.dbg("AutoStandby: No settings found, initializing defaults")
self.settings.data = {
forbidden = false, -- If forbidden, standby is never allowed to occur
filter = 1, -- Consider input only further than this many seconds apart
min = 1, -- Initial delay period during which we won't standby
mul = 1.5, -- Multiply the delay with each subsequent input that happens, scales up to max
max = 30, -- Input that happens further than 30 seconds since last input one reset delay back to 'min'
win = 5, -- Additional time window to consider input contributing to standby delay
bat = 60, -- If battery is below this percent, make auto-standby aggressive again (disables scaling by mul)
}
self.settings:flush()
end
UIManager.event_hook:registerWidget("InputEvent", self)
self.ui.menu:registerToMainMenu(self)
end
function AutoStandby:onCloseWidget()
logger.dbg("AutoStandby:onCloseWidget() instance=", tostring(self))
UIManager:unschedule(AutoStandby.allow)
end
function AutoStandby:addToMainMenu(menu_items)
menu_items.autostandby = {
sorting_hint = "device",
text = _("Auto-standby settings"),
sub_item_table = {
{
keep_menu_open = true,
text = _("Allow auto-standby"),
checked_func = function() return self:isAllowedByConfig() end,
callback = function() self.settings:saveSetting("forbidden", self:isAllowedByConfig()):flush() end,
},
self:genSpinMenuItem(_("Min input idle seconds"), "min", function() return 0 end, function() return self.settings:readSetting("max") end),
self:genSpinMenuItem(_("Max input idle seconds"), "max", function() return 0 end),
self:genSpinMenuItem(_("Input window seconds"), "win", function() return 0 end, function() return self.settings:readSetting("max") end),
self:genSpinMenuItem(_("Always standby if battery below"), "bat", function() return 0 end, function() return 100 end),
}
}
end
-- We've received touch/key event, so delay standby accordingly
function AutoStandby:onInputEvent()
logger.dbg("AutoStandby:onInputevent() instance=", tostring(self))
local config = self.settings.data
local t = os.time()
if t < AutoStandby.lastInput + config.filter then
-- packed too close together, ignore
logger.dbg("AutoStandby: input packed too close to previous one, ignoring")
return
end
-- Nuke past timer as we'll reschedule the allow (or not)
UIManager:unschedule(AutoStandby.allow)
if PowerD:getCapacityHW() <= config.bat then
-- battery is below threshold, so allow standby aggressively
logger.dbg("AutoStandby: battery below threshold, enabling aggressive standby")
self:allow()
return
elseif t > AutoStandby.lastInput + config.max then
-- too far apart, so reset delay
logger.dbg("AutoStandby: input too far in future, resetting adaptive standby delay from", AutoStandby.delay, "to", config.min)
AutoStandby.delay = config.min
elseif t < AutoStandby.lastInput + AutoStandby.delay + config.win then
-- otherwise widen the delay - "adaptive" - with frequent inputs, but don't grow beyonnd the max
AutoStandby.delay = math.min((AutoStandby.delay+1) * config.mul, config.max)
logger.dbg("AutoStandby: increasing standby delay to", AutoStandby.delay)
end -- equilibrium: when the event arrives beyond delay + win, but still below max, we keep the delay as-is
AutoStandby.lastInput = t
if not self:isAllowedByConfig() then
-- all standbys forbidden, always prevent
self:prevent()
return
elseif AutoStandby.delay == 0 then
-- If delay is 0 now, just allow straight
self:allow()
return
end
-- otherwise prevent for a while for duration of the delay
self:prevent()
-- and schedule standby re-enable once delay expires
UIManager:scheduleIn(AutoStandby.delay, AutoStandby.allow, AutoStandby)
end
-- Prevent standby (by timer)
function AutoStandby:prevent()
if not AutoStandby.preventing then
AutoStandby.preventing = true
UIManager:preventStandby()
end
end
-- Allow standby (by timer)
function AutoStandby:allow()
if AutoStandby.preventing then
AutoStandby.preventing = false
UIManager:allowStandby()
end
end
function AutoStandby:isAllowedByConfig()
return self.settings:isFalse("forbidden")
end
function AutoStandby:genSpinMenuItem(text, cfg, min, max)
return {
keep_menu_open = true,
text = text,
enabled_func = function() return self:isAllowedByConfig() end,
callback = function()
local spin = SpinWidget:new {
value = self.settings:readSetting(cfg),
value_min = min and min() or 0,
value_max = max and max() or 9999,
value_hold_step = 10,
ok_text = "Update",
title_text = text,
callback = function(spin) self.settings:saveSetting(cfg, spin.value):flush() end,
}
UIManager:show(spin)
end
}
end
-- koreader is merely waiting for user input right now.
-- UI signals us that standby is allowed at this very moment because nothing else goes on in the background.
function AutoStandby:onAllowStandby()
logger.dbg("AutoStandby: onAllowStandby()")
-- In case the OS frontend itself doesn't manage power state, we can do it on our own here.
-- One should also configure wake-up pins and perhaps wake alarm,
-- if we want to enter deeper sleep states later on from within standby.
--os.execute("echo mem > /sys/power/state")
end
return AutoStandby